summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/bin
ModeNameSize
-rw-r--r--compaction-times-record229logplain
-rw-r--r--compaction-times-report210logplain
-rw-r--r--event_analyzing_sample-record199logplain
-rw-r--r--event_analyzing_sample-report129logplain
-rw-r--r--export-to-postgresql-record205logplain
-rw-r--r--export-to-postgresql-report669logplain
-rw-r--r--export-to-sqlite-record202logplain
-rw-r--r--export-to-sqlite-report658logplain
-rw-r--r--failed-syscalls-by-pid-record109logplain
-rw-r--r--failed-syscalls-by-pid-report258logplain
-rw-r--r--futex-contention-record82logplain
-rw-r--r--futex-contention-report129logplain
-rw-r--r--intel-pt-events-record425logplain
-rw-r--r--intel-pt-events-report136logplain
-rw-r--r--mem-phys-addr-record464logplain
-rw-r--r--mem-phys-addr-report128logplain
-rwxr-xr-xnet_dropmonitor-record44logplain
-rwxr-xr-xnet_dropmonitor-report132logplain
-rw-r--r--netdev-times-record334logplain
-rw-r--r--netdev-times-report176logplain
-rw-r--r--powerpc-hcalls-record73logplain
-rw-r--r--powerpc-hcalls-report81logplain
-rw-r--r--sched-migration-record134logplain
-rw-r--r--sched-migration-report122logplain
-rw-r--r--sctop-record111logplain
-rw-r--r--sctop-report484logplain
-rwxr-xr-xstackcollapse-record190logplain
-rwxr-xr-xstackcollapse-report149logplain
-rw-r--r--syscall-counts-by-pid-record111logplain
-rw-r--r--syscall-counts-by-pid-report256logplain
-rw-r--r--syscall-counts-record111logplain
-rw-r--r--syscall-counts-report241logplain
s='mode'>-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus.h4
-rw-r--r--sound/aoa/soundbus/i2sbus/interface.h5
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c241
-rw-r--r--sound/aoa/soundbus/soundbus.h7
-rw-r--r--sound/aoa/soundbus/sysfs.c46
-rw-r--r--sound/arm/Kconfig7
-rw-r--r--sound/arm/Makefile7
-rw-r--r--sound/arm/aaci.c266
-rw-r--r--sound/arm/aaci.h5
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c178
-rw-r--r--sound/arm/pxa2xx-ac97-regs.h100
-rw-r--r--sound/arm/pxa2xx-ac97.c141
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c149
-rw-r--r--sound/arm/pxa2xx-pcm.c129
-rw-r--r--sound/arm/pxa2xx-pcm.h27
-rw-r--r--sound/atmel/Kconfig1
-rw-r--r--sound/atmel/Makefile3
-rw-r--r--sound/atmel/ac97c.c116
-rw-r--r--sound/atmel/ac97c.h5
-rw-r--r--sound/core/.kunitconfig5
-rw-r--r--sound/core/Kconfig131
-rw-r--r--sound/core/Makefile23
-rw-r--r--sound/core/compress_offload.c939
-rw-r--r--sound/core/control.c1475
-rw-r--r--sound/core/control_compat.c199
-rw-r--r--sound/core/control_led.c789
-rw-r--r--sound/core/ctljack.c8
-rw-r--r--sound/core/device.c28
-rw-r--r--sound/core/hrtimer.c50
-rw-r--r--sound/core/hwdep.c178
-rw-r--r--sound/core/hwdep_compat.c39
-rw-r--r--sound/core/info.c321
-rw-r--r--sound/core/info_oss.c34
-rw-r--r--sound/core/init.c663
-rw-r--r--sound/core/isadma.c58
-rw-r--r--sound/core/jack.c385
-rw-r--r--sound/core/memalloc.c1018
-rw-r--r--sound/core/memory.c92
-rw-r--r--sound/core/misc.c179
-rw-r--r--sound/core/oss/Makefile2
-rw-r--r--sound/core/oss/linear.c2
-rw-r--r--sound/core/oss/mixer_oss.c368
-rw-r--r--sound/core/oss/mulaw.c6
-rw-r--r--sound/core/oss/pcm_oss.c580
-rw-r--r--sound/core/oss/pcm_plugin.c172
-rw-r--r--sound/core/oss/pcm_plugin.h40
-rw-r--r--sound/core/oss/rate.c12
-rw-r--r--sound/core/oss/route.c2
-rw-r--r--sound/core/pcm.c427
-rw-r--r--sound/core/pcm_compat.c490
-rw-r--r--sound/core/pcm_dmaengine.c202
-rw-r--r--sound/core/pcm_drm_eld.c466
-rw-r--r--sound/core/pcm_iec958.c181
-rw-r--r--sound/core/pcm_lib.c532
-rw-r--r--sound/core/pcm_local.h43
-rw-r--r--sound/core/pcm_memory.c399
-rw-r--r--sound/core/pcm_misc.c98
-rw-r--r--sound/core/pcm_native.c2087
-rw-r--r--sound/core/pcm_timer.c22
-rw-r--r--sound/core/pcm_trace.h6
-rw-r--r--sound/core/rawmidi.c1033
-rw-r--r--sound/core/rawmidi_compat.c125
-rw-r--r--sound/core/seq/Kconfig14
-rw-r--r--sound/core/seq/Makefile15
-rw-r--r--sound/core/seq/oss/Makefile3
-rw-r--r--sound/core/seq/oss/seq_oss.c78
-rw-r--r--sound/core/seq/oss/seq_oss_device.h29
-rw-r--r--sound/core/seq/oss/seq_oss_event.c38
-rw-r--r--sound/core/seq/oss/seq_oss_event.h15
-rw-r--r--sound/core/seq/oss/seq_oss_init.c61
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c17
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c167
-rw-r--r--sound/core/seq/oss/seq_oss_midi.h15
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c25
-rw-r--r--sound/core/seq/oss/seq_oss_readq.h15
-rw-r--r--sound/core/seq/oss/seq_oss_rw.c35
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c314
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h18
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c17
-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.c50
-rw-r--r--sound/core/seq/seq_clientmgr.c1477
-rw-r--r--sound/core/seq/seq_clientmgr.h65
-rw-r--r--sound/core/seq/seq_compat.c31
-rw-r--r--sound/core/seq/seq_dummy.c56
-rw-r--r--sound/core/seq/seq_fifo.c94
-rw-r--r--sound/core/seq/seq_fifo.h20
-rw-r--r--sound/core/seq/seq_info.c27
-rw-r--r--sound/core/seq/seq_info.h23
-rw-r--r--sound/core/seq/seq_lock.c17
-rw-r--r--sound/core/seq/seq_memory.c203
-rw-r--r--sound/core/seq/seq_memory.h45
-rw-r--r--sound/core/seq/seq_midi.c115
-rw-r--r--sound/core/seq/seq_midi_emul.c75
-rw-r--r--sound/core/seq/seq_midi_event.c122
-rw-r--r--sound/core/seq/seq_ports.c246
-rw-r--r--sound/core/seq/seq_ports.h39
-rw-r--r--sound/core/seq/seq_prioq.c239
-rw-r--r--sound/core/seq/seq_prioq.h23
-rw-r--r--sound/core/seq/seq_queue.c272
-rw-r--r--sound/core/seq/seq_queue.h58
-rw-r--r--sound/core/seq/seq_system.c78
-rw-r--r--sound/core/seq/seq_system.h52
-rw-r--r--sound/core/seq/seq_timer.c213
-rw-r--r--sound/core/seq/seq_timer.h26
-rw-r--r--sound/core/seq/seq_ump_client.c539
-rw-r--r--sound/core/seq/seq_ump_convert.c1305
-rw-r--r--sound/core/seq/seq_ump_convert.h23
-rw-r--r--sound/core/seq/seq_virmidi.c221
-rw-r--r--sound/core/seq_device.c50
-rw-r--r--sound/core/sgbuf.c164
-rw-r--r--sound/core/sound.c70
-rw-r--r--sound/core/sound_kunit.c324
-rw-r--r--sound/core/sound_oss.c50
-rw-r--r--sound/core/timer.c1424
-rw-r--r--sound/core/timer_compat.c89
-rw-r--r--sound/core/ump.c1394
-rw-r--r--sound/core/ump_convert.c528
-rw-r--r--sound/core/vmaster.c344
-rw-r--r--sound/drivers/Kconfig78
-rw-r--r--sound/drivers/Makefile20
-rw-r--r--sound/drivers/aloop.c1027
-rw-r--r--sound/drivers/dummy.c235
-rw-r--r--sound/drivers/ml403-ac97cr.c1335
-rw-r--r--sound/drivers/mpu401/Makefile5
-rw-r--r--sound/drivers/mpu401/mpu401.c76
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c128
-rw-r--r--sound/drivers/mtpav.c156
-rw-r--r--sound/drivers/mts64.c148
-rw-r--r--sound/drivers/opl3/Makefile2
-rw-r--r--sound/drivers/opl3/opl3_drums.c46
-rw-r--r--sound/drivers/opl3/opl3_lib.c139
-rw-r--r--sound/drivers/opl3/opl3_midi.c192
-rw-r--r--sound/drivers/opl3/opl3_oss.c39
-rw-r--r--sound/drivers/opl3/opl3_seq.c54
-rw-r--r--sound/drivers/opl3/opl3_synth.c32
-rw-r--r--sound/drivers/opl3/opl3_voice.h21
-rw-r--r--sound/drivers/opl4/Makefile4
-rw-r--r--sound/drivers/opl4/opl4_lib.c49
-rw-r--r--sound/drivers/opl4/opl4_mixer.c25
-rw-r--r--sound/drivers/opl4/opl4_proc.c33
-rw-r--r--sound/drivers/opl4/opl4_seq.c32
-rw-r--r--sound/drivers/opl4/opl4_synth.c83
-rw-r--r--sound/drivers/opl4/yrw801.c2
-rw-r--r--sound/drivers/pcm-indirect2.c573
-rw-r--r--sound/drivers/pcm-indirect2.h140
-rw-r--r--sound/drivers/pcmtest.c780
-rw-r--r--sound/drivers/pcsp/Makefile3
-rw-r--r--sound/drivers/pcsp/pcsp.c89
-rw-r--r--sound/drivers/pcsp/pcsp_input.c22
-rw-r--r--sound/drivers/pcsp/pcsp_input.h1
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c72
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c10
-rw-r--r--sound/drivers/portman2x4.c93
-rw-r--r--sound/drivers/serial-generic.c376
-rw-r--r--sound/drivers/serial-u16550.c195
-rw-r--r--sound/drivers/virmidi.c55
-rw-r--r--sound/drivers/vx/Makefile3
-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.c222
-rw-r--r--sound/drivers/vx/vx_hwdep.c33
-rw-r--r--sound/drivers/vx/vx_mixer.c115
-rw-r--r--sound/drivers/vx/vx_pcm.c112
-rw-r--r--sound/drivers/vx/vx_uer.c35
-rw-r--r--sound/firewire/Kconfig55
-rw-r--r--sound/firewire/Makefile4
-rw-r--r--sound/firewire/amdtp-am824.c170
-rw-r--r--sound/firewire/amdtp-am824.h2
-rw-r--r--sound/firewire/amdtp-stream-trace.h173
-rw-r--r--sound/firewire/amdtp-stream.c1988
-rw-r--r--sound/firewire/amdtp-stream.h209
-rw-r--r--sound/firewire/bebob/Makefile2
-rw-r--r--sound/firewire/bebob/bebob.c337
-rw-r--r--sound/firewire/bebob/bebob.h34
-rw-r--r--sound/firewire/bebob/bebob_command.c39
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c6
-rw-r--r--sound/firewire/bebob/bebob_hwdep.c55
-rw-r--r--sound/firewire/bebob/bebob_maudio.c78
-rw-r--r--sound/firewire/bebob/bebob_midi.c86
-rw-r--r--sound/firewire/bebob/bebob_pcm.c176
-rw-r--r--sound/firewire/bebob/bebob_proc.c17
-rw-r--r--sound/firewire/bebob/bebob_stream.c626
-rw-r--r--sound/firewire/bebob/bebob_terratec.c3
-rw-r--r--sound/firewire/bebob/bebob_yamaha_terratec.c3
-rw-r--r--sound/firewire/cmp.c119
-rw-r--r--sound/firewire/cmp.h8
-rw-r--r--sound/firewire/dice/Makefile7
-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.c44
-rw-r--r--sound/firewire/dice/dice-interface.h9
-rw-r--r--sound/firewire/dice/dice-midi.c65
-rw-r--r--sound/firewire/dice/dice-mytek.c46
-rw-r--r--sound/firewire/dice/dice-pcm.c385
-rw-r--r--sound/firewire/dice/dice-presonus.c60
-rw-r--r--sound/firewire/dice/dice-proc.c75
-rw-r--r--sound/firewire/dice/dice-stream.c502
-rw-r--r--sound/firewire/dice/dice-tcelectronic.c104
-rw-r--r--sound/firewire/dice/dice-transaction.c61
-rw-r--r--sound/firewire/dice/dice-weiss.c104
-rw-r--r--sound/firewire/dice/dice.c420
-rw-r--r--sound/firewire/dice/dice.h43
-rw-r--r--sound/firewire/digi00x/Makefile3
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c132
-rw-r--r--sound/firewire/digi00x/digi00x-hwdep.c44
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c48
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c163
-rw-r--r--sound/firewire/digi00x/digi00x-proc.c21
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c310
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c11
-rw-r--r--sound/firewire/digi00x/digi00x.c128
-rw-r--r--sound/firewire/digi00x/digi00x.h13
-rw-r--r--sound/firewire/fcp.c25
-rw-r--r--sound/firewire/fireface/Makefile6
-rw-r--r--sound/firewire/fireface/amdtp-ff.c114
-rw-r--r--sound/firewire/fireface/ff-hwdep.c85
-rw-r--r--sound/firewire/fireface/ff-midi.c19
-rw-r--r--sound/firewire/fireface/ff-pcm.c201
-rw-r--r--sound/firewire/fireface/ff-proc.c45
-rw-r--r--sound/firewire/fireface/ff-protocol-ff400.c371
-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.c278
-rw-r--r--sound/firewire/fireface/ff-transaction.c128
-rw-r--r--sound/firewire/fireface/ff.c227
-rw-r--r--sound/firewire/fireface/ff.h73
-rw-r--r--sound/firewire/fireworks/Makefile3
-rw-r--r--sound/firewire/fireworks/fireworks.c201
-rw-r--r--sound/firewire/fireworks/fireworks.h28
-rw-r--r--sound/firewire/fireworks/fireworks_command.c19
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c53
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c86
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c157
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c17
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c359
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c42
-rw-r--r--sound/firewire/isight.c73
-rw-r--r--sound/firewire/iso-resources.c68
-rw-r--r--sound/firewire/lib.c36
-rw-r--r--sound/firewire/lib.h3
-rw-r--r--sound/firewire/motu/Makefile6
-rw-r--r--sound/firewire/motu/amdtp-motu-trace.h72
-rw-r--r--sound/firewire/motu/amdtp-motu.c316
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c179
-rw-r--r--sound/firewire/motu/motu-hwdep.c167
-rw-r--r--sound/firewire/motu/motu-midi.c91
-rw-r--r--sound/firewire/motu/motu-pcm.c195
-rw-r--r--sound/firewire/motu/motu-proc.c41
-rw-r--r--sound/firewire/motu/motu-protocol-v1.c467
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c335
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c324
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c413
-rw-r--r--sound/firewire/motu/motu-stream.c382
-rw-r--r--sound/firewire/motu/motu-transaction.c10
-rw-r--r--sound/firewire/motu/motu.c202
-rw-r--r--sound/firewire/motu/motu.h208
-rw-r--r--sound/firewire/oxfw/Makefile3
-rw-r--r--sound/firewire/oxfw/oxfw-command.c9
-rw-r--r--sound/firewire/oxfw/oxfw-hwdep.c54
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c67
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c170
-rw-r--r--sound/firewire/oxfw/oxfw-proc.c17
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c8
-rw-r--r--sound/firewire/oxfw/oxfw-spkr.c7
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c641
-rw-r--r--sound/firewire/oxfw/oxfw.c382
-rw-r--r--sound/firewire/oxfw/oxfw.h65
-rw-r--r--sound/firewire/packets-buffer.c6
-rw-r--r--sound/firewire/tascam/Makefile3
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c169
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c157
-rw-r--r--sound/firewire/tascam/tascam-midi.c25
-rw-r--r--sound/firewire/tascam/tascam-pcm.c157
-rw-r--r--sound/firewire/tascam/tascam-proc.c17
-rw-r--r--sound/firewire/tascam/tascam-stream.c400
-rw-r--r--sound/firewire/tascam/tascam-transaction.c5
-rw-r--r--sound/firewire/tascam/tascam.c152
-rw-r--r--sound/firewire/tascam/tascam.h40
-rw-r--r--sound/hda/Kconfig34
-rw-r--r--sound/hda/Makefile20
-rw-r--r--sound/hda/codecs/Kconfig137
-rw-r--r--sound/hda/codecs/Makefile34
-rw-r--r--sound/hda/codecs/analog.c (renamed from sound/pci/hda/patch_analog.c)287
-rw-r--r--sound/hda/codecs/ca0110.c (renamed from sound/pci/hda/patch_ca0110.c)56
-rw-r--r--sound/hda/codecs/ca0132.c10078
-rw-r--r--sound/hda/codecs/ca0132_regs.h (renamed from sound/pci/hda/ca0132_regs.h)17
-rw-r--r--sound/hda/codecs/cirrus/Kconfig44
-rw-r--r--sound/hda/codecs/cirrus/Makefile10
-rw-r--r--sound/hda/codecs/cirrus/cs420x.c (renamed from sound/pci/hda/patch_cirrus.c)584
-rw-r--r--sound/hda/codecs/cirrus/cs421x.c590
-rw-r--r--sound/hda/codecs/cirrus/cs8409-tables.c623
-rw-r--r--sound/hda/codecs/cirrus/cs8409.c1475
-rw-r--r--sound/hda/codecs/cirrus/cs8409.h377
-rw-r--r--sound/hda/codecs/cm9825.c312
-rw-r--r--sound/hda/codecs/cmedia.c106
-rw-r--r--sound/hda/codecs/conexant.c (renamed from sound/pci/hda/patch_conexant.c)571
-rw-r--r--sound/hda/codecs/generic.c (renamed from sound/pci/hda/hda_generic.c)466
-rw-r--r--sound/hda/codecs/generic.h (renamed from sound/pci/hda/hda_generic.h)34
-rw-r--r--sound/hda/codecs/hdmi/Kconfig88
-rw-r--r--sound/hda/codecs/hdmi/Makefile18
-rw-r--r--sound/hda/codecs/hdmi/atihdmi.c615
-rw-r--r--sound/hda/codecs/hdmi/eld.c230
-rw-r--r--sound/hda/codecs/hdmi/hdmi.c2363
-rw-r--r--sound/hda/codecs/hdmi/hdmi_local.h302
-rw-r--r--sound/hda/codecs/hdmi/intelhdmi.c811
-rw-r--r--sound/hda/codecs/hdmi/nvhdmi-mcp.c383
-rw-r--r--sound/hda/codecs/hdmi/nvhdmi.c240
-rw-r--r--sound/hda/codecs/hdmi/simplehdmi.c251
-rw-r--r--sound/hda/codecs/hdmi/tegrahdmi.c318
-rw-r--r--sound/hda/codecs/helpers/hp_x360.c95
-rw-r--r--sound/hda/codecs/helpers/ideapad_hotkey_led.c36
-rw-r--r--sound/hda/codecs/helpers/ideapad_s740.c492
-rw-r--r--sound/hda/codecs/helpers/thinkpad.c36
-rw-r--r--sound/hda/codecs/realtek/Kconfig104
-rw-r--r--sound/hda/codecs/realtek/Makefile26
-rw-r--r--sound/hda/codecs/realtek/alc260.c290
-rw-r--r--sound/hda/codecs/realtek/alc262.c213
-rw-r--r--sound/hda/codecs/realtek/alc268.c189
-rw-r--r--sound/hda/codecs/realtek/alc269.c8193
-rw-r--r--sound/hda/codecs/realtek/alc662.c1116
-rw-r--r--sound/hda/codecs/realtek/alc680.c67
-rw-r--r--sound/hda/codecs/realtek/alc861.c163
-rw-r--r--sound/hda/codecs/realtek/alc861vd.c137
-rw-r--r--sound/hda/codecs/realtek/alc880.c509
-rw-r--r--sound/hda/codecs/realtek/alc882.c861
-rw-r--r--sound/hda/codecs/realtek/realtek.c2271
-rw-r--r--sound/hda/codecs/realtek/realtek.h319
-rw-r--r--sound/hda/codecs/senarytech.c246
-rw-r--r--sound/hda/codecs/si3054.c (renamed from sound/pci/hda/patch_si3054.c)71
-rw-r--r--sound/hda/codecs/side-codecs/Kconfig128
-rw-r--r--sound/hda/codecs/side-codecs/Makefile28
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec.c73
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec.h13
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec_test.c332
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.c2096
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.h110
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c69
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_property.c582
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_property.h18
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_spi.c64
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c1182
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.h50
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c85
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda_spi.c88
-rw-r--r--sound/hda/codecs/side-codecs/hda_component.c205
-rw-r--r--sound/hda/codecs/side-codecs/hda_component.h102
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.c416
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.h90
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c827
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_spi.c956
-rw-r--r--sound/hda/codecs/sigmatel.c (renamed from sound/pci/hda/patch_sigmatel.c)581
-rw-r--r--sound/hda/codecs/via.c (renamed from sound/pci/hda/patch_via.c)610
-rw-r--r--sound/hda/common/Kconfig97
-rw-r--r--sound/hda/common/Makefile13
-rw-r--r--sound/hda/common/auto_parser.c (renamed from sound/pci/hda/hda_auto_parser.c)202
-rw-r--r--sound/hda/common/beep.c (renamed from sound/pci/hda/hda_beep.c)205
-rw-r--r--sound/hda/common/bind.c (renamed from sound/pci/hda/hda_bind.c)103
-rw-r--r--sound/hda/common/codec.c (renamed from sound/pci/hda/hda_codec.c)1094
-rw-r--r--sound/hda/common/controller.c (renamed from sound/pci/hda/hda_controller.c)378
-rw-r--r--sound/hda/common/controller_trace.h (renamed from sound/pci/hda/hda_controller_trace.h)2
-rw-r--r--sound/hda/common/hda_auto_parser.h (renamed from sound/pci/hda/hda_auto_parser.h)11
-rw-r--r--sound/hda/common/hda_beep.h (renamed from sound/pci/hda/hda_beep.h)26
-rw-r--r--sound/hda/common/hda_controller.h (renamed from sound/pci/hda/hda_controller.h)52
-rw-r--r--sound/hda/common/hda_jack.h195
-rw-r--r--sound/hda/common/hda_local.h (renamed from sound/pci/hda/hda_local.h)168
-rw-r--r--sound/hda/common/hwdep.c (renamed from sound/pci/hda/hda_hwdep.c)35
-rw-r--r--sound/hda/common/jack.c (renamed from sound/pci/hda/hda_jack.c)325
-rw-r--r--sound/hda/common/proc.c (renamed from sound/pci/hda/hda_proc.c)85
-rw-r--r--sound/hda/common/sysfs.c (renamed from sound/pci/hda/hda_sysfs.c)168
-rw-r--r--sound/hda/controllers/Kconfig42
-rw-r--r--sound/hda/controllers/Makefile13
-rw-r--r--sound/hda/controllers/acpi.c325
-rw-r--r--sound/hda/controllers/intel.c (renamed from sound/pci/hda/hda_intel.c)1702
-rw-r--r--sound/hda/controllers/intel.h38
-rw-r--r--sound/hda/controllers/intel_trace.h (renamed from sound/pci/hda/hda_intel_trace.h)4
-rw-r--r--sound/hda/controllers/tegra.c (renamed from sound/pci/hda/hda_tegra.c)457
-rw-r--r--sound/hda/core/Kconfig51
-rw-r--r--sound/hda/core/Makefile22
-rw-r--r--sound/hda/core/array.c (renamed from sound/hda/array.c)1
-rw-r--r--sound/hda/core/bus.c (renamed from sound/hda/hdac_bus.c)104
-rw-r--r--sound/hda/core/component.c351
-rw-r--r--sound/hda/core/controller.c (renamed from sound/hda/hdac_controller.c)272
-rw-r--r--sound/hda/core/device.c (renamed from sound/hda/hdac_device.c)281
-rw-r--r--sound/hda/core/ext/Makefile4
-rw-r--r--sound/hda/core/ext/bus.c142
-rw-r--r--sound/hda/core/ext/controller.c410
-rw-r--r--sound/hda/core/ext/stream.c449
-rw-r--r--sound/hda/core/hda_bus_type.c (renamed from sound/hda/hda_bus_type.c)13
-rw-r--r--sound/hda/core/hdmi_chmap.c (renamed from sound/hda/hdmi_chmap.c)25
-rw-r--r--sound/hda/core/i915.c208
-rw-r--r--sound/hda/core/intel-dsp-config.c843
-rw-r--r--sound/hda/core/intel-nhlt.c388
-rw-r--r--sound/hda/core/intel-sdw-acpi.c204
-rw-r--r--sound/hda/core/local.h25
-rw-r--r--sound/hda/core/regmap.c (renamed from sound/hda/hdac_regmap.c)139
-rw-r--r--sound/hda/core/stream.c (renamed from sound/hda/hdac_stream.c)554
-rw-r--r--sound/hda/core/sysfs.c (renamed from sound/hda/hdac_sysfs.c)67
-rw-r--r--sound/hda/core/trace.c (renamed from sound/hda/trace.c)0
-rw-r--r--sound/hda/core/trace.h (renamed from sound/hda/trace.h)41
-rw-r--r--sound/hda/ext/Makefile3
-rw-r--r--sound/hda/ext/hdac_ext_bus.c269
-rw-r--r--sound/hda/ext/hdac_ext_controller.c320
-rw-r--r--sound/hda/ext/hdac_ext_stream.c573
-rw-r--r--sound/hda/hdac_i915.c441
-rw-r--r--sound/hda/local.h36
-rw-r--r--sound/i2c/Makefile6
-rw-r--r--sound/i2c/cs8427.c103
-rw-r--r--sound/i2c/i2c.c35
-rw-r--r--sound/i2c/other/Makefile10
-rw-r--r--sound/i2c/other/ak4113.c82
-rw-r--r--sound/i2c/other/ak4114.c80
-rw-r--r--sound/i2c/other/ak4117.c83
-rw-r--r--sound/i2c/other/ak4xxx-adda.c45
-rw-r--r--sound/i2c/other/pt2258.c30
-rw-r--r--sound/i2c/tea6330t.c66
-rw-r--r--sound/isa/Kconfig25
-rw-r--r--sound/isa/Makefile18
-rw-r--r--sound/isa/ad1816a/Makefile3
-rw-r--r--sound/isa/ad1816a/ad1816a.c97
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c252
-rw-r--r--sound/isa/ad1848/Makefile3
-rw-r--r--sound/isa/ad1848/ad1848.c56
-rw-r--r--sound/isa/adlib.c34
-rw-r--r--sound/isa/als100.c97
-rw-r--r--sound/isa/azt2320.c97
-rw-r--r--sound/isa/cmi8328.c122
-rw-r--r--sound/isa/cmi8330.c155
-rw-r--r--sound/isa/cs423x/Makefile5
-rw-r--r--sound/isa/cs423x/cs4231.c55
-rw-r--r--sound/isa/cs423x/cs4236.c177
-rw-r--r--sound/isa/cs423x/cs4236_lib.c237
-rw-r--r--sound/isa/es1688/Makefile5
-rw-r--r--sound/isa/es1688/es1688.c74
-rw-r--r--sound/isa/es1688/es1688_lib.c412
-rw-r--r--sound/isa/es18xx.c397
-rw-r--r--sound/isa/galaxy/Makefile5
-rw-r--r--sound/isa/galaxy/azt1605.c15
-rw-r--r--sound/isa/galaxy/azt2316.c15
-rw-r--r--sound/isa/galaxy/galaxy.c106
-rw-r--r--sound/isa/gus/Makefile12
-rw-r--r--sound/isa/gus/gus_dma.c148
-rw-r--r--sound/isa/gus/gus_dram.c25
-rw-r--r--sound/isa/gus/gus_io.c238
-rw-r--r--sound/isa/gus/gus_irq.c29
-rw-r--r--sound/isa/gus/gus_main.c184
-rw-r--r--sound/isa/gus/gus_mem.c83
-rw-r--r--sound/isa/gus/gus_mem_proc.c19
-rw-r--r--sound/isa/gus/gus_mixer.c41
-rw-r--r--sound/isa/gus/gus_pcm.c301
-rw-r--r--sound/isa/gus/gus_reset.c111
-rw-r--r--sound/isa/gus/gus_tables.h17
-rw-r--r--sound/isa/gus/gus_timer.c41
-rw-r--r--sound/isa/gus/gus_uart.c70
-rw-r--r--sound/isa/gus/gus_volume.c33
-rw-r--r--sound/isa/gus/gusclassic.c63
-rw-r--r--sound/isa/gus/gusextreme.c107
-rw-r--r--sound/isa/gus/gusmax.c161
-rw-r--r--sound/isa/gus/interwave.c337
-rw-r--r--sound/isa/msnd/Makefile6
-rw-r--r--sound/isa/msnd/msnd.c120
-rw-r--r--sound/isa/msnd/msnd.h21
-rw-r--r--sound/isa/msnd/msnd_classic.h15
-rw-r--r--sound/isa/msnd/msnd_midi.c182
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c395
-rw-r--r--sound/isa/msnd/msnd_pinnacle.h15
-rw-r--r--sound/isa/msnd/msnd_pinnacle_mixer.c21
-rw-r--r--sound/isa/opl3sa2.c239
-rw-r--r--sound/isa/opti9xx/Makefile8
-rw-r--r--sound/isa/opti9xx/miro.c369
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c277
-rw-r--r--sound/isa/sb/Makefile18
-rw-r--r--sound/isa/sb/emu8000.c214
-rw-r--r--sound/isa/sb/emu8000_callback.c15
-rw-r--r--sound/isa/sb/emu8000_local.h15
-rw-r--r--sound/isa/sb/emu8000_patch.c39
-rw-r--r--sound/isa/sb/emu8000_pcm.c150
-rw-r--r--sound/isa/sb/emu8000_synth.c17
-rw-r--r--sound/isa/sb/jazz16.c108
-rw-r--r--sound/isa/sb/sb16.c190
-rw-r--r--sound/isa/sb/sb16_csp.c356
-rw-r--r--sound/isa/sb/sb16_main.c214
-rw-r--r--sound/isa/sb/sb8.c131
-rw-r--r--sound/isa/sb/sb8_main.c242
-rw-r--r--sound/isa/sb/sb8_midi.c148
-rw-r--r--sound/isa/sb/sb_common.c144
-rw-r--r--sound/isa/sb/sb_mixer.c184
-rw-r--r--sound/isa/sc6000.c301
-rw-r--r--sound/isa/sscape.c412
-rw-r--r--sound/isa/wavefront/Makefile3
-rw-r--r--sound/isa/wavefront/wavefront.c145
-rw-r--r--sound/isa/wavefront/wavefront_fx.c57
-rw-r--r--sound/isa/wavefront/wavefront_midi.c177
-rw-r--r--sound/isa/wavefront/wavefront_synth.c289
-rw-r--r--sound/isa/wss/Makefile3
-rw-r--r--sound/isa/wss/wss_lib.c618
-rw-r--r--sound/last.c27
-rw-r--r--sound/mips/Kconfig20
-rw-r--r--sound/mips/Makefile6
-rw-r--r--sound/mips/ad1843.c16
-rw-r--r--sound/mips/hal2.c130
-rw-r--r--sound/mips/hal2.h15
-rw-r--r--sound/mips/sgio2audio.c92
-rw-r--r--sound/mips/snd-n64.c369
-rw-r--r--sound/oss/.gitignore2
-rw-r--r--sound/oss/dmasound/Kconfig7
-rw-r--r--sound/oss/dmasound/Makefile1
-rw-r--r--sound/oss/dmasound/dmasound.h9
-rw-r--r--sound/oss/dmasound/dmasound_atari.c19
-rw-r--r--sound/oss/dmasound/dmasound_core.c58
-rw-r--r--sound/oss/dmasound/dmasound_paula.c15
-rw-r--r--sound/oss/dmasound/dmasound_q40.c1
-rw-r--r--sound/parisc/Kconfig1
-rw-r--r--sound/parisc/Makefile3
-rw-r--r--sound/parisc/harmony.c178
-rw-r--r--sound/pci/Kconfig69
-rw-r--r--sound/pci/Makefile49
-rw-r--r--sound/pci/ac97/Makefile1
-rw-r--r--sound/pci/ac97/ac97_codec.c369
-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.c286
-rw-r--r--sound/pci/ac97/ac97_patch.h17
-rw-r--r--sound/pci/ac97/ac97_pcm.c73
-rw-r--r--sound/pci/ac97/ac97_proc.c67
-rw-r--r--sound/pci/ad1889.c233
-rw-r--r--sound/pci/ak4531_codec.c57
-rw-r--r--sound/pci/ali5451/Makefile3
-rw-r--r--sound/pci/ali5451/ali5451.c316
-rw-r--r--sound/pci/als300.c178
-rw-r--r--sound/pci/als4000.c240
-rw-r--r--sound/pci/asihpi/Makefile3
-rw-r--r--sound/pci/asihpi/asihpi.c291
-rw-r--r--sound/pci/asihpi/hpi.h29
-rw-r--r--sound/pci/asihpi/hpi6000.c19
-rw-r--r--sound/pci/asihpi/hpi6000.h13
-rw-r--r--sound/pci/asihpi/hpi6205.c22
-rw-r--r--sound/pci/asihpi/hpi6205.h13
-rw-r--r--sound/pci/asihpi/hpi_internal.h20
-rw-r--r--sound/pci/asihpi/hpicmn.c39
-rw-r--r--sound/pci/asihpi/hpicmn.h15
-rw-r--r--sound/pci/asihpi/hpidebug.c15
-rw-r--r--sound/pci/asihpi/hpidebug.h22
-rw-r--r--sound/pci/asihpi/hpidspcd.c13
-rw-r--r--sound/pci/asihpi/hpidspcd.h15
-rw-r--r--sound/pci/asihpi/hpifunc.c1
-rw-r--r--sound/pci/asihpi/hpimsginit.c26
-rw-r--r--sound/pci/asihpi/hpimsginit.h13
-rw-r--r--sound/pci/asihpi/hpimsgx.c22
-rw-r--r--sound/pci/asihpi/hpimsgx.h13
-rw-r--r--sound/pci/asihpi/hpioctl.c41
-rw-r--r--sound/pci/asihpi/hpioctl.h13
-rw-r--r--sound/pci/asihpi/hpios.c15
-rw-r--r--sound/pci/asihpi/hpios.h15
-rw-r--r--sound/pci/asihpi/hpipcida.h13
-rw-r--r--sound/pci/atiixp.c287
-rw-r--r--sound/pci/atiixp_modem.c239
-rw-r--r--sound/pci/au88x0/Makefile6
-rw-r--r--sound/pci/au88x0/au88x0.c197
-rw-r--r--sound/pci/au88x0/au88x0.h22
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c67
-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.c83
-rw-r--r--sound/pci/au88x0/au88x0_eq.c56
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c18
-rw-r--r--sound/pci/au88x0/au88x0_game.c15
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c7
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c30
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c52
-rw-r--r--sound/pci/au88x0/au88x0_synth.c14
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c50
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h14
-rw-r--r--sound/pci/aw2/Makefile3
-rw-r--r--sound/pci/aw2/aw2-alsa.c227
-rw-r--r--sound/pci/aw2/aw2-saa7146.c17
-rw-r--r--sound/pci/aw2/aw2-saa7146.h20
-rw-r--r--sound/pci/aw2/aw2-tsl.c19
-rw-r--r--sound/pci/aw2/saa7146.h15
-rw-r--r--sound/pci/azt3328.c389
-rw-r--r--sound/pci/bt87x.c202
-rw-r--r--sound/pci/ca0106/Makefile3
-rw-r--r--sound/pci/ca0106/ca0106.h42
-rw-r--r--sound/pci/ca0106/ca0106_main.c350
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c86
-rw-r--r--sound/pci/ca0106/ca0106_proc.c86
-rw-r--r--sound/pci/ca0106/ca_midi.c193
-rw-r--r--sound/pci/ca0106/ca_midi.h16
-rw-r--r--sound/pci/cmipci.c552
-rw-r--r--sound/pci/cs4281.c288
-rw-r--r--sound/pci/cs46xx/Makefile1
-rw-r--r--sound/pci/cs46xx/cs46xx.c110
-rw-r--r--sound/pci/cs46xx/cs46xx.h21
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_scb_types.h17
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_spos.h23
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_task_types.h17
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c481
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h17
-rw-r--r--sound/pci/cs46xx/dsp_spos.c247
-rw-r--r--sound/pci/cs46xx/dsp_spos.h17
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c118
-rw-r--r--sound/pci/cs5530.c102
-rw-r--r--sound/pci/cs5535audio/Makefile1
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c165
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h6
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c19
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c65
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c17
-rw-r--r--sound/pci/ctxfi/Makefile3
-rw-r--r--sound/pci/ctxfi/ct20k1reg.h7
-rw-r--r--sound/pci/ctxfi/ct20k2reg.h7
-rw-r--r--sound/pci/ctxfi/ctamixer.c99
-rw-r--r--sound/pci/ctxfi/ctamixer.h16
-rw-r--r--sound/pci/ctxfi/ctatc.c98
-rw-r--r--sound/pci/ctxfi/ctatc.h8
-rw-r--r--sound/pci/ctxfi/ctdaio.c130
-rw-r--r--sound/pci/ctxfi/ctdaio.h12
-rw-r--r--sound/pci/ctxfi/cthardware.c8
-rw-r--r--sound/pci/ctxfi/cthardware.h11
-rw-r--r--sound/pci/ctxfi/cthw20k1.c81
-rw-r--r--sound/pci/ctxfi/cthw20k1.h8
-rw-r--r--sound/pci/ctxfi/cthw20k2.c31
-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.c30
-rw-r--r--sound/pci/ctxfi/ctmixer.h8
-rw-r--r--sound/pci/ctxfi/ctpcm.c28
-rw-r--r--sound/pci/ctxfi/ctpcm.h8
-rw-r--r--sound/pci/ctxfi/ctresource.c19
-rw-r--r--sound/pci/ctxfi/ctresource.h12
-rw-r--r--sound/pci/ctxfi/ctsrc.c128
-rw-r--r--sound/pci/ctxfi/ctsrc.h16
-rw-r--r--sound/pci/ctxfi/cttimer.c76
-rw-r--r--sound/pci/ctxfi/ctvmem.c25
-rw-r--r--sound/pci/ctxfi/ctvmem.h7
-rw-r--r--sound/pci/ctxfi/xfi.c14
-rw-r--r--sound/pci/echoaudio/Makefile28
-rw-r--r--sound/pci/echoaudio/darla20.c16
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c6
-rw-r--r--sound/pci/echoaudio/darla24.c16
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c6
-rw-r--r--sound/pci/echoaudio/echo3g.c16
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c3
-rw-r--r--sound/pci/echoaudio/echoaudio.c815
-rw-r--r--sound/pci/echoaudio/echoaudio.h28
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c21
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c49
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.h50
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c11
-rw-r--r--sound/pci/echoaudio/gina20.c16
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c6
-rw-r--r--sound/pci/echoaudio/gina24.c16
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c9
-rw-r--r--sound/pci/echoaudio/indigo.c16
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigodj.c16
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigodjx.c16
-rw-r--r--sound/pci/echoaudio/indigoio.c16
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigoiox.c16
-rw-r--r--sound/pci/echoaudio/layla20.c16
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c6
-rw-r--r--sound/pci/echoaudio/layla24.c16
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c15
-rw-r--r--sound/pci/echoaudio/mia.c16
-rw-r--r--sound/pci/echoaudio/mia_dsp.c6
-rw-r--r--sound/pci/echoaudio/midi.c52
-rw-r--r--sound/pci/echoaudio/mona.c16
-rw-r--r--sound/pci/echoaudio/mona_dsp.c14
-rw-r--r--sound/pci/emu10k1/Makefile6
-rw-r--r--sound/pci/emu10k1/emu10k1.c152
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c280
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c1188
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c227
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c29
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h15
-rw-r--r--sound/pci/emu10k1/emu10k1x.c483
-rw-r--r--sound/pci/emu10k1/emufx.c1290
-rw-r--r--sound/pci/emu10k1/emumixer.c1854
-rw-r--r--sound/pci/emu10k1/emumpu401.c212
-rw-r--r--sound/pci/emu10k1/emupcm.c1294
-rw-r--r--sound/pci/emu10k1/emuproc.c662
-rw-r--r--sound/pci/emu10k1/io.c570
-rw-r--r--sound/pci/emu10k1/irq.c97
-rw-r--r--sound/pci/emu10k1/memory.c242
-rw-r--r--sound/pci/emu10k1/p16v.c271
-rw-r--r--sound/pci/emu10k1/p16v.h75
-rw-r--r--sound/pci/emu10k1/p17v.h21
-rw-r--r--sound/pci/emu10k1/timer.c57
-rw-r--r--sound/pci/emu10k1/tina2.h17
-rw-r--r--sound/pci/emu10k1/voice.c172
-rw-r--r--sound/pci/ens1370.c571
-rw-r--r--sound/pci/es1938.c268
-rw-r--r--sound/pci/es1968.c442
-rw-r--r--sound/pci/fm801.c331
-rw-r--r--sound/pci/hda/Kconfig227
-rw-r--r--sound/pci/hda/Makefile50
-rw-r--r--sound/pci/hda/dell_wmi_helper.c152
-rw-r--r--sound/pci/hda/hda_codec.h533
-rw-r--r--sound/pci/hda/hda_eld.c780
-rw-r--r--sound/pci/hda/hda_intel.h49
-rw-r--r--sound/pci/hda/hda_jack.h95
-rw-r--r--sound/pci/hda/local.h40
-rw-r--r--sound/pci/hda/patch_ca0132.c4804
-rw-r--r--sound/pci/hda/patch_cmedia.c141
-rw-r--r--sound/pci/hda/patch_hdmi.c3868
-rw-r--r--sound/pci/hda/patch_realtek.c8161
-rw-r--r--sound/pci/hda/thinkpad_helper.c91
-rw-r--r--sound/pci/ice1712/Makefile6
-rw-r--r--sound/pci/ice1712/ak4xxx.c28
-rw-r--r--sound/pci/ice1712/amp.c16
-rw-r--r--sound/pci/ice1712/amp.h16
-rw-r--r--sound/pci/ice1712/aureon.c74
-rw-r--r--sound/pci/ice1712/aureon.h16
-rw-r--r--sound/pci/ice1712/delta.c117
-rw-r--r--sound/pci/ice1712/delta.h16
-rw-r--r--sound/pci/ice1712/envy24ht.h16
-rw-r--r--sound/pci/ice1712/ews.c106
-rw-r--r--sound/pci/ice1712/ews.h16
-rw-r--r--sound/pci/ice1712/hoontech.c40
-rw-r--r--sound/pci/ice1712/hoontech.h16
-rw-r--r--sound/pci/ice1712/ice1712.c441
-rw-r--r--sound/pci/ice1712/ice1712.h18
-rw-r--r--sound/pci/ice1712/ice1724.c599
-rw-r--r--sound/pci/ice1712/juli.c53
-rw-r--r--sound/pci/ice1712/maya44.c46
-rw-r--r--sound/pci/ice1712/phase.c30
-rw-r--r--sound/pci/ice1712/phase.h16
-rw-r--r--sound/pci/ice1712/pontis.c102
-rw-r--r--sound/pci/ice1712/pontis.h16
-rw-r--r--sound/pci/ice1712/prodigy192.c49
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c125
-rw-r--r--sound/pci/ice1712/prodigy_hifi.h16
-rw-r--r--sound/pci/ice1712/psc724.c37
-rw-r--r--sound/pci/ice1712/quartet.c60
-rw-r--r--sound/pci/ice1712/revo.c16
-rw-r--r--sound/pci/ice1712/revo.h16
-rw-r--r--sound/pci/ice1712/se.c24
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c20
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h16
-rw-r--r--sound/pci/ice1712/wm8766.c18
-rw-r--r--sound/pci/ice1712/wm8766.h16
-rw-r--r--sound/pci/ice1712/wm8776.c24
-rw-r--r--sound/pci/ice1712/wm8776.h16
-rw-r--r--sound/pci/ice1712/wtm.c30
-rw-r--r--sound/pci/intel8x0.c603
-rw-r--r--sound/pci/intel8x0m.c295
-rw-r--r--sound/pci/korg1212/Makefile3
-rw-r--r--sound/pci/korg1212/korg1212.c594
-rw-r--r--sound/pci/lola/Makefile1
-rw-r--r--sound/pci/lola/lola.c167
-rw-r--r--sound/pci/lola/lola.h26
-rw-r--r--sound/pci/lola/lola_clock.c19
-rw-r--r--sound/pci/lola/lola_mixer.c60
-rw-r--r--sound/pci/lola/lola_pcm.c95
-rw-r--r--sound/pci/lola/lola_proc.c31
-rw-r--r--sound/pci/lx6464es/Makefile3
-rw-r--r--sound/pci/lx6464es/lx6464es.c215
-rw-r--r--sound/pci/lx6464es/lx6464es.h18
-rw-r--r--sound/pci/lx6464es/lx_core.c160
-rw-r--r--sound/pci/lx6464es/lx_core.h20
-rw-r--r--sound/pci/lx6464es/lx_defs.h17
-rw-r--r--sound/pci/maestro3.c258
-rw-r--r--sound/pci/mixart/Makefile3
-rw-r--r--sound/pci/mixart/mixart.c140
-rw-r--r--sound/pci/mixart/mixart.h17
-rw-r--r--sound/pci/mixart/mixart_core.c98
-rw-r--r--sound/pci/mixart/mixart_core.h102
-rw-r--r--sound/pci/mixart/mixart_hwdep.c79
-rw-r--r--sound/pci/mixart/mixart_hwdep.h23
-rw-r--r--sound/pci/mixart/mixart_mixer.c98
-rw-r--r--sound/pci/mixart/mixart_mixer.h15
-rw-r--r--sound/pci/nm256/Makefile3
-rw-r--r--sound/pci/nm256/nm256.c281
-rw-r--r--sound/pci/nm256/nm256_coef.c4
-rw-r--r--sound/pci/oxygen/Makefile8
-rw-r--r--sound/pci/oxygen/oxygen.c34
-rw-r--r--sound/pci/oxygen/oxygen.h3
-rw-r--r--sound/pci/oxygen/oxygen_io.c14
-rw-r--r--sound/pci/oxygen/oxygen_lib.c249
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c96
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c249
-rw-r--r--sound/pci/oxygen/pcm1796.h1
-rw-r--r--sound/pci/oxygen/se6x.c18
-rw-r--r--sound/pci/oxygen/virtuoso.c20
-rw-r--r--sound/pci/oxygen/wm8776.h5
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c19
-rw-r--r--sound/pci/oxygen/xonar_dg.c14
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c45
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c13
-rw-r--r--sound/pci/oxygen/xonar_lib.c16
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c105
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c50
-rw-r--r--sound/pci/pcxhr/Makefile3
-rw-r--r--sound/pci/pcxhr/pcxhr.c184
-rw-r--r--sound/pci/pcxhr/pcxhr.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c38
-rw-r--r--sound/pci/pcxhr/pcxhr_core.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c26
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.c40
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c73
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.h15
-rw-r--r--sound/pci/riptide/Makefile3
-rw-r--r--sound/pci/riptide/riptide.c516
-rw-r--r--sound/pci/rme32.c409
-rw-r--r--sound/pci/rme96.c510
-rw-r--r--sound/pci/rme9652/Makefile6
-rw-r--r--sound/pci/rme9652/hdsp.c833
-rw-r--r--sound/pci/rme9652/hdspm.c726
-rw-r--r--sound/pci/rme9652/rme9652.c529
-rw-r--r--sound/pci/sis7019.c206
-rw-r--r--sound/pci/sis7019.h14
-rw-r--r--sound/pci/sonicvibes.c351
-rw-r--r--sound/pci/trident/Makefile3
-rw-r--r--sound/pci/trident/trident.c97
-rw-r--r--sound/pci/trident/trident.h32
-rw-r--r--sound/pci/trident/trident_main.c627
-rw-r--r--sound/pci/trident/trident_memory.c97
-rw-r--r--sound/pci/via82xx.c404
-rw-r--r--sound/pci/via82xx_modem.c204
-rw-r--r--sound/pci/vx222/Makefile3
-rw-r--r--sound/pci/vx222/vx222.c112
-rw-r--r--sound/pci/vx222/vx222.h19
-rw-r--r--sound/pci/vx222/vx222_ops.c65
-rw-r--r--sound/pci/ymfpci/Makefile3
-rw-r--r--sound/pci/ymfpci/ymfpci.c191
-rw-r--r--sound/pci/ymfpci/ymfpci.h155
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c627
-rw-r--r--sound/pcmcia/Kconfig2
-rw-r--r--sound/pcmcia/Makefile1
-rw-r--r--sound/pcmcia/pdaudiocf/Makefile3
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c46
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.h15
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_core.c62
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c18
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c71
-rw-r--r--sound/pcmcia/vx/Makefile3
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c30
-rw-r--r--sound/pcmcia/vx/vxp_ops.c51
-rw-r--r--sound/pcmcia/vx/vxpocket.c77
-rw-r--r--sound/pcmcia/vx/vxpocket.h17
-rw-r--r--sound/ppc/Kconfig1
-rw-r--r--sound/ppc/Makefile3
-rw-r--r--sound/ppc/awacs.c108
-rw-r--r--sound/ppc/awacs.h15
-rw-r--r--sound/ppc/beep.c41
-rw-r--r--sound/ppc/burgundy.c48
-rw-r--r--sound/ppc/burgundy.h15
-rw-r--r--sound/ppc/daca.c45
-rw-r--r--sound/ppc/keywest.c62
-rw-r--r--sound/ppc/pmac.c212
-rw-r--r--sound/ppc/pmac.h18
-rw-r--r--sound/ppc/powermac.c56
-rw-r--r--sound/ppc/snd_ps3.c111
-rw-r--r--sound/ppc/snd_ps3.h14
-rw-r--r--sound/ppc/snd_ps3_reg.h16
-rw-r--r--sound/ppc/tumbler.c148
-rw-r--r--sound/ppc/tumbler_volume.h12
-rw-r--r--sound/sh/Kconfig3
-rw-r--r--sound/sh/Makefile5
-rw-r--r--sound/sh/aica.c120
-rw-r--r--sound/sh/aica.h15
-rw-r--r--sound/sh/sh_dac_audio.c92
-rw-r--r--sound/soc/Kconfig89
-rw-r--r--sound/soc/Makefile52
-rw-r--r--sound/soc/adi/Kconfig11
-rw-r--r--sound/soc/adi/Makefile5
-rw-r--r--sound/soc/adi/axi-i2s.c83
-rw-r--r--sound/soc/adi/axi-spdif.c13
-rw-r--r--sound/soc/amd/Kconfig179
-rw-r--r--sound/soc/amd/Makefile20
-rw-r--r--sound/soc/amd/acp-config.c337
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c811
-rw-r--r--sound/soc/amd/acp-es8336.c320
-rw-r--r--sound/soc/amd/acp-pcm-dma.c971
-rw-r--r--sound/soc/amd/acp-rt5645.c59
-rw-r--r--sound/soc/amd/acp.h131
-rw-r--r--sound/soc/amd/acp/Kconfig189
-rw-r--r--sound/soc/amd/acp/Makefile50
-rw-r--r--sound/soc/amd/acp/acp-i2s.c699
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c647
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c245
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1797
-rw-r--r--sound/soc/amd/acp/acp-mach.h139
-rw-r--r--sound/soc/amd/acp/acp-pci.c301
-rw-r--r--sound/soc/amd/acp/acp-pdm.c187
-rw-r--r--sound/soc/amd/acp/acp-platform.c360
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c247
-rw-r--r--sound/soc/amd/acp/acp-renoir.c191
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c550
-rw-r--r--sound/soc/amd/acp/acp-sdw-mach-common.c98
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c453
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c174
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c452
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h12
-rw-r--r--sound/soc/amd/acp/acp63.c298
-rw-r--r--sound/soc/amd/acp/acp70.c228
-rw-r--r--sound/soc/amd/acp/acp_common.h20
-rw-r--r--sound/soc/amd/acp/amd-acp63-acpi-match.c144
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c172
-rw-r--r--sound/soc/amd/acp/amd-acpi-mach.c93
-rw-r--r--sound/soc/amd/acp/amd-sdw-acpi.c62
-rw-r--r--sound/soc/amd/acp/amd.h365
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h146
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h62
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c556
-rw-r--r--sound/soc/amd/mach-config.h38
-rw-r--r--sound/soc/amd/ps/Makefile11
-rw-r--r--sound/soc/amd/ps/acp63.h446
-rw-r--r--sound/soc/amd/ps/pci-ps.c746
-rw-r--r--sound/soc/amd/ps/ps-common.c493
-rw-r--r--sound/soc/amd/ps/ps-mach.c80
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c463
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c804
-rw-r--r--sound/soc/amd/raven/Makefile8
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c331
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c526
-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.c504
-rw-r--r--sound/soc/amd/renoir/acp3x-rn.c77
-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.c417
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c490
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c513
-rw-r--r--sound/soc/amd/vangogh/acp5x.h224
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c342
-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.c740
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c455
-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/Kconfig11
-rw-r--r--sound/soc/apple/Makefile3
-rw-r--r--sound/soc/apple/mca.c1212
-rw-r--r--sound/soc/atmel/Kconfig133
-rw-r--r--sound/soc/atmel/Makefile40
-rw-r--r--sound/soc/atmel/atmel-classd.c221
-rw-r--r--sound/soc/atmel/atmel-i2s.c742
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c35
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c141
-rw-r--r--sound/soc/atmel/atmel-pcm.h23
-rw-r--r--sound/soc/atmel/atmel-pdmic.c194
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c418
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h16
-rw-r--r--sound/soc/atmel/atmel_wm8904.c31
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c1138
-rw-r--r--sound/soc/atmel/mchp-pdmc.c1156
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c1203
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c903
-rw-r--r--sound/soc/atmel/mikroe-proto.c176
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c129
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c51
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c162
-rw-r--r--sound/soc/au1x/Kconfig7
-rw-r--r--sound/soc/au1x/Makefile16
-rw-r--r--sound/soc/au1x/ac97c.c21
-rw-r--r--sound/soc/au1x/db1000.c11
-rw-r--r--sound/soc/au1x/db1200.c62
-rw-r--r--sound/soc/au1x/dbdma2.c96
-rw-r--r--sound/soc/au1x/dma.c98
-rw-r--r--sound/soc/au1x/i2sc.c35
-rw-r--r--sound/soc/au1x/psc-ac97.c40
-rw-r--r--sound/soc/au1x/psc-i2s.c50
-rw-r--r--sound/soc/au1x/psc.h6
-rw-r--r--sound/soc/bcm/Kconfig17
-rw-r--r--sound/soc/bcm/Makefile9
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c121
-rw-r--r--sound/soc/bcm/bcm63xx-i2s-whistler.c303
-rw-r--r--sound/soc/bcm/bcm63xx-i2s.h89
-rw-r--r--sound/soc/bcm/bcm63xx-pcm-whistler.c418
-rw-r--r--sound/soc/bcm/cygnus-pcm.c191
-rw-r--r--sound/soc/bcm/cygnus-ssp.c99
-rw-r--r--sound/soc/bcm/cygnus-ssp.h16
-rw-r--r--sound/soc/blackfin/Kconfig205
-rw-r--r--sound/soc/blackfin/Makefile40
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c480
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c388
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h57
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c109
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c131
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c109
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c212
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c373
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h17
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c391
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1102
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h174
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c126
-rw-r--r--sound/soc/blackfin/bf6xx-i2s.c239
-rw-r--r--sound/soc/blackfin/bf6xx-sport.c425
-rw-r--r--sound/soc/blackfin/bf6xx-sport.h82
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c173
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1701.c113
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1x61.c142
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1x81.c129
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c145
-rw-r--r--sound/soc/cirrus/Kconfig53
-rw-r--r--sound/soc/cirrus/Makefile14
-rw-r--r--sound/soc/cirrus/edb93xx.c126
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c450
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c219
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c24
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.h13
-rw-r--r--sound/soc/cirrus/simone.c87
-rw-r--r--sound/soc/cirrus/snappercl15.c136
-rw-r--r--sound/soc/codecs/88pm860x-codec.c225
-rw-r--r--sound/soc/codecs/88pm860x-codec.h9
-rw-r--r--sound/soc/codecs/Kconfig2109
-rw-r--r--sound/soc/codecs/Makefile884
-rw-r--r--sound/soc/codecs/ab8500-codec.c338
-rw-r--r--sound/soc/codecs/ab8500-codec.h7
-rw-r--r--sound/soc/codecs/ac97.c65
-rw-r--r--sound/soc/codecs/ad1836.c80
-rw-r--r--sound/soc/codecs/ad1836.h3
-rw-r--r--sound/soc/codecs/ad193x-i2c.c17
-rw-r--r--sound/soc/codecs/ad193x-spi.c10
-rw-r--r--sound/soc/codecs/ad193x.c216
-rw-r--r--sound/soc/codecs/ad193x.h15
-rw-r--r--sound/soc/codecs/ad1980.c96
-rw-r--r--sound/soc/codecs/ad73311.c34
-rw-r--r--sound/soc/codecs/ad73311.h17
-rw-r--r--sound/soc/codecs/adau-utils.c3
-rw-r--r--sound/soc/codecs/adau1372-i2c.c41
-rw-r--r--sound/soc/codecs/adau1372-spi.c59
-rw-r--r--sound/soc/codecs/adau1372.c1073
-rw-r--r--sound/soc/codecs/adau1372.h22
-rw-r--r--sound/soc/codecs/adau1373.c312
-rw-r--r--sound/soc/codecs/adau1701.c224
-rw-r--r--sound/soc/codecs/adau1701.h3
-rw-r--r--sound/soc/codecs/adau1761-i2c.c11
-rw-r--r--sound/soc/codecs/adau1761-spi.c6
-rw-r--r--sound/soc/codecs/adau1761.c312
-rw-r--r--sound/soc/codecs/adau1761.h3
-rw-r--r--sound/soc/codecs/adau1781-i2c.c11
-rw-r--r--sound/soc/codecs/adau1781-spi.c6
-rw-r--r--sound/soc/codecs/adau1781.c55
-rw-r--r--sound/soc/codecs/adau1781.h3
-rw-r--r--sound/soc/codecs/adau17x1.c254
-rw-r--r--sound/soc/codecs/adau17x1.h12
-rw-r--r--sound/soc/codecs/adau1977-i2c.c15
-rw-r--r--sound/soc/codecs/adau1977-spi.c20
-rw-r--r--sound/soc/codecs/adau1977.c113
-rw-r--r--sound/soc/codecs/adau1977.h3
-rw-r--r--sound/soc/codecs/adau7002.c83
-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.c576
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/adav801.c10
-rw-r--r--sound/soc/codecs/adav803.c15
-rw-r--r--sound/soc/codecs/adav80x.c144
-rw-r--r--sound/soc/codecs/adav80x.h3
-rw-r--r--sound/soc/codecs/ads117x.c32
-rw-r--r--sound/soc/codecs/ak4104.c100
-rw-r--r--sound/soc/codecs/ak4118.c416
-rw-r--r--sound/soc/codecs/ak4375.c606
-rw-r--r--sound/soc/codecs/ak4458.c813
-rw-r--r--sound/soc/codecs/ak4458.h90
-rw-r--r--sound/soc/codecs/ak4535.c95
-rw-r--r--sound/soc/codecs/ak4535.h5
-rw-r--r--sound/soc/codecs/ak4554.c45
-rw-r--r--sound/soc/codecs/ak4613.c507
-rw-r--r--sound/soc/codecs/ak4619.c912
-rw-r--r--sound/soc/codecs/ak4641.c116
-rw-r--r--sound/soc/codecs/ak4642.c169
-rw-r--r--sound/soc/codecs/ak4671.c84
-rw-r--r--sound/soc/codecs/ak4671.h7
-rw-r--r--sound/soc/codecs/ak5386.c97
-rw-r--r--sound/soc/codecs/ak5558.c508
-rw-r--r--sound/soc/codecs/ak5558.h52
-rw-r--r--sound/soc/codecs/alc5623.c196
-rw-r--r--sound/soc/codecs/alc5623.h6
-rw-r--r--sound/soc/codecs/alc5632.c179
-rw-r--r--sound/soc/codecs/alc5632.h5
-rw-r--r--sound/soc/codecs/arizona-jack.c1670
-rw-r--r--sound/soc/codecs/arizona.c297
-rw-r--r--sound/soc/codecs/arizona.h75
-rw-r--r--sound/soc/codecs/audio-iio-aux.c314
-rw-r--r--sound/soc/codecs/aw8738.c104
-rw-r--r--sound/soc/codecs/aw87390.c461
-rw-r--r--sound/soc/codecs/aw87390.h85
-rw-r--r--sound/soc/codecs/aw88081.c1317
-rw-r--r--sound/soc/codecs/aw88081.h329
-rw-r--r--sound/soc/codecs/aw88166.c1928
-rw-r--r--sound/soc/codecs/aw88166.h534
-rw-r--r--sound/soc/codecs/aw88261.c1282
-rw-r--r--sound/soc/codecs/aw88261.h459
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c576
-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.c1720
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h214
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1174
-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/aw88399.c2350
-rw-r--r--sound/soc/codecs/aw88399.h634
-rw-r--r--sound/soc/codecs/bd28623.c241
-rw-r--r--sound/soc/codecs/bt-sco.c41
-rw-r--r--sound/soc/codecs/chv3-codec.c42
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cpcap.c1887
-rw-r--r--sound/soc/codecs/cq93vc.c58
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1066
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c1002
-rw-r--r--sound/soc/codecs/cs-amp-lib.c415
-rw-r--r--sound/soc/codecs/cs35l32.c114
-rw-r--r--sound/soc/codecs/cs35l32.h6
-rw-r--r--sound/soc/codecs/cs35l33.c243
-rw-r--r--sound/soc/codecs/cs35l33.h6
-rw-r--r--sound/soc/codecs/cs35l34.c230
-rw-r--r--sound/soc/codecs/cs35l34.h6
-rw-r--r--sound/soc/codecs/cs35l35.c170
-rw-r--r--sound/soc/codecs/cs35l35.h9
-rw-r--r--sound/soc/codecs/cs35l36.c1942
-rw-r--r--sound/soc/codecs/cs35l36.h446
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c96
-rw-r--r--sound/soc/codecs/cs35l41-lib.c1592
-rw-r--r--sound/soc/codecs/cs35l41-spi.c99
-rw-r--r--sound/soc/codecs/cs35l41.c1492
-rw-r--r--sound/soc/codecs/cs35l41.h41
-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.c332
-rw-r--r--sound/soc/codecs/cs35l45.c1515
-rw-r--r--sound/soc/codecs/cs35l45.h514
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c108
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c606
-rw-r--r--sound/soc/codecs/cs35l56-shared.c1355
-rw-r--r--sound/soc/codecs/cs35l56-spi.c95
-rw-r--r--sound/soc/codecs/cs35l56.c1564
-rw-r--r--sound/soc/codecs/cs35l56.h71
-rw-r--r--sound/soc/codecs/cs40l50-codec.c307
-rw-r--r--sound/soc/codecs/cs4234.c916
-rw-r--r--sound/soc/codecs/cs4234.h287
-rw-r--r--sound/soc/codecs/cs4265.c147
-rw-r--r--sound/soc/codecs/cs4265.h6
-rw-r--r--sound/soc/codecs/cs4270.c211
-rw-r--r--sound/soc/codecs/cs4271-i2c.c24
-rw-r--r--sound/soc/codecs/cs4271-spi.c19
-rw-r--r--sound/soc/codecs/cs4271.c192
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c104
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c625
-rw-r--r--sound/soc/codecs/cs42l42.c1628
-rw-r--r--sound/soc/codecs/cs42l42.h794
-rw-r--r--sound/soc/codecs/cs42l43-jack.c962
-rw-r--r--sound/soc/codecs/cs42l43-sdw.c71
-rw-r--r--sound/soc/codecs/cs42l43.c2476
-rw-r--r--sound/soc/codecs/cs42l43.h146
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c34
-rw-r--r--sound/soc/codecs/cs42l51.c409
-rw-r--r--sound/soc/codecs/cs42l51.h15
-rw-r--r--sound/soc/codecs/cs42l52.c307
-rw-r--r--sound/soc/codecs/cs42l52.h6
-rw-r--r--sound/soc/codecs/cs42l56.c308
-rw-r--r--sound/soc/codecs/cs42l56.h6
-rw-r--r--sound/soc/codecs/cs42l73.c247
-rw-r--r--sound/soc/codecs/cs42l73.h16
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c240
-rw-r--r--sound/soc/codecs/cs42l84.c1111
-rw-r--r--sound/soc/codecs/cs42l84.h210
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c32
-rw-r--r--sound/soc/codecs/cs42xx8.c227
-rw-r--r--sound/soc/codecs/cs42xx8.h3
-rw-r--r--sound/soc/codecs/cs43130.c715
-rw-r--r--sound/soc/codecs/cs43130.h167
-rw-r--r--sound/soc/codecs/cs4341.c352
-rw-r--r--sound/soc/codecs/cs4349.c79
-rw-r--r--sound/soc/codecs/cs4349.h11
-rw-r--r--sound/soc/codecs/cs47l15.c1506
-rw-r--r--sound/soc/codecs/cs47l24.c259
-rw-r--r--sound/soc/codecs/cs47l24.h5
-rw-r--r--sound/soc/codecs/cs47l35.c1781
-rw-r--r--sound/soc/codecs/cs47l85.c2732
-rw-r--r--sound/soc/codecs/cs47l90.c2656
-rw-r--r--sound/soc/codecs/cs47l92.c2104
-rw-r--r--sound/soc/codecs/cs48l32-tables.c538
-rw-r--r--sound/soc/codecs/cs48l32.c4076
-rw-r--r--sound/soc/codecs/cs48l32.h403
-rw-r--r--sound/soc/codecs/cs530x-i2c.c72
-rw-r--r--sound/soc/codecs/cs530x.c971
-rw-r--r--sound/soc/codecs/cs530x.h223
-rw-r--r--sound/soc/codecs/cs53l30.c164
-rw-r--r--sound/soc/codecs/cs53l30.h6
-rw-r--r--sound/soc/codecs/cx20442.c131
-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.c237
-rw-r--r--sound/soc/codecs/da7213.c785
-rw-r--r--sound/soc/codecs/da7213.h86
-rw-r--r--sound/soc/codecs/da7218.c543
-rw-r--r--sound/soc/codecs/da7218.h10
-rw-r--r--sound/soc/codecs/da7219-aad.c427
-rw-r--r--sound/soc/codecs/da7219-aad.h25
-rw-r--r--sound/soc/codecs/da7219.c1364
-rw-r--r--sound/soc/codecs/da7219.h28
-rw-r--r--sound/soc/codecs/da732x.c277
-rw-r--r--sound/soc/codecs/da732x.h17
-rw-r--r--sound/soc/codecs/da732x_reg.h5
-rw-r--r--sound/soc/codecs/da9055.c214
-rw-r--r--sound/soc/codecs/dio2125.c120
-rw-r--r--sound/soc/codecs/dmic.c162
-rw-r--r--sound/soc/codecs/es7134.c266
-rw-r--r--sound/soc/codecs/es7241.c311
-rw-r--r--sound/soc/codecs/es8311.c973
-rw-r--r--sound/soc/codecs/es8311.h162
-rw-r--r--sound/soc/codecs/es8316.c496
-rw-r--r--sound/soc/codecs/es8316.h16
-rw-r--r--sound/soc/codecs/es8323.c791
-rw-r--r--sound/soc/codecs/es8323.h78
-rw-r--r--sound/soc/codecs/es8326.c1374
-rw-r--r--sound/soc/codecs/es8326.h200
-rw-r--r--sound/soc/codecs/es8328-i2c.c21
-rw-r--r--sound/soc/codecs/es8328-spi.c12
-rw-r--r--sound/soc/codecs/es8328.c219
-rw-r--r--sound/soc/codecs/es8375.c794
-rw-r--r--sound/soc/codecs/es8375.h123
-rw-r--r--sound/soc/codecs/es8389.c962
-rw-r--r--sound/soc/codecs/es8389.h140
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.c89
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.h393
-rw-r--r--sound/soc/codecs/framer-codec.c413
-rw-r--r--sound/soc/codecs/fs-amp-lib.c265
-rw-r--r--sound/soc/codecs/fs-amp-lib.h150
-rw-r--r--sound/soc/codecs/fs210x.c1586
-rw-r--r--sound/soc/codecs/fs210x.h75
-rw-r--r--sound/soc/codecs/gtm601.c60
-rw-r--r--sound/soc/codecs/hda-dai.c105
-rw-r--r--sound/soc/codecs/hda.c396
-rw-r--r--sound/soc/codecs/hda.h19
-rw-r--r--sound/soc/codecs/hdac_hda.c679
-rw-r--r--sound/soc/codecs/hdac_hda.h34
-rw-r--r--sound/soc/codecs/hdac_hdmi.c959
-rw-r--r--sound/soc/codecs/hdac_hdmi.h10
-rw-r--r--sound/soc/codecs/hdmi-codec.c990
-rw-r--r--sound/soc/codecs/ics43432.c22
-rw-r--r--sound/soc/codecs/idt821034.c1183
-rw-r--r--sound/soc/codecs/inno_rk3036.c94
-rw-r--r--sound/soc/codecs/isabelle.c89
-rw-r--r--sound/soc/codecs/isabelle.h6
-rw-r--r--sound/soc/codecs/jz4725b.c667
-rw-r--r--sound/soc/codecs/jz4740.c98
-rw-r--r--sound/soc/codecs/jz4760.c871
-rw-r--r--sound/soc/codecs/jz4770.c919
-rw-r--r--sound/soc/codecs/l3.c138
-rw-r--r--sound/soc/codecs/lm4857.c12
-rw-r--r--sound/soc/codecs/lm49453.c119
-rw-r--r--sound/soc/codecs/lm49453.h6
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/lpass-macro-common.c93
-rw-r--r--sound/soc/codecs/lpass-macro-common.h77
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c4039
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c2542
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1747
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c3048
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h17
-rw-r--r--sound/soc/codecs/madera.c4812
-rw-r--r--sound/soc/codecs/madera.h458
-rw-r--r--sound/soc/codecs/max9759.c198
-rw-r--r--sound/soc/codecs/max9768.c66
-rw-r--r--sound/soc/codecs/max98088.c562
-rw-r--r--sound/soc/codecs/max98088.h5
-rw-r--r--sound/soc/codecs/max98090.c542
-rw-r--r--sound/soc/codecs/max98090.h13
-rw-r--r--sound/soc/codecs/max98095.c520
-rw-r--r--sound/soc/codecs/max98095.h7
-rw-r--r--sound/soc/codecs/max98357a.c123
-rw-r--r--sound/soc/codecs/max98363.c465
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98371.c65
-rw-r--r--sound/soc/codecs/max98371.h6
-rw-r--r--sound/soc/codecs/max98373-i2c.c616
-rw-r--r--sound/soc/codecs/max98373-sdw.c888
-rw-r--r--sound/soc/codecs/max98373-sdw.h72
-rw-r--r--sound/soc/codecs/max98373.c785
-rw-r--r--sound/soc/codecs/max98373.h31
-rw-r--r--sound/soc/codecs/max98388.c1013
-rw-r--r--sound/soc/codecs/max98388.h234
-rw-r--r--sound/soc/codecs/max98390.c1141
-rw-r--r--sound/soc/codecs/max98390.h667
-rw-r--r--sound/soc/codecs/max98396.c1915
-rw-r--r--sound/soc/codecs/max98396.h327
-rw-r--r--sound/soc/codecs/max9850.c118
-rw-r--r--sound/soc/codecs/max9850.h7
-rw-r--r--sound/soc/codecs/max98504.c17
-rw-r--r--sound/soc/codecs/max98504.h5
-rw-r--r--sound/soc/codecs/max98520.c766
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max9860.c149
-rw-r--r--sound/soc/codecs/max9860.h10
-rw-r--r--sound/soc/codecs/max9867.c807
-rw-r--r--sound/soc/codecs/max9867.h50
-rw-r--r--sound/soc/codecs/max9877.c12
-rw-r--r--sound/soc/codecs/max9877.h7
-rw-r--r--sound/soc/codecs/max98925.c98
-rw-r--r--sound/soc/codecs/max98925.h7
-rw-r--r--sound/soc/codecs/max98926.c85
-rw-r--r--sound/soc/codecs/max98926.h6
-rw-r--r--sound/soc/codecs/max98927.c480
-rw-r--r--sound/soc/codecs/max98927.h12
-rw-r--r--sound/soc/codecs/mc13783.c107
-rw-r--r--sound/soc/codecs/mc13783.h14
-rw-r--r--sound/soc/codecs/ml26124.c136
-rw-r--r--sound/soc/codecs/ml26124.h14
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c419
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c481
-rw-r--r--sound/soc/codecs/mt6351.c1496
-rw-r--r--sound/soc/codecs/mt6351.h105
-rw-r--r--sound/soc/codecs/mt6357.c1855
-rw-r--r--sound/soc/codecs/mt6357.h660
-rw-r--r--sound/soc/codecs/mt6358.c2431
-rw-r--r--sound/soc/codecs/mt6358.h2310
-rw-r--r--sound/soc/codecs/mt6359-accdet.c1062
-rw-r--r--sound/soc/codecs/mt6359-accdet.h137
-rw-r--r--sound/soc/codecs/mt6359.c2962
-rw-r--r--sound/soc/codecs/mt6359.h4289
-rw-r--r--sound/soc/codecs/mt6660.c581
-rw-r--r--sound/soc/codecs/mt6660.h77
-rw-r--r--sound/soc/codecs/nau8315.c167
-rw-r--r--sound/soc/codecs/nau8325.c900
-rw-r--r--sound/soc/codecs/nau8325.h391
-rw-r--r--sound/soc/codecs/nau8540.c302
-rw-r--r--sound/soc/codecs/nau8540.h33
-rw-r--r--sound/soc/codecs/nau8810.c186
-rw-r--r--sound/soc/codecs/nau8810.h13
-rw-r--r--sound/soc/codecs/nau8821.c1959
-rw-r--r--sound/soc/codecs/nau8821.h586
-rw-r--r--sound/soc/codecs/nau8822.c1221
-rw-r--r--sound/soc/codecs/nau8822.h224
-rw-r--r--sound/soc/codecs/nau8824.c407
-rw-r--r--sound/soc/codecs/nau8824.h12
-rw-r--r--sound/soc/codecs/nau8825.c770
-rw-r--r--sound/soc/codecs/nau8825.h61
-rw-r--r--sound/soc/codecs/ntp8835.c480
-rw-r--r--sound/soc/codecs/ntp8918.c396
-rw-r--r--sound/soc/codecs/ntpfw.c137
-rw-r--r--sound/soc/codecs/ntpfw.h23
-rw-r--r--sound/soc/codecs/pcm1681.c91
-rw-r--r--sound/soc/codecs/pcm1754.c185
-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.c24
-rw-r--r--sound/soc/codecs/pcm179x-spi.c20
-rw-r--r--sound/soc/codecs/pcm179x.c60
-rw-r--r--sound/soc/codecs/pcm179x.h12
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c33
-rw-r--r--sound/soc/codecs/pcm186x-spi.c10
-rw-r--r--sound/soc/codecs/pcm186x.c181
-rw-r--r--sound/soc/codecs/pcm186x.h5
-rw-r--r--sound/soc/codecs/pcm3008.c96
-rw-r--r--sound/soc/codecs/pcm3008.h22
-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.c23
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c11
-rw-r--r--sound/soc/codecs/pcm3168a.c489
-rw-r--r--sound/soc/codecs/pcm3168a.h5
-rw-r--r--sound/soc/codecs/pcm5102a.c27
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c43
-rw-r--r--sound/soc/codecs/pcm512x-spi.c17
-rw-r--r--sound/soc/codecs/pcm512x.c520
-rw-r--r--sound/soc/codecs/pcm512x.h12
-rw-r--r--sound/soc/codecs/pcm6240.c2170
-rw-r--r--sound/soc/codecs/pcm6240.h247
-rw-r--r--sound/soc/codecs/peb2466.c2064
-rw-r--r--sound/soc/codecs/pm4125-sdw.c545
-rw-r--r--sound/soc/codecs/pm4125.c1780
-rw-r--r--sound/soc/codecs/pm4125.h307
-rw-r--r--sound/soc/codecs/rk3308_codec.c974
-rw-r--r--sound/soc/codecs/rk3308_codec.h579
-rw-r--r--sound/soc/codecs/rk3328_codec.c534
-rw-r--r--sound/soc/codecs/rk3328_codec.h210
-rw-r--r--sound/soc/codecs/rk817_codec.c540
-rw-r--r--sound/soc/codecs/rl6231.c39
-rw-r--r--sound/soc/codecs/rl6231.h8
-rw-r--r--sound/soc/codecs/rl6347a.c9
-rw-r--r--sound/soc/codecs/rl6347a.h5
-rw-r--r--sound/soc/codecs/rt-sdw-common.c238
-rw-r--r--sound/soc/codecs/rt-sdw-common.h66
-rw-r--r--sound/soc/codecs/rt1011.c2492
-rw-r--r--sound/soc/codecs/rt1011.h704
-rw-r--r--sound/soc/codecs/rt1015.c1193
-rw-r--r--sound/soc/codecs/rt1015.h449
-rw-r--r--sound/soc/codecs/rt1015p.c154
-rw-r--r--sound/soc/codecs/rt1016.c693
-rw-r--r--sound/soc/codecs/rt1016.h232
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c822
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.h183
-rw-r--r--sound/soc/codecs/rt1019.c609
-rw-r--r--sound/soc/codecs/rt1019.h164
-rw-r--r--sound/soc/codecs/rt1305.c1179
-rw-r--r--sound/soc/codecs/rt1305.h273
-rw-r--r--sound/soc/codecs/rt1308-sdw.c817
-rw-r--r--sound/soc/codecs/rt1308-sdw.h172
-rw-r--r--sound/soc/codecs/rt1308.c871
-rw-r--r--sound/soc/codecs/rt1308.h294
-rw-r--r--sound/soc/codecs/rt1316-sdw.c794
-rw-r--r--sound/soc/codecs/rt1316-sdw.h52
-rw-r--r--sound/soc/codecs/rt1318-sdw.c868
-rw-r--r--sound/soc/codecs/rt1318-sdw.h96
-rw-r--r--sound/soc/codecs/rt1318.c1353
-rw-r--r--sound/soc/codecs/rt1318.h342
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1823
-rw-r--r--sound/soc/codecs/rt1320-sdw.h113
-rw-r--r--sound/soc/codecs/rt274.c276
-rw-r--r--sound/soc/codecs/rt274.h5
-rw-r--r--sound/soc/codecs/rt286.c298
-rw-r--r--sound/soc/codecs/rt286.h7
-rw-r--r--sound/soc/codecs/rt298.c328
-rw-r--r--sound/soc/codecs/rt298.h7
-rw-r--r--sound/soc/codecs/rt5514-spi.c110
-rw-r--r--sound/soc/codecs/rt5514-spi.h5
-rw-r--r--sound/soc/codecs/rt5514.c179
-rw-r--r--sound/soc/codecs/rt5514.h7
-rw-r--r--sound/soc/codecs/rt5616.c250
-rw-r--r--sound/soc/codecs/rt5616.h5
-rw-r--r--sound/soc/codecs/rt5631.c376
-rw-r--r--sound/soc/codecs/rt5640.c1152
-rw-r--r--sound/soc/codecs/rt5640.h94
-rw-r--r--sound/soc/codecs/rt5645.c946
-rw-r--r--sound/soc/codecs/rt5645.h20
-rw-r--r--sound/soc/codecs/rt5651.c1031
-rw-r--r--sound/soc/codecs/rt5651.h40
-rw-r--r--sound/soc/codecs/rt5659.c582
-rw-r--r--sound/soc/codecs/rt5659.h26
-rw-r--r--sound/soc/codecs/rt5660.c196
-rw-r--r--sound/soc/codecs/rt5660.h7
-rw-r--r--sound/soc/codecs/rt5663.c800
-rw-r--r--sound/soc/codecs/rt5663.h9
-rw-r--r--sound/soc/codecs/rt5665.c517
-rw-r--r--sound/soc/codecs/rt5665.h9
-rw-r--r--sound/soc/codecs/rt5668.c2585
-rw-r--r--sound/soc/codecs/rt5668.h1312
-rw-r--r--sound/soc/codecs/rt5670-dsp.h5
-rw-r--r--sound/soc/codecs/rt5670.c720
-rw-r--r--sound/soc/codecs/rt5670.h44
-rw-r--r--sound/soc/codecs/rt5677-spi.c454
-rw-r--r--sound/soc/codecs/rt5677-spi.h22
-rw-r--r--sound/soc/codecs/rt5677.c1139
-rw-r--r--sound/soc/codecs/rt5677.h158
-rw-r--r--sound/soc/codecs/rt5682-i2c.c348
-rw-r--r--sound/soc/codecs/rt5682-sdw.c811
-rw-r--r--sound/soc/codecs/rt5682.c3191
-rw-r--r--sound/soc/codecs/rt5682.h1499
-rw-r--r--sound/soc/codecs/rt5682s.c3348
-rw-r--r--sound/soc/codecs/rt5682s.h1492
-rw-r--r--sound/soc/codecs/rt700-sdw.c572
-rw-r--r--sound/soc/codecs/rt700-sdw.h335
-rw-r--r--sound/soc/codecs/rt700.c1241
-rw-r--r--sound/soc/codecs/rt700.h171
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c496
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt711-sdca.c1672
-rw-r--r--sound/soc/codecs/rt711-sdca.h244
-rw-r--r--sound/soc/codecs/rt711-sdw.c585
-rw-r--r--sound/soc/codecs/rt711-sdw.h283
-rw-r--r--sound/soc/codecs/rt711.c1341
-rw-r--r--sound/soc/codecs/rt711.h251
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c987
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h107
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c510
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h75
-rw-r--r--sound/soc/codecs/rt712-sdca.c1932
-rw-r--r--sound/soc/codecs/rt712-sdca.h261
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c284
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h171
-rw-r--r--sound/soc/codecs/rt715-sdca.c1075
-rw-r--r--sound/soc/codecs/rt715-sdca.h132
-rw-r--r--sound/soc/codecs/rt715-sdw.c551
-rw-r--r--sound/soc/codecs/rt715-sdw.h337
-rw-r--r--sound/soc/codecs/rt715.c1140
-rw-r--r--sound/soc/codecs/rt715.h224
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c548
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.h150
-rw-r--r--sound/soc/codecs/rt721-sdca.c1560
-rw-r--r--sound/soc/codecs/rt721-sdca.h273
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c555
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h122
-rw-r--r--sound/soc/codecs/rt722-sdca.c1583
-rw-r--r--sound/soc/codecs/rt722-sdca.h250
-rw-r--r--sound/soc/codecs/rt9120.c643
-rw-r--r--sound/soc/codecs/rt9123.c500
-rw-r--r--sound/soc/codecs/rt9123p.c171
-rw-r--r--sound/soc/codecs/rtq9124.c543
-rw-r--r--sound/soc/codecs/rtq9128.c789
-rw-r--r--sound/soc/codecs/sdw-mockup.c275
-rw-r--r--sound/soc/codecs/sgtl5000.c649
-rw-r--r--sound/soc/codecs/sgtl5000.h16
-rw-r--r--sound/soc/codecs/si476x.c54
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c5
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c5
-rw-r--r--sound/soc/codecs/sigmadsp.c61
-rw-r--r--sound/soc/codecs/sigmadsp.h6
-rw-r--r--sound/soc/codecs/simple-amplifier.c110
-rw-r--r--sound/soc/codecs/simple-mux.c191
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c583
-rw-r--r--sound/soc/codecs/sirf-audio-codec.h125
-rw-r--r--sound/soc/codecs/sma1303.c1820
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/sma1307.c2065
-rw-r--r--sound/soc/codecs/sma1307.h444
-rw-r--r--sound/soc/codecs/spdif_receiver.c33
-rw-r--r--sound/soc/codecs/spdif_transmitter.c33
-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.c107
-rw-r--r--sound/soc/codecs/ssm2518.h3
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c15
-rw-r--r--sound/soc/codecs/ssm2602-spi.c10
-rw-r--r--sound/soc/codecs/ssm2602.c211
-rw-r--r--sound/soc/codecs/ssm2602.h16
-rw-r--r--sound/soc/codecs/ssm3515.c448
-rw-r--r--sound/soc/codecs/ssm4567.c60
-rw-r--r--sound/soc/codecs/sta32x.c242
-rw-r--r--sound/soc/codecs/sta32x.h6
-rw-r--r--sound/soc/codecs/sta350.c216
-rw-r--r--sound/soc/codecs/sta350.h8
-rw-r--r--sound/soc/codecs/sta529.c80
-rw-r--r--sound/soc/codecs/stac9766.c83
-rw-r--r--sound/soc/codecs/sti-sas.c122
-rw-r--r--sound/soc/codecs/tas2552.c244
-rw-r--r--sound/soc/codecs/tas2552.h12
-rw-r--r--sound/soc/codecs/tas2562.c781
-rw-r--r--sound/soc/codecs/tas2562.h90
-rw-r--r--sound/soc/codecs/tas2764-quirks.h180
-rw-r--r--sound/soc/codecs/tas2764.c950
-rw-r--r--sound/soc/codecs/tas2764.h129
-rw-r--r--sound/soc/codecs/tas2770.c890
-rw-r--r--sound/soc/codecs/tas2770.h151
-rw-r--r--sound/soc/codecs/tas2780.c652
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c371
-rw-r--r--sound/soc/codecs/tas2781-comlib.c221
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c2618
-rw-r--r--sound/soc/codecs/tas2781-i2c.c2084
-rw-r--r--sound/soc/codecs/tas2783-sdw.c1331
-rw-r--r--sound/soc/codecs/tas2783.h110
-rw-r--r--sound/soc/codecs/tas5086.c153
-rw-r--r--sound/soc/codecs/tas571x.c397
-rw-r--r--sound/soc/codecs/tas571x.h56
-rw-r--r--sound/soc/codecs/tas5720.c398
-rw-r--r--sound/soc/codecs/tas5720.h28
-rw-r--r--sound/soc/codecs/tas5805m.c613
-rw-r--r--sound/soc/codecs/tas6424.c273
-rw-r--r--sound/soc/codecs/tas6424.h16
-rw-r--r--sound/soc/codecs/tda7419.c641
-rw-r--r--sound/soc/codecs/tfa9879.c106
-rw-r--r--sound/soc/codecs/tfa9879.h7
-rw-r--r--sound/soc/codecs/tfa989x.c425
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1522
-rw-r--r--sound/soc/codecs/tlv320adcx140.c1217
-rw-r--r--sound/soc/codecs/tlv320adcx140.h159
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c19
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c12
-rw-r--r--sound/soc/codecs/tlv320aic23.c140
-rw-r--r--sound/soc/codecs/tlv320aic23.h5
-rw-r--r--sound/soc/codecs/tlv320aic26.c119
-rw-r--r--sound/soc/codecs/tlv320aic26.h6
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c881
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h53
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c499
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c40
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c36
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c954
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h42
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c72
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c77
-rw-r--r--sound/soc/codecs/tlv320aic3x.c602
-rw-r--r--sound/soc/codecs/tlv320aic3x.h61
-rw-r--r--sound/soc/codecs/tlv320dac33.c513
-rw-r--r--sound/soc/codecs/tlv320dac33.h16
-rw-r--r--sound/soc/codecs/tpa6130a2.c76
-rw-r--r--sound/soc/codecs/tpa6130a2.h16
-rw-r--r--sound/soc/codecs/ts3a227e.c100
-rw-r--r--sound/soc/codecs/ts3a227e.h5
-rw-r--r--sound/soc/codecs/tscs42xx.c450
-rw-r--r--sound/soc/codecs/tscs42xx.h10
-rw-r--r--sound/soc/codecs/tscs454.c3483
-rw-r--r--sound/soc/codecs/tscs454.h2323
-rw-r--r--sound/soc/codecs/twl4030.c556
-rw-r--r--sound/soc/codecs/twl6040.c293
-rw-r--r--sound/soc/codecs/twl6040.h26
-rw-r--r--sound/soc/codecs/uda1334.c294
-rw-r--r--sound/soc/codecs/uda1342.c347
-rw-r--r--sound/soc/codecs/uda1342.h78
-rw-r--r--sound/soc/codecs/uda134x.c596
-rw-r--r--sound/soc/codecs/uda134x.h33
-rw-r--r--sound/soc/codecs/uda1380.c187
-rw-r--r--sound/soc/codecs/uda1380.h5
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c905
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.h66
-rw-r--r--sound/soc/codecs/wcd-common.c144
-rw-r--r--sound/soc/codecs/wcd-common.h46
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1647
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h343
-rw-r--r--sound/soc/codecs/wcd9335.c5164
-rw-r--r--sound/soc/codecs/wcd9335.h641
-rw-r--r--sound/soc/codecs/wcd934x.c5917
-rw-r--r--sound/soc/codecs/wcd937x-sdw.c1115
-rw-r--r--sound/soc/codecs/wcd937x.c2964
-rw-r--r--sound/soc/codecs/wcd937x.h618
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1293
-rw-r--r--sound/soc/codecs/wcd938x.c3560
-rw-r--r--sound/soc/codecs/wcd938x.h686
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c1471
-rw-r--r--sound/soc/codecs/wcd939x.c3623
-rw-r--r--sound/soc/codecs/wcd939x.h947
-rw-r--r--sound/soc/codecs/wl1273.c523
-rw-r--r--sound/soc/codecs/wl1273.h30
-rw-r--r--sound/soc/codecs/wm0010.c266
-rw-r--r--sound/soc/codecs/wm1250-ev1.c159
-rw-r--r--sound/soc/codecs/wm2000.c146
-rw-r--r--sound/soc/codecs/wm2000.h5
-rw-r--r--sound/soc/codecs/wm2200.c321
-rw-r--r--sound/soc/codecs/wm2200.h6
-rw-r--r--sound/soc/codecs/wm5100-tables.c18
-rw-r--r--sound/soc/codecs/wm5100.c409
-rw-r--r--sound/soc/codecs/wm5100.h8
-rw-r--r--sound/soc/codecs/wm5102.c313
-rw-r--r--sound/soc/codecs/wm5102.h5
-rw-r--r--sound/soc/codecs/wm5110.c410
-rw-r--r--sound/soc/codecs/wm5110.h5
-rw-r--r--sound/soc/codecs/wm8350.c227
-rw-r--r--sound/soc/codecs/wm8350.h10
-rw-r--r--sound/soc/codecs/wm8400.c238
-rw-r--r--sound/soc/codecs/wm8400.h7
-rw-r--r--sound/soc/codecs/wm8510.c175
-rw-r--r--sound/soc/codecs/wm8510.h5
-rw-r--r--sound/soc/codecs/wm8523.c118
-rw-r--r--sound/soc/codecs/wm8523.h7
-rw-r--r--sound/soc/codecs/wm8524.c123
-rw-r--r--sound/soc/codecs/wm8580.c197
-rw-r--r--sound/soc/codecs/wm8580.h7
-rw-r--r--sound/soc/codecs/wm8711.c140
-rw-r--r--sound/soc/codecs/wm8711.h5
-rw-r--r--sound/soc/codecs/wm8727.c34
-rw-r--r--sound/soc/codecs/wm8728.c104
-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.c358
-rw-r--r--sound/soc/codecs/wm8731.h32
-rw-r--r--sound/soc/codecs/wm8737.c130
-rw-r--r--sound/soc/codecs/wm8737.h5
-rw-r--r--sound/soc/codecs/wm8741.c174
-rw-r--r--sound/soc/codecs/wm8741.h5
-rw-r--r--sound/soc/codecs/wm8750.c141
-rw-r--r--sound/soc/codecs/wm8750.h6
-rw-r--r--sound/soc/codecs/wm8753.c332
-rw-r--r--sound/soc/codecs/wm8753.h7
-rw-r--r--sound/soc/codecs/wm8770.c168
-rw-r--r--sound/soc/codecs/wm8770.h5
-rw-r--r--sound/soc/codecs/wm8776.c127
-rw-r--r--sound/soc/codecs/wm8776.h5
-rw-r--r--sound/soc/codecs/wm8782.c143
-rw-r--r--sound/soc/codecs/wm8804-i2c.c30
-rw-r--r--sound/soc/codecs/wm8804-spi.c10
-rw-r--r--sound/soc/codecs/wm8804.c120
-rw-r--r--sound/soc/codecs/wm8804.h5
-rw-r--r--sound/soc/codecs/wm8900.c293
-rw-r--r--sound/soc/codecs/wm8900.h5
-rw-r--r--sound/soc/codecs/wm8903.c262
-rw-r--r--sound/soc/codecs/wm8903.h8
-rw-r--r--sound/soc/codecs/wm8904.c871
-rw-r--r--sound/soc/codecs/wm8904.h6
-rw-r--r--sound/soc/codecs/wm8940.c307
-rw-r--r--sound/soc/codecs/wm8940.h8
-rw-r--r--sound/soc/codecs/wm8955.c214
-rw-r--r--sound/soc/codecs/wm8955.h5
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c357
-rw-r--r--sound/soc/codecs/wm8960.c423
-rw-r--r--sound/soc/codecs/wm8960.h9
-rw-r--r--sound/soc/codecs/wm8961.c271
-rw-r--r--sound/soc/codecs/wm8961.h5
-rw-r--r--sound/soc/codecs/wm8962.c645
-rw-r--r--sound/soc/codecs/wm8962.h7
-rw-r--r--sound/soc/codecs/wm8971.c134
-rw-r--r--sound/soc/codecs/wm8971.h7
-rw-r--r--sound/soc/codecs/wm8974.c192
-rw-r--r--sound/soc/codecs/wm8974.h5
-rw-r--r--sound/soc/codecs/wm8978.c213
-rw-r--r--sound/soc/codecs/wm8978.h5
-rw-r--r--sound/soc/codecs/wm8983.c194
-rw-r--r--sound/soc/codecs/wm8983.h5
-rw-r--r--sound/soc/codecs/wm8985.c226
-rw-r--r--sound/soc/codecs/wm8985.h5
-rw-r--r--sound/soc/codecs/wm8988.c157
-rw-r--r--sound/soc/codecs/wm8988.h6
-rw-r--r--sound/soc/codecs/wm8990.c298
-rw-r--r--sound/soc/codecs/wm8990.h7
-rw-r--r--sound/soc/codecs/wm8991.c198
-rw-r--r--sound/soc/codecs/wm8991.h6
-rw-r--r--sound/soc/codecs/wm8993.c285
-rw-r--r--sound/soc/codecs/wm8994.c1143
-rw-r--r--sound/soc/codecs/wm8994.h37
-rw-r--r--sound/soc/codecs/wm8995.c333
-rw-r--r--sound/soc/codecs/wm8995.h5
-rw-r--r--sound/soc/codecs/wm8996.c529
-rw-r--r--sound/soc/codecs/wm8996.h10
-rw-r--r--sound/soc/codecs/wm8997.c163
-rw-r--r--sound/soc/codecs/wm8997.h5
-rw-r--r--sound/soc/codecs/wm8998.c221
-rw-r--r--sound/soc/codecs/wm8998.h5
-rw-r--r--sound/soc/codecs/wm9081.c260
-rw-r--r--sound/soc/codecs/wm9081.h5
-rw-r--r--sound/soc/codecs/wm9090.c131
-rw-r--r--sound/soc/codecs/wm9090.h15
-rw-r--r--sound/soc/codecs/wm9705.c100
-rw-r--r--sound/soc/codecs/wm9712.c139
-rw-r--r--sound/soc/codecs/wm9713.c244
-rw-r--r--sound/soc/codecs/wm_adsp.c3466
-rw-r--r--sound/soc/codecs/wm_adsp.h166
-rw-r--r--sound/soc/codecs/wm_hubs.c264
-rw-r--r--sound/soc/codecs/wm_hubs.h27
-rw-r--r--sound/soc/codecs/wmfw.h171
-rw-r--r--sound/soc/codecs/wsa881x.c1230
-rw-r--r--sound/soc/codecs/wsa883x.c1724
-rw-r--r--sound/soc/codecs/wsa884x.c2181
-rw-r--r--sound/soc/codecs/zl38060.c639
-rw-r--r--sound/soc/codecs/zx_aud96p22.c403
-rw-r--r--sound/soc/davinci/Kconfig106
-rw-r--r--sound/soc/davinci/Makefile16
-rw-r--r--sound/soc/davinci/davinci-evm.c511
-rw-r--r--sound/soc/davinci/davinci-vcif.c259
-rw-r--r--sound/soc/davinci/edma-pcm.h32
-rw-r--r--sound/soc/dwc/Kconfig6
-rw-r--r--sound/soc/dwc/Makefile1
-rw-r--r--sound/soc/dwc/dwc-i2s.c545
-rw-r--r--sound/soc/dwc/dwc-pcm.c79
-rw-r--r--sound/soc/dwc/local.h38
-rw-r--r--sound/soc/fsl/Kconfig244
-rw-r--r--sound/soc/fsl/Makefile73
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c26
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c75
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c842
-rw-r--r--sound/soc/fsl/fsl_asrc.c1102
-rw-r--r--sound/soc/fsl/fsl_asrc.h145
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h178
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c356
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c729
-rw-r--r--sound/soc/fsl/fsl_aud2htx.c309
-rw-r--r--sound/soc/fsl/fsl_aud2htx.h67
-rw-r--r--sound/soc/fsl/fsl_audmix.c569
-rw-r--r--sound/soc/fsl/fsl_audmix.h103
-rw-r--r--sound/soc/fsl/fsl_dma.c151
-rw-r--r--sound/soc/fsl/fsl_dma.h5
-rw-r--r--sound/soc/fsl/fsl_easrc.c2363
-rw-r--r--sound/soc/fsl/fsl_easrc.h655
-rw-r--r--sound/soc/fsl/fsl_esai.c618
-rw-r--r--sound/soc/fsl/fsl_esai.h5
-rw-r--r--sound/soc/fsl/fsl_micfil.c1532
-rw-r--r--sound/soc/fsl/fsl_micfil.h214
-rw-r--r--sound/soc/fsl/fsl_mqs.c471
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c974
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c353
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h47
-rw-r--r--sound/soc/fsl/fsl_sai.c1608
-rw-r--r--sound/soc/fsl/fsl_sai.h242
-rw-r--r--sound/soc/fsl/fsl_spdif.c744
-rw-r--r--sound/soc/fsl/fsl_spdif.h37
-rw-r--r--sound/soc/fsl/fsl_ssi.c1044
-rw-r--r--sound/soc/fsl/fsl_ssi.h17
-rw-r--r--sound/soc/fsl/fsl_ssi_dbg.c52
-rw-r--r--sound/soc/fsl/fsl_utils.c139
-rw-r--r--sound/soc/fsl/fsl_utils.h19
-rw-r--r--sound/soc/fsl/fsl_xcvr.c1861
-rw-r--r--sound/soc/fsl/fsl_xcvr.h403
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c145
-rw-r--r--sound/soc/fsl/imx-audmix.c339
-rw-r--r--sound/soc/fsl/imx-audmux.c170
-rw-r--r--sound/soc/fsl/imx-card.c943
-rw-r--r--sound/soc/fsl/imx-es8328.c111
-rw-r--r--sound/soc/fsl/imx-hdmi.c224
-rw-r--r--sound/soc/fsl/imx-mc13783.c168
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c9
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c153
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c852
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.h512
-rw-r--r--sound/soc/fsl/imx-pcm.h24
-rw-r--r--sound/soc/fsl/imx-rpmsg.c254
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c68
-rw-r--r--sound/soc/fsl/imx-spdif.c101
-rw-r--r--sound/soc/fsl/imx-ssi.c661
-rw-r--r--sound/soc/fsl/imx-ssi.h8
-rw-r--r--sound/soc/fsl/imx-wm8962.c312
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.c372
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.h80
-rw-r--r--sound/soc/fsl/lpc3xxx-pcm.c72
-rw-r--r--sound/soc/fsl/mpc5200_dma.c145
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c28
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c29
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c432
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c234
-rw-r--r--sound/soc/fsl/p1022_ds.c84
-rw-r--r--sound/soc/fsl/p1022_rdk.c114
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c61
-rw-r--r--sound/soc/fsl/phycore-ac97.c125
-rw-r--r--sound/soc/fsl/wm1133-ev1.c292
-rw-r--r--sound/soc/generic/Kconfig38
-rw-r--r--sound/soc/generic/Makefile16
-rw-r--r--sound/soc/generic/audio-graph-card.c775
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.c187
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample1.dtsi396
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample2.dtsi382
-rw-r--r--sound/soc/generic/audio-graph-card2.c1412
-rw-r--r--sound/soc/generic/audio-graph-scu-card.c415
-rw-r--r--sound/soc/generic/simple-card-utils.c1215
-rw-r--r--sound/soc/generic/simple-card.c990
-rw-r--r--sound/soc/generic/simple-scu-card.c315
-rw-r--r--sound/soc/generic/test-component.c657
-rw-r--r--sound/soc/google/Kconfig9
-rw-r--r--sound/soc/google/Makefile2
-rw-r--r--sound/soc/google/chv3-i2s.c339
-rw-r--r--sound/soc/hisilicon/Kconfig5
-rw-r--r--sound/soc/hisilicon/Makefile1
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c71
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h13
-rw-r--r--sound/soc/img/Kconfig15
-rw-r--r--sound/soc/img/img-i2s-in.c63
-rw-r--r--sound/soc/img/img-i2s-out.c80
-rw-r--r--sound/soc/img/img-parallel-out.c60
-rw-r--r--sound/soc/img/img-spdif-in.c46
-rw-r--r--sound/soc/img/img-spdif-out.c63
-rw-r--r--sound/soc/img/pistachio-internal-dac.c59
-rw-r--r--sound/soc/intel/Kconfig156
-rw-r--r--sound/soc/intel/Makefile8
-rw-r--r--sound/soc/intel/atom/Makefile6
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c148
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h17
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h18
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c68
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c194
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h22
-rw-r--r--sound/soc/intel/atom/sst/Makefile14
-rw-r--r--sound/soc/intel/atom/sst/sst.c75
-rw-r--r--sound/soc/intel/atom/sst/sst.h84
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c164
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c111
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c25
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c51
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c72
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c61
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c181
-rw-r--r--sound/soc/intel/avs/Makefile20
-rw-r--r--sound/soc/intel/avs/apl.c270
-rw-r--r--sound/soc/intel/avs/avs.h367
-rw-r--r--sound/soc/intel/avs/board_selection.c672
-rw-r--r--sound/soc/intel/avs/boards/Kconfig199
-rw-r--r--sound/soc/intel/avs/boards/Makefile41
-rw-r--r--sound/soc/intel/avs/boards/da7219.c283
-rw-r--r--sound/soc/intel/avs/boards/dmic.c126
-rw-r--r--sound/soc/intel/avs/boards/es8336.c330
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c254
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c129
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c158
-rw-r--r--sound/soc/intel/avs/boards/max98373.c213
-rw-r--r--sound/soc/intel/avs/boards/max98927.c210
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c316
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c155
-rw-r--r--sound/soc/intel/avs/boards/probe.c83
-rw-r--r--sound/soc/intel/avs/boards/rt274.c280
-rw-r--r--sound/soc/intel/avs/boards/rt286.c250
-rw-r--r--sound/soc/intel/avs/boards/rt298.c269
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c196
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c270
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c268
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c345
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c199
-rw-r--r--sound/soc/intel/avs/cldma.c290
-rw-r--r--sound/soc/intel/avs/cldma.h32
-rw-r--r--sound/soc/intel/avs/cnl.c92
-rw-r--r--sound/soc/intel/avs/control.c215
-rw-r--r--sound/soc/intel/avs/control.h27
-rw-r--r--sound/soc/intel/avs/core.c959
-rw-r--r--sound/soc/intel/avs/debug.h91
-rw-r--r--sound/soc/intel/avs/debugfs.c441
-rw-r--r--sound/soc/intel/avs/dsp.c329
-rw-r--r--sound/soc/intel/avs/icl.c203
-rw-r--r--sound/soc/intel/avs/ipc.c583
-rw-r--r--sound/soc/intel/avs/lnl.c28
-rw-r--r--sound/soc/intel/avs/loader.c750
-rw-r--r--sound/soc/intel/avs/messages.c904
-rw-r--r--sound/soc/intel/avs/messages.h1035
-rw-r--r--sound/soc/intel/avs/mtl.c201
-rw-r--r--sound/soc/intel/avs/path.c1603
-rw-r--r--sound/soc/intel/avs/path.h95
-rw-r--r--sound/soc/intel/avs/pcm.c1770
-rw-r--r--sound/soc/intel/avs/pcm.h16
-rw-r--r--sound/soc/intel/avs/probes.c312
-rw-r--r--sound/soc/intel/avs/ptl.c99
-rw-r--r--sound/soc/intel/avs/registers.h184
-rw-r--r--sound/soc/intel/avs/skl.c180
-rw-r--r--sound/soc/intel/avs/sysfs.c35
-rw-r--r--sound/soc/intel/avs/tgl.c87
-rw-r--r--sound/soc/intel/avs/topology.c2140
-rw-r--r--sound/soc/intel/avs/topology.h230
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h156
-rw-r--r--sound/soc/intel/avs/utils.c302
-rw-r--r--sound/soc/intel/avs/utils.h77
-rw-r--r--sound/soc/intel/baytrail/Makefile4
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-dsp.c366
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c782
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.h73
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-pcm.c507
-rw-r--r--sound/soc/intel/boards/Kconfig521
-rw-r--r--sound/soc/intel/boards/Makefile97
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c335
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c243
-rw-r--r--sound/soc/intel/boards/bdw_rt286.c259
-rw-r--r--sound/soc/intel/boards/broadwell.c288
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c602
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c617
-rw-r--r--sound/soc/intel/boards/byt-max98090.c188
-rw-r--r--sound/soc/intel/boards/byt-rt5640.c230
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c295
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c121
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c602
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c57
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c1749
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c965
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c670
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c302
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c308
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c233
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c273
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c316
-rw-r--r--sound/soc/intel/boards/haswell.c209
-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.c173
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c1048
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c691
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c178
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c685
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c742
-rw-r--r--sound/soc/intel/boards/skl_rt286.c565
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.c783
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.h173
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c206
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.h26
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c307
-rw-r--r--sound/soc/intel/boards/sof_da7219.c491
-rw-r--r--sound/soc/intel/boards/sof_es8336.c856
-rw-r--r--sound/soc/intel/boards/sof_hdmi_common.h24
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c610
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h48
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c345
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.c73
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.h22
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c443
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c687
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h63
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c925
-rw-r--r--sound/soc/intel/boards/sof_sdw.c1444
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h66
-rw-r--r--sound/soc/intel/boards/sof_sdw_hdmi.c43
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c245
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c302
-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.c1204
-rw-r--r--sound/soc/intel/catpt/registers.h178
-rw-r--r--sound/soc/intel/catpt/sysfs.c56
-rw-r--r--sound/soc/intel/catpt/trace.h83
-rw-r--r--sound/soc/intel/common/Makefile29
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c783
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c534
-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.c202
-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.c203
-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.c84
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ehl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c68
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hda-match.c24
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c53
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-icl-match.c189
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c117
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-kbl-match.c119
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c801
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c1246
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c746
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c568
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c42
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c208
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h18
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-skl-match.c39
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ssp-common.c168
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c834
-rw-r--r--sound/soc/intel/common/soc-intel-quirks.h98
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c136
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.h15
-rw-r--r--sound/soc/intel/common/sst-acpi.c244
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h392
-rw-r--r--sound/soc/intel/common/sst-dsp.c424
-rw-r--r--sound/soc/intel/common/sst-dsp.h293
-rw-r--r--sound/soc/intel/common/sst-firmware.c1280
-rw-r--r--sound/soc/intel/common/sst-ipc.c327
-rw-r--r--sound/soc/intel/common/sst-ipc.h96
-rw-r--r--sound/soc/intel/haswell/Makefile4
-rw-r--r--sound/soc/intel/haswell/sst-haswell-dsp.c709
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c2225
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.h534
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c1418
-rw-r--r--sound/soc/intel/keembay/Makefile4
-rw-r--r--sound/soc/intel/keembay/kmb_platform.c928
-rw-r--r--sound/soc/intel/keembay/kmb_platform.h156
-rw-r--r--sound/soc/intel/skylake/Makefile21
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c642
-rw-r--r--sound/soc/intel/skylake/cnl-sst-dsp.c274
-rw-r--r--sound/soc/intel/skylake/cnl-sst-dsp.h112
-rw-r--r--sound/soc/intel/skylake/cnl-sst.c497
-rw-r--r--sound/soc/intel/skylake/skl-debug.c261
-rw-r--r--sound/soc/intel/skylake/skl-i2s.h95
-rw-r--r--sound/soc/intel/skylake/skl-messages.c1389
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c451
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.h128
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c1451
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.c429
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.h117
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c375
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.h251
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c470
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h266
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c1033
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h221
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c448
-rw-r--r--sound/soc/intel/skylake/skl-sst.c641
-rw-r--r--sound/soc/intel/skylake/skl-topology.c3502
-rw-r--r--sound/soc/intel/skylake/skl-topology.h508
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h172
-rw-r--r--sound/soc/intel/skylake/skl.c1166
-rw-r--r--sound/soc/intel/skylake/skl.h175
-rw-r--r--sound/soc/jz4740/Kconfig27
-rw-r--r--sound/soc/jz4740/Makefile8
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c650
-rw-r--r--sound/soc/jz4740/jz4740-i2s.h16
-rw-r--r--sound/soc/jz4740/qi_lb60.c112
-rw-r--r--sound/soc/kirkwood/Kconfig5
-rw-r--r--sound/soc/kirkwood/Makefile5
-rw-r--r--sound/soc/kirkwood/armada-370-db.c60
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c140
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c176
-rw-r--r--sound/soc/kirkwood/kirkwood.h12
-rw-r--r--sound/soc/loongson/Kconfig49
-rw-r--r--sound/soc/loongson/Makefile15
-rw-r--r--sound/soc/loongson/loongson1_ac97.c398
-rw-r--r--sound/soc/loongson/loongson_card.c220
-rw-r--r--sound/soc/loongson/loongson_dma.c345
-rw-r--r--sound/soc/loongson/loongson_dma.h16
-rw-r--r--sound/soc/loongson/loongson_i2s.c276
-rw-r--r--sound/soc/loongson/loongson_i2s.h71
-rw-r--r--sound/soc/loongson/loongson_i2s_pci.c162
-rw-r--r--sound/soc/loongson/loongson_i2s_plat.c185
-rw-r--r--sound/soc/mediatek/Kconfig238
-rw-r--r--sound/soc/mediatek/Makefile9
-rw-r--r--sound/soc/mediatek/common/Makefile20
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c466
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h28
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c129
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h25
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h87
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c1410
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.c70
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.h45
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c274
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.h43
-rw-r--r--sound/soc/mediatek/common/mtk-soc-card.h23
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c349
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.h56
-rw-r--r--sound/soc/mediatek/mt2701/Makefile16
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c66
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h23
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h59
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c439
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c220
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h11
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c76
-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.c900
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-adda.c323
-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.c255
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-reg.h1015
-rw-r--r--sound/soc/mediatek/mt7986/Makefile9
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-common.h49
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-pcm.c609
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-dai-etdm.c426
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-reg.h196
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-wm8960.c175
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h10
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c225
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c87
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c126
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c175
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c185
-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.c979
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c887
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-adda.c426
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-i2s.c1058
-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.c899
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-reg.h1668
-rw-r--r--sound/soc/mediatek/mt8186/Makefile21
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c589
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.h103
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-common.h198
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-control.c254
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.c242
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c3005
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.c152
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h45
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c776
-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.c643
-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.c1376
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-reg.h2913
-rw-r--r--sound/soc/mediatek/mt8188/Makefile16
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c755
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h137
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h153
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c3401
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c210
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h87
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c512
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-dmic.c683
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c2713
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-pcm.c368
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c1475
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h3195
-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.c161
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.c307
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c2341
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c1365
-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.c1239
-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.c716
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.h119
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-common.h158
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c3209
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.c215
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h93
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c753
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c2779
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c369
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c1576
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-reg.h2797
-rw-r--r--sound/soc/mediatek/mt8365/Makefile15
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.c420
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.h32
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-common.h448
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-pcm.c2273
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-adda.c311
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-dmic.c310
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-i2s.c845
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-pcm.c293
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-mt6357.c346
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-reg.h993
-rw-r--r--sound/soc/meson/Kconfig138
-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.c334
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c209
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c173
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c188
-rw-r--r--sound/soc/meson/aiu-fifo.c215
-rw-r--r--sound/soc/meson/aiu-fifo.h48
-rw-r--r--sound/soc/meson/aiu.c358
-rw-r--r--sound/soc/meson/aiu.h88
-rw-r--r--sound/soc/meson/axg-card.c373
-rw-r--r--sound/soc/meson/axg-fifo.c398
-rw-r--r--sound/soc/meson/axg-fifo.h95
-rw-r--r--sound/soc/meson/axg-frddr.c400
-rw-r--r--sound/soc/meson/axg-pdm.c641
-rw-r--r--sound/soc/meson/axg-spdifin.c496
-rw-r--r--sound/soc/meson/axg-spdifout.c447
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c437
-rw-r--r--sound/soc/meson/axg-tdm-formatter.h45
-rw-r--r--sound/soc/meson/axg-tdm-interface.c584
-rw-r--r--sound/soc/meson/axg-tdm.h81
-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.c353
-rw-r--r--sound/soc/meson/g12a-toacodec.c358
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c287
-rw-r--r--sound/soc/meson/gx-card.c142
-rw-r--r--sound/soc/meson/meson-card-utils.c334
-rw-r--r--sound/soc/meson/meson-card.h54
-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.c314
-rw-r--r--sound/soc/mxs/Kconfig3
-rw-r--r--sound/soc/mxs/Makefile6
-rw-r--r--sound/soc/mxs/mxs-pcm.c16
-rw-r--r--sound/soc/mxs/mxs-pcm.h15
-rw-r--r--sound/soc/mxs/mxs-saif.c58
-rw-r--r--sound/soc/mxs/mxs-saif.h15
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c68
-rw-r--r--sound/soc/nuc900/Kconfig28
-rw-r--r--sound/soc/nuc900/Makefile12
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c397
-rw-r--r--sound/soc/nuc900/nuc900-audio.c75
-rw-r--r--sound/soc/nuc900/nuc900-audio.h112
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c324
-rw-r--r--sound/soc/omap/Kconfig125
-rw-r--r--sound/soc/omap/Makefile32
-rw-r--r--sound/soc/omap/am3517evm.c141
-rw-r--r--sound/soc/omap/mcbsp.c1104
-rw-r--r--sound/soc/omap/omap-mcbsp.c906
-rw-r--r--sound/soc/omap/omap-mcbsp.h44
-rw-r--r--sound/soc/omap/omap-pcm.c261
-rw-r--r--sound/soc/pxa/Kconfig202
-rw-r--r--sound/soc/pxa/Makefile49
-rw-r--r--sound/soc/pxa/brownstone.c136
-rw-r--r--sound/soc/pxa/corgi.c319
-rw-r--r--sound/soc/pxa/e740_wm9705.c167
-rw-r--r--sound/soc/pxa/e750_wm9705.c150
-rw-r--r--sound/soc/pxa/e800_wm9712.c149
-rw-r--r--sound/soc/pxa/em-x270.c93
-rw-r--r--sound/soc/pxa/hx4700.c217
-rw-r--r--sound/soc/pxa/imote2.c95
-rw-r--r--sound/soc/pxa/magician.c534
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c209
-rw-r--r--sound/soc/pxa/mmp-pcm.c250
-rw-r--r--sound/soc/pxa/mmp-sspa.c481
-rw-r--r--sound/soc/pxa/mmp-sspa.h48
-rw-r--r--sound/soc/pxa/palm27x.c161
-rw-r--r--sound/soc/pxa/poodle.c291
-rw-r--r--sound/soc/pxa/pxa-ssp.c342
-rw-r--r--sound/soc/pxa/pxa-ssp.h5
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c143
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c157
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h5
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c93
-rw-r--r--sound/soc/pxa/raumfeld.c318
-rw-r--r--sound/soc/pxa/spitz.c91
-rw-r--r--sound/soc/pxa/tosa.c263
-rw-r--r--sound/soc/pxa/ttc-dkb.c153
-rw-r--r--sound/soc/pxa/z2.c220
-rw-r--r--sound/soc/pxa/zylonite.c274
-rw-r--r--sound/soc/qcom/Kconfig238
-rw-r--r--sound/soc/qcom/Makefile43
-rw-r--r--sound/soc/qcom/apq8016_sbc.c284
-rw-r--r--sound/soc/qcom/apq8096.c145
-rw-r--r--sound/soc/qcom/common.c278
-rw-r--r--sound/soc/qcom/common.h16
-rw-r--r--sound/soc/qcom/lpass-apq8016.c119
-rw-r--r--sound/soc/qcom/lpass-cdc-dma.c303
-rw-r--r--sound/soc/qcom/lpass-cpu.c1155
-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.c90
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h379
-rw-r--r--sound/soc/qcom/lpass-platform.c1216
-rw-r--r--sound/soc/qcom/lpass-sc7180.c325
-rw-r--r--sound/soc/qcom/lpass-sc7280.c455
-rw-r--r--sound/soc/qcom/lpass.h349
-rw-r--r--sound/soc/qcom/qdsp6/Makefile20
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c1441
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h821
-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.c120
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c1147
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c1962
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h274
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c895
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c324
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c851
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h156
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c1334
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c1753
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h151
-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.c103
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h25
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-errno.h51
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c185
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h30
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c658
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h23
-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.c1176
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.h9
-rw-r--r--sound/soc/qcom/qdsp6/q6usb.c421
-rw-r--r--sound/soc/qcom/qdsp6/topology.c1340
-rw-r--r--sound/soc/qcom/sc7180.c580
-rw-r--r--sound/soc/qcom/sc7280.c461
-rw-r--r--sound/soc/qcom/sc8280xp.c219
-rw-r--r--sound/soc/qcom/sdm845.c638
-rw-r--r--sound/soc/qcom/sdw.c192
-rw-r--r--sound/soc/qcom/sdw.h19
-rw-r--r--sound/soc/qcom/sm8250.c231
-rw-r--r--sound/soc/qcom/storm.c34
-rw-r--r--sound/soc/qcom/usb_offload_utils.c56
-rw-r--r--sound/soc/qcom/usb_offload_utils.h30
-rw-r--r--sound/soc/qcom/x1e80100.c237
-rw-r--r--sound/soc/renesas/Kconfig (renamed from sound/soc/sh/Kconfig)28
-rw-r--r--sound/soc/renesas/Makefile (renamed from sound/soc/sh/Makefile)18
-rw-r--r--sound/soc/renesas/dma-sh7760.c (renamed from sound/soc/sh/dma-sh7760.c)121
-rw-r--r--sound/soc/renesas/fsi.c (renamed from sound/soc/sh/fsi.c)175
-rw-r--r--sound/soc/renesas/hac.c (renamed from sound/soc/sh/hac.c)30
-rw-r--r--sound/soc/renesas/migor.c (renamed from sound/soc/sh/migor.c)36
-rw-r--r--sound/soc/renesas/rcar/Makefile6
-rw-r--r--sound/soc/renesas/rcar/adg.c819
-rw-r--r--sound/soc/renesas/rcar/cmd.c (renamed from sound/soc/sh/rcar/cmd.c)78
-rw-r--r--sound/soc/renesas/rcar/core.c (renamed from sound/soc/sh/rcar/core.c)1129
-rw-r--r--sound/soc/renesas/rcar/ctu.c (renamed from sound/soc/sh/rcar/ctu.c)191
-rw-r--r--sound/soc/renesas/rcar/debugfs.c96
-rw-r--r--sound/soc/renesas/rcar/dma.c (renamed from sound/soc/sh/rcar/dma.c)431
-rw-r--r--sound/soc/renesas/rcar/dvc.c (renamed from sound/soc/sh/rcar/dvc.c)73
-rw-r--r--sound/soc/renesas/rcar/gen.c495
-rw-r--r--sound/soc/renesas/rcar/mix.c (renamed from sound/soc/sh/rcar/mix.c)51
-rw-r--r--sound/soc/renesas/rcar/msiof.c629
-rw-r--r--sound/soc/renesas/rcar/rsnd.h (renamed from sound/soc/sh/rcar/rsnd.h)586
-rw-r--r--sound/soc/renesas/rcar/src.c (renamed from sound/soc/sh/rcar/src.c)386
-rw-r--r--sound/soc/renesas/rcar/ssi.c (renamed from sound/soc/sh/rcar/ssi.c)638
-rw-r--r--sound/soc/renesas/rcar/ssiu.c606
-rw-r--r--sound/soc/renesas/rz-ssi.c1258
-rw-r--r--sound/soc/renesas/sh7760-ac97.c (renamed from sound/soc/sh/sh7760-ac97.c)25
-rw-r--r--sound/soc/renesas/siu.h (renamed from sound/soc/sh/siu.h)30
-rw-r--r--sound/soc/renesas/siu_dai.c (renamed from sound/soc/sh/siu_dai.c)41
-rw-r--r--sound/soc/renesas/siu_pcm.c (renamed from sound/soc/sh/siu_pcm.c)163
-rw-r--r--sound/soc/renesas/ssi.c (renamed from sound/soc/sh/ssi.c)36
-rw-r--r--sound/soc/rockchip/Kconfig50
-rw-r--r--sound/soc/rockchip/Makefile18
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c114
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c237
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c494
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h15
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c1436
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.h400
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c388
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c346
-rw-r--r--sound/soc/rockchip/rockchip_pdm.h29
-rw-r--r--sound/soc/rockchip/rockchip_rt5645.c96
-rw-r--r--sound/soc/rockchip/rockchip_sai.c1529
-rw-r--r--sound/soc/rockchip/rockchip_sai.h251
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c127
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h5
-rw-r--r--sound/soc/samsung/Kconfig133
-rw-r--r--sound/soc/samsung/Makefile69
-rw-r--r--sound/soc/samsung/aries_wm8994.c702
-rw-r--r--sound/soc/samsung/arndale.c217
-rw-r--r--sound/soc/samsung/arndale_rt5631.c140
-rw-r--r--sound/soc/samsung/bells.c180
-rw-r--r--sound/soc/samsung/dma.h11
-rw-r--r--sound/soc/samsung/dmaengine.c38
-rw-r--r--sound/soc/samsung/h1940_uda1380.c252
-rw-r--r--sound/soc/samsung/i2s-regs.h20
-rw-r--r--sound/soc/samsung/i2s.c874
-rw-r--r--sound/soc/samsung/i2s.h19
-rw-r--r--sound/soc/samsung/idma.c89
-rw-r--r--sound/soc/samsung/idma.h9
-rw-r--r--sound/soc/samsung/jive_wm8750.c146
-rw-r--r--sound/soc/samsung/littlemill.c112
-rw-r--r--sound/soc/samsung/lowland.c89
-rw-r--r--sound/soc/samsung/midas_wm1811.c775
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c395
-rw-r--r--sound/soc/samsung/odroid.c266
-rw-r--r--sound/soc/samsung/pcm.c68
-rw-r--r--sound/soc/samsung/pcm.h8
-rw-r--r--sound/soc/samsung/regs-i2s-v2.h115
-rw-r--r--sound/soc/samsung/regs-iis.h69
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c273
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c741
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.h114
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c208
-rw-r--r--sound/soc/samsung/s3c2412-i2s.h27
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c476
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.h35
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.c372
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.h22
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_hermes.c114
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c102
-rw-r--r--sound/soc/samsung/s3c24xx_uda134x.c259
-rw-r--r--sound/soc/samsung/smartq_wm8987.c229
-rw-r--r--sound/soc/samsung/smdk_spdif.c30
-rw-r--r--sound/soc/samsung/smdk_wm8580.c214
-rw-r--r--sound/soc/samsung/smdk_wm8994.c78
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c38
-rw-r--r--sound/soc/samsung/snow.c265
-rw-r--r--sound/soc/samsung/spdif.c69
-rw-r--r--sound/soc/samsung/spdif.h8
-rw-r--r--sound/soc/samsung/speyside.c151
-rw-r--r--sound/soc/samsung/tm2_wm5110.c312
-rw-r--r--sound/soc/samsung/tobermory.c65
-rw-r--r--sound/soc/sdca/Kconfig31
-rw-r--r--sound/soc/sdca/Makefile7
-rw-r--r--sound/soc/sdca/sdca_asoc.c1616
-rw-r--r--sound/soc/sdca/sdca_device.c88
-rw-r--r--sound/soc/sdca/sdca_functions.c2041
-rw-r--r--sound/soc/sdca/sdca_hid.c127
-rw-r--r--sound/soc/sdca/sdca_interrupts.c444
-rw-r--r--sound/soc/sdca/sdca_regmap.c335
-rw-r--r--sound/soc/sdw_utils/Kconfig6
-rw-r--r--sound/soc/sdw_utils/Makefile11
-rw-r--r--sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c155
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l42.c91
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l43.c171
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c124
-rw-r--r--sound/soc/sdw_utils/soc_sdw_dmic.c45
-rw-r--r--sound/soc/sdw_utils/soc_sdw_maxim.c139
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt5682.c91
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt700.c88
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt711.c160
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp.c306
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h300
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_dmic.c45
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c83
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c222
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c92
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c1471
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/adg.c644
-rw-r--r--sound/soc/sh/rcar/gen.c424
-rw-r--r--sound/soc/sh/rcar/ssiu.c292
-rw-r--r--sound/soc/sirf/Kconfig20
-rw-r--r--sound/soc/sirf/Makefile8
-rw-r--r--sound/soc/sirf/sirf-audio-port.c87
-rw-r--r--sound/soc/sirf/sirf-audio.c156
-rw-r--r--sound/soc/sirf/sirf-usp.c439
-rw-r--r--sound/soc/sirf/sirf-usp.h293
-rw-r--r--sound/soc/soc-ac97.c221
-rw-r--r--sound/soc/soc-acpi.c144
-rw-r--r--sound/soc/soc-cache.c53
-rw-r--r--sound/soc/soc-card-test.c129
-rw-r--r--sound/soc/soc-card.c248
-rw-r--r--sound/soc/soc-component.c1300
-rw-r--r--sound/soc/soc-compress.c1161
-rw-r--r--sound/soc/soc-core.c5066
-rw-r--r--sound/soc/soc-dai.c815
-rw-r--r--sound/soc/soc-dapm.c2795
-rw-r--r--sound/soc/soc-devres.c65
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c381
-rw-r--r--sound/soc/soc-io.c293
-rw-r--r--sound/soc/soc-jack.c143
-rw-r--r--sound/soc/soc-link.c213
-rw-r--r--sound/soc/soc-ops-test.c548
-rw-r--r--sound/soc/soc-ops.c767
-rw-r--r--sound/soc/soc-pcm.c3969
-rw-r--r--sound/soc/soc-topology-test.c821
-rw-r--r--sound/soc/soc-topology.c2329
-rw-r--r--sound/soc/soc-usb.c322
-rw-r--r--sound/soc/soc-utils-test.c232
-rw-r--r--sound/soc/soc-utils.c440
-rw-r--r--sound/soc/sof/Kconfig306
-rw-r--r--sound/soc/sof/Makefile61
-rw-r--r--sound/soc/sof/amd/Kconfig107
-rw-r--r--sound/soc/sof/amd/Makefile20
-rw-r--r--sound/soc/sof/amd/acp-common.c267
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h143
-rw-r--r--sound/soc/sof/amd/acp-ipc.c310
-rw-r--r--sound/soc/sof/amd/acp-loader.c320
-rw-r--r--sound/soc/sof/amd/acp-pcm.c120
-rw-r--r--sound/soc/sof/amd/acp-probes.c147
-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.c968
-rw-r--r--sound/soc/sof/amd/acp.h373
-rw-r--r--sound/soc/sof/amd/acp63.c142
-rw-r--r--sound/soc/sof/amd/acp70.c142
-rw-r--r--sound/soc/sof/amd/pci-acp63.c115
-rw-r--r--sound/soc/sof/amd/pci-acp70.c121
-rw-r--r--sound/soc/sof/amd/pci-rmb.c104
-rw-r--r--sound/soc/sof/amd/pci-rn.c108
-rw-r--r--sound/soc/sof/amd/pci-vangogh.c101
-rw-r--r--sound/soc/sof/amd/rembrandt.c142
-rw-r--r--sound/soc/sof/amd/renoir.c117
-rw-r--r--sound/soc/sof/amd/vangogh.c177
-rw-r--r--sound/soc/sof/compress.c391
-rw-r--r--sound/soc/sof/control.c220
-rw-r--r--sound/soc/sof/core.c843
-rw-r--r--sound/soc/sof/debug.c469
-rw-r--r--sound/soc/sof/fw-file-profile.c334
-rw-r--r--sound/soc/sof/imx/Kconfig44
-rw-r--r--sound/soc/sof/imx/Makefile9
-rw-r--r--sound/soc/sof/imx/imx-common.c484
-rw-r--r--sound/soc/sof/imx/imx-common.h169
-rw-r--r--sound/soc/sof/imx/imx8.c473
-rw-r--r--sound/soc/sof/imx/imx9.c137
-rw-r--r--sound/soc/sof/intel/Kconfig397
-rw-r--r--sound/soc/sof/intel/Makefile51
-rw-r--r--sound/soc/sof/intel/apl.c121
-rw-r--r--sound/soc/sof/intel/atom.c421
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/bdw.c698
-rw-r--r--sound/soc/sof/intel/byt.c480
-rw-r--r--sound/soc/sof/intel/cnl.c519
-rw-r--r--sound/soc/sof/intel/ext_manifest.h35
-rw-r--r--sound/soc/sof/intel/hda-bus.c112
-rw-r--r--sound/soc/sof/intel/hda-codec.c458
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c108
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c338
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c660
-rw-r--r--sound/soc/sof/intel/hda-dai.c942
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1634
-rw-r--r--sound/soc/sof/intel/hda-ipc.c556
-rw-r--r--sound/soc/sof/intel/hda-ipc.h56
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c578
-rw-r--r--sound/soc/sof/intel/hda-loader.c728
-rw-r--r--sound/soc/sof/intel/hda-mlink.c1120
-rw-r--r--sound/soc/sof/intel/hda-pcm.c353
-rw-r--r--sound/soc/sof/intel/hda-probes.c150
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c445
-rw-r--r--sound/soc/sof/intel/hda-stream.c1213
-rw-r--r--sound/soc/sof/intel/hda-trace.c98
-rw-r--r--sound/soc/sof/intel/hda.c1548
-rw-r--r--sound/soc/sof/intel/hda.h1055
-rw-r--r--sound/soc/sof/intel/icl.c196
-rw-r--r--sound/soc/sof/intel/lnl.c189
-rw-r--r--sound/soc/sof/intel/lnl.h21
-rw-r--r--sound/soc/sof/intel/mtl.c817
-rw-r--r--sound/soc/sof/intel/mtl.h134
-rw-r--r--sound/soc/sof/intel/pci-apl.c111
-rw-r--r--sound/soc/sof/intel/pci-cnl.c149
-rw-r--r--sound/soc/sof/intel/pci-icl.c115
-rw-r--r--sound/soc/sof/intel/pci-lnl.c84
-rw-r--r--sound/soc/sof/intel/pci-mtl.c147
-rw-r--r--sound/soc/sof/intel/pci-ptl.c113
-rw-r--r--sound/soc/sof/intel/pci-skl.c95
-rw-r--r--sound/soc/sof/intel/pci-tgl.c324
-rw-r--r--sound/soc/sof/intel/pci-tng.c250
-rw-r--r--sound/soc/sof/intel/ptl.c156
-rw-r--r--sound/soc/sof/intel/ptl.h19
-rw-r--r--sound/soc/sof/intel/shim.h218
-rw-r--r--sound/soc/sof/intel/skl.c117
-rw-r--r--sound/soc/sof/intel/telemetry.c96
-rw-r--r--sound/soc/sof/intel/telemetry.h35
-rw-r--r--sound/soc/sof/intel/tgl.c252
-rw-r--r--sound/soc/sof/intel/tracepoints.c5
-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.c731
-rw-r--r--sound/soc/sof/ipc3-dtrace.c664
-rw-r--r--sound/soc/sof/ipc3-loader.c419
-rw-r--r--sound/soc/sof/ipc3-pcm.c439
-rw-r--r--sound/soc/sof/ipc3-priv.h67
-rw-r--r--sound/soc/sof/ipc3-topology.c2713
-rw-r--r--sound/soc/sof/ipc3.c1157
-rw-r--r--sound/soc/sof/ipc4-control.c867
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-loader.c633
-rw-r--r--sound/soc/sof/ipc4-mtrace.c672
-rw-r--r--sound/soc/sof/ipc4-pcm.c1324
-rw-r--r--sound/soc/sof/ipc4-priv.h132
-rw-r--r--sound/soc/sof/ipc4-telemetry.c95
-rw-r--r--sound/soc/sof/ipc4-telemetry.h73
-rw-r--r--sound/soc/sof/ipc4-topology.c3809
-rw-r--r--sound/soc/sof/ipc4-topology.h528
-rw-r--r--sound/soc/sof/ipc4.c906
-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.h50
-rw-r--r--sound/soc/sof/mediatek/mt8186/Makefile4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c100
-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.c570
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h93
-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.c526
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h161
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c215
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.h20
-rw-r--r--sound/soc/sof/nocodec.c115
-rw-r--r--sound/soc/sof/ops.c187
-rw-r--r--sound/soc/sof/ops.h663
-rw-r--r--sound/soc/sof/pcm.c793
-rw-r--r--sound/soc/sof/pm.c389
-rw-r--r--sound/soc/sof/sof-acpi-dev.c102
-rw-r--r--sound/soc/sof/sof-acpi-dev.h16
-rw-r--r--sound/soc/sof/sof-audio.c979
-rw-r--r--sound/soc/sof/sof-audio.h685
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c383
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c161
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c339
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c243
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c409
-rw-r--r--sound/soc/sof/sof-client-probes.c582
-rw-r--r--sound/soc/sof/sof-client-probes.h72
-rw-r--r--sound/soc/sof/sof-client.c669
-rw-r--r--sound/soc/sof/sof-client.h76
-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.c296
-rw-r--r--sound/soc/sof/sof-pci-dev.h17
-rw-r--r--sound/soc/sof/sof-priv.h904
-rw-r--r--sound/soc/sof/sof-utils.c76
-rw-r--r--sound/soc/sof/sof-utils.h19
-rw-r--r--sound/soc/sof/stream-ipc.c131
-rw-r--r--sound/soc/sof/topology.c2576
-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.c155
-rw-r--r--sound/soc/spear/Kconfig5
-rw-r--r--sound/soc/spear/Makefile6
-rw-r--r--sound/soc/spear/spdif_in.c8
-rw-r--r--sound/soc/spear/spdif_in_regs.h15
-rw-r--r--sound/soc/spear/spdif_out.c17
-rw-r--r--sound/soc/spear/spdif_out_regs.h15
-rw-r--r--sound/soc/spear/spear_pcm.h13
-rw-r--r--sound/soc/sprd/Kconfig20
-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/starfive/Kconfig20
-rw-r--r--sound/soc/starfive/Makefile3
-rw-r--r--sound/soc/starfive/jh7110_pwmdac.c528
-rw-r--r--sound/soc/starfive/jh7110_tdm.c662
-rw-r--r--sound/soc/sti/Kconfig3
-rw-r--r--sound/soc/sti/Makefile3
-rw-r--r--sound/soc/sti/sti_uniperif.c49
-rw-r--r--sound/soc/sti/uniperif.h9
-rw-r--r--sound/soc/sti/uniperif_player.c10
-rw-r--r--sound/soc/sti/uniperif_reader.c3
-rw-r--r--sound/soc/stm/Kconfig6
-rw-r--r--sound/soc/stm/Makefile8
-rw-r--r--sound/soc/stm/stm32_adfsdm.c222
-rw-r--r--sound/soc/stm/stm32_i2s.c818
-rw-r--r--sound/soc/stm/stm32_sai.c238
-rw-r--r--sound/soc/stm/stm32_sai.h73
-rw-r--r--sound/soc/stm/stm32_sai_sub.c1105
-rw-r--r--sound/soc/stm/stm32_spdifrx.c248
-rw-r--r--sound/soc/sunxi/Kconfig28
-rw-r--r--sound/soc/sunxi/Makefile3
-rw-r--r--sound/soc/sunxi/sun4i-codec.c1149
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c1202
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c233
-rw-r--r--sound/soc/sunxi/sun50i-codec-analog.c596
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c437
-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.c95
-rw-r--r--sound/soc/sunxi/sun8i-codec.c1689
-rw-r--r--sound/soc/tegra/Kconfig194
-rw-r--r--sound/soc/tegra/Makefile63
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1052
-rw-r--r--sound/soc/tegra/tegra186_asrc.h116
-rw-r--r--sound/soc/tegra/tegra186_dspk.c554
-rw-r--r--sound/soc/tegra/tegra186_dspk.h70
-rw-r--r--sound/soc/tegra/tegra20_ac97.c114
-rw-r--r--sound/soc/tegra/tegra20_ac97.h16
-rw-r--r--sound/soc/tegra/tegra20_das.c221
-rw-r--r--sound/soc/tegra/tegra20_das.h134
-rw-r--r--sound/soc/tegra/tegra20_i2s.c153
-rw-r--r--sound/soc/tegra/tegra20_i2s.h17
-rw-r--r--sound/soc/tegra/tegra20_spdif.c236
-rw-r--r--sound/soc/tegra/tegra20_spdif.h17
-rw-r--r--sound/soc/tegra/tegra210_admaif.c1045
-rw-r--r--sound/soc/tegra/tegra210_admaif.h241
-rw-r--r--sound/soc/tegra/tegra210_adx.c752
-rw-r--r--sound/soc/tegra/tegra210_adx.h96
-rw-r--r--sound/soc/tegra/tegra210_ahub.c2272
-rw-r--r--sound/soc/tegra/tegra210_ahub.h171
-rw-r--r--sound/soc/tegra/tegra210_amx.c801
-rw-r--r--sound/soc/tegra/tegra210_amx.h113
-rw-r--r--sound/soc/tegra/tegra210_dmic.c570
-rw-r--r--sound/soc/tegra/tegra210_dmic.h82
-rw-r--r--sound/soc/tegra/tegra210_i2s.c1172
-rw-r--r--sound/soc/tegra/tegra210_i2s.h178
-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.c687
-rw-r--r--sound/soc/tegra/tegra210_mixer.h100
-rw-r--r--sound/soc/tegra/tegra210_mvc.c779
-rw-r--r--sound/soc/tegra/tegra210_mvc.h122
-rw-r--r--sound/soc/tegra/tegra210_ope.c420
-rw-r--r--sound/soc/tegra/tegra210_ope.h90
-rw-r--r--sound/soc/tegra/tegra210_peq.c433
-rw-r--r--sound/soc/tegra/tegra210_peq.h56
-rw-r--r--sound/soc/tegra/tegra210_sfc.c3644
-rw-r--r--sound/soc/tegra/tegra210_sfc.h78
-rw-r--r--sound/soc/tegra/tegra30_ahub.c248
-rw-r--r--sound/soc/tegra/tegra30_ahub.h21
-rw-r--r--sound/soc/tegra/tegra30_i2s.c146
-rw-r--r--sound/soc/tegra/tegra30_i2s.h15
-rw-r--r--sound/soc/tegra/tegra_alc5632.c249
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c1023
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.h51
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c240
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h53
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c267
-rw-r--r--sound/soc/tegra/tegra_cif.h87
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.c130
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.h31
-rw-r--r--sound/soc/tegra/tegra_max98090.c304
-rw-r--r--sound/soc/tegra/tegra_pcm.c171
-rw-r--r--sound/soc/tegra/tegra_pcm.h31
-rw-r--r--sound/soc/tegra/tegra_rt5640.c250
-rw-r--r--sound/soc/tegra/tegra_rt5677.c322
-rw-r--r--sound/soc/tegra/tegra_sgtl5000.c209
-rw-r--r--sound/soc/tegra/tegra_wm8753.c216
-rw-r--r--sound/soc/tegra/tegra_wm8903.c382
-rw-r--r--sound/soc/tegra/tegra_wm9712.c175
-rw-r--r--sound/soc/tegra/trimslice.c203
-rw-r--r--sound/soc/ti/Kconfig195
-rw-r--r--sound/soc/ti/Makefile46
-rw-r--r--sound/soc/ti/ams-delta.c (renamed from sound/soc/omap/ams-delta.c)160
-rw-r--r--sound/soc/ti/davinci-evm.c261
-rw-r--r--sound/soc/ti/davinci-i2s.c (renamed from sound/soc/davinci/davinci-i2s.c)432
-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.c (renamed from sound/soc/davinci/davinci-mcasp.c)1300
-rw-r--r--sound/soc/ti/davinci-mcasp.h (renamed from sound/soc/davinci/davinci-mcasp.h)39
-rw-r--r--sound/soc/ti/edma-pcm.c (renamed from sound/soc/davinci/edma-pcm.c)30
-rw-r--r--sound/soc/ti/edma-pcm.h24
-rw-r--r--sound/soc/ti/j721e-evm.c938
-rw-r--r--sound/soc/ti/n810.c (renamed from sound/soc/omap/n810.c)82
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c (renamed from sound/soc/omap/omap-abe-twl6040.c)138
-rw-r--r--sound/soc/ti/omap-dmic.c (renamed from sound/soc/omap/omap-dmic.c)83
-rw-r--r--sound/soc/ti/omap-dmic.h (renamed from sound/soc/omap/omap-dmic.h)5
-rw-r--r--sound/soc/ti/omap-hdmi.c (renamed from sound/soc/omap/omap-hdmi-audio.c)60
-rw-r--r--sound/soc/ti/omap-mcbsp-priv.h (renamed from sound/soc/omap/mcbsp.h)126
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c502
-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.c (renamed from sound/soc/omap/omap-mcpdm.c)119
-rw-r--r--sound/soc/ti/omap-mcpdm.h (renamed from sound/soc/omap/omap-mcpdm.h)16
-rw-r--r--sound/soc/ti/omap-twl4030.c (renamed from sound/soc/omap/omap-twl4030.c)92
-rw-r--r--sound/soc/ti/omap3pandora.c (renamed from sound/soc/omap/omap3pandora.c)109
-rw-r--r--sound/soc/ti/osk5912.c (renamed from sound/soc/omap/osk5912.c)38
-rw-r--r--sound/soc/ti/rx51.c (renamed from sound/soc/omap/rx51.c)107
-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/Makefile12
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c235
-rw-r--r--sound/soc/txx9/txx9aclc-generic.c89
-rw-r--r--sound/soc/txx9/txx9aclc.c431
-rw-r--r--sound/soc/txx9/txx9aclc.h74
-rw-r--r--sound/soc/uniphier/Kconfig35
-rw-r--r--sound/soc/uniphier/Makefile10
-rw-r--r--sound/soc/uniphier/aio-compress.c433
-rw-r--r--sound/soc/uniphier/aio-core.c1266
-rw-r--r--sound/soc/uniphier/aio-cpu.c832
-rw-r--r--sound/soc/uniphier/aio-dma.c279
-rw-r--r--sound/soc/uniphier/aio-ld11.c356
-rw-r--r--sound/soc/uniphier/aio-pxs2.c265
-rw-r--r--sound/soc/uniphier/aio-reg.h476
-rw-r--r--sound/soc/uniphier/aio.h354
-rw-r--r--sound/soc/uniphier/evea.c148
-rw-r--r--sound/soc/ux500/Kconfig3
-rw-r--r--sound/soc/ux500/Makefile6
-rw-r--r--sound/soc/ux500/mop500.c70
-rw-r--r--sound/soc/ux500/mop500_ab8500.c36
-rw-r--r--sound/soc/ux500/mop500_ab8500.h11
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c96
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h7
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c84
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h21
-rw-r--r--sound/soc/ux500/ux500_pcm.c92
-rw-r--r--sound/soc/ux500/ux500_pcm.h7
-rw-r--r--sound/soc/xilinx/Kconfig27
-rw-r--r--sound/soc/xilinx/Makefile7
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c727
-rw-r--r--sound/soc/xilinx/xlnx_i2s.c259
-rw-r--r--sound/soc/xilinx/xlnx_spdif.c318
-rw-r--r--sound/soc/xtensa/Kconfig5
-rw-r--r--sound/soc/xtensa/Makefile3
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c91
-rw-r--r--sound/soc/zte/Kconfig25
-rw-r--r--sound/soc/zte/Makefile3
-rw-r--r--sound/soc/zte/zx-i2s.c456
-rw-r--r--sound/soc/zte/zx-spdif.c365
-rw-r--r--sound/soc/zte/zx-tdm.c461
-rw-r--r--sound/sound_core.c71
-rw-r--r--sound/sparc/Kconfig1
-rw-r--r--sound/sparc/Makefile6
-rw-r--r--sound/sparc/amd7930.c176
-rw-r--r--sound/sparc/cs4231.c380
-rw-r--r--sound/sparc/dbri.c311
-rw-r--r--sound/spi/Kconfig3
-rw-r--r--sound/spi/Makefile2
-rw-r--r--sound/spi/at73c213.c172
-rw-r--r--sound/spi/at73c213.h19
-rw-r--r--sound/synth/Kconfig1
-rw-r--r--sound/synth/Makefile2
-rw-r--r--sound/synth/emux/Makefile2
-rw-r--r--sound/synth/emux/emux.c54
-rw-r--r--sound/synth/emux/emux_effect.c58
-rw-r--r--sound/synth/emux/emux_hwdep.c39
-rw-r--r--sound/synth/emux/emux_nrpn.c25
-rw-r--r--sound/synth/emux/emux_oss.c31
-rw-r--r--sound/synth/emux/emux_proc.c26
-rw-r--r--sound/synth/emux/emux_seq.c51
-rw-r--r--sound/synth/emux/emux_synth.c97
-rw-r--r--sound/synth/emux/emux_voice.h15
-rw-r--r--sound/synth/emux/soundfont.c320
-rw-r--r--sound/synth/util_mem.c48
-rw-r--r--sound/usb/6fire/Makefile3
-rw-r--r--sound/usb/6fire/chip.c61
-rw-r--r--sound/usb/6fire/chip.h6
-rw-r--r--sound/usb/6fire/comm.c8
-rw-r--r--sound/usb/6fire/comm.h6
-rw-r--r--sound/usb/6fire/common.h6
-rw-r--r--sound/usb/6fire/control.c16
-rw-r--r--sound/usb/6fire/control.h6
-rw-r--r--sound/usb/6fire/firmware.c46
-rw-r--r--sound/usb/6fire/firmware.h6
-rw-r--r--sound/usb/6fire/midi.c29
-rw-r--r--sound/usb/6fire/midi.h6
-rw-r--r--sound/usb/6fire/pcm.c128
-rw-r--r--sound/usb/6fire/pcm.h6
-rw-r--r--sound/usb/Kconfig75
-rw-r--r--sound/usb/Makefile17
-rw-r--r--sound/usb/bcd2000/Makefile3
-rw-r--r--sound/usb/bcd2000/bcd2000.c40
-rw-r--r--sound/usb/caiaq/Makefile1
-rw-r--r--sound/usb/caiaq/audio.c106
-rw-r--r--sound/usb/caiaq/audio.h1
-rw-r--r--sound/usb/caiaq/control.c35
-rw-r--r--sound/usb/caiaq/device.c57
-rw-r--r--sound/usb/caiaq/device.h2
-rw-r--r--sound/usb/caiaq/input.c38
-rw-r--r--sound/usb/caiaq/input.h1
-rw-r--r--sound/usb/caiaq/midi.c17
-rw-r--r--sound/usb/card.c858
-rw-r--r--sound/usb/card.h108
-rw-r--r--sound/usb/clock.c588
-rw-r--r--sound/usb/clock.h13
-rw-r--r--sound/usb/debug.h16
-rw-r--r--sound/usb/endpoint.c1308
-rw-r--r--sound/usb/endpoint.h68
-rw-r--r--sound/usb/fcp.c1127
-rw-r--r--sound/usb/fcp.h7
-rw-r--r--sound/usb/format.c468
-rw-r--r--sound/usb/format.h6
-rw-r--r--sound/usb/helper.c64
-rw-r--r--sound/usb/helper.h21
-rw-r--r--sound/usb/hiface/Makefile3
-rw-r--r--sound/usb/hiface/chip.c40
-rw-r--r--sound/usb/hiface/chip.h6
-rw-r--r--sound/usb/hiface/pcm.c114
-rw-r--r--sound/usb/hiface/pcm.h6
-rw-r--r--sound/usb/implicit.c494
-rw-r--r--sound/usb/implicit.h14
-rw-r--r--sound/usb/line6/Kconfig1
-rw-r--r--sound/usb/line6/capture.c21
-rw-r--r--sound/usb/line6/capture.h8
-rw-r--r--sound/usb/line6/driver.c203
-rw-r--r--sound/usb/line6/driver.h34
-rw-r--r--sound/usb/line6/midi.c30
-rw-r--r--sound/usb/line6/midi.h8
-rw-r--r--sound/usb/line6/midibuf.c35
-rw-r--r--sound/usb/line6/midibuf.h13
-rw-r--r--sound/usb/line6/pcm.c135
-rw-r--r--sound/usb/line6/pcm.h8
-rw-r--r--sound/usb/line6/playback.c15
-rw-r--r--sound/usb/line6/playback.h8
-rw-r--r--sound/usb/line6/pod.c133
-rw-r--r--sound/usb/line6/podhd.c269
-rw-r--r--sound/usb/line6/toneport.c83
-rw-r--r--sound/usb/line6/variax.c154
-rw-r--r--sound/usb/media.c325
-rw-r--r--sound/usb/media.h74
-rw-r--r--sound/usb/midi.c360
-rw-r--r--sound/usb/midi.h7
-rw-r--r--sound/usb/midi2.c1233
-rw-r--r--sound/usb/midi2.h33
-rw-r--r--sound/usb/misc/Makefile3
-rw-r--r--sound/usb/misc/ua101.c319
-rw-r--r--sound/usb/mixer.c2068
-rw-r--r--sound/usb/mixer.h43
-rw-r--r--sound/usb/mixer_maps.c412
-rw-r--r--sound/usb/mixer_quirks.c3243
-rw-r--r--sound/usb/mixer_quirks.h2
-rw-r--r--sound/usb/mixer_s1810c.c650
-rw-r--r--sound/usb/mixer_s1810c.h7
-rw-r--r--sound/usb/mixer_scarlett.c92
-rw-r--r--sound/usb/mixer_scarlett2.c9434
-rw-r--r--sound/usb/mixer_scarlett2.h7
-rw-r--r--sound/usb/mixer_us16x08.c107
-rw-r--r--sound/usb/mixer_us16x08.h2
-rw-r--r--sound/usb/pcm.c1956
-rw-r--r--sound/usb/pcm.h26
-rw-r--r--sound/usb/power.c107
-rw-r--r--sound/usb/power.h30
-rw-r--r--sound/usb/proc.c122
-rw-r--r--sound/usb/qcom/Makefile4
-rw-r--r--sound/usb/qcom/mixer_usb_offload.c155
-rw-r--r--sound/usb/qcom/mixer_usb_offload.h11
-rw-r--r--sound/usb/qcom/qc_audio_offload.c1993
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.c863
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.h164
-rw-r--r--sound/usb/quirks-table.h3542
-rw-r--r--sound/usb/quirks.c1725
-rw-r--r--sound/usb/quirks.h31
-rw-r--r--sound/usb/stream.c1012
-rw-r--r--sound/usb/usbaudio.h208
-rw-r--r--sound/usb/usx2y/Makefile6
-rw-r--r--sound/usb/usx2y/us122l.c226
-rw-r--r--sound/usb/usx2y/us122l.h4
-rw-r--r--sound/usb/usx2y/us144mkii.c620
-rw-r--r--sound/usb/usx2y/us144mkii.h367
-rw-r--r--sound/usb/usx2y/us144mkii_capture.c319
-rw-r--r--sound/usb/usx2y/us144mkii_controls.c444
-rw-r--r--sound/usb/usx2y/us144mkii_midi.c403
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.c370
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.h165
-rw-r--r--sound/usb/usx2y/us144mkii_playback.c456
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c180
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h2
-rw-r--r--sound/usb/usx2y/usb_stream.c201
-rw-r--r--sound/usb/usx2y/usb_stream.h24
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h119
-rw-r--r--sound/usb/usx2y/usbusx2y.c414
-rw-r--r--sound/usb/usx2y/usbusx2y.h97
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c803
-rw-r--r--sound/usb/usx2y/usx2y.h15
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c705
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h4
-rw-r--r--sound/usb/validate.c366
-rw-r--r--sound/virtio/Kconfig10
-rw-r--r--sound/virtio/Makefile14
-rw-r--r--sound/virtio/virtio_card.c450
-rw-r--r--sound/virtio/virtio_card.h133
-rw-r--r--sound/virtio/virtio_chmap.c219
-rw-r--r--sound/virtio/virtio_ctl_msg.c303
-rw-r--r--sound/virtio/virtio_ctl_msg.h78
-rw-r--r--sound/virtio/virtio_jack.c233
-rw-r--r--sound/virtio/virtio_kctl.c477
-rw-r--r--sound/virtio/virtio_pcm.c524
-rw-r--r--sound/virtio/virtio_pcm.h127
-rw-r--r--sound/virtio/virtio_pcm_msg.c415
-rw-r--r--sound/virtio/virtio_pcm_ops.c525
-rw-r--r--sound/x86/Kconfig5
-rw-r--r--sound/x86/Makefile3
-rw-r--r--sound/x86/intel_hdmi_audio.c381
-rw-r--r--sound/x86/intel_hdmi_audio.h3
-rw-r--r--sound/x86/intel_hdmi_lpe_audio.h10
-rw-r--r--sound/xen/Kconfig12
-rw-r--r--sound/xen/Makefile8
-rw-r--r--sound/xen/xen_snd_front.c387
-rw-r--r--sound/xen/xen_snd_front.h54
-rw-r--r--sound/xen/xen_snd_front_alsa.c833
-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.c463
-rw-r--r--sound/xen/xen_snd_front_evtchnl.h86
3060 files changed, 792607 insertions, 207594 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index 6833db9002ec..8b40205394fe 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SOUND
tristate "Sound card support"
- depends on HAS_IOMEM
+ depends on HAS_IOMEM || INDIRECT_IOMEM
help
If you have a sound card in your computer, i.e. if it can say more
than an occasional beep, say Y.
@@ -38,8 +39,6 @@ config SOUND_OSS_CORE_PRECLAIM
source "sound/oss/dmasound/Kconfig"
-if !UML
-
menuconfig SND
tristate "Advanced Linux Sound Architecture"
help
@@ -96,13 +95,14 @@ source "sound/x86/Kconfig"
source "sound/synth/Kconfig"
-endif # SND
+source "sound/xen/Kconfig"
-endif # !UML
+source "sound/virtio/Kconfig"
+
+endif # SND
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 99d8c31262c8..5942311a4232 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -5,7 +5,8 @@
obj-$(CONFIG_SOUND) += soundcore.o
obj-$(CONFIG_DMASOUND) += oss/dmasound/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
- firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/
+ 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
@@ -16,4 +17,4 @@ ifeq ($(CONFIG_SND),y)
obj-y += last.o
endif
-soundcore-objs := sound_core.o
+soundcore-y := sound_core.o
diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig
index f8a64e15e5bf..f0e31f2bb04d 100644
--- a/sound/ac97/Kconfig
+++ b/sound/ac97/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# AC97 configuration
#
@@ -5,7 +6,6 @@
config AC97_BUS_NEW
tristate
- select AC97
help
This is the new AC97 bus type, successor of AC97_BUS. The ported
drivers which benefit from the AC97 automatic probing should "select"
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile
index f9c2640bfb59..f5efa1ad7e7f 100644
--- a/sound/ac97/Makefile
+++ b/sound/ac97/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# make for AC97 bus drivers
#
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h
index 08441a4fda7c..5a9677c3d4c3 100644
--- a/sound/ac97/ac97_core.h
+++ b/sound/ac97/ac97_core.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
- *
- * 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.
*/
-unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97,
+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,
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
index 31f858eceffc..f4254703d29f 100644
--- a/sound/ac97/bus.c
+++ b/sound/ac97/bus.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
- *
- * 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>
@@ -13,10 +10,12 @@
#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/codec.h>
#include <sound/ac97/controller.h>
#include <sound/ac97/regs.h>
@@ -30,8 +29,6 @@ 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)
{
@@ -68,6 +65,27 @@ ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
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;
@@ -76,6 +94,7 @@ static void ac97_codec_release(struct device *dev)
adev = to_ac97_device(dev);
ac97_ctrl = adev->ac97_ctrl;
ac97_ctrl->codecs[adev->num] = NULL;
+ of_node_put(dev->of_node);
kfree(adev);
}
@@ -98,18 +117,16 @@ static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
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)
- goto err_free_codec;
+ if (ret) {
+ put_device(&codec->dev);
+ return ret;
+ }
return 0;
-err_free_codec:
- put_device(&codec->dev);
- kfree(codec);
- ac97_ctrl->codecs[idx] = NULL;
-
- return ret;
}
unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
@@ -163,7 +180,7 @@ static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
/**
* snd_ac97_codec_driver_register - register an AC97 codec driver
- * @dev: AC97 driver codec to register
+ * @drv: AC97 driver codec to register
*
* Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
* controller.
@@ -179,7 +196,7 @@ EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
/**
* snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
- * @dev: AC97 codec driver to unregister
+ * @drv: AC97 codec driver to unregister
*
* Unregister a previously registered ac97 codec driver.
*/
@@ -224,10 +241,9 @@ static ssize_t cold_reset_store(struct device *dev,
{
struct ac97_controller *ac97_ctrl;
- mutex_lock(&ac97_controllers_mutex);
+ guard(mutex)(&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);
@@ -241,10 +257,9 @@ static ssize_t warm_reset_store(struct device *dev,
if (!dev)
return -ENODEV;
- mutex_lock(&ac97_controllers_mutex);
+ guard(mutex)(&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);
@@ -255,7 +270,7 @@ static struct attribute *ac97_controller_device_attrs[] = {
NULL
};
-static struct attribute_group ac97_adapter_attr_group = {
+static const struct attribute_group ac97_adapter_attr_group = {
.name = "ac97_operations",
.attrs = ac97_controller_device_attrs,
};
@@ -267,10 +282,10 @@ static const struct attribute_group *ac97_adapter_groups[] = {
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);
+ scoped_guard(mutex, &ac97_controllers_mutex) {
+ ac97_ctrl_codecs_unregister(ac97_ctrl);
+ list_del(&ac97_ctrl->controllers);
+ }
device_unregister(&ac97_ctrl->adap);
}
@@ -294,7 +309,7 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
{
int ret;
- mutex_lock(&ac97_controllers_mutex);
+ guard(mutex)(&ac97_controllers_mutex);
ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
ac97_ctrl->nr = ret;
if (ret >= 0) {
@@ -305,13 +320,11 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
if (ret)
put_device(&ac97_ctrl->adap);
}
- if (!ret)
+ 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;
}
@@ -321,6 +334,7 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
* @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
+ * @codecs_pdata: codec platform data
*
* 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.
@@ -369,7 +383,6 @@ void snd_ac97_controller_unregister(struct ac97_controller *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);
@@ -401,7 +414,6 @@ static int ac97_pm_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops ac97_pm = {
.suspend = pm_generic_suspend,
@@ -410,10 +422,7 @@ static const struct dev_pm_ops ac97_pm = {
.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)
+ RUNTIME_PM_OPS(ac97_pm_runtime_suspend, ac97_pm_runtime_resume, NULL)
};
static int ac97_get_enable_clk(struct ac97_codec_device *adev)
@@ -442,9 +451,9 @@ static ssize_t vendor_id_show(struct device *dev,
{
struct ac97_codec_device *codec = to_ac97_device(dev);
- return sprintf(buf, "%08x", codec->vendor_id);
+ return sysfs_emit(buf, "%08x", codec->vendor_id);
}
-DEVICE_ATTR_RO(vendor_id);
+static DEVICE_ATTR_RO(vendor_id);
static struct attribute *ac97_dev_attrs[] = {
&dev_attr_vendor_id.attr,
@@ -452,10 +461,10 @@ static struct attribute *ac97_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(ac97_dev);
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+static int ac97_bus_match(struct device *dev, const 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_codec_driver *adrv = to_ac97_driver(drv);
const struct ac97_id *id = adrv->id_table;
int i = 0;
@@ -496,29 +505,28 @@ static int ac97_bus_probe(struct device *dev)
return ret;
}
-static int ac97_bus_remove(struct device *dev)
+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_get_sync(dev);
- if (ret)
- return ret;
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return;
- ret = adrv->remove(adev);
+ adrv->remove(adev);
pm_runtime_put_noidle(dev);
- if (ret == 0)
- ac97_put_disable_clk(adev);
+ ac97_put_disable_clk(adev);
- return ret;
+ pm_runtime_disable(dev);
}
-static struct bus_type ac97_bus_type = {
+const struct bus_type ac97_bus_type = {
.name = "ac97bus",
.dev_groups = ac97_dev_groups,
.match = ac97_bus_match,
- .pm = &ac97_pm,
+ .pm = pm_ptr(&ac97_pm),
.probe = ac97_bus_probe,
.remove = ac97_bus_remove,
};
@@ -535,5 +543,6 @@ static void __exit ac97_bus_exit(void)
}
module_exit(ac97_bus_exit);
+MODULE_DESCRIPTION("AC97 bus interface");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c
index a835f03744bf..1c8357ad6cb4 100644
--- a/sound/ac97/codec.c
+++ b/sound/ac97/codec.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
- *
- * 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/ac97_codec.h>
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c
index 61544e0d8de4..d2479bba75bf 100644
--- a/sound/ac97/snd_ac97_compat.c
+++ b/sound/ac97/snd_ac97_compat.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
- *
- * 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/list.h>
@@ -15,6 +12,11 @@
#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);
@@ -51,7 +53,7 @@ static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
return actrl->ops->read(actrl, ac97->num, reg);
}
-static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
+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,
@@ -65,21 +67,31 @@ static struct snd_ac97_bus compat_soc_ac97_bus = {
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->dev = adev->dev;
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)
{
- kfree(ac97);
+ device_unregister(&ac97->dev);
}
EXPORT_SYMBOL_GPL(snd_ac97_compat_release);
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index 52e4bc54c9ac..1484fc178fa4 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>
@@ -59,7 +55,7 @@ static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id,
int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
unsigned int id_mask)
{
- struct snd_ac97_bus_ops *ops = ac97->bus->ops;
+ const struct snd_ac97_bus_ops *ops = ac97->bus->ops;
if (try_warm && ops->warm_reset) {
ops->warm_reset(ac97);
@@ -79,19 +75,8 @@ int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
}
EXPORT_SYMBOL_GPL(snd_ac97_reset);
-/*
- * 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.
- */
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
-{
- return 1;
-}
-
-struct bus_type ac97_bus_type = {
+const struct bus_type ac97_bus_type = {
.name = "ac97",
- .match = ac97_bus_match,
};
static int __init ac97_bus_init(void)
@@ -110,4 +95,5 @@ module_exit(ac97_bus_exit);
EXPORT_SYMBOL(ac97_bus_type);
+MODULE_DESCRIPTION("Legacy AC97 bus interface");
MODULE_LICENSE("GPL");
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..77ae75d7594c 100644
--- a/sound/aoa/aoa-gpio.h
+++ b/sound/aoa/aoa-gpio.h
@@ -1,16 +1,14 @@
+/* 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
#define __AOA_GPIO_H
#include <linux/workqueue.h>
#include <linux/mutex.h>
-#include <asm/prom.h>
typedef void (*notify_func_t)(void *data);
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
index 34c668f27798..badff9f7cd54 100644
--- a/sound/aoa/aoa.h
+++ b/sound/aoa/aoa.h
@@ -1,14 +1,12 @@
+/* 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
#define __AOA_H
-#include <asm/prom.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/asound.h>
@@ -117,7 +115,7 @@ struct aoa_card {
};
extern int aoa_snd_device_new(enum snd_device_type type,
- void * device_data, struct snd_device_ops * ops);
+ 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 0c68e32834c3..03f89dbcca75 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,8 +1,9 @@
+# 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).
@@ -11,14 +12,14 @@ 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 95f4c3849d55..8feedc771bd9 100644
--- a/sound/aoa/codecs/Makefile
+++ b/sound/aoa/codecs/Makefile
@@ -1,7 +1,7 @@
# 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
+snd-aoa-codec-onyx-y := onyx.o
+snd-aoa-codec-tas-y := tas.o
+snd-aoa-codec-toonie-y := toonie.o
obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
index d2d96ca082b7..4cf959017c9d 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,10 +27,10 @@
* 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>
+#include <linux/of.h>
#include <linux/slab.h>
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL");
@@ -74,8 +72,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 +98,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,
};
@@ -122,10 +122,9 @@ static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
s8 l, r;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
@@ -146,15 +145,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
return -EINVAL;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
- r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
- mutex_unlock(&onyx->mutex);
+ r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1])
return 0;
- }
onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
ucontrol->value.integer.value[0]
@@ -162,7 +159,6 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
ucontrol->value.integer.value[1]
- VOLUME_RANGE_SHIFT);
- mutex_unlock(&onyx->mutex);
return 1;
}
@@ -198,9 +194,8 @@ static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 ig;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] =
(ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
@@ -217,14 +212,13 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
return -EINVAL;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
n = v;
n &= ~ONYX_ADC_PGA_GAIN_MASK;
n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
& ONYX_ADC_PGA_GAIN_MASK;
onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
- mutex_unlock(&onyx->mutex);
return n != v;
}
@@ -252,9 +246,8 @@ static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
s8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
- mutex_unlock(&onyx->mutex);
ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
@@ -265,13 +258,12 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic)
{
s8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
v &= ~ONYX_ADC_INPUT_MIC;
if (mic)
v |= ONYX_ADC_INPUT_MIC;
onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
- mutex_unlock(&onyx->mutex);
}
static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
@@ -312,9 +304,8 @@ static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 c;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
@@ -329,9 +320,9 @@ static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
u8 v = 0, c = 0;
int err = -EBUSY;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (onyx->analog_locked)
- goto out_unlock;
+ return -EBUSY;
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
c = v;
@@ -342,9 +333,6 @@ static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
c |= ONYX_MUTE_RIGHT;
err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
return !err ? (v != c) : err;
}
@@ -373,9 +361,8 @@ static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
u8 address = (pv >> 8) & 0xff;
u8 mask = pv & 0xff;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, address, &c);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
@@ -394,11 +381,10 @@ static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
u8 address = (pv >> 8) & 0xff;
u8 mask = pv & 0xff;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (spdiflock && onyx->spdif_locked) {
/* even if alsamixer doesn't care.. */
- err = -EBUSY;
- goto out_unlock;
+ return -EBUSY;
}
onyx_read_register(onyx, address, &v);
c = v;
@@ -407,14 +393,11 @@ static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
c |= mask;
err = onyx_write_register(onyx, address, c);
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
return !err ? (v != c) : err;
}
#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, \
@@ -490,7 +473,7 @@ static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
ucontrol->value.iec958.status[0] = v & 0x3e;
@@ -502,7 +485,6 @@ static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
ucontrol->value.iec958.status[4] = v & 0x0f;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -513,7 +495,7 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
@@ -528,7 +510,6 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
- mutex_unlock(&onyx->mutex);
return 1;
}
@@ -544,7 +525,7 @@ static const 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,
@@ -560,7 +541,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,
@@ -673,14 +654,13 @@ static int onyx_usable(struct codec_info_item *cii,
struct onyx *onyx = cii->codec_data;
int spdif_enabled, analog_enabled;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
analog_enabled =
(v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
!= (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
- mutex_unlock(&onyx->mutex);
switch (ti->tag) {
case 0: return 1;
@@ -696,9 +676,8 @@ static int onyx_prepare(struct codec_info_item *cii,
{
u8 v;
struct onyx *onyx = cii->codec_data;
- int err = -EBUSY;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
@@ -707,10 +686,9 @@ static int onyx_prepare(struct codec_info_item *cii,
if (onyx_write_register(onyx,
ONYX_REG_DAC_CONTROL,
v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
- goto out_unlock;
+ return -EBUSY;
onyx->analog_locked = 1;
- err = 0;
- goto out_unlock;
+ return 0;
}
#endif
switch (substream->runtime->rate) {
@@ -720,8 +698,7 @@ static int onyx_prepare(struct codec_info_item *cii,
/* these rates are ok for all outputs */
/* FIXME: program spdif channel control bits here so that
* userspace doesn't have to if it only plays pcm! */
- err = 0;
- goto out_unlock;
+ return 0;
default:
/* got some rate that the digital output can't do,
* so disable and lock it */
@@ -729,16 +706,12 @@ static int onyx_prepare(struct codec_info_item *cii,
if (onyx_write_register(onyx,
ONYX_REG_DIG_INFO4,
v & ~ONYX_SPDIF_ENABLE))
- goto out_unlock;
+ return -EBUSY;
onyx->spdif_locked = 1;
- err = 0;
- goto out_unlock;
+ return 0;
}
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return -EBUSY;
}
static int onyx_open(struct codec_info_item *cii,
@@ -746,9 +719,8 @@ static int onyx_open(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx->open_count++;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -758,11 +730,10 @@ static int onyx_close(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx->open_count--;
if (!onyx->open_count)
onyx->spdif_locked = onyx->analog_locked = 0;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -772,7 +743,7 @@ static int onyx_switch_clock(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
/* this *MUST* be more elaborate later... */
switch (what) {
case CLOCK_SWITCH_PREPARE_SLAVE:
@@ -784,7 +755,6 @@ static int onyx_switch_clock(struct codec_info_item *cii,
default: /* silence warning */
break;
}
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -795,27 +765,21 @@ static int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
{
struct onyx *onyx = cii->codec_data;
u8 v;
- int err = -ENXIO;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
+ return -ENXIO;
onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
/* Apple does a sleep here but the datasheet says to do it on resume */
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return 0;
}
static int onyx_resume(struct codec_info_item *cii)
{
struct onyx *onyx = cii->codec_data;
u8 v;
- int err = -ENXIO;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
/* reset codec */
onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
@@ -827,17 +791,13 @@ static int onyx_resume(struct codec_info_item *cii)
/* take codec out of suspend (if it still is after reset) */
if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
+ return -ENXIO;
onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
/* FIXME: should divide by sample rate, but 8k is the lowest we go */
msleep(2205000/8000);
/* reset all values */
onyx_register_init(onyx);
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return 0;
}
#endif /* CONFIG_PM */
@@ -991,8 +951,7 @@ static void onyx_exit_codec(struct aoa_codec *codec)
onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
}
-static int onyx_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int onyx_i2c_probe(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
struct onyx *onyx;
@@ -1014,7 +973,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");
onyx->codec.owner = THIS_MODULE;
onyx->codec.init = onyx_init_codec;
onyx->codec.exit = onyx_exit_codec;
@@ -1030,7 +989,7 @@ static int onyx_i2c_probe(struct i2c_client *client,
return -ENODEV;
}
-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);
@@ -1038,11 +997,10 @@ static int onyx_i2c_remove(struct i2c_client *client)
of_node_put(onyx->codec.node);
kfree(onyx->codec_info);
kfree(onyx);
- return 0;
}
static const struct i2c_device_id onyx_i2c_id[] = {
- { "MAC,pcm3052", 0 },
+ { "MAC,pcm3052" },
{ }
};
MODULE_DEVICE_TABLE(i2c,onyx_i2c_id);
diff --git a/sound/aoa/codecs/onyx.h b/sound/aoa/codecs/onyx.h
index ffd20254ff76..bbdca841fe90 100644
--- a/sound/aoa/codecs/onyx.h
+++ b/sound/aoa/codecs/onyx.h
@@ -1,16 +1,13 @@
+/* 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>
/* PCM3052 register definitions */
diff --git a/sound/aoa/codecs/tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h
index 770935af66af..14dc4e9eea73 100644
--- a/sound/aoa/codecs/tas-basstreble.h
+++ b/sound/aoa/codecs/tas-basstreble.h
@@ -13,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,
@@ -99,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 77b8e7dc55fd..c9ea01488181 100644
--- a/sound/aoa/codecs/tas-gain-table.h
+++ b/sound/aoa/codecs/tas-gain-table.h
@@ -27,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 15c05755d270..7085e0b93e29 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,13 +58,12 @@
* 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>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/slab.h>
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
@@ -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,
};
@@ -237,10 +235,9 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->cached_volume_l;
ucontrol->value.integer.value[1] = tas->cached_volume_r;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -256,18 +253,15 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] > 177)
return -EINVAL;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->cached_volume_l == ucontrol->value.integer.value[0]
- && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->cached_volume_r == ucontrol->value.integer.value[1])
return 0;
- }
tas->cached_volume_l = ucontrol->value.integer.value[0];
tas->cached_volume_r = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -287,10 +281,9 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = !tas->mute_l;
ucontrol->value.integer.value[1] = !tas->mute_r;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -299,18 +292,15 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->mute_l == !ucontrol->value.integer.value[0]
- && tas->mute_r == !ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->mute_r == !ucontrol->value.integer.value[1])
return 0;
- }
tas->mute_l = !ucontrol->value.integer.value[0];
tas->mute_r = !ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -339,10 +329,9 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->mixer_l[idx];
ucontrol->value.integer.value[1] = tas->mixer_r[idx];
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -353,24 +342,21 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
- && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->mixer_r[idx] == ucontrol->value.integer.value[1])
return 0;
- }
tas->mixer_l[idx] = ucontrol->value.integer.value[0];
tas->mixer_r[idx] = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_mixer(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
#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, \
@@ -398,9 +384,8 @@ static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_range;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -413,16 +398,13 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->drc_range == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->drc_range == ucontrol->value.integer.value[0])
return 0;
- }
tas->drc_range = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -442,9 +424,8 @@ static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_enabled;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -453,16 +434,13 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
- if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->drc_enabled == ucontrol->value.integer.value[0])
return 0;
- }
tas->drc_enabled = !!ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -488,9 +466,8 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -502,7 +479,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] > 1)
return -EINVAL;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
oldacr = tas->acr;
/*
@@ -514,13 +491,10 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0])
tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL |
TAS_ACR_B_MON_SEL_RIGHT;
- if (oldacr == tas->acr) {
- mutex_unlock(&tas->mtx);
+ if (oldacr == tas->acr)
return 0;
- }
if (tas->hw_enabled)
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -559,9 +533,8 @@ static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->treble;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -573,16 +546,13 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->treble == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->treble == ucontrol->value.integer.value[0])
return 0;
- }
tas->treble = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas_set_treble(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -610,9 +580,8 @@ static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->bass;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -624,16 +593,13 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->bass == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->bass == ucontrol->value.integer.value[0])
return 0;
- }
tas->bass = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas_set_bass(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
@@ -724,13 +690,13 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
break;
case CLOCK_SWITCH_SLAVE:
/* Clocks are back, re-init the codec */
- mutex_lock(&tas->mtx);
- tas_reset_init(tas);
- tas_set_volume(tas);
- tas_set_mixer(tas);
- tas->hw_enabled = 1;
- tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
- mutex_unlock(&tas->mtx);
+ scoped_guard(mutex, &tas->mtx) {
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+ }
break;
default:
/* doesn't happen as of now */
@@ -745,23 +711,21 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
* our i2c device is suspended, and then take note of that! */
static int tas_suspend(struct tas *tas)
{
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
return 0;
}
static int tas_resume(struct tas *tas)
{
/* reset codec */
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
tas_reset_init(tas);
tas_set_volume(tas);
tas_set_mixer(tas);
tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -804,14 +768,13 @@ static int tas_init_codec(struct aoa_codec *codec)
return -EINVAL;
}
- mutex_lock(&tas->mtx);
- if (tas_reset_init(tas)) {
- printk(KERN_ERR PFX "tas failed to initialise\n");
- mutex_unlock(&tas->mtx);
- return -ENXIO;
+ scoped_guard(mutex, &tas->mtx) {
+ if (tas_reset_init(tas)) {
+ printk(KERN_ERR PFX "tas failed to initialise\n");
+ return -ENXIO;
+ }
+ tas->hw_enabled = 1;
}
- tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(),
@@ -877,8 +840,7 @@ static void tas_exit_codec(struct aoa_codec *codec)
}
-static int tas_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas_i2c_probe(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
struct tas *tas;
@@ -895,7 +857,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");
tas->codec.owner = THIS_MODULE;
tas->codec.init = tas_init_codec;
tas->codec.exit = tas_exit_codec;
@@ -914,7 +876,7 @@ static int tas_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
-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;
@@ -927,11 +889,10 @@ 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[] = {
- { "MAC,tas3004", 0 },
+ { "MAC,tas3004" },
{ }
};
MODULE_DEVICE_TABLE(i2c,tas_i2c_id);
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 7e8c3417cd85..b59967c49e0a 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,
};
@@ -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");
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 056d69683b1e..f586c340fe12 100644
--- a/sound/aoa/core/Makefile
+++ b/sound/aoa/core/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_AOA) += snd-aoa.o
-snd-aoa-objs := core.o \
+snd-aoa-y := core.o \
alsa.o \
gpio-pmf.o \
gpio-feature.o
diff --git a/sound/aoa/core/alsa.c b/sound/aoa/core/alsa.c
index 4a7e4e6b746f..aad7dfe089c7 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"
@@ -29,10 +28,10 @@ int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
return err;
aoa_card = alsa_card->private_data;
aoa_card->alsa_card = alsa_card;
- 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");
+ strscpy(alsa_card->shortname, name);
+ strscpy(alsa_card->longname, name);
+ strscpy(alsa_card->mixername, name);
err = snd_card_register(aoa_card->alsa_card);
if (err < 0) {
printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
@@ -60,7 +59,7 @@ void aoa_alsa_cleanup(void)
}
int aoa_snd_device_new(enum snd_device_type type,
- void * device_data, struct snd_device_ops * ops)
+ 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 71960089e207..19ed0e6907da 100644
--- a/sound/aoa/core/gpio-feature.c
+++ b/sound/aoa/core/gpio-feature.c
@@ -1,10 +1,9 @@
+// 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.
@@ -82,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;
@@ -210,10 +212,9 @@ static void ftr_handle_notify(struct work_struct *work)
struct gpio_notification *notif =
container_of(work, struct gpio_notification, work.work);
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
if (notif->notify)
notif->notify(notif->data);
- mutex_unlock(&notif->mutex);
}
static void gpio_enable_dual_edge(int gpio)
@@ -339,19 +340,17 @@ static int ftr_set_notify(struct gpio_runtime *rt,
if (!irq)
return -ENODEV;
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
old = notif->notify;
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
+ if (!old && !notify)
+ return 0;
if (old && notify) {
if (old == notify && notif->data == data)
err = 0;
- goto out_unlock;
+ return err;
}
if (old && !notify)
@@ -360,16 +359,13 @@ static int ftr_set_notify(struct gpio_runtime *rt,
if (!old && notify) {
err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
if (err)
- goto out_unlock;
+ return err;
}
notif->notify = notify;
notif->data = data;
- err = 0;
- out_unlock:
- mutex_unlock(&notif->mutex);
- return err;
+ return 0;
}
static int ftr_get_detect(struct gpio_runtime *rt,
diff --git a/sound/aoa/core/gpio-pmf.c b/sound/aoa/core/gpio-pmf.c
index c8d8a1a6f964..e76bde25e41a 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>
@@ -75,10 +74,9 @@ static void pmf_handle_notify(struct work_struct *work)
struct gpio_notification *notif =
container_of(work, struct gpio_notification, work.work);
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
if (notif->notify)
notif->notify(notif->data);
- mutex_unlock(&notif->mutex);
}
static void pmf_gpio_init(struct gpio_runtime *rt)
@@ -155,19 +153,17 @@ static int pmf_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
old = notif->notify;
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
+ if (!old && !notify)
+ return 0;
if (old && notify) {
if (old == notify && notif->data == data)
err = 0;
- goto out_unlock;
+ return err;
}
if (old && !notify) {
@@ -179,10 +175,8 @@ static int pmf_set_notify(struct gpio_runtime *rt,
if (!old && notify) {
irq_client = kzalloc(sizeof(struct pmf_irq_client),
GFP_KERNEL);
- if (!irq_client) {
- err = -ENOMEM;
- goto out_unlock;
- }
+ if (!irq_client)
+ return -ENOMEM;
irq_client->data = notif;
irq_client->handler = pmf_handle_notify_irq;
irq_client->owner = THIS_MODULE;
@@ -193,17 +187,14 @@ static int pmf_set_notify(struct gpio_runtime *rt,
printk(KERN_ERR "snd-aoa: gpio layer failed to"
" register %s irq (%d)\n", name, err);
kfree(irq_client);
- goto out_unlock;
+ return err;
}
notif->gpio_private = irq_client;
}
notif->notify = notify;
notif->data = data;
- err = 0;
- out_unlock:
- mutex_unlock(&notif->mutex);
- return err;
+ return 0;
}
static int pmf_get_detect(struct gpio_runtime *rt,
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..2c3bee6cfa2c 100644
--- a/sound/aoa/fabrics/Makefile
+++ b/sound/aoa/fabrics/Makefile
@@ -1,3 +1,4 @@
-snd-aoa-fabric-layout-objs += layout.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-aoa-fabric-layout-y += 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 1eddf8fa188f..bb2a0ef3004b 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1,17 +1,16 @@
+// 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.
*/
-#include <asm/prom.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../aoa.h"
#include "../soundbus/soundbus.h"
@@ -657,7 +656,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol, \
!!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, \
@@ -776,7 +775,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);
@@ -950,8 +949,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,
- "Headphone Switch", sizeof(ctl->id.name));
+ strscpy(ctl->id.name, "Headphone Switch");
ldev->lineout_ctrl = ctl;
aoa_snd_ctl_add(ctl);
ldev->have_lineout_detect =
@@ -964,16 +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,
- "Headphone Detect Autoswitch",
- sizeof(ctl->id.name));
+ strscpy(ctl->id.name,
+ "Headphone Detect Autoswitch");
aoa_snd_ctl_add(ctl);
ctl = snd_ctl_new1(&lineout_detected,
ldev);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Detected",
- sizeof(ctl->id.name));
+ strscpy(ctl->id.name,
+ "Headphone Detected");
ldev->lineout_detected_ctrl = ctl;
aoa_snd_ctl_add(ctl);
}
@@ -1008,8 +1004,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)
@@ -1096,7 +1092,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
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;
@@ -1125,10 +1121,8 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
kfree(ldev);
sdev->pcmid = -1;
sdev->pcmname = NULL;
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int aoa_fabric_layout_suspend(struct device *dev)
{
struct layout_dev *ldev = dev_get_drvdata(dev);
@@ -1149,11 +1143,9 @@ static int aoa_fabric_layout_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
+static DEFINE_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 = {
.name = "snd_aoa_soundbus_drv",
.owner = THIS_MODULE,
@@ -1161,9 +1153,7 @@ static struct soundbus_driver aoa_soundbus_driver = {
.remove = aoa_fabric_layout_remove,
.driver = {
.owner = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
.pm = &aoa_fabric_layout_pm_ops,
-#endif
}
};
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..a10b102daf81 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
+snd-aoa-soundbus-y := 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 70bcaa7f93dd..2a295f610594 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -1,12 +1,13 @@
+// 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>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include "soundbus.h"
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
@@ -56,10 +57,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 +75,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 +106,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 +114,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)
@@ -128,7 +127,7 @@ static void soundbus_device_shutdown(struct device *dev)
/* soundbus_dev_attrs is declared in sysfs.c */
ATTRIBUTE_GROUPS(soundbus_dev);
-static struct bus_type soundbus_bus_type = {
+static const struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
.uevent = soundbus_uevent,
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile
index 1b949b2a4028..1ddaa0e17d67 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
+snd-aoa-i2sbus-y := core.o pcm.o control.o
diff --git a/sound/aoa/soundbus/i2sbus/control.c b/sound/aoa/soundbus/i2sbus/control.c
index f4495decc699..a003ef06de63 100644
--- a/sound/aoa/soundbus/i2sbus/control.c
+++ b/sound/aoa/soundbus/i2sbus/control.c
@@ -1,9 +1,8 @@
+// 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>
@@ -11,7 +10,6 @@
#include <linux/slab.h>
#include <linux/io.h>
-#include <asm/prom.h>
#include <asm/macio.h>
#include <asm/pmac_feature.h>
#include <asm/pmac_pfunc.h>
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 000b58522106..f4d43c854bbd 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,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -47,8 +47,8 @@ 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_zalloc_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
- r->size, &r->bus_addr, GFP_KERNEL);
+ r->space = dma_alloc_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size, &r->bus_addr, GFP_KERNEL);
if (!r->space)
return -ENOMEM;
@@ -93,14 +93,12 @@ static irqreturn_t i2sbus_bus_intr(int irq, void *devid)
struct i2sbus_dev *dev = devid;
u32 intreg;
- spin_lock(&dev->low_lock);
+ guard(spinlock)(&dev->low_lock);
intreg = in_le32(&dev->intfregs->intr_ctl);
/* acknowledge interrupt reasons */
out_le32(&dev->intfregs->intr_ctl, intreg);
- spin_unlock(&dev->low_lock);
-
return IRQ_HANDLED;
}
@@ -148,27 +146,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[8];
+ 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);
@@ -176,8 +176,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;
}
@@ -213,7 +213,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);
@@ -228,13 +228,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);
@@ -302,6 +302,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;
}
@@ -329,7 +333,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
{
- struct device_node *np = NULL;
+ struct device_node *np;
int got = 0, err;
struct i2sbus_control *control = NULL;
@@ -341,7 +345,7 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
return -ENODEV;
}
- while ((np = of_get_next_child(dev->ofdev.dev.of_node, np))) {
+ for_each_child_of_node(dev->ofdev.dev.of_node, np) {
if (of_device_is_compatible(np, "i2sbus") ||
of_device_is_compatible(np, "i2s-modem")) {
got += i2sbus_add_dev(dev, control, np);
@@ -359,15 +363,13 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
return 0;
}
-static int i2sbus_remove(struct macio_dev* dev)
+static void i2sbus_remove(struct macio_dev *dev)
{
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
struct i2sbus_dev *i2sdev, *tmp;
list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
soundbus_remove_one(&i2sdev->sound);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -379,10 +381,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 */
- /* 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..7a3cae0d6c26 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
@@ -14,7 +13,6 @@
#include <sound/pcm.h>
-#include <asm/prom.h>
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
diff --git a/sound/aoa/soundbus/i2sbus/interface.h b/sound/aoa/soundbus/i2sbus/interface.h
index c6b5f5452d20..a136274266ea 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
@@ -35,7 +34,7 @@ struct i2s_interface_regs {
__le32 peak_level_in1; /* 0x90 */
PAD(12);
/* total size: 0x100 bytes */
-} __attribute__((__packed__));
+} __packed;
/* interrupt register is just a bitfield with
* interrupt enable and pending bits */
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index e618531757e0..4c480ad2c05d 100644
--- a/sound/aoa/soundbus/i2sbus/pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
@@ -1,9 +1,8 @@
+// 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 <linux/io.h>
@@ -80,11 +79,10 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
u64 formats = 0;
unsigned int rates = 0;
struct transfer_info v;
- int result = 0;
int bus_factor = 0, sysclock_factor = 0;
int found_this;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, &other);
@@ -93,8 +91,7 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
if (pi->active) {
/* alsa messed up */
- result = -EBUSY;
- goto out_unlock;
+ return -EBUSY;
}
/* we now need to assign the hw */
@@ -118,10 +115,8 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
ti++;
}
}
- if (!masks_inited || !bus_factor || !sysclock_factor) {
- result = -ENODEV;
- goto out_unlock;
- }
+ if (!masks_inited || !bus_factor || !sysclock_factor)
+ return -ENODEV;
/* bus dependent stuff */
hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME |
@@ -195,15 +190,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
hw->periods_max = MAX_DBDMA_COMMANDS;
err = snd_pcm_hw_constraint_integer(pi->substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- result = err;
- goto out_unlock;
- }
+ if (err < 0)
+ return err;
list_for_each_entry(cii, &sdev->codec_list, list) {
if (cii->codec->open) {
err = cii->codec->open(cii, pi->substream);
if (err) {
- result = err;
/* unwind */
found_this = 0;
list_for_each_entry_reverse(rev,
@@ -215,14 +207,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
if (rev == cii)
found_this = 1;
}
- goto out_unlock;
+ return err;
}
}
}
- out_unlock:
- mutex_unlock(&i2sdev->lock);
- return result;
+ return 0;
}
#undef CHECK_RATE
@@ -233,7 +223,7 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
struct pcm_info *pi;
int err = 0, tmp;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, NULL);
@@ -247,7 +237,6 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
pi->substream = NULL;
pi->active = 0;
- mutex_unlock(&i2sdev->lock);
return err;
}
@@ -255,26 +244,25 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,
struct pcm_info *pi)
{
unsigned long flags;
- struct completion done;
- long timeout;
+ DECLARE_COMPLETION_ONSTACK(done);
+ unsigned long time_left;
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);
+ time_left = wait_for_completion_timeout(&done, HZ);
spin_lock_irqsave(&i2sdev->low_lock, flags);
pi->stop_completion = NULL;
- if (timeout == 0) {
+ if (time_left == 0) {
/* timeout expired, stop dbdma forcefully */
printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n");
/* make sure RUN, PAUSE and S0 bits are cleared */
out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
pi->dbdma_ring.stopping = 0;
- timeout = 10;
+ time_left = 10;
while (in_le32(&pi->dbdma->status) & ACTIVE) {
- if (--timeout <= 0)
+ if (--time_left <= 0)
break;
udelay(1);
}
@@ -295,12 +283,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);
@@ -309,7 +291,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;
}
@@ -339,33 +320,26 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
int input_16bit;
struct pcm_info *pi, *other;
int cnt;
- int result = 0;
unsigned int cmd, stopaddr;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, &other);
- if (pi->dbdma_ring.running) {
- result = -EBUSY;
- goto out_unlock;
- }
+ if (pi->dbdma_ring.running)
+ return -EBUSY;
if (pi->dbdma_ring.stopping)
i2sbus_wait_for_stop(i2sdev, pi);
- if (!pi->substream || !pi->substream->runtime) {
- result = -EINVAL;
- goto out_unlock;
- }
+ if (!pi->substream || !pi->substream->runtime)
+ return -EINVAL;
runtime = pi->substream->runtime;
pi->active = 1;
if (other->active &&
((i2sdev->format != runtime->format)
- || (i2sdev->rate != runtime->rate))) {
- result = -EINVAL;
- goto out_unlock;
- }
+ || (i2sdev->rate != runtime->rate)))
+ return -EINVAL;
i2sdev->format = runtime->format;
i2sdev->rate = runtime->rate;
@@ -421,10 +395,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
bi.bus_factor = cii->codec->bus_factor;
break;
}
- if (!bi.bus_factor) {
- result = -ENODEV;
- goto out_unlock;
- }
+ if (!bi.bus_factor)
+ return -ENODEV;
input_16bit = 1;
break;
case SNDRV_PCM_FORMAT_S32_BE:
@@ -435,8 +407,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
input_16bit = 0;
break;
default:
- result = -EINVAL;
- goto out_unlock;
+ return -EINVAL;
}
/* we assume all sysclocks are the same! */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
@@ -447,10 +418,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
if (clock_and_divisors(bi.sysclock_factor,
bi.bus_factor,
runtime->rate,
- &sfr) < 0) {
- result = -EINVAL;
- goto out_unlock;
- }
+ &sfr) < 0)
+ return -EINVAL;
switch (bi.bus_factor) {
case 32:
sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X;
@@ -466,10 +435,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
int err = 0;
if (cii->codec->prepare)
err = cii->codec->prepare(cii, &bi, pi->substream);
- if (err) {
- result = err;
- goto out_unlock;
- }
+ if (err)
+ return err;
}
/* codecs are fine with it, so set our clocks */
if (input_16bit)
@@ -485,7 +452,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
/* not locking these is fine since we touch them only in this function */
if (in_le32(&i2sdev->intfregs->serial_format) == sfr
&& in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
- goto out_unlock;
+ return 0;
/* let's notify the codecs about clocks going away.
* For now we only do mastering on the i2s cell... */
@@ -523,9 +490,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
if (cii->codec->switch_clock)
cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
- out_unlock:
- mutex_unlock(&i2sdev->lock);
- return result;
+ return 0;
}
#ifdef CONFIG_PM
@@ -540,20 +505,16 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
{
struct codec_info_item *cii;
struct pcm_info *pi;
- int result = 0;
- unsigned long flags;
- spin_lock_irqsave(&i2sdev->low_lock, flags);
+ guard(spinlock_irqsave)(&i2sdev->low_lock);
get_pcm_info(i2sdev, in, &pi, NULL);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- if (pi->dbdma_ring.running) {
- result = -EALREADY;
- goto out_unlock;
- }
+ if (pi->dbdma_ring.running)
+ return -EALREADY;
list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
if (cii->codec->start)
cii->codec->start(cii, pi->substream);
@@ -567,7 +528,7 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
udelay(10);
if (in_le32(&pi->dbdma->status) & ACTIVE) {
pi->dbdma_ring.stopping = 0;
- goto out_unlock; /* keep running */
+ return 0; /* keep running */
}
}
}
@@ -593,10 +554,8 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- if (!pi->dbdma_ring.running) {
- result = -EALREADY;
- goto out_unlock;
- }
+ if (!pi->dbdma_ring.running)
+ return -EALREADY;
pi->dbdma_ring.running = 0;
/* Set the S0 bit to make the DMA branch to the stop cmd */
@@ -608,13 +567,10 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
cii->codec->stop(cii, pi->substream);
break;
default:
- result = -EINVAL;
- goto out_unlock;
+ return -EINVAL;
}
- out_unlock:
- spin_unlock_irqrestore(&i2sdev->low_lock, flags);
- return result;
+ return 0;
}
static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in)
@@ -641,70 +597,67 @@ static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
int dma_stopped = 0;
struct snd_pcm_runtime *runtime;
- spin_lock(&i2sdev->low_lock);
- get_pcm_info(i2sdev, in, &pi, NULL);
- if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping)
- goto out_unlock;
+ scoped_guard(spinlock, &i2sdev->low_lock) {
+ get_pcm_info(i2sdev, in, &pi, NULL);
+ if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping)
+ return;
+
+ i = pi->current_period;
+ runtime = pi->substream->runtime;
+ while (pi->dbdma_ring.cmds[i].xfer_status) {
+ if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT)
+ /*
+ * BT is the branch taken bit. If it took a branch
+ * it is because we set the S0 bit to make it
+ * branch to the stop command.
+ */
+ dma_stopped = 1;
+ pi->dbdma_ring.cmds[i].xfer_status = 0;
+
+ if (++i >= runtime->periods) {
+ i = 0;
+ pi->frame_count += runtime->buffer_size;
+ }
+ pi->current_period = i;
- i = pi->current_period;
- runtime = pi->substream->runtime;
- while (pi->dbdma_ring.cmds[i].xfer_status) {
- if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT)
/*
- * BT is the branch taken bit. If it took a branch
- * it is because we set the S0 bit to make it
- * branch to the stop command.
+ * Check the frame count. The DMA tends to get a bit
+ * ahead of the frame counter, which confuses the core.
*/
- dma_stopped = 1;
- pi->dbdma_ring.cmds[i].xfer_status = 0;
-
- if (++i >= runtime->periods) {
- i = 0;
- pi->frame_count += runtime->buffer_size;
+ fc = in_le32(&i2sdev->intfregs->frame_count);
+ nframes = i * runtime->period_size;
+ if (fc < pi->frame_count + nframes)
+ pi->frame_count = fc - nframes;
}
- pi->current_period = i;
-
- /*
- * Check the frame count. The DMA tends to get a bit
- * ahead of the frame counter, which confuses the core.
- */
- fc = in_le32(&i2sdev->intfregs->frame_count);
- nframes = i * runtime->period_size;
- if (fc < pi->frame_count + nframes)
- pi->frame_count = fc - nframes;
- }
- if (dma_stopped) {
- timeout = 1000;
- for (;;) {
- status = in_le32(&pi->dbdma->status);
- if (!(status & ACTIVE) && (!in || (status & 0x80)))
- break;
- if (--timeout <= 0) {
- printk(KERN_ERR "i2sbus: timed out "
- "waiting for DMA to stop!\n");
- break;
+ if (dma_stopped) {
+ timeout = 1000;
+ for (;;) {
+ status = in_le32(&pi->dbdma->status);
+ if (!(status & ACTIVE) && (!in || (status & 0x80)))
+ break;
+ if (--timeout <= 0) {
+ printk(KERN_ERR
+ "i2sbus: timed out waiting for DMA to stop!\n");
+ break;
+ }
+ udelay(1);
}
- udelay(1);
- }
- /* Turn off DMA controller, clear S0 bit */
- out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
+ /* Turn off DMA controller, clear S0 bit */
+ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
- pi->dbdma_ring.stopping = 0;
- if (pi->stop_completion)
- complete(pi->stop_completion);
+ pi->dbdma_ring.stopping = 0;
+ if (pi->stop_completion)
+ complete(pi->stop_completion);
+ }
+
+ if (!pi->dbdma_ring.running)
+ return;
}
- if (!pi->dbdma_ring.running)
- goto out_unlock;
- spin_unlock(&i2sdev->low_lock);
/* may call _trigger again, hence needs to be unlocked */
snd_pcm_period_elapsed(pi->substream);
- return;
-
- out_unlock:
- spin_unlock(&i2sdev->low_lock);
}
irqreturn_t i2sbus_tx_intr(int irq, void *devid)
@@ -781,8 +734,6 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
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,
@@ -851,8 +802,6 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
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,
@@ -931,10 +880,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);
@@ -987,7 +934,7 @@ 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->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent =
&dev->ofdev.dev;
i2sdev->out.created = 1;
}
@@ -1004,7 +951,7 @@ 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->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent =
&dev->ofdev.dev;
i2sdev->in.created = 1;
}
@@ -1027,9 +974,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 ae4022438e64..877cbad93f12 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -1,14 +1,13 @@
+/* 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
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <linux/list.h>
@@ -186,7 +185,7 @@ 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 (*shutdown)(struct soundbus_dev* dev);
diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c
index 81da020bddef..e87b28428b99 100644
--- a/sound/aoa/soundbus/sysfs.c
+++ b/sound/aoa/soundbus/sysfs.c
@@ -1,41 +1,43 @@
// 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);
- }
-
- return length;
+ 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);
-soundbus_config_of_attr (name, "%s\n");
+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 sysfs_emit(buf, "%pOFn\n", of->dev.of_node);
+}
static DEVICE_ATTR_RO(name);
-soundbus_config_of_attr (type, "%s\n");
+
+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;
+
+ return sysfs_emit(buf, "%s\n", of_node_get_device_type(of->dev.of_node));
+}
static DEVICE_ATTR_RO(type);
struct attribute *soundbus_dev_attrs[] = {
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 65171f6657a2..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,14 +18,9 @@ config SND_ARMAACI
select SND_PCM
select SND_AC97_CODEC
-config SND_PXA2XX_PCM
- tristate
- select SND_PCM
-
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
@@ -36,7 +32,6 @@ endif # SND_ARM
config SND_PXA2XX_LIB
tristate
- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
select SND_DMAENGINE_PCM
config SND_PXA2XX_LIB_AC97
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index e10d5b169565..899edb4bb278 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -4,14 +4,11 @@
#
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
+snd-aaci-y := aaci.o
obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
-snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
+snd-pxa2xx-ac97-y := pxa2xx-ac97.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 0114ffed56dd..5548ed8e6b1c 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>
@@ -76,7 +73,7 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
if (ac97->num >= 4)
return;
- mutex_lock(&aaci->ac97_sem);
+ guard(mutex)(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
@@ -100,8 +97,6 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
if (v & (SLFR_1TXB|SLFR_2TXB))
dev_err(&aaci->dev->dev,
"timeout waiting for write to complete\n");
-
- mutex_unlock(&aaci->ac97_sem);
}
/*
@@ -116,7 +111,7 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
if (ac97->num >= 4)
return ~0;
- mutex_lock(&aaci->ac97_sem);
+ guard(mutex)(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
@@ -137,8 +132,7 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
if (v & SLFR_1TXB) {
dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
- v = ~0;
- goto out;
+ return ~0;
}
/* Now wait for the response frame */
@@ -154,8 +148,7 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
if (v != (SLFR_1RXV|SLFR_2RXV)) {
dev_err(&aaci->dev->dev, "timeout on RX valid\n");
- v = ~0;
- goto out;
+ return ~0;
}
do {
@@ -174,8 +167,6 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
v = ~0;
}
} while (retries);
- out:
- mutex_unlock(&aaci->ac97_sem);
return v;
}
@@ -219,45 +210,43 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
return;
}
- spin_lock(&aacirun->lock);
-
- ptr = aacirun->ptr;
- do {
- unsigned int len = aacirun->fifo_bytes;
- u32 val;
-
- if (aacirun->bytes <= 0) {
- aacirun->bytes += aacirun->period;
- period_elapsed = true;
- }
- if (!(aacirun->cr & CR_EN))
- break;
-
- val = readl(aacirun->base + AACI_SR);
- if (!(val & SR_RXHF))
- break;
- if (!(val & SR_RXFF))
- len >>= 1;
-
- aacirun->bytes -= len;
-
- /* reading 16 bytes at a time */
- for( ; len > 0; len -= 16) {
- asm(
- "ldmia %1, {r0, r1, r2, r3}\n\t"
- "stmia %0!, {r0, r1, r2, r3}"
- : "+r" (ptr)
- : "r" (aacirun->fifo)
- : "r0", "r1", "r2", "r3", "cc");
-
- if (ptr >= aacirun->end)
- ptr = aacirun->start;
- }
- } while(1);
-
- aacirun->ptr = ptr;
-
- spin_unlock(&aacirun->lock);
+ scoped_guard(spinlock, &aacirun->lock) {
+ ptr = aacirun->ptr;
+ do {
+ unsigned int len = aacirun->fifo_bytes;
+ u32 val;
+
+ if (aacirun->bytes <= 0) {
+ aacirun->bytes += aacirun->period;
+ period_elapsed = true;
+ }
+ if (!(aacirun->cr & CR_EN))
+ break;
+
+ val = readl(aacirun->base + AACI_SR);
+ if (!(val & SR_RXHF))
+ break;
+ if (!(val & SR_RXFF))
+ len >>= 1;
+
+ aacirun->bytes -= len;
+
+ /* reading 16 bytes at a time */
+ for( ; len > 0; len -= 16) {
+ asm(
+ "ldmia %1, {r0, r1, r2, r3}\n\t"
+ "stmia %0!, {r0, r1, r2, r3}"
+ : "+r" (ptr)
+ : "r" (aacirun->fifo)
+ : "r0", "r1", "r2", "r3", "cc");
+
+ if (ptr >= aacirun->end)
+ ptr = aacirun->start;
+ }
+ } while(1);
+
+ aacirun->ptr = ptr;
+ }
if (period_elapsed)
snd_pcm_period_elapsed(aacirun->substream);
@@ -279,45 +268,43 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
return;
}
- spin_lock(&aacirun->lock);
-
- ptr = aacirun->ptr;
- do {
- unsigned int len = aacirun->fifo_bytes;
- u32 val;
-
- if (aacirun->bytes <= 0) {
- aacirun->bytes += aacirun->period;
- period_elapsed = true;
- }
- if (!(aacirun->cr & CR_EN))
- break;
-
- val = readl(aacirun->base + AACI_SR);
- if (!(val & SR_TXHE))
- break;
- if (!(val & SR_TXFE))
- len >>= 1;
-
- aacirun->bytes -= len;
-
- /* writing 16 bytes at a time */
- for ( ; len > 0; len -= 16) {
- asm(
- "ldmia %0!, {r0, r1, r2, r3}\n\t"
- "stmia %1, {r0, r1, r2, r3}"
- : "+r" (ptr)
- : "r" (aacirun->fifo)
- : "r0", "r1", "r2", "r3", "cc");
-
- if (ptr >= aacirun->end)
- ptr = aacirun->start;
- }
- } while (1);
-
- aacirun->ptr = ptr;
-
- spin_unlock(&aacirun->lock);
+ scoped_guard(spinlock, &aacirun->lock) {
+ ptr = aacirun->ptr;
+ do {
+ unsigned int len = aacirun->fifo_bytes;
+ u32 val;
+
+ if (aacirun->bytes <= 0) {
+ aacirun->bytes += aacirun->period;
+ period_elapsed = true;
+ }
+ if (!(aacirun->cr & CR_EN))
+ break;
+
+ val = readl(aacirun->base + AACI_SR);
+ if (!(val & SR_TXHE))
+ break;
+ if (!(val & SR_TXFE))
+ len >>= 1;
+
+ aacirun->bytes -= len;
+
+ /* writing 16 bytes at a time */
+ for ( ; len > 0; len -= 16) {
+ asm(
+ "ldmia %0!, {r0, r1, r2, r3}\n\t"
+ "stmia %1, {r0, r1, r2, r3}"
+ : "+r" (ptr)
+ : "r" (aacirun->fifo)
+ : "r0", "r1", "r2", "r3", "cc");
+
+ if (ptr >= aacirun->end)
+ ptr = aacirun->start;
+ }
+ } while (1);
+
+ aacirun->ptr = ptr;
+ }
if (period_elapsed)
snd_pcm_period_elapsed(aacirun->substream);
@@ -383,7 +370,7 @@ static const struct snd_pcm_hardware aaci_hw_info = {
static int aaci_rule_channels(struct snd_pcm_hw_params *p,
struct snd_pcm_hw_rule *rule)
{
- static unsigned int channel_list[] = { 2, 4, 6 };
+ static const unsigned int channel_list[] = { 2, 4, 6 };
struct aaci *aaci = rule->private;
unsigned int mask = 1 << 0, slots;
@@ -440,14 +427,13 @@ static int aaci_pcm_open(struct snd_pcm_substream *substream)
*/
runtime->hw.fifo_size = aaci->fifo_depth * 2;
- mutex_lock(&aaci->irq_lock);
+ guard(mutex)(&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);
return ret;
}
@@ -465,10 +451,9 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream)
aacirun->substream = NULL;
- mutex_lock(&aaci->irq_lock);
+ guard(mutex)(&aaci->irq_lock);
if (!--aaci->users)
free_irq(aaci->dev->irq[0], aaci);
- mutex_unlock(&aaci->irq_lock);
return 0;
}
@@ -486,11 +471,6 @@ 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;
}
@@ -505,6 +485,7 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
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;
@@ -520,25 +501,19 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
if (dbl && channels != 2)
return -EINVAL;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(params));
- if (err >= 0) {
- struct aaci *aaci = substream->private_data;
+ err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
+ 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->cr |= channels_to_slotmask[channels + dbl * 2];
- aacirun->pcm_open = err == 0;
- aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
- aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
-
- /*
- * 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;
- }
+ /*
+ * 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;
}
@@ -598,10 +573,8 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned long flags;
- int ret = 0;
- spin_lock_irqsave(&aacirun->lock, flags);
+ guard(spinlock_irqsave)(&aacirun->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -627,18 +600,15 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- spin_unlock_irqrestore(&aacirun->lock, flags);
-
- return ret;
+ return 0;
}
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_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_prepare,
@@ -683,10 +653,8 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned long flags;
- int ret = 0;
- spin_lock_irqsave(&aacirun->lock, flags);
+ guard(spinlock_irqsave)(&aacirun->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -712,12 +680,10 @@ static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- spin_unlock_irqrestore(&aacirun->lock, flags);
-
- return ret;
+ return 0;
}
static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -741,7 +707,6 @@ static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
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_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_capture_prepare,
@@ -752,12 +717,9 @@ static const struct snd_pcm_ops aaci_capture_ops = {
/*
* Power Management.
*/
-#ifdef CONFIG_PM
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;
}
@@ -779,12 +741,7 @@ static int aaci_resume(struct device *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_DEV_PM_OPS NULL
-#endif
-
+static DEFINE_SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
static const struct ac97_pcm ac97_defs[] = {
[0] = { /* Front PCM */
@@ -827,7 +784,7 @@ static const struct ac97_pcm ac97_defs[] = {
}
};
-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,
};
@@ -906,8 +863,8 @@ static struct aaci *aaci_init_card(struct amba_device *dev)
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 PL%03x rev%u at 0x%08llx, irq %d",
card->shortname, amba_part(dev), amba_rev(dev),
@@ -937,12 +894,13 @@ static int 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;
@@ -1070,7 +1028,7 @@ static int aaci_probe(struct amba_device *dev,
return ret;
}
-static int aaci_remove(struct amba_device *dev)
+static void aaci_remove(struct amba_device *dev)
{
struct snd_card *card = amba_get_drvdata(dev);
@@ -1081,11 +1039,9 @@ static int aaci_remove(struct amba_device *dev)
snd_card_free(card);
amba_release_regions(dev);
}
-
- return 0;
}
-static struct amba_id aaci_ids[] = {
+static const struct amba_id aaci_ids[] = {
{
.id = 0x00041041,
.mask = 0x000fffff,
@@ -1098,7 +1054,7 @@ MODULE_DEVICE_TABLE(amba, aaci_ids);
static struct amba_driver aaci_driver = {
.drv = {
.name = DRIVER_NAME,
- .pm = AACI_DEV_PM_OPS,
+ .pm = &aaci_dev_pm_ops,
},
.probe = aaci_probe,
.remove = aaci_remove,
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
index 5791bd5bd2ab..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
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 5950a9e218d9..64510318091f 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>
@@ -19,12 +16,14 @@
#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/pxa2xx-lib.h>
-#include <mach/irqs.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);
@@ -32,8 +31,7 @@ static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
-
-extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
+static void __iomem *ac97_reg_base;
/*
* Beware PXA27x bugs:
@@ -48,71 +46,71 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
int pxa2xx_ac97_read(int slot, unsigned short reg)
{
int val = -ENODEV;
- volatile u32 *reg_addr;
+ u32 __iomem *reg_addr;
if (slot > 0)
return -ENODEV;
- mutex_lock(&car_mutex);
+ guard(mutex)(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = slot ? &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 & 0xffff);
+ 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)) {
+ return val;
+ 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 = -ETIMEDOUT;
- goto out;
+ __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits);
+ return -ETIMEDOUT;
}
/* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- val = (*reg_addr & 0xffff);
+ val = (readl(reg_addr) & 0xffff);
/* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out: mutex_unlock(&car_mutex);
+ wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1);
return val;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
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);
+ guard(mutex)(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = slot ? &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);
@@ -122,17 +120,17 @@ static inline void pxa_ac97_warm_pxa25x(void)
{
gsr_bits = 0;
- GCR |= GCR_WARM_RST;
+ 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;
+ writel(GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
@@ -144,15 +142,15 @@ static inline void pxa_ac97_warm_pxa27x(void)
/* warm reset broken on Bulverde, so manually keep AC97 reset high */
pxa27x_configure_ac97reset(reset_gpio, true);
udelay(10);
- GCR |= GCR_WARM_RST;
+ 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;
@@ -160,7 +158,7 @@ static inline void pxa_ac97_cold_pxa27x(void)
clk_prepare_enable(ac97conf_clk);
udelay(5);
clk_disable_unprepare(ac97conf_clk);
- GCR = GCR_COLD_RST | GCR_WARM_RST;
+ writel(GCR_COLD_RST | GCR_WARM_RST, ac97_reg_base + GCR);
}
#endif
@@ -170,26 +168,26 @@ static inline void pxa_ac97_warm_pxa3xx(void)
gsr_bits = 0;
/* Can't use interrupts */
- GCR |= GCR_WARM_RST;
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
}
static inline void pxa_ac97_cold_pxa3xx(void)
{
/* 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;
+ writel(GCR_WARM_RST | GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
@@ -215,10 +213,10 @@ bool pxa2xx_ac97_try_warm_reset(void)
#endif
snd_BUG();
- while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
- gsr = GSR | gsr_bits;
+ 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);
@@ -252,10 +250,10 @@ bool pxa2xx_ac97_try_cold_reset(void)
#endif
snd_BUG();
- while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
- gsr = GSR | gsr_bits;
+ 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);
@@ -270,8 +268,10 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
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);
@@ -279,9 +279,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);
@@ -289,9 +289,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;
@@ -303,7 +303,7 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
#ifdef CONFIG_PM
int pxa2xx_ac97_hw_suspend(void)
{
- GCR |= GCR_ACLINK_OFF;
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
clk_disable_unprepare(ac97_clk);
return 0;
}
@@ -320,8 +320,15 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
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:
@@ -337,6 +344,17 @@ int 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;
@@ -377,14 +395,20 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
if (ret)
goto err_clk2;
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "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;
@@ -402,8 +426,8 @@ void pxa2xx_ac97_hw_remove(struct platform_device *dev)
{
if (cpu_is_pxa27x())
gpio_free(reset_gpio);
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
+ 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;
@@ -414,6 +438,24 @@ void pxa2xx_ac97_hw_remove(struct platform_device *dev)
}
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 4bc244c40f80..77b11616a7ee 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -1,13 +1,10 @@
+// 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>
@@ -15,7 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
-#include <linux/dma/pxa-dma.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -24,10 +21,7 @@
#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_legacy_reset(struct snd_ac97 *ac97)
{
@@ -52,72 +46,55 @@ static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
- int __always_unused ret;
-
- ret = pxa2xx_ac97_write(ac97->num, reg, val);
+ pxa2xx_ac97_write(ac97->num, reg, val);
}
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
+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 pxad_param pxa2xx_ac97_pcm_out_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 12,
-};
-
-static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
- .addr = __PREG(PCDR),
- .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- .maxburst = 32,
- .filter_data = &pxa2xx_ac97_pcm_out_req,
-};
-
-static struct pxad_param pxa2xx_ac97_pcm_in_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 11,
-};
-
-static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
- .addr = __PREG(PCDR),
- .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- .maxburst = 32,
- .filter_data = &pxa2xx_ac97_pcm_in_req,
-};
-
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)
@@ -125,25 +102,20 @@ 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;
- return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
-}
+ int ret;
-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,
-};
+ ret = pxa2xx_pcm_prepare(substream);
+ if (ret < 0)
+ return ret;
-#ifdef CONFIG_PM_SLEEP
+ return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
+}
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);
@@ -190,8 +162,43 @@ static int pxa2xx_ac97_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
+
+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)
{
@@ -212,9 +219,9 @@ static int pxa2xx_ac97_probe(struct platform_device *dev)
if (ret < 0)
goto err;
- strlcpy(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;
@@ -252,7 +259,7 @@ err_dev:
return ret;
}
-static int pxa2xx_ac97_remove(struct platform_device *dev)
+static void pxa2xx_ac97_remove(struct platform_device *dev)
{
struct snd_card *card = platform_get_drvdata(dev);
@@ -260,8 +267,6 @@ static int pxa2xx_ac97_remove(struct platform_device *dev)
snd_card_free(card);
pxa2xx_ac97_hw_remove(dev);
}
-
- return 0;
}
static struct platform_driver pxa2xx_ac97_driver = {
@@ -269,9 +274,7 @@ static struct platform_driver pxa2xx_ac97_driver = {
.remove = pxa2xx_ac97_remove,
.driver = {
.name = "pxa2xx-ac97",
-#ifdef CONFIG_PM_SLEEP
.pm = &pxa2xx_ac97_pm_ops,
-#endif
},
};
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index e8da3b8ee721..571e9d909cdf 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -1,8 +1,4 @@
-/*
- * 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>
@@ -16,8 +12,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include "pxa2xx-pcm.h"
-
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -25,8 +19,8 @@ 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,
@@ -35,16 +29,16 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.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 dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_dmaengine_dai_dma_data *dma_params;
struct dma_slave_config config;
int ret;
- dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dma_params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dma_params)
return 0;
@@ -53,25 +47,16 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
snd_dmaengine_pcm_set_config_from_dai_data(substream,
- snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
+ snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream),
&config);
ret = dmaengine_slave_config(chan, &config);
if (ret)
return ret;
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
return 0;
}
-EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
-
-int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- 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)
{
@@ -86,22 +71,22 @@ pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
-int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
-EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
+EXPORT_SYMBOL(pxa2xx_pcm_prepare);
-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_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
- dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dma_params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dma_params)
return 0;
@@ -125,61 +110,85 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
return ret;
- return snd_dmaengine_pcm_open_request_chan(substream,
- pxad_filter_fn,
- dma_params->filter_data);
+ return snd_dmaengine_pcm_open(
+ substream, dma_request_slave_channel(snd_soc_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)
{
return snd_dmaengine_pcm_close_release_chan(substream);
}
-EXPORT_SYMBOL(__pxa2xx_pcm_close);
+EXPORT_SYMBOL(pxa2xx_pcm_close);
-int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
- runtime->dma_addr, runtime->dma_bytes);
+ 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_mmap);
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
-int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- 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_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
+ 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_preallocate_dma_buffer);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_new);
-void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_open(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_open);
+
+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_wc(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 1c6f4b436de3..000000000000
--- a/sound/arm/pxa2xx-pcm.c
+++ /dev/null
@@ -1,129 +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 <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-
-#include <mach/dma.h>
-
-#include <sound/core.h>
-#include <sound/pxa2xx-lib.h>
-#include <sound/dmaengine_pcm.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 = client->startup(substream);
- if (!ret)
- goto err2;
-
- return 0;
-
- 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;
-
- client->shutdown(substream);
-
- return __pxa2xx_pcm_close(substream);
-}
-
-static const 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,
-};
-
-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;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- goto out;
-
- 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 8fa2b7c9e6b8..000000000000
--- a/sound/arm/pxa2xx-pcm.h
+++ /dev/null
@@ -1,27 +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.
- */
-
-struct pxa2xx_runtime_data {
- int dma_ch;
- struct snd_dmaengine_dai_dma_data *params;
-};
-
-struct pxa2xx_pcm_client {
- struct snd_dmaengine_dai_dma_data *playback_params;
- struct snd_dmaengine_dai_dma_data *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 d789cbcb9106..6ed2d4a73374 100644
--- a/sound/atmel/Kconfig
+++ b/sound/atmel/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menu "Atmel devices (AT91)"
depends on ARCH_AT91
diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile
index d4009d1430ed..a8917d1854c7 100644
--- a/sound/atmel/Makefile
+++ b/sound/atmel/Makefile
@@ -1,3 +1,4 @@
-snd-atmel-ac97c-objs := ac97c.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-atmel-ac97c-y := ac97c.o
obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 380025887aef..df0a049192de 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -1,11 +1,8 @@
+// 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>
@@ -15,13 +12,13 @@
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
+#include <linux/string.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>
@@ -91,7 +88,7 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
@@ -100,7 +97,6 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
}
if (chip->cur_format)
runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
- mutex_unlock(&opened_mutex);
chip->playback_substream = substream;
return 0;
}
@@ -110,7 +106,7 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
@@ -119,7 +115,6 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
}
if (chip->cur_format)
runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
- mutex_unlock(&opened_mutex);
chip->capture_substream = substream;
return 0;
}
@@ -128,13 +123,12 @@ static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
- mutex_unlock(&opened_mutex);
chip->playback_substream = NULL;
@@ -145,13 +139,12 @@ static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
- mutex_unlock(&opened_mutex);
chip->capture_substream = NULL;
@@ -162,40 +155,26 @@ 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;
/* Set restrictions to params. */
- mutex_lock(&opened_mutex);
+ guard(mutex)(&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;
/* Set restrictions to params. */
- mutex_lock(&opened_mutex);
+ guard(mutex)(&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_playback_prepare(struct snd_pcm_substream *substream)
@@ -234,7 +213,7 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -316,7 +295,7 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -371,14 +350,14 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
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:
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:
ptcr |= ATMEL_PDC_TXTDIS;
if (chip->opened <= 1)
@@ -403,14 +382,14 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
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:
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:
ptcr |= ATMEL_PDC_RXTDIS;
if (chip->opened <= 1)
@@ -462,9 +441,7 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
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 = snd_pcm_lib_free_pages,
.prepare = atmel_ac97c_playback_prepare,
.trigger = atmel_ac97c_playback_trigger,
.pointer = atmel_ac97c_playback_pointer,
@@ -473,9 +450,7 @@ static const struct snd_pcm_ops atmel_ac97_playback_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 = snd_pcm_lib_free_pages,
.prepare = atmel_ac97c_capture_prepare,
.trigger = atmel_ac97c_capture_trigger,
.pointer = atmel_ac97c_capture_pointer,
@@ -494,12 +469,12 @@ 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" : "");
+ (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);
@@ -540,11 +515,11 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
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;
}
@@ -603,15 +578,13 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
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;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
return 0;
@@ -723,7 +696,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
struct atmel_ac97c *chip;
struct resource *regs;
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,
};
@@ -770,9 +743,9 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
spin_lock_init(&chip->lock);
- strcpy(card->driver, "Atmel AC97C");
- strcpy(card->shortname, "Atmel AC97C");
- sprintf(card->longname, "Atmel AC97 controller");
+ strscpy(card->driver, "Atmel AC97C");
+ strscpy(card->shortname, "Atmel AC97C");
+ strscpy(card->longname, "Atmel AC97 controller");
chip->card = card;
chip->pclk = pclk;
@@ -839,7 +812,6 @@ err_prepare_enable:
return retval;
}
-#ifdef CONFIG_PM_SLEEP
static int atmel_ac97c_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
@@ -858,13 +830,9 @@ static int atmel_ac97c_resume(struct device *pdev)
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_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume);
-static int 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);
@@ -879,8 +847,6 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
free_irq(chip->irq, chip);
snd_card_free(card);
-
- return 0;
}
static struct platform_driver atmel_ac97c_driver = {
@@ -888,7 +854,7 @@ static struct platform_driver atmel_ac97c_driver = {
.remove = atmel_ac97c_remove,
.driver = {
.name = "atmel_ac97c",
- .pm = ATMEL_AC97C_PM_OPS,
+ .pm = pm_ptr(&atmel_ac97c_pm),
.of_match_table = atmel_ac97c_dt_ids,
},
};
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/.kunitconfig b/sound/core/.kunitconfig
new file mode 100644
index 000000000000..440f974ba0b7
--- /dev/null
+++ b/sound/core/.kunitconfig
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_PCM=y
+CONFIG_SND_CORE_TEST=y
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 6e937a8146a1..48db44fa56fe 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA soundcard-configuration
config SND_TIMER
tristate
@@ -25,9 +26,42 @@ config SND_RAWMIDI
tristate
select SND_SEQ_DEVICE if SND_SEQUENCER != n
+config SND_UMP
+ tristate
+ select SND_RAWMIDI
+
+config SND_UMP_LEGACY_RAWMIDI
+ bool "Legacy raw MIDI support for UMP streams"
+ depends on SND_UMP
+ help
+ This option enables the legacy raw MIDI support for UMP streams.
+ When this option is set, an additional rawmidi device for the
+ legacy MIDI 1.0 byte streams is created for each UMP Endpoint.
+ The device contains 16 substreams corresponding to UMP groups.
+
+config SND_CORE_TEST
+ tristate "Sound core KUnit test"
+ depends on KUNIT
+ select SND_PCM
+ default KUNIT_ALL_TESTS
+ help
+ This options enables the sound core functions KUnit test.
+
+ KUnit tests run during boot and output the results to the debug
+ log in TAP format (https://testanything.org/). Only useful for
+ kernel devs running KUnit test harness and are not for inclusion
+ into a production build.
+
+ For more information on KUnit and unit tests in general, refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+
config SND_COMPRESS_OFFLOAD
tristate
+config SND_COMPRESS_ACCEL
+ bool
+
config SND_JACK
bool
@@ -48,7 +82,7 @@ config SND_MIXER_OSS
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.
@@ -61,7 +95,7 @@ config SND_PCM_OSS
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.
@@ -71,11 +105,11 @@ 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_PCM_TIMER
bool "PCM timer interface" if EXPERT
@@ -83,7 +117,7 @@ config SND_PCM_TIMER
help
If you disable this option, pcm timer will be unavailable, so
those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work
- incorrectlly.
+ incorrectly.
For some embedded devices, we may disable it to reduce memory
footprint, about 20KB on x86_64 platform.
@@ -127,13 +161,13 @@ config SND_SUPPORT_OLD_API
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.
+ 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"
@@ -141,17 +175,18 @@ config SND_VERBOSE_PROCFS
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"
+config SND_CTL_FAST_LOOKUP
+ bool "Fast lookup of control elements" if EXPERT
+ default y
+ select XARRAY_MULTI
help
- Say Y here to enable verbose log messages. These messages
- will help to identify source file and position containing
- printed messages.
-
- You don't need this unless you're debugging ALSA.
+ 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"
@@ -163,7 +198,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.
@@ -177,6 +212,49 @@ 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_UTIMER
+ bool "Enable support for userspace-controlled virtual timers"
+ depends on SND_TIMER
+ help
+ Say Y to enable the support of userspace-controlled timers. These
+ timers are purely virtual, and they are supposed to be triggered
+ from userspace. They could be quite useful when synchronizing the
+ sound timing with userspace applications (for instance, when sending
+ data through snd-aloop).
+
config SND_VMASTER
bool
@@ -184,4 +262,9 @@ 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
+
source "sound/core/seq/Kconfig"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index ee4a4a6b99ba..31a0623cc89d 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -17,7 +17,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.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_DMA_SGBUF) += sgbuf.o
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
@@ -25,18 +24,21 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
CFLAGS_pcm_lib.o := -I$(src)
CFLAGS_pcm_native.o := -I$(src)
-snd-pcm-dmaengine-objs := pcm_dmaengine.o
+snd-pcm-dmaengine-y := pcm_dmaengine.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-ctl-led-y := control_led.o
+snd-rawmidi-y := rawmidi.o
+snd-ump-y := ump.o
+snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o
+snd-timer-y := timer.o
+snd-hrtimer-y := hrtimer.o
+snd-hwdep-y := hwdep.o
+snd-seq-device-y := seq_device.o
-snd-compress-objs := compress_offload.o
+snd-compress-y := 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
@@ -44,6 +46,9 @@ 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_UMP) += snd-ump.o
+
+obj-$(CONFIG_SND_CORE_TEST) += sound_kunit.o
obj-$(CONFIG_SND_OSSEMUL) += oss/
obj-$(CONFIG_SND_SEQUENCER) += seq/
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index a12b9555e910..da514fef45bc 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* compress_core.c - compress offload core
*
@@ -6,21 +7,7 @@
* Pierre-Louis Bossart <pierre-louis.bossart@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; 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 FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
@@ -37,6 +24,7 @@
#include <linux/types.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
+#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/compat.h>
#include <sound/core.h>
@@ -60,8 +48,6 @@
* driver should be able to register multiple nodes
*/
-static DEFINE_MUTEX(device_mutex);
-
struct snd_compr_file {
unsigned long caps;
struct snd_compr_stream stream;
@@ -69,6 +55,12 @@ struct snd_compr_file {
static void error_delayed_work(struct work_struct *work);
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+static void snd_compr_task_free_all(struct snd_compr_stream *stream);
+#else
+static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { }
+#endif
+
/*
* a note on stream states used:
* we use following states in the compressed core
@@ -100,6 +92,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
dirn = SND_COMPRESS_PLAYBACK;
else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
dirn = SND_COMPRESS_CAPTURE;
+ else if ((f->f_flags & O_ACCMODE) == O_RDWR)
+ dirn = SND_COMPRESS_ACCEL;
else
return -EINVAL;
@@ -140,11 +134,13 @@ static int snd_compr_open(struct inode *inode, struct file *f)
}
runtime->state = SNDRV_PCM_STATE_OPEN;
init_waitqueue_head(&runtime->sleep);
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ INIT_LIST_HEAD(&runtime->tasks);
+#endif
data->stream.runtime = runtime;
f->private_data = (void *)data;
- mutex_lock(&compr->lock);
- ret = compr->ops->open(&data->stream);
- mutex_unlock(&compr->lock);
+ scoped_guard(mutex, &compr->lock)
+ ret = compr->ops->open(&data->stream);
if (ret) {
kfree(runtime);
kfree(data);
@@ -170,21 +166,35 @@ static int snd_compr_free(struct inode *inode, struct file *f)
break;
}
+ snd_compr_task_free_all(&data->stream);
+
data->stream.ops->free(&data->stream);
- kfree(data->stream.runtime->buffer);
+ if (!data->stream.runtime->dma_buffer_p)
+ kfree(data->stream.runtime->buffer);
kfree(data->stream.runtime);
kfree(data);
return 0;
}
+static void
+snd_compr_tstamp32_from_64(struct snd_compr_tstamp *tstamp32,
+ const struct snd_compr_tstamp64 *tstamp64)
+{
+ tstamp32->byte_offset = tstamp64->byte_offset;
+ tstamp32->copied_total = (u32)tstamp64->copied_total;
+ tstamp32->pcm_frames = (u32)tstamp64->pcm_frames;
+ tstamp32->pcm_io_frames = (u32)tstamp64->pcm_io_frames;
+ tstamp32->sampling_rate = tstamp64->sampling_rate;
+}
+
static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
- struct snd_compr_tstamp *tstamp)
+ struct snd_compr_tstamp64 *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);
+ pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset,
+ tstamp->copied_total);
if (stream->direction == SND_COMPRESS_PLAYBACK)
stream->runtime->total_bytes_transferred = tstamp->copied_total;
else
@@ -193,7 +203,7 @@ static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
}
static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
- struct snd_compr_avail *avail)
+ struct snd_compr_avail64 *avail)
{
memset(avail, 0, sizeof(*avail));
snd_compr_update_tstamp(stream, &avail->tstamp);
@@ -205,9 +215,9 @@ static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
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);
+ pr_debug("app wrote %llu, DSP consumed %llu\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) {
@@ -224,25 +234,43 @@ static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
if (stream->direction == SND_COMPRESS_PLAYBACK)
avail->avail = stream->runtime->buffer_size - avail->avail;
- pr_debug("ret avail as %lld\n", avail->avail);
+ pr_debug("ret avail as %zu\n", (size_t)avail->avail);
return avail->avail;
}
static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
{
- struct snd_compr_avail avail;
+ struct snd_compr_avail64 avail;
return snd_compr_calc_avail(stream, &avail);
}
-static int
-snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
+static void snd_compr_avail32_from_64(struct snd_compr_avail *avail32,
+ const struct snd_compr_avail64 *avail64)
+{
+ avail32->avail = avail64->avail;
+ snd_compr_tstamp32_from_64(&avail32->tstamp, &avail64->tstamp);
+}
+
+static int snd_compr_ioctl_avail(struct snd_compr_stream *stream,
+ unsigned long arg, bool is_32bit)
{
- struct snd_compr_avail ioctl_avail;
+ struct snd_compr_avail64 ioctl_avail64;
+ struct snd_compr_avail ioctl_avail32;
size_t avail;
+ const void *copy_from = &ioctl_avail64;
+ size_t copy_size = sizeof(ioctl_avail64);
- avail = snd_compr_calc_avail(stream, &ioctl_avail);
- ioctl_avail.avail = avail;
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+
+ avail = snd_compr_calc_avail(stream, &ioctl_avail64);
+ ioctl_avail64.avail = avail;
+ if (is_32bit) {
+ snd_compr_avail32_from_64(&ioctl_avail32, &ioctl_avail64);
+ copy_from = &ioctl_avail32;
+ copy_size = sizeof(ioctl_avail32);
+ }
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
@@ -253,8 +281,7 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
break;
}
- if (copy_to_user((__u64 __user *)arg,
- &ioctl_avail, sizeof(ioctl_avail)))
+ if (copy_to_user((__u64 __user *)arg, copy_from, copy_size))
return -EFAULT;
return 0;
}
@@ -272,8 +299,7 @@ static int snd_compr_write_data(struct snd_compr_stream *stream,
(app_pointer * runtime->buffer_size);
dstn = runtime->buffer + app_pointer;
- pr_debug("copying %ld at %lld\n",
- (unsigned long)count, app_pointer);
+ pr_debug("copying %lu at %llu\n", (unsigned long)count, app_pointer);
if (count < runtime->buffer_size - app_pointer) {
if (copy_from_user(dstn, buf, count))
return -EFAULT;
@@ -302,20 +328,21 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
return -EFAULT;
stream = &data->stream;
- mutex_lock(&stream->device->lock);
- /* write is allowed when stream is running or has been steup */
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+ guard(mutex)(&stream->device->lock);
+ /* write is allowed when stream is running or has been setup */
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);
+ pr_debug("avail returned %lu\n", (unsigned long)avail);
/* calculate how much we can write to buffer */
if (avail > count)
avail = count;
@@ -336,7 +363,6 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
pr_debug("stream prepared, Houston we are good to go\n");
}
- mutex_unlock(&stream->device->lock);
return retval;
}
@@ -353,7 +379,9 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
return -EFAULT;
stream = &data->stream;
- mutex_lock(&stream->device->lock);
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+ guard(mutex)(&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
@@ -364,30 +392,24 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_SUSPENDED:
case SNDRV_PCM_STATE_DISCONNECTED:
- retval = -EBADFD;
- goto out;
+ return -EBADFD;
case SNDRV_PCM_STATE_XRUN:
- retval = -EPIPE;
- goto out;
+ return -EPIPE;
}
avail = snd_compr_get_avail(stream);
- pr_debug("avail returned %ld\n", (unsigned long)avail);
+ pr_debug("avail returned %lu\n", (unsigned long)avail);
/* calculate how much we can read from buffer */
if (avail > count)
avail = count;
- if (stream->ops->copy) {
+ if (stream->ops->copy)
retval = stream->ops->copy(stream, buf, avail);
- } else {
- retval = -ENXIO;
- goto out;
- }
+ else
+ return -ENXIO;
if (retval > 0)
stream->runtime->total_bytes_transferred += retval;
-out:
- mutex_unlock(&stream->device->lock);
return retval;
}
@@ -399,59 +421,72 @@ static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
{
if (stream->direction == SND_COMPRESS_PLAYBACK)
- return POLLOUT | POLLWRNORM;
+ return EPOLLOUT | EPOLLWRNORM;
else
- return POLLIN | POLLRDNORM;
+ 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;
+ struct snd_compr_runtime *runtime;
size_t avail;
__poll_t retval = 0;
if (snd_BUG_ON(!data))
- return POLLERR;
+ return EPOLLERR;
stream = &data->stream;
+ runtime = stream->runtime;
- mutex_lock(&stream->device->lock);
+ guard(mutex)(&stream->device->lock);
- switch (stream->runtime->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_XRUN:
- retval = snd_compr_get_poll(stream) | POLLERR;
- goto out;
+ return snd_compr_get_poll(stream) | EPOLLERR;
default:
break;
}
- poll_wait(f, &stream->runtime->sleep, wait);
+ poll_wait(f, &runtime->sleep, wait);
+
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ if (stream->direction == SND_COMPRESS_ACCEL) {
+ struct snd_compr_task_runtime *task;
+ if (runtime->fragments > runtime->active_tasks)
+ retval |= EPOLLOUT | EPOLLWRNORM;
+ task = list_first_entry_or_null(&runtime->tasks,
+ struct snd_compr_task_runtime,
+ list);
+ if (task && task->state == SND_COMPRESS_TASK_STATE_FINISHED)
+ retval |= EPOLLIN | EPOLLRDNORM;
+ return retval;
+ }
+#endif
avail = snd_compr_get_avail(stream);
- pr_debug("avail is %ld\n", (unsigned long)avail);
+ pr_debug("avail is %lu\n", (unsigned long)avail);
/* check if we have at least one fragment to fill */
- switch (stream->runtime->state) {
+ switch (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;
+ 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)
+ if (avail >= runtime->fragment_size)
retval = snd_compr_get_poll(stream);
break;
default:
- retval = snd_compr_get_poll(stream) | POLLERR;
- break;
+ return snd_compr_get_poll(stream) | EPOLLERR;
}
-out:
- mutex_unlock(&stream->device->lock);
+
return retval;
}
@@ -479,7 +514,7 @@ static int
snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
{
int retval;
- struct snd_compr_codec_caps *caps;
+ struct snd_compr_codec_caps *caps __free(kfree) = NULL;
if (!stream->ops->get_codec_caps)
return -ENXIO;
@@ -490,22 +525,65 @@ snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
retval = stream->ops->get_codec_caps(stream, caps);
if (retval)
- goto out;
+ return retval;
if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
- retval = -EFAULT;
-
-out:
- kfree(caps);
+ return -EFAULT;
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;
+ void *buffer = NULL;
+
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ goto params;
buffer_size = params->buffer.fragment_size * params->buffer.fragments;
if (stream->ops->copy) {
@@ -514,22 +592,46 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
* the data from core
*/
} else {
- buffer = kmalloc(buffer_size, GFP_KERNEL);
+ 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;
+params:
+ stream->runtime->fragment_size = params->buffer.fragment_size;
+ stream->runtime->fragments = params->buffer.fragments;
return 0;
}
-static int snd_compress_check_input(struct snd_compr_params *params)
+static int
+snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_params *params)
{
+ u32 max_fragments;
+
/* first let's check the buffer parameter's */
- if (params->buffer.fragment_size == 0 ||
- params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
+ if (params->buffer.fragment_size == 0)
+ return -EINVAL;
+
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ max_fragments = 64; /* safe value */
+ else
+ max_fragments = U32_MAX / params->buffer.fragment_size;
+
+ if (params->buffer.fragments > max_fragments ||
+ params->buffer.fragments == 0)
return -EINVAL;
/* now codec parameters */
@@ -545,10 +647,10 @@ static int snd_compress_check_input(struct snd_compr_params *params)
static int
snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_params *params;
+ struct snd_compr_params *params __free(kfree) = NULL;
int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
/*
* we should allow parameter change only when stream has been
* opened not in other cases
@@ -557,39 +659,35 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
if (IS_ERR(params))
return PTR_ERR(params);
- retval = snd_compress_check_input(params);
+ retval = snd_compress_check_input(stream, params);
if (retval)
- goto out;
+ return retval;
retval = snd_compr_allocate_buffer(stream, params);
- if (retval) {
- retval = -ENOMEM;
- goto out;
- }
+ if (retval)
+ return -ENOMEM;
retval = stream->ops->set_params(stream, params);
if (retval)
- goto out;
+ return retval;
+
+ if (stream->next_track)
+ return retval;
stream->metadata_set = false;
stream->next_track = false;
- if (stream->direction == SND_COMPRESS_PLAYBACK)
- stream->runtime->state = SNDRV_PCM_STATE_SETUP;
- else
- stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+ 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;
+ struct snd_codec *params __free(kfree) = NULL;
int retval;
if (!stream->ops->get_params)
@@ -600,12 +698,9 @@ snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
return -ENOMEM;
retval = stream->ops->get_params(stream, params);
if (retval)
- goto out;
+ return retval;
if (copy_to_user((char __user *)arg, params, sizeof(*params)))
- retval = -EFAULT;
-
-out:
- kfree(params);
+ return -EFAULT;
return retval;
}
@@ -652,16 +747,26 @@ snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
return retval;
}
-static inline int
-snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
+static inline int snd_compr_tstamp(struct snd_compr_stream *stream,
+ unsigned long arg, bool is_32bit)
{
- struct snd_compr_tstamp tstamp = {0};
+ struct snd_compr_tstamp64 tstamp64 = { 0 };
+ struct snd_compr_tstamp tstamp32 = { 0 };
+ const void *copy_from = &tstamp64;
+ size_t copy_size = sizeof(tstamp64);
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;
+ ret = snd_compr_update_tstamp(stream, &tstamp64);
+ if (ret == 0) {
+ if (is_32bit) {
+ snd_compr_tstamp32_from_64(&tstamp32, &tstamp64);
+ copy_from = &tstamp32;
+ copy_size = sizeof(tstamp32);
+ }
+ ret = copy_to_user((void __user *)arg, copy_from, copy_size) ?
+ -EFAULT :
+ 0;
+ }
return ret;
}
@@ -669,11 +774,22 @@ static int snd_compr_pause(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ 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;
- retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
- if (!retval)
- stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
+ }
return retval;
}
@@ -681,11 +797,22 @@ static int snd_compr_resume(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
+ 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;
- retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
- if (!retval)
- stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ }
return retval;
}
@@ -693,8 +820,17 @@ static int snd_compr_start(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
+ 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;
@@ -705,11 +841,21 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
- stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+ 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;
@@ -723,15 +869,13 @@ static void error_delayed_work(struct work_struct *work)
stream = container_of(work, struct snd_compr_stream, error_work.work);
- mutex_lock(&stream->device->lock);
+ guard(mutex)(&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
@@ -739,6 +883,8 @@ static void error_delayed_work(struct work_struct *work)
* 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)
@@ -795,9 +941,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
- stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+ 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) {
@@ -817,6 +971,10 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
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
*/
@@ -834,13 +992,28 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
static int snd_compr_partial_drain(struct snd_compr_stream *stream)
{
int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
- stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+
+ 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");
@@ -852,74 +1025,348 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
return snd_compress_wait_for_drain(stream);
}
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+
+static struct snd_compr_task_runtime *
+snd_compr_find_task(struct snd_compr_stream *stream, __u64 seqno)
+{
+ struct snd_compr_task_runtime *task;
+
+ list_for_each_entry(task, &stream->runtime->tasks, list) {
+ if (task->seqno == seqno)
+ return task;
+ }
+ return NULL;
+}
+
+static void snd_compr_task_free(struct snd_compr_task_runtime *task)
+{
+ if (task->output)
+ dma_buf_put(task->output);
+ if (task->input)
+ dma_buf_put(task->input);
+ kfree(task);
+}
+
+static u64 snd_compr_seqno_next(struct snd_compr_stream *stream)
+{
+ u64 seqno = ++stream->runtime->task_seqno;
+ if (seqno == 0)
+ seqno = ++stream->runtime->task_seqno;
+ return seqno;
+}
+
+static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask)
+{
+ struct snd_compr_task_runtime *task;
+ int retval, fd_i, fd_o;
+
+ if (stream->runtime->total_tasks >= stream->runtime->fragments)
+ return -EBUSY;
+ if (utask->origin_seqno != 0 || utask->input_size != 0)
+ return -EINVAL;
+ task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (task == NULL)
+ return -ENOMEM;
+ task->seqno = utask->seqno = snd_compr_seqno_next(stream);
+ task->input_size = utask->input_size;
+ retval = stream->ops->task_create(stream, task);
+ if (retval < 0)
+ goto cleanup;
+ /* similar functionality as in dma_buf_fd(), but ensure that both
+ file descriptors are allocated before fd_install() */
+ if (!task->input || !task->input->file || !task->output || !task->output->file) {
+ retval = -EINVAL;
+ goto cleanup;
+ }
+ fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC);
+ if (fd_i < 0)
+ goto cleanup;
+ fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC);
+ if (fd_o < 0) {
+ put_unused_fd(fd_i);
+ goto cleanup;
+ }
+ /* keep dmabuf reference until freed with task free ioctl */
+ get_dma_buf(task->input);
+ get_dma_buf(task->output);
+ fd_install(fd_i, task->input->file);
+ fd_install(fd_o, task->output->file);
+ utask->input_fd = fd_i;
+ utask->output_fd = fd_o;
+ list_add_tail(&task->list, &stream->runtime->tasks);
+ stream->runtime->total_tasks++;
+ return 0;
+cleanup:
+ snd_compr_task_free(task);
+ return retval;
+}
+
+static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task *task __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ task = memdup_user((void __user *)arg, sizeof(*task));
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ retval = snd_compr_task_new(stream, task);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, task, sizeof(*task)))
+ retval = -EFAULT;
+ return retval;
+}
+
+static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task,
+ struct snd_compr_task *utask)
+{
+ if (task == NULL)
+ return -EINVAL;
+ if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED)
+ return -EBUSY;
+ if (utask->input_size > task->input->size)
+ return -EINVAL;
+ task->flags = utask->flags;
+ task->input_size = utask->input_size;
+ task->state = SND_COMPRESS_TASK_STATE_IDLE;
+ return 0;
+}
+
+static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_compr_task *utask)
+{
+ struct snd_compr_task_runtime *task;
+ int retval;
+
+ if (utask->origin_seqno > 0) {
+ task = snd_compr_find_task(stream, utask->origin_seqno);
+ retval = snd_compr_task_start_prepare(task, utask);
+ if (retval < 0)
+ return retval;
+ task->seqno = utask->seqno = snd_compr_seqno_next(stream);
+ utask->origin_seqno = 0;
+ list_move_tail(&task->list, &stream->runtime->tasks);
+ } else {
+ task = snd_compr_find_task(stream, utask->seqno);
+ if (task && task->state != SND_COMPRESS_TASK_STATE_IDLE)
+ return -EBUSY;
+ retval = snd_compr_task_start_prepare(task, utask);
+ if (retval < 0)
+ return retval;
+ }
+ retval = stream->ops->task_start(stream, task);
+ if (retval >= 0) {
+ task->state = SND_COMPRESS_TASK_STATE_ACTIVE;
+ stream->runtime->active_tasks++;
+ }
+ return retval;
+}
+
+static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task *task __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ task = memdup_user((void __user *)arg, sizeof(*task));
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ retval = snd_compr_task_start(stream, task);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, task, sizeof(*task)))
+ retval = -EFAULT;
+ return retval;
+}
+
+static void snd_compr_task_stop_one(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ if (task->state != SND_COMPRESS_TASK_STATE_ACTIVE)
+ return;
+ stream->ops->task_stop(stream, task);
+ if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
+ stream->runtime->active_tasks--;
+ list_move_tail(&task->list, &stream->runtime->tasks);
+ task->state = SND_COMPRESS_TASK_STATE_IDLE;
+}
+
+static void snd_compr_task_free_one(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ snd_compr_task_stop_one(stream, task);
+ stream->ops->task_free(stream, task);
+ list_del(&task->list);
+ snd_compr_task_free(task);
+ stream->runtime->total_tasks--;
+}
+
+static void snd_compr_task_free_all(struct snd_compr_stream *stream)
+{
+ struct snd_compr_task_runtime *task, *temp;
+
+ list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
+ snd_compr_task_free_one(stream, task);
+}
+
+typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task);
+
+static int snd_compr_task_seq(struct snd_compr_stream *stream, unsigned long arg,
+ snd_compr_seq_func_t fcn)
+{
+ struct snd_compr_task_runtime *task, *temp;
+ __u64 seqno;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ retval = copy_from_user(&seqno, (__u64 __user *)arg, sizeof(seqno));
+ if (retval)
+ return -EFAULT;
+ retval = 0;
+ if (seqno == 0) {
+ list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
+ fcn(stream, task);
+ } else {
+ task = snd_compr_find_task(stream, seqno);
+ if (task == NULL) {
+ retval = -EINVAL;
+ } else {
+ fcn(stream, task);
+ }
+ }
+ return retval;
+}
+
+static int snd_compr_task_status(struct snd_compr_stream *stream,
+ struct snd_compr_task_status *status)
+{
+ struct snd_compr_task_runtime *task;
+
+ task = snd_compr_find_task(stream, status->seqno);
+ if (task == NULL)
+ return -EINVAL;
+ status->input_size = task->input_size;
+ status->output_size = task->output_size;
+ status->state = task->state;
+ return 0;
+}
+
+static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task_status *status __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ status = memdup_user((void __user *)arg, sizeof(*status));
+ if (IS_ERR(status))
+ return PTR_ERR(status);
+ retval = snd_compr_task_status(stream, status);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, status, sizeof(*status)))
+ retval = -EFAULT;
+ return retval;
+}
+
+/**
+ * snd_compr_task_finished: Notify that the task was finished
+ * @stream: pointer to stream
+ * @task: runtime task structure
+ *
+ * Set the finished task state and notify waiters.
+ */
+void snd_compr_task_finished(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ guard(mutex)(&stream->device->lock);
+ if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
+ stream->runtime->active_tasks--;
+ task->state = SND_COMPRESS_TASK_STATE_FINISHED;
+ wake_up(&stream->runtime->sleep);
+}
+EXPORT_SYMBOL_GPL(snd_compr_task_finished);
+
+MODULE_IMPORT_NS("DMA_BUF");
+#endif /* CONFIG_SND_COMPRESS_ACCEL */
+
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,
+ guard(mutex)(&stream->device->lock);
+ switch (cmd) {
+ case SNDRV_COMPRESS_IOCTL_VERSION:
+ return 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;
+ case SNDRV_COMPRESS_GET_CAPS:
+ return snd_compr_get_caps(stream, arg);
#ifndef COMPR_CODEC_CAPS_OVERFLOW
- case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
- retval = snd_compr_get_codec_caps(stream, arg);
- break;
+ case SNDRV_COMPRESS_GET_CODEC_CAPS:
+ return snd_compr_get_codec_caps(stream, arg);
#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;
+ case SNDRV_COMPRESS_SET_PARAMS:
+ return snd_compr_set_params(stream, arg);
+ case SNDRV_COMPRESS_GET_PARAMS:
+ return snd_compr_get_params(stream, arg);
+ case SNDRV_COMPRESS_SET_METADATA:
+ return snd_compr_set_metadata(stream, arg);
+ case SNDRV_COMPRESS_GET_METADATA:
+ return snd_compr_get_metadata(stream, arg);
+ }
+ if (stream->direction == SND_COMPRESS_ACCEL) {
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ switch (cmd) {
+ case SNDRV_COMPRESS_TASK_CREATE:
+ return snd_compr_task_create(stream, arg);
+ case SNDRV_COMPRESS_TASK_FREE:
+ return snd_compr_task_seq(stream, arg, snd_compr_task_free_one);
+ case SNDRV_COMPRESS_TASK_START:
+ return snd_compr_task_start_ioctl(stream, arg);
+ case SNDRV_COMPRESS_TASK_STOP:
+ return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one);
+ case SNDRV_COMPRESS_TASK_STATUS:
+ return snd_compr_task_status_ioctl(stream, arg);
+ }
+#endif
+ return -ENOTTY;
}
- mutex_unlock(&stream->device->lock);
- return retval;
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP:
+ return snd_compr_tstamp(stream, arg, true);
+ case SNDRV_COMPRESS_TSTAMP64:
+ return snd_compr_tstamp(stream, arg, false);
+ case SNDRV_COMPRESS_AVAIL:
+ return snd_compr_ioctl_avail(stream, arg, true);
+ case SNDRV_COMPRESS_AVAIL64:
+ return snd_compr_ioctl_avail(stream, arg, false);
+ case SNDRV_COMPRESS_PAUSE:
+ return snd_compr_pause(stream);
+ case SNDRV_COMPRESS_RESUME:
+ return snd_compr_resume(stream);
+ case SNDRV_COMPRESS_START:
+ return snd_compr_start(stream);
+ case SNDRV_COMPRESS_STOP:
+ return snd_compr_stop(stream);
+ case SNDRV_COMPRESS_DRAIN:
+ return snd_compr_drain(stream);
+ case SNDRV_COMPRESS_PARTIAL_DRAIN:
+ return snd_compr_partial_drain(stream);
+ case SNDRV_COMPRESS_NEXT_TRACK:
+ return snd_compr_next_track(stream);
+ }
+
+ return -ENOTTY;
}
/* support of 32bit userspace on 64bit platforms */
@@ -947,7 +1394,7 @@ static const struct file_operations snd_compr_file_ops = {
static int snd_compress_dev_register(struct snd_device *device)
{
- int ret = -EINVAL;
+ int ret;
struct snd_compr *compr;
if (snd_BUG_ON(!device || !device->device_data))
@@ -959,7 +1406,7 @@ static int snd_compress_dev_register(struct snd_device *device)
/* register compressed device */
ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
compr->card, compr->device,
- &snd_compr_file_ops, compr, &compr->dev);
+ &snd_compr_file_ops, compr, compr->dev);
if (ret < 0) {
pr_err("snd_register_device failed %d\n", ret);
return ret;
@@ -973,7 +1420,7 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
struct snd_compr *compr;
compr = device->device_data;
- snd_unregister_device(&compr->dev);
+ snd_unregister_device(compr->dev);
return 0;
}
@@ -1001,23 +1448,14 @@ static int snd_compress_proc_init(struct snd_compr *compr)
compr->card->proc_root);
if (!entry)
return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(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) {
+ if (entry)
snd_info_set_text_ops(entry, compr,
snd_compress_proc_info_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
compr->proc_info_entry = entry;
return 0;
@@ -1033,7 +1471,7 @@ static void snd_compress_proc_done(struct snd_compr *compr)
static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
{
- strlcpy(compr->id, id, sizeof(compr->id));
+ strscpy(compr->id, id, sizeof(compr->id));
}
#else
static inline int snd_compress_proc_init(struct snd_compr *compr)
@@ -1056,122 +1494,57 @@ static int snd_compress_dev_free(struct snd_device *device)
compr = device->device_data;
snd_compress_proc_done(compr);
- put_device(&compr->dev);
+ 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 struct snd_device_ops ops = {
+ 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;
+#if !IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ if (snd_BUG_ON(dirn == SND_COMPRESS_ACCEL))
+ return -EINVAL;
+#endif
+
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_alloc(&compr->dev, card);
+ if (ret)
+ return ret;
+ 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);
+ else
+ put_device(compr->dev);
return ret;
}
EXPORT_SYMBOL_GPL(snd_compress_new);
-static int snd_compress_add_device(struct snd_compr *device)
-{
- int ret;
-
- if (!device->card)
- return -EINVAL;
-
- /* register the card */
- ret = snd_card_register(device->card);
- if (ret)
- goto out;
- return 0;
-
-out:
- pr_err("failed with %d\n", ret);
- return ret;
-
-}
-
-static int snd_compress_remove_device(struct snd_compr *device)
-{
- return snd_card_free(device->card);
-}
-
-/**
- * snd_compress_register - register compressed device
- *
- * @device: compressed device to register
- */
-int snd_compress_register(struct snd_compr *device)
-{
- int retval;
-
- if (device->name == NULL || device->ops == NULL)
- return -EINVAL;
-
- pr_debug("Registering compressed device %s\n", device->name);
- if (snd_BUG_ON(!device->ops->open))
- return -EINVAL;
- if (snd_BUG_ON(!device->ops->free))
- return -EINVAL;
- if (snd_BUG_ON(!device->ops->set_params))
- return -EINVAL;
- if (snd_BUG_ON(!device->ops->trigger))
- return -EINVAL;
-
- mutex_init(&device->lock);
-
- /* register a compressed card */
- mutex_lock(&device_mutex);
- retval = snd_compress_add_device(device);
- mutex_unlock(&device_mutex);
- return retval;
-}
-EXPORT_SYMBOL_GPL(snd_compress_register);
-
-int snd_compress_deregister(struct snd_compr *device)
-{
- pr_debug("Removing compressed device %s\n", device->name);
- mutex_lock(&device_mutex);
- snd_compress_remove_device(device);
- mutex_unlock(&device_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_compress_deregister);
-
-static int __init snd_compress_init(void)
-{
- return 0;
-}
-
-static void __exit snd_compress_exit(void)
-{
-}
-
-module_init(snd_compress_init);
-module_exit(snd_compress_exit);
-
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 50fa16022f1f..9c3fd5113a61 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1,39 +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 {
@@ -42,19 +32,23 @@ 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_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol);
static int snd_ctl_open(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
int i, err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -85,9 +79,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
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);
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
+ list_add_tail(&ctl->list, &card->ctl_files);
snd_card_unref(card);
return 0;
@@ -103,21 +96,18 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
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);
+
+ guard(spinlock_irqsave)(&ctl->read_lock);
while (!list_empty(&ctl->events)) {
cread = snd_kctl_event(ctl->events.next);
list_del(&cread->list);
kfree(cread);
}
- spin_unlock_irqrestore(&ctl->read_lock, flags);
}
static int snd_ctl_release(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
struct snd_kcontrol *control;
@@ -126,15 +116,18 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
ctl = file->private_data;
file->private_data = NULL;
card = ctl->card;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_del(&ctl->list);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
- down_write(&card->controls_rwsem);
- list_for_each_entry(control, &card->controls, list)
- for (idx = 0; idx < control->count; idx++)
- if (control->vd[idx].owner == ctl)
- control->vd[idx].owner = NULL;
- up_write(&card->controls_rwsem);
+
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
+ list_del(&ctl->list);
+
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ list_for_each_entry(control, &card->controls, list)
+ for (idx = 0; idx < control->count; idx++)
+ if (control->vd[idx].owner == ctl)
+ control->vd[idx].owner = NULL;
+ }
+
+ snd_fasync_free(ctl->fasync);
snd_ctl_empty_read_queue(ctl);
put_pid(ctl->pid);
kfree(ctl);
@@ -156,46 +149,70 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
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;
if (card->shutdown)
return;
- read_lock(&card->ctl_files_rwlock);
+
+ guard(read_lock_irqsave)(&card->controls_rwlock);
#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);
- list_for_each_entry(ev, &ctl->events, list) {
- if (ev->id.numid == id->numid) {
- ev->mask |= mask;
- goto _found;
+ scoped_guard(spinlock, &ctl->read_lock) {
+ list_for_each_entry(ev, &ctl->events, list) {
+ if (ev->id.numid == id->numid) {
+ ev->mask |= mask;
+ goto _found;
+ }
}
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (ev) {
+ ev->id = *id;
+ ev->mask = mask;
+ list_add_tail(&ev->list, &ctl->events);
+ } else {
+ dev_err(card->dev, "No memory available to allocate event\n");
+ }
+_found:
+ wake_up(&ctl->change_sleep);
}
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (ev) {
- ev->id = *id;
- ev->mask = mask;
- list_add_tail(&ev->list, &ctl->events);
- } else {
- 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);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
- read_unlock(&card->ctl_files_rwlock);
}
EXPORT_SYMBOL(snd_ctl_notify);
/**
+ * 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
+ *
+ * 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);
+ guard(rwsem_read)(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ lops->lnotify(card, mask, kctl, ioff);
+}
+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
@@ -211,24 +228,20 @@ EXPORT_SYMBOL(snd_ctl_notify);
static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
unsigned int access, struct snd_ctl_file *file)
{
- unsigned int size;
unsigned int idx;
-
+
if (count == 0 || count > MAX_CONTROL_COUNT)
return -EINVAL;
- size = sizeof(struct snd_kcontrol);
- size += sizeof(struct snd_kcontrol_volatile) * count;
-
- *kctl = kzalloc(size, GFP_KERNEL);
+ *kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
if (!*kctl)
return -ENOMEM;
+ (*kctl)->count = count;
for (idx = 0; idx < count; idx++) {
(*kctl)->vd[idx].access = access;
(*kctl)->vd[idx].owner = file;
}
- (*kctl)->count = count;
return 0;
}
@@ -238,7 +251,7 @@ static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
* @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.
*
@@ -251,7 +264,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
unsigned int count;
unsigned int access;
int err;
-
+
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
@@ -267,7 +280,9 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
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_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)
@@ -278,7 +293,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
kctl->id.device = ncontrol->device;
kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
- strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
+ 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);
@@ -348,67 +363,190 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
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.
- *
- * It frees automatically the control which cannot be added.
- *
- * Return: Zero if successful, or a negative error code on failure.
- *
+/* 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;
- unsigned int count;
+ struct snd_kcontrol *old;
+ int err;
+
+ lockdep_assert_held_write(&card->controls_rwsem);
+
+ id = kcontrol->id;
+ 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_locked(card, old);
+ if (err < 0)
+ return err;
+ }
+
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0)
+ return -ENOMEM;
+
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ 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;
- id = kcontrol->id;
- if (id.index > UINT_MAX - kcontrol->count)
- goto error;
- down_write(&card->controls_rwsem);
- if (snd_ctl_find_id(card, &id)) {
- up_write(&card->controls_rwsem);
- dev_err(card->dev, "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;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = __snd_ctl_add_replace(card, kcontrol, mode);
+
+ if (err < 0)
goto error;
- }
- list_add_tail(&kcontrol->list, &card->controls);
- card->controls_count += kcontrol->count;
- kcontrol->id.numid = card->last_numid + 1;
- card->last_numid += kcontrol->count;
- id = kcontrol->id;
- count = kcontrol->count;
- up_write(&card->controls_rwsem);
- for (idx = 0; idx < count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
return 0;
error:
snd_ctl_free_one(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);
/**
@@ -428,55 +566,41 @@ EXPORT_SYMBOL(snd_ctl_add);
int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
bool add_on_replace)
{
- struct snd_ctl_elem_id id;
- unsigned int count;
+ 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)
+{
unsigned int idx;
- struct snd_kcontrol *old;
- int ret;
- if (!kcontrol)
+ lockdep_assert_held_write(&card->controls_rwsem);
+
+ if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
- if (snd_BUG_ON(!card || !kcontrol->info)) {
- ret = -EINVAL;
- goto error;
- }
- id = kcontrol->id;
- down_write(&card->controls_rwsem);
- old = snd_ctl_find_id(card, &id);
- if (!old) {
- if (add_on_replace)
- goto add;
- up_write(&card->controls_rwsem);
- ret = -EINVAL;
- goto error;
- }
- ret = snd_ctl_remove(card, old);
- if (ret < 0) {
- up_write(&card->controls_rwsem);
- goto error;
- }
-add:
- if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
- up_write(&card->controls_rwsem);
- ret = -ENOMEM;
- goto error;
+
+ if (remove_hash)
+ remove_hash_entries(card, kcontrol);
+
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ list_del(&kcontrol->list);
+ card->controls_count -= kcontrol->count;
}
- list_add_tail(&kcontrol->list, &card->controls);
- card->controls_count += kcontrol->count;
- kcontrol->id.numid = card->last_numid + 1;
- card->last_numid += kcontrol->count;
- id = kcontrol->id;
- count = kcontrol->count;
- up_write(&card->controls_rwsem);
- for (idx = 0; idx < count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
- return 0;
-error:
+ 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 ret;
+ return 0;
+}
+
+static inline int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ return __snd_ctl_remove(card, kcontrol, true);
}
-EXPORT_SYMBOL(snd_ctl_replace);
/**
* snd_ctl_remove - remove the control from the card and release it
@@ -484,25 +608,19 @@ EXPORT_SYMBOL(snd_ctl_replace);
* @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).
+ * You don't need to call snd_ctl_free_one().
+ * Passing NULL to @kcontrol argument is allowed as noop.
*
* Return: 0 if successful, or a negative error code on failure.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
- struct snd_ctl_elem_id id;
- unsigned int idx;
-
- if (snd_BUG_ON(!card || !kcontrol))
- return -EINVAL;
- list_del(&kcontrol->list);
- 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);
- snd_ctl_free_one(kcontrol);
- return 0;
+ if (!kcontrol)
+ return 0;
+ guard(rwsem_write)(&card->controls_rwsem);
+ return snd_ctl_remove_locked(card, kcontrol);
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -519,17 +637,12 @@ EXPORT_SYMBOL(snd_ctl_remove);
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
- int ret;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
- ret = snd_ctl_remove(card, kctl);
- up_write(&card->controls_rwsem);
- return ret;
+ return snd_ctl_remove_locked(card, kctl);
}
EXPORT_SYMBOL(snd_ctl_remove_id);
@@ -548,30 +661,18 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
{
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
- int idx, ret;
+ int idx;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
- if (kctl == NULL) {
- ret = -ENOENT;
- goto error;
- }
- if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
- ret = -EINVAL;
- goto error;
- }
+ if (kctl == NULL)
+ return -ENOENT;
+ if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER))
+ return -EINVAL;
for (idx = 0; idx < kctl->count; idx++)
- if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
- ret = -EBUSY;
- 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;
+ if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file)
+ return -EBUSY;
+ return snd_ctl_remove_locked(card, kctl);
}
/**
@@ -613,11 +714,13 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
snd_ctl_build_ioff(id, kctl, index_offset);
- ret = 1;
+ 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);
- if (ret > 0)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
return ret;
}
EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
@@ -631,52 +734,96 @@ EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
* Finds the control with the old id from the card, and replaces the
* id with the new one.
*
+ * The function tries to keep the already assigned numid while replacing
+ * the rest.
+ *
+ * Note that this function should be used only in the card initialization
+ * phase. Calling after the card instantiation may cause issues with
+ * user-space expecting persistent numids.
+ *
* 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)
{
struct snd_kcontrol *kctl;
+ int saved_numid;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, src_id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
+ saved_numid = kctl->id.numid;
+ remove_hash_entries(card, kctl);
kctl->id = *dst_id;
- kctl->id.numid = card->last_numid + 1;
- card->last_numid += kctl->count;
- up_write(&card->controls_rwsem);
+ kctl->id.numid = saved_numid;
+ add_hash_entries(card, kctl);
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
- *
- * 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).
+ * @kctl: the control to rename
+ * @name: the new name
*
- * Return: The pointer of the instance if found, or %NULL if not.
+ * Renames the specified control on the card to the new name.
*
+ * Note that this function takes card->controls_rwsem lock internally.
*/
-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)
+{
+ guard(rwsem_write)(&card->controls_rwsem);
+ 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;
+ guard(read_lock_irqsave)(&card->controls_rwlock);
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.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwlock lock internally.
+ */
+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);
/**
@@ -686,36 +833,33 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
*
* Finds the control instance with the given 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.
*
+ * Note that this function takes card->controls_rwlock lock internally.
*/
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
- struct snd_ctl_elem_id *id)
+ const struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id))
return NULL;
+
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each_entry(kctl, &card->controls, list) {
- 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 */
+ guard(read_lock_irqsave)(&card->controls_rwlock);
+ list_for_each_entry(kctl, &card->controls, list)
+ if (elem_id_matches(kctl, id))
+ return kctl;
+
return NULL;
}
EXPORT_SYMBOL(snd_ctl_find_id);
@@ -723,117 +867,270 @@ EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
- struct snd_ctl_card_info *info;
+ struct snd_ctl_card_info *info __free(kfree) = NULL;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
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));
- up_read(&snd_ioctl_rwsem);
- if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
- kfree(info);
- return -EFAULT;
+ scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+ info->card = card->number;
+ 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));
}
- kfree(info);
+ if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
+ return -EFAULT;
return 0;
}
static int snd_ctl_elem_list(struct snd_card *card,
- struct snd_ctl_elem_list __user *_list)
+ struct snd_ctl_elem_list *list)
{
- struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id id;
unsigned int offset, space, jidx;
- int err = 0;
-
+
+ offset = list->offset;
+ space = list->space;
+
+ guard(rwsem_read)(&card->controls_rwsem);
+ list->count = card->controls_count;
+ list->used = 0;
+ if (!space)
+ return 0;
+ 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)))
+ return -EFAULT;
+ list->used++;
+ if (!--space)
+ return 0;
+ }
+ offset = 0;
+ }
+ return 0;
+}
+
+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;
- offset = list.offset;
- space = list.space;
-
- down_read(&card->controls_rwsem);
- list.count = card->controls_count;
- list.used = 0;
- if (space > 0) {
- 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;
- }
- offset = 0;
- }
+ err = snd_ctl_elem_list(card, &list);
+ if (err)
+ return err;
+ if (copy_to_user(_list, &list, sizeof(list)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* 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;
}
- out:
- up_read(&card->controls_rwsem);
- if (!err && copy_to_user(_list, &list, sizeof(list)))
- err = -EFAULT;
- return err;
+ 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;
}
-static bool validate_element_member_dimension(struct snd_ctl_elem_info *info)
+/* 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)
{
- unsigned int members;
- unsigned int i;
+ size_t offset = value_sizes[info->type] * info->count;
- if (info->dimen.d[0] == 0)
- return true;
+ offset = DIV_ROUND_UP(offset, sizeof(u32));
+ memset32((u32 *)control->value.bytes.data + offset, pattern,
+ sizeof(control->value) / sizeof(u32) - offset);
+}
- members = 1;
- for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) {
- if (info->dimen.d[i] == 0)
- break;
- members *= info->dimen.d[i];
-
- /*
- * info->count should be validated in advance, to guarantee
- * calculation soundness.
- */
- if (members > info->count)
- return false;
+/* 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;
}
- for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) {
- if (info->dimen.d[i] > 0)
- return false;
+ 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 members == info->count;
+ return 0;
}
-static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
- struct snd_ctl_elem_info *info)
+/* 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
@@ -852,25 +1149,45 @@ 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;
}
- up_read(&card->controls_rwsem);
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;
+
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &info->id);
+ if (!kctl)
+ return -ENOENT;
+ return __snd_ctl_elem_info(card, kctl, info, ctl);
+}
+
static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info __user *_info)
{
+ struct snd_card *card = ctl->card;
struct snd_ctl_elem_info info;
int result;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
- result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
- if (result < 0)
+ result = snd_power_ref_and_wait(card);
+ if (result)
return result;
result = snd_ctl_elem_info(ctl, &info);
+ snd_power_unref(card);
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;
@@ -882,44 +1199,68 @@ static int snd_ctl_elem_read(struct snd_card *card,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
+ struct snd_ctl_elem_info info;
+ const u32 pattern = 0xdeadbeef;
+ int ret;
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
- if (kctl == NULL)
+ if (!kctl)
return -ENOENT;
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)
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || !kctl->get)
return -EPERM;
snd_ctl_build_ioff(&control->id, kctl, index_offset);
- return kctl->get(kctl, control);
+
+#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)
+ return ret;
+#endif
+
+ if (!snd_ctl_skip_validation(&info))
+ fill_remaining_elem_value(control, &info, pattern);
+ ret = kctl->get(kctl, control);
+ if (ret < 0)
+ return ret;
+ 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);
+ return -EINVAL;
+ }
+ return 0;
}
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
int result;
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
return PTR_ERR(control);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result < 0)
- goto error;
-
- down_read(&card->controls_rwsem);
+ result = snd_power_ref_and_wait(card);
+ if (result)
+ return result;
result = snd_ctl_elem_read(card, control);
- up_read(&card->controls_rwsem);
+ snd_power_unref(card);
if (result < 0)
- goto error;
+ return result;
if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- error:
- kfree(control);
+ return -EFAULT;
return result;
}
@@ -929,27 +1270,48 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result;
+ int result = 0;
+ down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
- if (kctl == NULL)
+ if (kctl == NULL) {
+ 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 = kctl->put(kctl, control);
- if (result < 0)
+ /* validate input values */
+ if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION)) {
+ 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);
+ if (result < 0) {
+ up_write(&card->controls_rwsem);
return result;
+ }
if (result > 0) {
- struct snd_ctl_elem_id id = control->id;
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
+ downgrade_write(&card->controls_rwsem);
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
+ up_read(&card->controls_rwsem);
+ } else {
+ up_write(&card->controls_rwsem);
}
return 0;
@@ -958,7 +1320,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
struct snd_card *card;
int result;
@@ -967,20 +1329,16 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
return PTR_ERR(control);
card = file->card;
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
+ result = snd_power_ref_and_wait(card);
if (result < 0)
- goto error;
-
- down_write(&card->controls_rwsem);
+ return result;
result = snd_ctl_elem_write(card, file, control);
- up_write(&card->controls_rwsem);
+ snd_power_unref(card);
if (result < 0)
- goto error;
+ return result;
if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- error:
- kfree(control);
+ return -EFAULT;
return result;
}
@@ -991,25 +1349,18 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
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);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner != NULL)
- result = -EBUSY;
- else {
- vd->owner = file;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (vd->owner)
+ return -EBUSY;
+ vd->owner = file;
+ return 0;
}
static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
@@ -1019,27 +1370,20 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
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);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner == NULL)
- result = -EINVAL;
- else if (vd->owner != file)
- result = -EPERM;
- else {
- vd->owner = NULL;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (!vd->owner)
+ return -EINVAL;
+ if (vd->owner != file)
+ return -EPERM;
+ vd->owner = NULL;
+ return 0;
}
struct user_element {
@@ -1052,10 +1396,16 @@ struct user_element {
void *priv_data; /* private data (like strings for enumerated type) */
};
+// 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;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
unsigned int offset;
offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
@@ -1068,7 +1418,7 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
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;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
const char *names;
unsigned int item;
unsigned int offset;
@@ -1085,7 +1435,7 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
names = ue->priv_data;
for (; item > 0; --item)
names += strlen(names) + 1;
- strcpy(uinfo->value.enumerated.name, names);
+ strscpy(uinfo->value.enumerated.name, names);
return 0;
}
@@ -1093,7 +1443,7 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct user_element *ue = kcontrol->private_data;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
unsigned int size = ue->elem_data_size;
char *src = ue->elem_data +
snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
@@ -1105,31 +1455,41 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int change;
- struct user_element *ue = kcontrol->private_data;
+ int err, change;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
unsigned int size = ue->elem_data_size;
char *dst = ue->elem_data +
snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
+ err = sanity_check_input_values(ue->card, ucontrol, &ue->info, false);
+ if (err < 0)
+ return err;
+
change = memcmp(&ucontrol->value, dst, size) != 0;
if (change)
memcpy(dst, &ucontrol->value, size);
return change;
}
+/* 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 = kctl->private_data;
+ struct user_element *ue = snd_kcontrol_chip(kctl);
unsigned int *container;
- struct snd_ctl_elem_id id;
unsigned int mask = 0;
int i;
int change;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
if (size > 1024 * 128) /* sane value */
return -EINVAL;
+ // 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);
@@ -1147,17 +1507,20 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
for (i = 0; i < kctl->count; ++i)
kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
mask = SNDRV_CTL_EVENT_MASK_INFO;
+ } else {
+ ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ ue->tlv_data_size = 0;
+ kvfree(ue->tlv_data);
}
- 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_build_ioff(&id, kctl, i);
- snd_ctl_notify(ue->card, mask, &id);
- }
+ for (i = 0; i < kctl->count; ++i)
+ snd_ctl_notify_one(ue->card, mask, kctl, i);
return change;
}
@@ -1165,7 +1528,7 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
static int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
unsigned int size)
{
- struct user_element *ue = kctl->private_data;
+ struct user_element *ue = snd_kcontrol_chip(kctl);
if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
return -ENXIO;
@@ -1188,6 +1551,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
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;
@@ -1195,16 +1559,19 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
unsigned int i;
const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
- if (ue->info.value.enumerated.names_length > 64 * 1024)
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
+ buf_len = ue->info.value.enumerated.names_length;
+ if (buf_len > 64 * 1024)
return -EINVAL;
- names = vmemdup_user((const void __user *)user_ptrval,
- ue->info.value.enumerated.names_length);
+ 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 */
- buf_len = ue->info.value.enumerated.names_length;
p = names;
for (i = 0; i < ue->info.value.enumerated.items; ++i) {
name_len = strnlen(p, buf_len);
@@ -1218,13 +1585,26 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
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;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+
+ // 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);
@@ -1234,28 +1614,12 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct snd_ctl_elem_info *info, int replace)
{
- /* 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),
- };
- 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,
- };
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
long private_size;
+ size_t alloc_size;
struct user_element *ue;
unsigned int offset;
int err;
@@ -1273,17 +1637,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
return err;
}
- /*
- * The number of userspace controls are counted control by control,
- * not element by element.
- */
- if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
- return -ENOMEM;
-
/* Check the number of elements for this userspace control. */
count = info->owner;
if (count == 0)
count = 1;
+ if (count > MAX_CONTROL_COUNT)
+ return -EINVAL;
/* Arrange access permissions if needed. */
access = info->access;
@@ -1302,18 +1661,19 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
* Check information and calculate the size of data specific to
* this userspace control.
*/
- if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
- info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64)
- return -EINVAL;
- if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
- info->value.enumerated.items == 0)
- return -EINVAL;
- if (info->count < 1 ||
- info->count > max_value_counts[info->type])
- return -EINVAL;
- if (!validate_element_member_dimension(info))
+ /* pass NULL to card for suppressing error messages */
+ err = snd_ctl_check_elem_info(NULL, info);
+ if (err < 0)
+ return err;
+ /* 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);
+
+ guard(rwsem_write)(&card->controls_rwsem);
+ if (check_user_elem_overflow(card, alloc_size))
+ return -ENOMEM;
/*
* Keep memory object for this userspace control. After passing this
@@ -1325,16 +1685,18 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (err < 0)
return err;
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
- kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count,
- GFP_KERNEL);
- if (kctl->private_data == NULL) {
+ ue = kzalloc(alloc_size, GFP_KERNEL);
+ if (!ue) {
kfree(kctl);
return -ENOMEM;
}
+ 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 = (struct user_element *)kctl->private_data;
ue->card = card;
ue->info = *info;
ue->info.access = 0;
@@ -1361,9 +1723,11 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
kctl->tlv.c = snd_ctl_elem_user_tlv;
/* This function manage to free the instance on failure. */
- err = snd_ctl_add(card, kctl);
- if (err < 0)
+ err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
+ if (err < 0) {
+ snd_ctl_free_one(kctl);
return err;
+ }
offset = snd_ctl_get_ioff(kctl, &info->id);
snd_ctl_build_ioff(&info->id, kctl, offset);
/*
@@ -1373,11 +1737,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
* applications because the field originally means PID of a process
* which locks the element.
*/
-
- down_write(&card->controls_rwsem);
- card->user_ctl_count++;
- up_write(&card->controls_rwsem);
-
return 0;
}
@@ -1458,8 +1817,9 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
if (kctl->tlv.c == NULL)
return -ENXIO;
- /* When locked, this is unavailable. */
- if (vd->owner != NULL && vd->owner != file)
+ /* 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;
return kctl->tlv.c(kctl, op_flag, size, buf);
@@ -1492,12 +1852,14 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
int op_flag)
{
struct snd_ctl_tlv header;
- unsigned int *container;
+ unsigned int __user *container;
unsigned int container_size;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
+ lockdep_assert_held(&file->card->controls_rwsem);
+
if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
@@ -1553,7 +1915,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:
@@ -1573,38 +1935,41 @@ 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:
- down_read(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
- up_read(&ctl->card->controls_rwsem);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_read, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
+ snd_power_unref(card);
return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- down_write(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
- up_write(&ctl->card->controls_rwsem);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
+ snd_power_unref(card);
return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- down_write(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
- up_write(&ctl->card->controls_rwsem);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
+ snd_power_unref(card);
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);
+
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
- up_read(&snd_ioctl_rwsem);
dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
@@ -1679,7 +2044,7 @@ static __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
mask = 0;
if (!list_empty(&ctl->events))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
@@ -1696,9 +2061,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
if (pn == NULL)
return -ENOMEM;
pn->fioctl = fcn;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_add_tail(&pn->list, lists);
- up_write(&snd_ioctl_rwsem);
return 0;
}
@@ -1707,6 +2071,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
* @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)
{
@@ -1719,6 +2085,8 @@ EXPORT_SYMBOL(snd_ctl_register_ioctl);
* 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)
{
@@ -1737,16 +2105,14 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
if (snd_BUG_ON(!fcn))
return -EINVAL;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
list_del(&p->list);
- up_write(&snd_ioctl_rwsem);
kfree(p);
return 0;
}
}
- up_write(&snd_ioctl_rwsem);
snd_BUG();
return -EINVAL;
}
@@ -1754,6 +2120,8 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
/**
* 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)
{
@@ -1763,9 +2131,11 @@ EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT
/**
- * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit
- * control-ioctls
+ * 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)
{
@@ -1779,7 +2149,7 @@ 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;
@@ -1790,7 +2160,7 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
struct snd_ctl_file *kctl;
int subdevice = -1;
- read_lock(&card->ctl_files_rwlock);
+ guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == task_pid(current)) {
subdevice = kctl->preferred_subdevice[type];
@@ -1798,7 +2168,6 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
break;
}
}
- read_unlock(&card->ctl_files_rwlock);
return subdevice;
}
EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
@@ -1813,6 +2182,84 @@ EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
#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;
+ scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) {
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ if (strcmp(lops->module_name, module_name) == 0)
+ 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;
+
+ scoped_guard(rwsem_write, &snd_ctl_layer_rwsem) {
+ lops->next = snd_ctl_layer;
+ snd_ctl_layer = lops;
+ }
+ for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
+ card = snd_card_ref(card_number);
+ if (card) {
+ scoped_guard(rwsem_read, &card->controls_rwsem)
+ lops->lregister(card);
+ 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;
+
+ guard(rwsem_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;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
+
+/*
* INIT PART
*/
@@ -1822,22 +2269,36 @@ static const struct file_operations snd_ctl_f_ops =
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
- .llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
+/* call lops under rwsems; called from snd_ctl_dev_*() below() */
+#define call_snd_ctl_lops(_card, _op) \
+ do { \
+ struct snd_ctl_layer_ops *lops; \
+ guard(rwsem_read)(&(_card)->controls_rwsem); \
+ guard(rwsem_read)(&snd_ctl_layer_rwsem); \
+ for (lops = snd_ctl_layer; lops; lops = lops->next) \
+ lops->_op(_card); \
+ } while (0)
+
/*
* registration of the control device
*/
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
+ int err;
- return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
- &snd_ctl_f_ops, card, &card->ctl_dev);
+ err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
+ &snd_ctl_f_ops, card, card->ctl_dev);
+ if (err < 0)
+ return err;
+ call_snd_ctl_lops(card, lregister);
+ return 0;
}
/*
@@ -1848,14 +2309,15 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(ctl, &card->ctl_files, list) {
- wake_up(&ctl->change_sleep);
- kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
+ scoped_guard(read_lock_irqsave, &card->controls_rwlock) {
+ list_for_each_entry(ctl, &card->ctl_files, list) {
+ wake_up(&ctl->change_sleep);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+ }
}
- read_unlock(&card->ctl_files_rwlock);
- return snd_unregister_device(&card->ctl_dev);
+ call_snd_ctl_lops(card, ldisconnect);
+ return snd_unregister_device(card->ctl_dev);
}
/*
@@ -1866,13 +2328,18 @@ static int snd_ctl_dev_free(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_kcontrol *control;
- down_write(&card->controls_rwsem);
- while (!list_empty(&card->controls)) {
- control = snd_kcontrol(card->controls.next);
- snd_ctl_remove(card, control);
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ while (!list_empty(&card->controls)) {
+ control = snd_kcontrol(card->controls.next);
+ __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);
+ put_device(card->ctl_dev);
return 0;
}
@@ -1882,7 +2349,7 @@ 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,
@@ -1894,12 +2361,14 @@ int snd_ctl_create(struct snd_card *card)
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_alloc(&card->ctl_dev, card);
+ if (err < 0)
+ return err;
+ 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);
+ put_device(card->ctl_dev);
return err;
}
@@ -1915,6 +2384,8 @@ int snd_ctl_create(struct snd_card *card)
*
* 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)
@@ -1935,6 +2406,8 @@ EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
*
* 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)
@@ -1958,7 +2431,7 @@ EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
* If the control's accessibility is not the default (readable and writable),
* the caller has to fill @info->access.
*
- * Return: Zero.
+ * 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[])
@@ -1973,7 +2446,7 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
"ALSA: too long item name '%s'\n",
names[info->value.enumerated.item]);
- strlcpy(info->value.enumerated.name,
+ strscpy(info->value.enumerated.name,
names[info->value.enumerated.item],
sizeof(info->value.enumerated.name));
return 0;
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index a848836a5de0..6459809ed364 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;
}
@@ -89,69 +74,66 @@ struct snd_ctl_elem_info32 {
unsigned char reserved[128];
} value;
unsigned char reserved[64];
-} __attribute__((packed));
+} __packed;
static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info32 __user *data32)
{
- struct snd_ctl_elem_info *data;
+ struct snd_card *card = ctl->card;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
int err;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* copy id */
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
- goto error;
+ return -EFAULT;
/* we need to copy the item index.
* hope this doesn't break anything..
*/
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
- goto error;
+ return -EFAULT;
- err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
+ err = snd_power_ref_and_wait(card);
if (err < 0)
- goto error;
+ return err;
err = snd_ctl_elem_info(ctl, data);
+ snd_power_unref(card);
if (err < 0)
- goto error;
+ return err;
/* restore info to 32bit */
- err = -EFAULT;
/* id, type, access, count */
if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
- goto error;
+ return -EFAULT;
if (put_user(data->owner, &data32->owner))
- goto error;
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (put_user(data->value.integer.min, &data32->value.integer.min) ||
put_user(data->value.integer.max, &data32->value.integer.max) ||
put_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_to_user(&data32->value.integer64,
&data->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_to_user(&data32->value.enumerated,
&data->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
break;
default:
break;
}
- err = 0;
- error:
- kfree(data);
- return err;
+ return 0;
}
/* read / write */
@@ -168,7 +150,7 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128];
};
-#ifdef CONFIG_X86_X32
+#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;
@@ -180,39 +162,33 @@ struct snd_ctl_elem_value_x32 {
} value;
unsigned char reserved[128];
};
-#endif /* CONFIG_X86_X32 */
+#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,
int *countp)
{
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info *info __free(kfree) = NULL;
int err;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
- if (! kctl) {
- up_read(&card->controls_rwsem);
+ if (!kctl)
return -ENOENT;
- }
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- up_read(&card->controls_rwsem);
+ if (info == NULL)
return -ENOMEM;
- }
info->id = *id;
err = kctl->info(kctl, info);
- up_read(&card->controls_rwsem);
if (err >= 0) {
err = info->type;
*countp = info->count;
}
- kfree(info);
return err;
}
-static int get_elem_size(int type, int count)
+static int get_elem_size(snd_ctl_elem_type_t type, int count)
{
switch (type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
@@ -236,7 +212,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
{
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)))
@@ -249,8 +225,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type < 0)
return type;
- if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
- type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
s32 __user *intp = valuep;
int val;
@@ -259,7 +235,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
data->value.integer.value[i] = val;
}
} else {
- size = get_elem_size(type, count);
+ size = get_elem_size((__force snd_ctl_elem_type_t)type, count);
if (size < 0) {
dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
@@ -279,10 +255,11 @@ static int copy_ctl_value_to_user(void __user *userdata,
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) {
+ if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
s32 __user *intp = valuep;
int val;
@@ -291,17 +268,19 @@ static int copy_ctl_value_to_user(void __user *userdata,
return -EFAULT;
}
} else {
- size = get_elem_size(type, count);
+ size = get_elem_size((__force snd_ctl_elem_type_t)type, count);
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 ctl_elem_read_user(struct snd_card *card,
- void __user *userdata, void __user *valuep)
+static int __ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data;
+ struct snd_ctl_elem_value *data __free(kfree) = NULL;
int err, type, count;
data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -311,24 +290,31 @@ static int ctl_elem_read_user(struct snd_card *card,
err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
- goto error;
+ return err;
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err < 0)
- goto error;
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;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
+}
+
+static int ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
+{
+ int err;
+
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ err = __ctl_elem_read_user(card, userdata, valuep);
+ snd_power_unref(card);
return err;
}
-static int ctl_elem_write_user(struct snd_ctl_file *file,
- void __user *userdata, void __user *valuep)
+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_ctl_elem_value *data __free(kfree) = NULL;
struct snd_card *card = file->card;
int err, type, count;
@@ -339,17 +325,25 @@ static int ctl_elem_write_user(struct snd_ctl_file *file,
err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
- goto error;
+ return err;
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err < 0)
- goto error;
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;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
+}
+
+static int ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
+{
+ struct snd_card *card = file->card;
+ int err;
+
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ err = __ctl_elem_write_user(file, userdata, valuep);
+ snd_power_unref(card);
return err;
}
@@ -365,7 +359,7 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
return ctl_elem_write_user(file, data32, &data32->value);
}
-#ifdef CONFIG_X86_X32
+#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)
{
@@ -377,57 +371,51 @@ static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
{
return ctl_elem_write_user(file, data32, &data32->value);
}
-#endif /* CONFIG_X86_X32 */
+#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,
int replace)
{
- struct snd_ctl_elem_info *data;
- int err;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* id, type, access, count */ \
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))
- goto error;
+ return -EFAULT;
+ if (get_user(data->owner, &data32->owner))
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (get_user(data->value.integer.min, &data32->value.integer.min) ||
get_user(data->value.integer.max, &data32->value.integer.max) ||
get_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_from_user(&data->value.integer64,
&data32->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_from_user(&data->value.enumerated,
&data32->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
data->value.enumerated.names_ptr =
(uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
break;
default:
break;
}
- err = snd_ctl_elem_add(file, data, replace);
- error:
- kfree(data);
- return err;
+ return snd_ctl_elem_add(file, data, replace);
}
enum {
@@ -437,10 +425,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
+#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 */
+#endif /* CONFIG_X86_X32_ABI */
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -479,24 +467,21 @@ 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
+#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 */
+#endif /* CONFIG_X86_X32_ABI */
}
- down_read(&snd_ioctl_rwsem);
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_compat_ioctls, list) {
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
}
- up_read(&snd_ioctl_rwsem);
return -ENOIOCTLCMD;
}
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
new file mode 100644
index 000000000000..e33dfcf863cf
--- /dev/null
+++ b/sound/core/control_led.c
@@ -0,0 +1,789 @@
+// 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 led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS];
+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;
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ /* the card may not be registered (active) at this point */
+ if (card && !snd_ctl_led_card_valid[card->number])
+ 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));
+ }
+ }
+ }
+ 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) {
+ struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
+
+ led_trigger_event(trig, 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;
+
+ guard(mutex)(&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);
+ }
+ 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);
+ }
+}
+
+DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
+
+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 __free(snd_card_unref) = NULL;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int ioff, access, new_access;
+
+ card = snd_card_ref(card_number);
+ if (!card)
+ return -ENXIO;
+ guard(rwsem_write)(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (!kctl)
+ return -ENOENT;
+ 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))
+ return -EXDEV;
+ 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);
+ }
+ return 0;
+}
+
+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_ctl *lctl, *_lctl;
+ struct snd_ctl_led *led;
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
+ if (!card || lctl->card == card)
+ snd_ctl_led_ctl_destroy(lctl);
+ }
+}
+
+static int snd_ctl_led_reset(int card_number, unsigned int group)
+{
+ struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_ctl_led_ctl *lctl, *_lctl;
+ struct snd_ctl_led *led;
+ struct snd_kcontrol_volatile *vd;
+ bool change = false;
+
+ card = snd_card_ref(card_number);
+ if (!card)
+ return -ENXIO;
+
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ if (!snd_ctl_led_card_valid[card_number])
+ return -ENXIO;
+ led = &snd_ctl_leds[group];
+ list_for_each_entry_safe(lctl, _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;
+ }
+ }
+ if (change)
+ snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
+ 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;
+ scoped_guard(mutex, &snd_ctl_led_mutex)
+ snd_ctl_led_card_valid[card->number] = true;
+ /* 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);
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ snd_ctl_led_card_valid[card->number] = false;
+ snd_ctl_led_clean(card);
+ }
+ 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;
+
+ scoped_guard(mutex, &snd_ctl_led_mutex)
+ led->mode = mode;
+
+ 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);
+ struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
+
+ return sysfs_emit(buf, "%u\n", led_trigger_get_brightness(trig));
+}
+
+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 __free(snd_card_unref) = NULL;
+ struct snd_ctl_led_ctl *lctl;
+ size_t l = 0;
+
+ card = snd_card_ref(led_card->number);
+ if (!card)
+ return -ENXIO;
+ guard(rwsem_read)(&card->controls_rwsem);
+ guard(mutex)(&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);
+ }
+ }
+ 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);
+ if (sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj,
+ link_name))
+ dev_err(card->dev,
+ "%s: can't create symlink to controlC%i device\n",
+ __func__, card->number);
+ if (sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj,
+ "card"))
+ dev_err(card->dev,
+ "%s: can't create symlink to card%i\n",
+ __func__, card->number);
+
+ continue;
+cerr:
+ put_device(&led_card->dev);
+cerr2:
+ dev_err(card->dev, "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;
+
+ led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
+ led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
+
+ 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);
+
+ led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
+ led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
+}
+
+module_init(snd_ctl_led_init)
+module_exit(snd_ctl_led_exit)
+
+MODULE_ALIAS("ledtrig:audio-mute");
+MODULE_ALIAS("ledtrig:audio-micmute");
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
index 0249d5e6ac23..709b1a9c2caa 100644
--- a/sound/core/ctljack.c
+++ b/sound/core/ctljack.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Helper functions for jack-detection kcontrols
*
* Copyright (c) 2011 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.
*/
#include <linux/kernel.h>
@@ -39,7 +35,7 @@ static int get_available_index(struct snd_card *card, const char *name)
sid.index = 0;
sid.iface = SNDRV_CTL_ELEM_IFACE_CARD;
- strlcpy(sid.name, name, sizeof(sid.name));
+ strscpy(sid.name, name, sizeof(sid.name));
while (snd_ctl_find_id(card, &sid)) {
sid.index++;
diff --git a/sound/core/device.c b/sound/core/device.c
index cb0e46f66cc9..cdc5af526739 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -1,22 +1,7 @@
+// 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>
@@ -42,7 +27,7 @@
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_device_new(struct snd_card *card, enum snd_device_type type,
- void *device_data, struct snd_device_ops *ops)
+ void *device_data, const struct snd_device_ops *ops)
{
struct snd_device *dev;
struct list_head *p;
@@ -240,6 +225,15 @@ void snd_device_free_all(struct snd_card *card)
if (snd_BUG_ON(!card))
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);
+ }
+
+ /* free all */
list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
__snd_device_free(dev);
}
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 18cb6f476bf4..2d5f4d47071f 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -1,25 +1,12 @@
+// 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>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/hrtimer.h>
@@ -49,29 +36,27 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
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);
+ scoped_guard(spinlock, &t->lock) {
+ if (!t->running)
+ return HRTIMER_NORESTART; /* fast path */
+ stime->in_callback = true;
+ ticks = t->sticks;
+ }
/* calculate the drift */
- delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
+ delta = ktime_sub(hrtimer_cb_get_time(hrt), hrtimer_get_expires(hrt));
if (delta > 0)
ticks += ktime_divns(delta, ticks * resolution);
snd_timer_interrupt(stime->timer, ticks);
- spin_lock(&t->lock);
+ guard(spinlock)(&t->lock);
if (t->running) {
hrtimer_add_expires_ns(hrt, t->sticks * resolution);
ret = HRTIMER_RESTART;
}
stime->in_callback = false;
- out:
- spin_unlock(&t->lock);
return ret;
}
@@ -82,9 +67,8 @@ static int snd_hrtimer_open(struct snd_timer *t)
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;
+ hrtimer_setup(&stime->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
t->private_data = stime;
return 0;
}
@@ -94,10 +78,10 @@ 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);
+ scoped_guard(spinlock_irq, &t->lock) {
+ t->running = 0; /* just to be sure */
+ stime->in_callback = 1; /* skip start/stop */
+ }
hrtimer_cancel(&stime->hrt);
kfree(stime);
@@ -128,7 +112,7 @@ static int snd_hrtimer_stop(struct snd_timer *t)
}
static const struct snd_timer_hardware hrtimer_hw __initconst = {
- .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET,
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
.open = snd_hrtimer_open,
.close = snd_hrtimer_close,
.start = snd_hrtimer_start,
@@ -155,7 +139,7 @@ static int __init snd_hrtimer_init(void)
return err;
timer->module = THIS_MODULE;
- strcpy(timer->name, "HR timer");
+ strscpy(timer->name, "HR timer");
timer->hw = hrtimer_hw;
timer->hw.resolution = resolution;
timer->hw.ticks = NANO_SEC / resolution;
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 26e71cf05f1e..09200df2932c 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>
@@ -164,12 +149,12 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
struct snd_hwdep *hw = file->private_data;
struct module *mod = hw->card->module;
- mutex_lock(&hw->open_mutex);
- if (hw->ops.release)
- err = hw->ops.release(hw, file);
- if (hw->used > 0)
- hw->used--;
- mutex_unlock(&hw->open_mutex);
+ scoped_guard(mutex, &hw->open_mutex) {
+ if (hw->ops.release)
+ err = hw->ops.release(hw, file);
+ if (hw->used > 0)
+ hw->used--;
+ }
wake_up(&hw->open_wait);
snd_card_file_remove(hw->card, file);
@@ -192,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;
@@ -210,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;
@@ -218,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)
+ 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;
- 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)
{
@@ -253,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);
@@ -279,23 +272,23 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
- mutex_lock(&register_mutex);
-
- 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;
- device++;
+
+ scoped_guard(mutex, &register_mutex) {
+ 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;
+ device++;
+ }
+ if (device >= SNDRV_MINOR_HWDEPS)
+ device = -1;
}
- if (device >= SNDRV_MINOR_HWDEPS)
- device = -1;
- mutex_unlock(&register_mutex);
if (put_user(device, (int __user *)arg))
return -EFAULT;
return 0;
@@ -303,19 +296,18 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
case SNDRV_CTL_IOCTL_HWDEP_INFO:
{
struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
- int device, err;
+ int device;
struct snd_hwdep *hwdep;
if (get_user(device, &info->device))
return -EFAULT;
- mutex_lock(&register_mutex);
- hwdep = snd_hwdep_search(card, device);
- if (hwdep)
- err = snd_hwdep_info(hwdep, info);
- else
- err = -ENXIO;
- mutex_unlock(&register_mutex);
- return err;
+ scoped_guard(mutex, &register_mutex) {
+ hwdep = snd_hwdep_search(card, device);
+ if (!hwdep)
+ return -ENXIO;
+ return snd_hwdep_info(hwdep, info);
+ }
+ break;
}
}
return -ENOIOCTLCMD;
@@ -345,9 +337,14 @@ static const struct file_operations snd_hwdep_f_ops =
.mmap = snd_hwdep_mmap,
};
-static void release_hwdep_device(struct device *dev)
+static void snd_hwdep_free(struct snd_hwdep *hwdep)
{
- kfree(container_of(dev, struct snd_hwdep, dev));
+ if (!hwdep)
+ return;
+ if (hwdep->private_free)
+ hwdep->private_free(hwdep);
+ put_device(hwdep->dev);
+ kfree(hwdep);
}
/**
@@ -368,7 +365,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
{
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,
@@ -387,18 +384,22 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
hwdep->card = card;
hwdep->device = device;
if (id)
- strlcpy(hwdep->id, id, sizeof(hwdep->id));
+ strscpy(hwdep->id, id, sizeof(hwdep->id));
+
+ err = snd_device_alloc(&hwdep->dev, card);
+ if (err < 0) {
+ snd_hwdep_free(hwdep);
+ return err;
+ }
- snd_device_initialize(&hwdep->dev, card);
- hwdep->dev.release = release_hwdep_device;
- dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
+ dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device);
#ifdef CONFIG_SND_OSSEMUL
hwdep->oss_type = -1;
#endif
err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
if (err < 0) {
- put_device(&hwdep->dev);
+ snd_hwdep_free(hwdep);
return err;
}
@@ -410,12 +411,7 @@ EXPORT_SYMBOL(snd_hwdep_new);
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);
- put_device(&hwdep->dev);
+ snd_hwdep_free(device->device_data);
return 0;
}
@@ -425,19 +421,16 @@ static int snd_hwdep_dev_register(struct snd_device *device)
struct snd_card *card = hwdep->card;
int err;
- mutex_lock(&register_mutex);
- if (snd_hwdep_search(card, hwdep->device)) {
- mutex_unlock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ if (snd_hwdep_search(card, hwdep->device))
return -EBUSY;
- }
list_add_tail(&hwdep->list, &snd_hwdep_devices);
err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep, &hwdep->dev);
+ &snd_hwdep_f_ops, hwdep, hwdep->dev);
if (err < 0) {
- dev_err(&hwdep->dev, "unable to register\n");
+ dev_err(hwdep->dev, "unable to register\n");
list_del(&hwdep->list);
- mutex_unlock(&register_mutex);
return err;
}
@@ -446,18 +439,17 @@ static int snd_hwdep_dev_register(struct snd_device *device)
if (hwdep->oss_type >= 0) {
if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
hwdep->device)
- dev_warn(&hwdep->dev,
+ 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,
+ dev_warn(hwdep->dev,
"unable to register OSS compatibility device\n");
else
hwdep->ossreg = 1;
}
#endif
- mutex_unlock(&register_mutex);
return 0;
}
@@ -467,21 +459,17 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
if (snd_BUG_ON(!hwdep))
return -ENXIO;
- mutex_lock(&register_mutex);
- if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
- mutex_unlock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep)
return -EINVAL;
- }
- mutex_lock(&hwdep->open_mutex);
+ guard(mutex)(&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(&hwdep->dev);
+ snd_unregister_device(hwdep->dev);
list_del_init(&hwdep->list);
- mutex_unlock(&hwdep->open_mutex);
- mutex_unlock(&register_mutex);
return 0;
}
@@ -495,11 +483,10 @@ static void snd_hwdep_proc_read(struct snd_info_entry *entry,
{
struct snd_hwdep *hwdep;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(hwdep, &snd_hwdep_devices, list)
snd_iprintf(buffer, "%02i-%02i: %s\n",
hwdep->card->number, hwdep->device, hwdep->name);
- mutex_unlock(&register_mutex);
}
static struct snd_info_entry *snd_hwdep_proc_entry;
@@ -508,7 +495,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);
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 aa86f3f8e056..1f5b8a3d9e3b 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>
@@ -31,11 +16,10 @@
#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
-#include <stdarg.h>
int snd_info_check_reserved_words(const char *str)
{
- static char *reserved[] =
+ static const char * const reserved[] =
{
"version",
"meminfo",
@@ -50,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))
@@ -72,7 +56,7 @@ struct snd_info_private_data {
};
static int snd_info_version_init(void);
-static void snd_info_disconnect(struct snd_info_entry *entry);
+static void snd_info_clear_entries(struct snd_info_entry *entry);
/*
@@ -121,17 +105,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
struct snd_info_entry *entry;
- loff_t ret = -EINVAL, size;
+ loff_t size;
data = file->private_data;
entry = data->entry;
- mutex_lock(&entry->access);
- if (entry->c.ops->llseek) {
- offset = entry->c.ops->llseek(entry,
- data->file_private_data,
- file, offset, orig);
- goto out;
- }
+ guard(mutex)(&entry->access);
+ if (entry->c.ops->llseek)
+ return entry->c.ops->llseek(entry,
+ data->file_private_data,
+ file, offset, orig);
size = entry->size;
switch (orig) {
@@ -142,21 +124,18 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
break;
case SEEK_END:
if (!size)
- goto out;
+ return -EINVAL;
offset += size;
break;
default:
- goto out;
+ return -EINVAL;
}
if (offset < 0)
- goto out;
+ return -EINVAL;
if (size && offset > size)
offset = size;
file->f_pos = offset;
- ret = offset;
- out:
- mutex_unlock(&entry->access);
- return ret;
+ return offset;
}
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
@@ -214,9 +193,9 @@ static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
data->file_private_data,
file, wait);
if (entry->c.ops->read)
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
if (entry->c.ops->write)
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
return mask;
}
@@ -250,14 +229,14 @@ static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
static int snd_info_entry_open(struct inode *inode, struct file *file)
{
- struct snd_info_entry *entry = PDE_DATA(inode);
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
int mode, err;
- mutex_lock(&info_mutex);
+ guard(mutex)(&info_mutex);
err = alloc_info_private(entry, &data);
if (err < 0)
- goto unlock;
+ return err;
mode = file->f_flags & O_ACCMODE;
if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
@@ -273,14 +252,11 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
}
file->private_data = data;
- mutex_unlock(&info_mutex);
return 0;
error:
kfree(data);
module_put(entry->module);
- unlock:
- mutex_unlock(&info_mutex);
return err;
}
@@ -297,17 +273,16 @@ static int snd_info_entry_release(struct inode *inode, struct file *file)
return 0;
}
-static const struct file_operations snd_info_entry_operations =
+static const struct proc_ops snd_info_entry_operations =
{
- .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,
+ .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,
};
/*
@@ -323,7 +298,6 @@ static ssize_t snd_info_text_entry_write(struct file *file,
struct snd_info_buffer *buf;
loff_t pos;
size_t next;
- int err = 0;
if (!entry->c.text.write)
return -EIO;
@@ -334,34 +308,24 @@ static ssize_t snd_info_text_entry_write(struct file *file,
/* don't handle too large text inputs */
if (next > 16 * 1024)
return -EIO;
- mutex_lock(&entry->access);
+ guard(mutex)(&entry->access);
buf = data->wbuffer;
if (!buf) {
data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
- if (!buf) {
- err = -ENOMEM;
- goto error;
- }
+ if (!buf)
+ return -ENOMEM;
}
if (next > buf->len) {
char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
- if (!nbuf) {
- err = -ENOMEM;
- goto error;
- }
+ if (!nbuf)
+ return -ENOMEM;
kvfree(buf->buffer);
buf->buffer = nbuf;
buf->len = PAGE_ALIGN(next);
}
- if (copy_from_user(buf->buffer + pos, buffer, count)) {
- err = -EFAULT;
- goto error;
- }
+ if (copy_from_user(buf->buffer + pos, buffer, count))
+ return -EFAULT;
buf->size = next;
- error:
- mutex_unlock(&entry->access);
- if (err < 0)
- return err;
*offset = next;
return count;
}
@@ -382,14 +346,14 @@ static int snd_info_seq_show(struct seq_file *seq, void *p)
static int snd_info_text_entry_open(struct inode *inode, struct file *file)
{
- struct snd_info_entry *entry = PDE_DATA(inode);
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
int err;
- mutex_lock(&info_mutex);
+ guard(mutex)(&info_mutex);
err = alloc_info_private(entry, &data);
if (err < 0)
- goto unlock;
+ return err;
data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
if (!data->rbuffer) {
@@ -403,15 +367,12 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file)
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;
}
@@ -436,14 +397,13 @@ static int snd_info_text_entry_release(struct inode *inode, struct file *file)
return 0;
}
-static const struct file_operations snd_info_text_entry_ops =
+static const struct proc_ops snd_info_text_entry_ops =
{
- .owner = THIS_MODULE,
- .open = snd_info_text_entry_open,
- .release = snd_info_text_entry_release,
- .write = snd_info_text_entry_write,
- .llseek = seq_lseek,
- .read = seq_read,
+ .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,
};
static struct snd_info_entry *create_subdir(struct module *mod,
@@ -454,7 +414,7 @@ static struct snd_info_entry *create_subdir(struct module *mod,
entry = snd_info_create_module_entry(mod, name, NULL);
if (!entry)
return NULL;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return NULL;
@@ -463,14 +423,15 @@ static struct snd_info_entry *create_subdir(struct module *mod,
}
static struct snd_info_entry *
-snd_info_create_entry(const char *name, struct snd_info_entry *parent);
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+ struct module *module);
int __init snd_info_init(void)
{
- snd_proc_root = snd_info_create_entry("asound", NULL);
+ snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
if (!snd_proc_root)
return -ENOMEM;
- snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ snd_proc_root->mode = S_IFDIR | 0555;
snd_proc_root->p = proc_mkdir("asound", NULL);
if (!snd_proc_root->p)
goto error;
@@ -503,6 +464,14 @@ int __exit snd_info_done(void)
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
* called from init.c
@@ -520,28 +489,8 @@ int snd_info_card_create(struct snd_card *card)
if (!entry)
return -ENOMEM;
card->proc_root = entry;
- return 0;
-}
-/* register all pending info entries */
-static int snd_info_register_recursive(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_recursive(p);
- if (err < 0)
- return err;
- }
-
- return 0;
+ return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}
/*
@@ -557,7 +506,7 @@ int snd_info_card_register(struct snd_card *card)
if (snd_BUG_ON(!card))
return -ENXIO;
- err = snd_info_register_recursive(card->proc_root);
+ err = snd_info_register(card->proc_root);
if (err < 0)
return err;
@@ -578,7 +527,7 @@ int snd_info_card_register(struct snd_card *card)
*/
void snd_info_card_id_change(struct snd_card *card)
{
- mutex_lock(&info_mutex);
+ guard(mutex)(&info_mutex);
if (card->proc_root_link) {
proc_remove(card->proc_root_link);
card->proc_root_link = NULL;
@@ -587,7 +536,6 @@ void snd_info_card_id_change(struct snd_card *card)
card->proc_root_link = proc_symlink(card->id,
snd_proc_root->p,
card->proc_root->name);
- mutex_unlock(&info_mutex);
}
/*
@@ -598,12 +546,16 @@ void snd_info_card_disconnect(struct snd_card *card)
{
if (!card)
return;
- mutex_lock(&info_mutex);
+
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);
+ proc_remove(card->proc_root->p);
+
+ guard(mutex)(&info_mutex);
+ if (card->proc_root)
+ snd_info_clear_entries(card->proc_root);
+ card->proc_root_link = NULL;
+ card->proc_root = NULL;
}
/*
@@ -632,9 +584,11 @@ int snd_info_card_free(struct snd_card *card)
*/
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
- int c = -1;
+ int c;
- if (snd_BUG_ON(!buffer || !buffer->buffer))
+ if (snd_BUG_ON(!buffer))
+ return 1;
+ if (!buffer->buffer)
return 1;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
@@ -705,7 +659,8 @@ EXPORT_SYMBOL(snd_info_get_str);
* Return: The pointer of the new instance, or %NULL on failure.
*/
static struct snd_info_entry *
-snd_info_create_entry(const char *name, struct snd_info_entry *parent)
+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);
@@ -716,14 +671,17 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent)
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;
- if (parent)
+ entry->module = module;
+ if (parent) {
+ guard(mutex)(&parent->access);
list_add_tail(&entry->list, &parent->children);
+ }
return entry;
}
@@ -741,10 +699,9 @@ 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, parent);
- if (entry)
- entry->module = module;
- return entry;
+ if (!parent)
+ parent = snd_proc_root;
+ return snd_info_create_entry(name, parent, module);
}
EXPORT_SYMBOL(snd_info_create_module_entry);
@@ -762,24 +719,20 @@ 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, parent);
- if (entry) {
- entry->module = card->module;
- entry->card = card;
- }
- 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)
+static void snd_info_clear_entries(struct snd_info_entry *entry)
{
struct snd_info_entry *p;
if (!entry->p)
return;
list_for_each_entry(p, &entry->children, list)
- snd_info_disconnect(p);
- proc_remove(entry->p);
+ snd_info_clear_entries(p);
entry->p = NULL;
}
@@ -796,16 +749,20 @@ void snd_info_free_entry(struct snd_info_entry * entry)
if (!entry)
return;
if (entry->p) {
- mutex_lock(&info_mutex);
- snd_info_disconnect(entry);
- mutex_unlock(&info_mutex);
+ proc_remove(entry->p);
+ guard(mutex)(&info_mutex);
+ snd_info_clear_entries(entry);
}
/* free all children at first */
list_for_each_entry_safe(p, n, &entry->children, list)
snd_info_free_entry(p);
- list_del(&entry->list);
+ p = entry->parent;
+ if (p) {
+ guard(mutex)(&p->access);
+ list_del(&entry->list);
+ }
kfree(entry->name);
if (entry->private_free)
entry->private_free(entry);
@@ -813,48 +770,100 @@ void snd_info_free_entry(struct snd_info_entry * entry)
}
EXPORT_SYMBOL(snd_info_free_entry);
-/**
- * snd_info_register - register the info entry
- * @entry: the info entry
- *
- * Registers the proc info entry.
- *
- * Return: 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->p : entry->parent->p;
- mutex_lock(&info_mutex);
+ guard(mutex)(&info_mutex);
+ if (entry->p || !root)
+ return 0;
if (S_ISDIR(entry->mode)) {
p = proc_mkdir_mode(entry->name, entry->mode, root);
- if (!p) {
- mutex_unlock(&info_mutex);
+ if (!p)
return -ENOMEM;
- }
} else {
- const struct file_operations *ops;
+ 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);
+ if (!p)
return -ENOMEM;
- }
proc_set_size(p, entry->size);
}
entry->p = p;
- 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);
+
/*
*/
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index f479374b6bd8..0dbbb8005570 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.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/slab.h>
@@ -44,21 +29,17 @@ int snd_oss_info_register(int dev, int num, char *string)
return -ENXIO;
if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
return -ENXIO;
- mutex_lock(&strings);
+ guard(mutex)(&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) {
- mutex_unlock(&strings);
+ if (x == NULL)
return -ENOMEM;
- }
}
snd_sndstat_strings[num][dev] = x;
- mutex_unlock(&strings);
return 0;
}
EXPORT_SYMBOL(snd_oss_info_register);
@@ -69,7 +50,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
char *str;
snd_iprintf(buf, "\n%s:", id);
- mutex_lock(&strings);
+ guard(mutex)(&strings);
for (idx = 0; idx < SNDRV_CARDS; idx++) {
str = snd_sndstat_strings[idx][dev];
if (str) {
@@ -80,7 +61,6 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
snd_iprintf(buf, "%i: %s\n", idx, str);
}
}
- mutex_unlock(&strings);
if (ok < 0)
snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
return ok;
diff --git a/sound/core/init.c b/sound/core/init.c
index 8753440c3a6e..c372b3228785 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1,22 +1,7 @@
+// 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>
@@ -28,7 +13,9 @@
#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>
@@ -49,8 +36,7 @@ static const struct file_operations snd_shutdown_f_ops;
/* locked for registering/using */
static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
-struct snd_card *snd_cards[SNDRV_CARDS];
-EXPORT_SYMBOL(snd_cards);
+static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
@@ -64,7 +50,7 @@ MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
static int module_slot_match(struct module *module, int idx)
{
int match = 1;
-#ifdef MODULE
+#ifdef CONFIG_MODULES
const char *s1, *s2;
if (!module || !*module->name || !slots[idx])
@@ -91,7 +77,7 @@ static int module_slot_match(struct module *module, int idx)
if (!c1)
break;
}
-#endif /* MODULE */
+#endif /* CONFIG_MODULES */
return match;
}
@@ -100,31 +86,6 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
-#ifdef CONFIG_SND_PROC_FS
-static void snd_card_id_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- snd_iprintf(buffer, "%s\n", entry->card->id);
-}
-
-static int init_info_for_card(struct snd_card *card)
-{
- struct snd_info_entry *entry;
-
- entry = snd_info_create_card_entry(card, "id", card->proc_root);
- if (!entry) {
- dev_dbg(card->dev, "unable to create card entry\n");
- return -ENOMEM;
- }
- entry->c.text.read = snd_card_id_read;
- card->proc_id = entry;
-
- return snd_info_card_register(card);
-}
-#else /* !CONFIG_SND_PROC_FS */
-#define init_info_for_card(card)
-#endif
-
static int check_empty_slot(struct module *module, int slot)
{
return !slots[slot] || !*slots[slot];
@@ -150,29 +111,40 @@ static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
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)
+/* the default release callback set in snd_device_alloc() */
+static void default_release_alloc(struct device *dev)
{
+ kfree(dev);
}
/**
- * snd_device_initialize - Initialize struct device for sound devices
- * @dev: device to initialize
+ * snd_device_alloc - Allocate and initialize struct device for sound devices
+ * @dev_p: pointer to store the allocated device
* @card: card to assign, optional
+ *
+ * For releasing the allocated device, call put_device().
*/
-void snd_device_initialize(struct device *dev, struct snd_card *card)
+int snd_device_alloc(struct device **dev_p, struct snd_card *card)
{
+ struct device *dev;
+
+ *dev_p = NULL;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
device_initialize(dev);
if (card)
dev->parent = &card->card_dev;
- dev->class = sound_class;
- dev->release = default_release;
+ dev->class = &sound_class;
+ dev->release = default_release_alloc;
+ *dev_p = dev;
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_device_initialize);
+EXPORT_SYMBOL_GPL(snd_device_alloc);
+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;
@@ -190,8 +162,6 @@ static void release_card_device(struct device *dev)
* @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.
@@ -214,52 +184,158 @@ int snd_card_new(struct device *parent, 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;
+
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) /* 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 (test_bit(idx, snd_cards_lock))
- err = -EBUSY; /* invalid */
- } else if (idx >= SNDRV_CARDS)
- err = -ENODEV;
+ scoped_guard(mutex, &snd_card_mutex) {
+ 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 (test_bit(idx, snd_cards_lock))
+ err = -EBUSY; /* invalid */
+ } else if (idx >= SNDRV_CARDS)
+ err = -ENODEV;
+ if (!err) {
+ set_bit(idx, snd_cards_lock); /* lock it */
+ if (idx >= snd_ecards_limit)
+ snd_ecards_limit = idx + 1; /* increase the limit */
+ }
+ }
if (err < 0) {
- mutex_unlock(&snd_card_mutex);
dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
- idx, snd_ecards_limit - 1, err);
- kfree(card);
+ idx, snd_ecards_limit - 1, err);
+ if (!card->managed)
+ kfree(card); /* manually free here, as no destructor called */
return err;
}
- 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;
+ WARN_ON(IS_MODULE(CONFIG_SND) && !module);
card->module = module;
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
- rwlock_init(&card->ctl_files_rwlock);
+ rwlock_init(&card->controls_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);
+ mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM
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.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;
@@ -282,7 +358,11 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
dev_err(parent, "unable to create card info\n");
goto __error_ctl;
}
- *card_ret = card;
+
+#ifdef CONFIG_SND_DEBUG
+ card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev),
+ sound_debugfs_root);
+#endif
return 0;
__error_ctl:
@@ -291,17 +371,33 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
put_device(&card->card_dev);
return err;
}
-EXPORT_SYMBOL(snd_card_new);
+
+/**
+ * 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;
+
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
+ get_device(&card->card_dev);
+ return card;
+}
+EXPORT_SYMBOL_GPL(snd_card_ref);
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
{
- int locked;
-
- mutex_lock(&snd_card_mutex);
- locked = test_bit(card, snd_cards_lock);
- mutex_unlock(&snd_card_mutex);
- return locked;
+ guard(mutex)(&snd_card_mutex);
+ return test_bit(card, snd_cards_lock);
}
static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
@@ -325,15 +421,15 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
{
struct snd_monitor_file *df = NULL, *_df;
- spin_lock(&shutdown_lock);
- list_for_each_entry(_df, &shutdown_files, shutdown_list) {
- if (_df->file == file) {
- df = _df;
- list_del_init(&df->shutdown_list);
- break;
+ scoped_guard(spinlock, &shutdown_lock) {
+ list_for_each_entry(_df, &shutdown_files, shutdown_list) {
+ if (_df->file == file) {
+ df = _df;
+ list_del_init(&df->shutdown_list);
+ break;
+ }
}
}
- spin_unlock(&shutdown_lock);
if (likely(df)) {
if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
@@ -346,7 +442,7 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
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,
@@ -392,45 +488,41 @@ static const struct file_operations snd_shutdown_f_ops =
* 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;
if (!card)
- return -EINVAL;
+ return;
- spin_lock(&card->files_lock);
- if (card->shutdown) {
- spin_unlock(&card->files_lock);
- return 0;
- }
- 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;
- clear_bit(card->number, snd_cards_lock);
- mutex_unlock(&snd_card_mutex);
-
- /* phase 2: replace file->f_op with special dummy operations */
-
- spin_lock(&card->files_lock);
- list_for_each_entry(mfile, &card->files_list, list) {
- /* it's critical part, use endless loop */
- /* we have no room to fail */
- mfile->disconnected_f_op = mfile->file->f_op;
-
- spin_lock(&shutdown_lock);
- list_add(&mfile->shutdown_list, &shutdown_files);
- spin_unlock(&shutdown_lock);
-
- mfile->file->f_op = &snd_shutdown_f_ops;
- fops_get(mfile->file->f_op);
+ scoped_guard(spinlock, &card->files_lock) {
+ if (card->shutdown)
+ return;
+ card->shutdown = 1;
+
+ /* replace file->f_op with special dummy operations */
+ list_for_each_entry(mfile, &card->files_list, list) {
+ /* it's critical part, use endless loop */
+ /* we have no room to fail */
+ mfile->disconnected_f_op = mfile->file->f_op;
+
+ scoped_guard(spinlock, &shutdown_lock)
+ list_add(&mfile->shutdown_list, &shutdown_files);
+
+ mfile->file->f_op = &snd_shutdown_f_ops;
+ fops_get(mfile->file->f_op);
+ }
}
- spin_unlock(&card->files_lock);
- /* phase 3: notify all connected devices about disconnection */
+#ifdef CONFIG_PM
+ /* wake up sleepers here before other callbacks for avoiding potential
+ * deadlocks with other locks (e.g. in kctls);
+ * then this notifies the shutdown and sleepers would abort immediately
+ */
+ wake_up_all(&card->power_sleep);
+#endif
+
+ /* notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
@@ -441,15 +533,27 @@ int snd_card_disconnect(struct snd_card *card)
/* notify all devices that we are disconnected */
snd_device_disconnect_all(card);
+ if (card->sync_irq > 0)
+ synchronize_irq(card->sync_irq);
+
snd_info_card_disconnect(card);
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(card->debugfs_root);
+ card->debugfs_root = NULL;
+#endif
+
if (card->registered) {
device_del(&card->card_dev);
card->registered = false;
}
-#ifdef CONFIG_PM
- wake_up(&card->power_sleep);
-#endif
- return 0;
+
+ /* disable fops (user space) operations for ALSA API */
+ scoped_guard(mutex, &snd_card_mutex) {
+ snd_cards[card->number] = NULL;
+ clear_bit(card->number, snd_cards_lock);
+ }
+
+ snd_power_sync_ref(card);
}
EXPORT_SYMBOL(snd_card_disconnect);
@@ -464,26 +568,18 @@ EXPORT_SYMBOL(snd_card_disconnect);
*/
void snd_card_disconnect_sync(struct snd_card *card)
{
- int err;
-
- err = snd_card_disconnect(card);
- if (err < 0) {
- dev_err(card->dev,
- "snd_card_disconnect error (%d), skipping sync\n",
- err);
- return;
- }
+ snd_card_disconnect(card);
- spin_lock_irq(&card->files_lock);
+ guard(spinlock_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)
{
+ 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);
@@ -491,14 +587,14 @@ static int snd_card_do_free(struct snd_card *card)
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) {
dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
}
if (card->release_completion)
complete(card->release_completion);
- kfree(card);
+ if (!card->managed)
+ kfree(card);
return 0;
}
@@ -510,14 +606,17 @@ static int snd_card_do_free(struct snd_card *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
*/
-int snd_card_free_when_closed(struct snd_card *card)
+void snd_card_free_when_closed(struct snd_card *card)
{
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
+ if (!card)
+ return;
+
+ snd_card_disconnect(card);
put_device(&card->card_dev);
- return 0;
+ return;
}
EXPORT_SYMBOL(snd_card_free_when_closed);
@@ -534,29 +633,40 @@ EXPORT_SYMBOL(snd_card_free_when_closed);
* Return: Zero. Frees all associated devices and frees the control
* interface associated to given soundcard.
*/
-int snd_card_free(struct snd_card *card)
+void snd_card_free(struct snd_card *card)
{
- struct completion released;
- int 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;
- init_completion(&released);
card->release_completion = &released;
- ret = snd_card_free_when_closed(card);
- if (ret)
- return ret;
+ snd_card_free_when_closed(card);
+
/* wait, until all devices are ready for the free operation */
wait_for_completion(&released);
- return 0;
}
EXPORT_SYMBOL(snd_card_free);
+/* check, if the character is in the valid ASCII range */
+static inline bool safe_ascii_char(char c)
+{
+ return isascii(c) && isalnum(c);
+}
+
/* retrieve the last word of shortname or longname */
static const char *retrieve_id_from_card_name(const char *name)
{
const char *spos = name;
while (*name) {
- if (isspace(*name) && isalnum(name[1]))
+ if (isspace(*name) && safe_ascii_char(name[1]))
spos = name + 1;
name++;
}
@@ -583,12 +693,12 @@ static void copy_valid_id_string(struct snd_card *card, const char *src,
{
char *id = card->id;
- while (*nid && !isalnum(*nid))
+ while (*nid && !safe_ascii_char(*nid))
nid++;
if (isdigit(*nid))
*id++ = isalpha(*src) ? *src : 'D';
while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
- if (isalnum(*nid))
+ if (safe_ascii_char(*nid))
*id++ = *nid;
nid++;
}
@@ -613,27 +723,25 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
* ("card" conflicts with proc directories)
*/
if (!*id || !strncmp(id, "card", 4)) {
- strcpy(id, "Default");
+ strscpy(card->id, "Default");
is_default = true;
}
len = strlen(id);
for (loops = 0; loops < SNDRV_CARDS; loops++) {
- char *spos;
char sfxstr[5]; /* "_012" */
- int sfxlen;
+ int sfxlen, slen;
if (card_id_ok(card, id))
return; /* OK */
/* Add _XYZ suffix */
- sprintf(sfxstr, "_%X", loops + 1);
- sfxlen = strlen(sfxstr);
+ sfxlen = scnprintf(sfxstr, sizeof(sfxstr), "_%X", loops + 1);
if (len + sfxlen >= sizeof(card->id))
- spos = id + sizeof(card->id) - sfxlen - 1;
+ slen = sizeof(card->id) - sfxlen - 1;
else
- spos = id + len;
- strcpy(spos, sfxstr);
+ slen = len;
+ strscpy(id + slen, sfxstr, sizeof(card->id) - slen);
}
/* fallback to the default id */
if (!is_default) {
@@ -643,7 +751,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
/* last resort... */
dev_err(card->dev, "unable to set card id (%s)\n", id);
if (card->proc_root->name)
- strlcpy(card->id, card->proc_root->name, sizeof(card->id));
+ strscpy(card->id, card->proc_root->name, sizeof(card->id));
}
/**
@@ -659,23 +767,20 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
/* check if user specified own card->id */
if (card->id[0] != '\0')
return;
- mutex_lock(&snd_card_mutex);
+ guard(mutex)(&snd_card_mutex);
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 = container_of(dev, struct snd_card, card_dev);
- return snprintf(buf, PAGE_SIZE, "%s\n", card->id);
+ 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 = container_of(dev, struct snd_card, card_dev);
char buf1[sizeof(card->id)];
@@ -686,34 +791,30 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
for (idx = 0; idx < copy; idx++) {
c = buf[idx];
- if (!isalnum(c) && c != '_' && c != '-')
+ if (!safe_ascii_char(c) && c != '_' && c != '-')
return -EINVAL;
}
memcpy(buf1, buf, copy);
buf1[copy] = '\0';
- mutex_lock(&snd_card_mutex);
- if (!card_id_ok(NULL, buf1)) {
- mutex_unlock(&snd_card_mutex);
+ guard(mutex)(&snd_card_mutex);
+ if (!card_id_ok(NULL, buf1))
return -EEXIST;
- }
- strcpy(card->id, buf1);
+ strscpy(card->id, buf1);
snd_info_card_id_change(card);
- mutex_unlock(&snd_card_mutex);
return count;
}
-static DEVICE_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 = container_of(dev, struct snd_card, card_dev);
- return snprintf(buf, PAGE_SIZE, "%i\n", card->number);
+ return sysfs_emit(buf, "%i\n", card->number);
}
-static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+static DEVICE_ATTR_RO(number);
static struct attribute *card_dev_attrs[] = {
&dev_attr_id.attr,
@@ -729,6 +830,8 @@ static const struct attribute_group card_dev_attr_group = {
* 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)
@@ -748,6 +851,11 @@ int snd_card_add_dev_attr(struct snd_card *card,
}
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
* @card: soundcard structure
@@ -771,31 +879,45 @@ int snd_card_register(struct snd_card *card)
if (err < 0)
return err;
card->registered = true;
+ } else {
+ if (card->managed)
+ devm_remove_action(card->dev, trigger_card_free, card);
}
- if ((err = snd_device_register_all(card)) < 0)
- return err;
- mutex_lock(&snd_card_mutex);
- if (snd_cards[card->number]) {
- /* already registered */
- mutex_unlock(&snd_card_mutex);
- return snd_info_card_register(card); /* register pending info */
+ if (card->managed) {
+ err = devm_add_action(card->dev, trigger_card_free, card);
+ if (err < 0)
+ return err;
}
- 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));
+
+ err = snd_device_register_all(card);
+ if (err < 0)
+ return err;
+ scoped_guard(mutex, &snd_card_mutex) {
+ if (snd_cards[card->number]) {
+ /* already registered */
+ 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_cards[card->number] = card;
}
- snd_cards[card->number] = card;
- mutex_unlock(&snd_card_mutex);
- init_info_for_card(card);
+ 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);
@@ -812,8 +934,9 @@ static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
idx,
@@ -823,7 +946,6 @@ static void snd_card_info_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " %s\n",
card->longname);
}
- mutex_unlock(&snd_card_mutex);
}
if (!count)
snd_iprintf(buffer, "--- no soundcards ---\n");
@@ -836,12 +958,12 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%s\n", card->longname);
}
- mutex_unlock(&snd_card_mutex);
}
if (!count) {
snd_iprintf(buffer, "--- no soundcards ---\n");
@@ -850,7 +972,7 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
-#ifdef MODULE
+#ifdef CONFIG_MODULES
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -858,11 +980,11 @@ static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_card *card;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL)
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
snd_iprintf(buffer, "%2i %s\n",
idx, card->module->name);
- mutex_unlock(&snd_card_mutex);
}
}
#endif
@@ -878,7 +1000,7 @@ int __init snd_card_info_init(void)
if (snd_info_register(entry) < 0)
return -ENOMEM; /* freed in error path */
-#ifdef MODULE
+#ifdef CONFIG_MODULES
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
if (!entry)
return -ENOMEM;
@@ -944,15 +1066,13 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
mfile->file = file;
mfile->disconnected_f_op = NULL;
INIT_LIST_HEAD(&mfile->shutdown_list);
- spin_lock(&card->files_lock);
+ guard(spinlock)(&card->files_lock);
if (card->shutdown) {
- spin_unlock(&card->files_lock);
kfree(mfile);
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);
@@ -974,22 +1094,21 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
{
struct snd_monitor_file *mfile, *found = NULL;
- 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;
- break;
+ scoped_guard(spinlock, &card->files_lock) {
+ list_for_each_entry(mfile, &card->files_list, list) {
+ if (mfile->file == file) {
+ list_del(&mfile->list);
+ scoped_guard(spinlock, &shutdown_lock)
+ list_del(&mfile->shutdown_list);
+ if (mfile->disconnected_f_op)
+ fops_put(mfile->disconnected_f_op);
+ found = mfile;
+ break;
+ }
}
+ if (list_empty(&card->files_list))
+ wake_up_all(&card->remove_sleep);
}
- if (list_empty(&card->files_list))
- wake_up_all(&card->remove_sleep);
- spin_unlock(&card->files_lock);
if (!found) {
dev_err(card->dev, "card file remove problem (%p)\n", file);
return -ENOENT;
@@ -1002,36 +1121,48 @@ 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.
*
- * Return: Zero if successful, or a negative error code.
+ * 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_entry_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);
- schedule_timeout(30 * HZ);
- }
- 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 7a8515abb5f9..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
- *
*/
/*
@@ -27,8 +12,8 @@
#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
@@ -112,3 +97,42 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
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 84c2a17c56ee..93e357a23f17 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -1,27 +1,16 @@
+// 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>
@@ -30,24 +19,35 @@ 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
};
#ifdef CONFIG_SND_JACK_INPUT_DEV
-static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
+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,
+ SW_USB_INSERT,
};
#endif /* CONFIG_SND_JACK_INPUT_DEV */
+static void snd_jack_remove_debugfs(struct snd_jack *jack);
+
static int snd_jack_dev_disconnect(struct snd_device *device)
{
-#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
+ snd_jack_remove_debugfs(jack);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ guard(mutex)(&jack->input_dev_lock);
if (!jack->input_dev)
return 0;
@@ -72,6 +72,7 @@ static int snd_jack_dev_free(struct snd_device *device)
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
+
if (jack->private_free)
jack->private_free(jack);
@@ -93,6 +94,7 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
+ guard(mutex)(&jack->input_dev_lock);
if (!jack->input_dev)
return 0;
@@ -123,6 +125,289 @@ static int snd_jack_dev_register(struct snd_device *device)
}
#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)",
+ "USB(0x0040)", "", "", "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_remove_debugfs(struct snd_jack *jack)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ list_for_each_entry(jack_kctl, &jack->kctl_list, list) {
+ 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_remove_debugfs(struct snd_jack *jack)
+{
+}
+#endif /* CONFIG_SND_JACK_INJECTION_DEBUG */
+
static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
{
struct snd_jack_kctl *jack_kctl;
@@ -136,7 +421,9 @@ static void snd_jack_kctl_private_free(struct snd_kcontrol *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)
@@ -215,12 +502,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack *jack;
struct snd_jack_kctl *jack_kctl = NULL;
int err;
- 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 */
+ .dev_disconnect = snd_jack_dev_disconnect,
};
if (initial_kctl) {
@@ -234,10 +521,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
return -ENOMEM;
jack->id = kstrdup(id, GFP_KERNEL);
+ if (jack->id == NULL) {
+ kfree(jack);
+ return -ENOMEM;
+ }
- /* don't creat input device for phantom jack */
- if (!phantom_jack) {
#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();
@@ -255,8 +548,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
-#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
@@ -284,26 +577,6 @@ 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 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);
- if (!jack->input_dev)
- return;
-
- jack->input_dev->dev.parent = parent;
-}
-EXPORT_SYMBOL(snd_jack_set_parent);
-
-/**
* snd_jack_set_key - Set a key mapping on a jack
*
* @jack: The jack to configure
@@ -347,6 +620,8 @@ EXPORT_SYMBOL(snd_jack_set_key);
/**
* 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
@@ -354,38 +629,48 @@ EXPORT_SYMBOL(snd_jack_set_key);
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
+ struct input_dev *idev;
int i;
#endif
if (!jack)
return;
+ jack->hw_status_cache = status;
+
list_for_each_entry(jack_kctl, &jack->kctl_list, list)
- snd_kctl_jack_report(jack->card, jack_kctl->kctl,
- status & jack_kctl->mask_bits);
+ 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
- if (!jack->input_dev)
+ idev = input_get_device(jack->input_dev);
+ if (!idev)
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],
+ input_report_key(idev, jack->key[i],
status & testbit);
}
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,
+ input_report_switch(idev,
jack_switch_types[i],
status & testbit);
}
- input_sync(jack->input_dev);
+ input_sync(idev);
+ input_put_device(idev);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 7f89d3c79a4b..b3853583d2ae 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -1,167 +1,56 @@
+// 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/slab.h>
#include <linux/mm.h>
#include <linux/dma-mapping.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>
-/*
- *
- * Generic memory allocators
- *
- */
-
-/**
- * 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.
- *
- * Return: The pointer of the buffer, or %NULL if no enough memory.
- */
-void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
-{
- int pg;
-
- 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);
- return (void *) __get_free_pages(gfp_flags, pg);
-}
-EXPORT_SYMBOL(snd_malloc_pages);
-
-/**
- * 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;
+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);
+};
- if (ptr == NULL)
- return;
- pg = get_order(size);
- free_pages((unsigned long) ptr, pg);
-}
-EXPORT_SYMBOL(snd_free_pages);
+#define DEFAULT_GFP \
+ (GFP_KERNEL | \
+ __GFP_RETRY_MAYFAIL | /* don't trigger OOM-killer */ \
+ __GFP_NOWARN) /* no stack trace print - this call is non-critical */
-/*
- *
- * Bus-specific memory allocators
- *
- */
+static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab);
-#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;
- 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 */
- return dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
+ return ops->alloc(dmab, size);
}
-/* 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);
- dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
-}
-
-#ifdef CONFIG_GENERIC_ALLOCATOR
/**
- * snd_malloc_dev_iram - allocate memory from on-chip internal ram
- * @dmab: buffer allocation record to store the allocated data
- * @size: number of bytes to allocate from the iram
- *
- * This function requires iram phandle provided via of_node
- */
-static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
-{
- struct device *dev = dmab->dev.dev;
- struct gen_pool *pool = NULL;
-
- dmab->area = NULL;
- dmab->addr = 0;
-
- if (dev->of_node)
- pool = of_gen_pool_get(dev->of_node, "iram", 0);
-
- if (!pool)
- return;
-
- /* Assign the pool into private_data field */
- dmab->private_data = pool;
-
- dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr);
-}
-
-/**
- * snd_free_dev_iram - free allocated specific memory from on-chip internal ram
- * @dmab: buffer allocation record to store the allocated data
- */
-static void snd_free_dev_iram(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);
-}
-#endif /* CONFIG_GENERIC_ALLOCATOR */
-#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
*
@@ -171,55 +60,29 @@ static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
* 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,
- (__force gfp_t)(unsigned long)device);
- dmab->addr = 0;
- break;
-#ifdef CONFIG_HAS_DMA
-#ifdef CONFIG_GENERIC_ALLOCATOR
- case SNDRV_DMA_TYPE_DEV_IRAM:
- snd_malloc_dev_iram(dmab, size);
- if (dmab->area)
- break;
- /* 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;
-#endif /* CONFIG_GENERIC_ALLOCATOR */
- 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:
- pr_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_pages);
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
/**
* snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -242,16 +105,12 @@ 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;
@@ -259,7 +118,6 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
}
EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
-
/**
* snd_dma_free_pages - release the allocated buffer
* @dmab: the buffer allocation record to release
@@ -268,27 +126,787 @@ EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
*/
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;
+ 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_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.
+ *
+ * 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.
+ *
+ * Return: the snd_dma_buffer object at success, or NULL if failed
+ */
+struct snd_dma_buffer *
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+ enum dma_data_direction dir, size_t size)
+{
+ struct snd_dma_buffer *dmab;
+ int err;
+
+ if (WARN_ON(type == SNDRV_DMA_TYPE_CONTINUOUS ||
+ type == SNDRV_DMA_TYPE_VMALLOC))
+ return NULL;
+
+ 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;
+ }
+
+ devres_add(dev, dmab);
+ return dmab;
+}
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
+
+/**
+ * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
+ * @dmab: buffer allocation information
+ * @area: VM area information
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ const struct snd_malloc_ops *ops;
+
+ 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);
+
+/*
+ * Continuous pages allocator
+ */
+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;
+ }
+ }
+#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);
+}
+
+
+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,
+};
+
+/*
+ * VMALLOC allocator
+ */
+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;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+
+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
- case SNDRV_DMA_TYPE_DEV_IRAM:
- snd_free_dev_iram(dmab);
- break;
+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);
+}
+
+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 */
- case SNDRV_DMA_TYPE_DEV:
- snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
-#endif
+
+/*
+ * 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,
+};
+
+/*
+ * Write-combined pages
+ */
#ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_free_sgbuf_pages(dmab);
- break;
+/* x86-specific allocations */
+static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p = do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true);
+
+ if (!p)
+ return NULL;
+ dmab->addr = dma_map_single(dmab->dev.dev, p, size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dmab->dev.dev, dmab->addr)) {
+ do_free_pages(dmab->area, size, true);
+ return NULL;
+ }
+ return p;
+}
+
+static void snd_dma_wc_free(struct snd_dma_buffer *dmab)
+{
+ dma_unmap_single(dmab->dev.dev, dmab->addr, dmab->bytes,
+ DMA_BIDIRECTIONAL);
+ 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 dma_mmap_coherent(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
+#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
- default:
- pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type);
+
+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,
+};
+
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct sg_table *sgt;
+ void *p;
+
+ sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+ DEFAULT_GFP, 0);
+ 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,
+};
+
+#ifdef CONFIG_SND_DMA_SGBUF
+/* Fallback SG-buffer allocations for x86 */
+struct snd_dma_sg_fallback {
+ struct sg_table sgt; /* used by get_addr - must be the first item */
+ size_t count;
+ struct page **pages;
+ unsigned int *npages;
+};
+
+static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
+ struct snd_dma_sg_fallback *sgbuf)
+{
+ bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG;
+ size_t i, size;
+
+ if (sgbuf->pages && sgbuf->npages) {
+ i = 0;
+ while (i < sgbuf->count) {
+ size = sgbuf->npages[i];
+ if (!size)
+ break;
+ do_free_pages(page_address(sgbuf->pages[i]),
+ size << PAGE_SHIFT, wc);
+ i += size;
+ }
+ }
+ kvfree(sgbuf->pages);
+ kvfree(sgbuf->npages);
+ kfree(sgbuf);
+}
+
+/* fallback manual S/G buffer allocations */
+static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG;
+ struct snd_dma_sg_fallback *sgbuf;
+ struct page **pagep, *curp;
+ size_t chunk;
+ dma_addr_t addr;
+ unsigned int idx, npages;
+ void *p;
+
+ sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
+ if (!sgbuf)
+ return NULL;
+ size = PAGE_ALIGN(size);
+ sgbuf->count = size >> PAGE_SHIFT;
+ sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
+ sgbuf->npages = kvcalloc(sgbuf->count, sizeof(*sgbuf->npages), GFP_KERNEL);
+ if (!sgbuf->pages || !sgbuf->npages)
+ goto error;
+
+ pagep = sgbuf->pages;
+ chunk = size;
+ idx = 0;
+ while (size > 0) {
+ chunk = min(size, chunk);
+ p = do_alloc_pages(dmab->dev.dev, chunk, &addr, wc);
+ 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;
+ sgbuf->npages[idx] = npages;
+ idx += npages;
+ curp = virt_to_page(p);
+ while (npages--)
+ *pagep++ = curp++;
+ }
+
+ if (sg_alloc_table_from_pages(&sgbuf->sgt, sgbuf->pages, sgbuf->count,
+ 0, sgbuf->count << PAGE_SHIFT, GFP_KERNEL))
+ goto error;
+
+ if (dma_map_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0))
+ goto error_dma_map;
+
+ p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
+ if (!p)
+ goto error_vmap;
+
+ dmab->private_data = sgbuf;
+ /* store the first page address for convenience */
+ dmab->addr = snd_sgbuf_get_addr(dmab, 0);
+ return p;
+
+ error_vmap:
+ dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0);
+ error_dma_map:
+ sg_free_table(&sgbuf->sgt);
+ error:
+ __snd_dma_sg_fallback_free(dmab, sgbuf);
+ return NULL;
+}
+
+static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ vunmap(dmab->area);
+ dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(&sgbuf->sgt);
+ __snd_dma_sg_fallback_free(dmab, dmab->private_data);
+}
+
+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)
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return vm_map_pages(area, sgbuf->pages, sgbuf->count);
+}
+
+static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ int type = dmab->dev.type;
+ void *p;
+
+ /* try the standard DMA API allocation at first */
+ if (type == SNDRV_DMA_TYPE_DEV_WC_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC;
+ else
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ p = __snd_dma_alloc_pages(dmab, size);
+ if (p)
+ return p;
+
+ dmab->dev.type = type; /* restore the type */
+ return snd_dma_sg_fallback_alloc(dmab, size);
+}
+
+static const struct snd_malloc_ops snd_dma_sg_ops = {
+ .alloc = snd_dma_sg_alloc,
+ .free = snd_dma_sg_fallback_free,
+ .mmap = snd_dma_sg_fallback_mmap,
+ /* reuse noncontig helper */
+ .get_addr = snd_dma_noncontig_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 */
+
+/*
+ * Non-coherent pages allocator
+ */
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p;
+
+ 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;
+}
+
+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_SG] = &snd_dma_sg_ops,
+ [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops,
+#endif
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+#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];
}
-EXPORT_SYMBOL(snd_dma_free_pages);
diff --git a/sound/core/memory.c b/sound/core/memory.c
index 19c9ea90d9bf..d683442b4c97 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -1,29 +1,15 @@
+// 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 <linux/export.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <sound/core.h>
+#include <sound/pcm.h>
/**
* copy_to_user_fromio - copy data from mmio-space to user-space
@@ -37,25 +23,50 @@
*/
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_DEST, dst, count, &iter))
+ return -EFAULT;
+ if (copy_to_iter_fromio((const void __iomem *)src, count, &iter) != count)
+ return -EFAULT;
+ return 0;
+}
+EXPORT_SYMBOL(copy_to_user_fromio);
+
+/**
+ * copy_to_iter_fromio - copy data from mmio-space to iov_iter
+ * @src: the source pointer on mmio
+ * @count: the data size to copy in bytes
+ * @dst: the destination iov_iter
+ *
+ * Copies the data from mmio-space to iov_iter.
+ *
+ * Return: number of bytes to be copied
+ */
+size_t copy_to_iter_fromio(const void __iomem *src, size_t count,
+ struct iov_iter *dst)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0;
+ return copy_to_iter((const void __force *)src, count, dst);
#else
char buf[256];
+ size_t res = 0;
+
while (count) {
size_t c = count;
if (c > sizeof(buf))
c = sizeof(buf);
memcpy_fromio(buf, (void __iomem *)src, c);
- if (copy_to_user(dst, buf, c))
- return -EFAULT;
+ if (copy_to_iter(buf, c, dst) != c)
+ return res;
count -= c;
- dst += c;
src += c;
+ res += c;
}
- return 0;
+ return res;
#endif
}
-EXPORT_SYMBOL(copy_to_user_fromio);
+EXPORT_SYMBOL(copy_to_iter_fromio);
/**
* copy_from_user_toio - copy data from user-space to mmio-space
@@ -69,22 +80,47 @@ EXPORT_SYMBOL(copy_to_user_fromio);
*/
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter))
+ return -EFAULT;
+ if (copy_from_iter_toio((void __iomem *)dst, count, &iter) != count)
+ return -EFAULT;
+ return 0;
+}
+EXPORT_SYMBOL(copy_from_user_toio);
+
+/**
+ * copy_from_iter_toio - copy data from iov_iter to mmio-space
+ * @dst: the destination pointer on mmio-space
+ * @count: the data size to copy in bytes
+ * @src: the source iov_iter
+ *
+ * Copies the data from iov_iter to mmio-space.
+ *
+ * Return: number of bytes to be copied
+ */
+size_t copy_from_iter_toio(void __iomem *dst, size_t count,
+ struct iov_iter *src)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0;
+ return copy_from_iter((void __force *)dst, count, src);
#else
char buf[256];
+ size_t res = 0;
+
while (count) {
size_t c = count;
if (c > sizeof(buf))
c = sizeof(buf);
- if (copy_from_user(buf, src, c))
- return -EFAULT;
+ if (copy_from_iter(buf, c, src) != c)
+ return res;
memcpy_toio(dst, buf, c);
count -= c;
dst += c;
- src += c;
+ res += c;
}
- return 0;
+ return res;
#endif
}
-EXPORT_SYMBOL(copy_from_user_toio);
+EXPORT_SYMBOL(copy_from_iter_toio);
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 0f818d593c9e..88d9e1f9a6e9 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -1,22 +1,7 @@
+// 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>
@@ -25,22 +10,9 @@
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/fs.h>
#include <sound/core.h>
-#ifdef CONFIG_SND_DEBUG
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-#define DEFAULT_DEBUG_LEVEL 2
-#else
-#define DEFAULT_DEBUG_LEVEL 1
-#endif
-
-static int debug = DEFAULT_DEBUG_LEVEL;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
-
-#endif /* CONFIG_SND_DEBUG */
-
void release_and_free_resource(struct resource *res)
{
if (res) {
@@ -50,63 +22,6 @@ void release_and_free_resource(struct resource *res)
}
EXPORT_SYMBOL(release_and_free_resource);
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-/* strip the leading path if the given path is absolute */
-static const char *sanity_file_name(const char *path)
-{
- if (*path == '/')
- return strrchr(path, '/') + 1;
- else
- return path;
-}
-#endif
-
-#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
-void __snd_printk(unsigned int level, const char *path, int line,
- const char *format, ...)
-{
- va_list args;
-#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);
-#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);
-#endif
-
#ifdef CONFIG_PCI
#include <linux/pci.h>
/**
@@ -127,7 +42,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 ||
@@ -160,3 +75,91 @@ snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *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);
+ }
+
+ scoped_guard(spinlock_irq, &snd_fasync_lock) {
+ if (*fasyncp) {
+ kfree(fasync);
+ fasync = *fasyncp;
+ } else {
+ if (!fasync)
+ return 0;
+ *fasyncp = fasync;
+ }
+ fasync->on = on;
+ }
+ 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)
+{
+ if (!fasync || !fasync->on)
+ return;
+ guard(spinlock_irqsave)(&snd_fasync_lock);
+ fasync->signal = signal;
+ fasync->poll = poll;
+ list_move(&fasync->list, &snd_fasync_list);
+ schedule_work(&snd_fasync_work);
+}
+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 ae25edcc3b42..d5f48ae6ba96 100644
--- a/sound/core/oss/Makefile
+++ b/sound/core/oss/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mixer-oss-objs := mixer_oss.o
+snd-mixer-oss-y := mixer_oss.o
snd-pcm-oss-y := pcm_oss.o
snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 2045697f449d..797d838a2f9e 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -107,6 +107,8 @@ 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;
}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 379bf486ccc7..e839a4bb93f8 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1,22 +1,7 @@
+// 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>
@@ -102,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;
@@ -118,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;
@@ -145,6 +130,7 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume || pslot->put_recsrc)
@@ -161,6 +147,7 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume && pslot->stereo)
@@ -176,6 +163,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
result = mixer->mask_recsrc;
} else {
@@ -197,11 +185,12 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
- int err;
unsigned int index;
- if ((err = mixer->get_recsrc(fmixer, &index)) < 0)
- return err;
+ result = mixer->get_recsrc(fmixer, &index);
+ if (result < 0)
+ return result;
result = 1 << index;
} else {
struct snd_mixer_oss_slot *pslot;
@@ -216,7 +205,8 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
}
}
}
- return mixer->oss_recsrc = result;
+ mixer->oss_recsrc = result;
+ return result;
}
static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
@@ -229,6 +219,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
if (recsrc & ~mixer->oss_recsrc)
recsrc &= ~mixer->oss_recsrc;
@@ -265,6 +256,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
if (mixer == NULL || slot > 30)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
left = pslot->volume[0];
right = pslot->volume[1];
@@ -293,6 +285,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (mixer == NULL || slot > 30)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
if (left > 100)
left = 100;
@@ -306,7 +299,8 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
return result;
pslot->volume[0] = left;
pslot->volume[1] = right;
- return (left & 0xff) | ((right & 0xff) << 8);
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ return result;
}
static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
@@ -418,7 +412,6 @@ static const struct file_operations snd_mixer_oss_f_ops =
.owner = THIS_MODULE,
.open = snd_mixer_oss_open,
.release = snd_mixer_oss_release,
- .llseek = no_llseek,
.unlocked_ioctl = snd_mixer_oss_ioctl,
.compat_ioctl = snd_mixer_oss_ioctl_compat,
};
@@ -433,7 +426,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) */
@@ -501,7 +494,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;
};
@@ -514,7 +507,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;
- strlcpy(id.name, name, sizeof(id.name));
+ strscpy(id.name, name, sizeof(id.name));
id.index = index;
return snd_ctl_find_id(card, &id);
}
@@ -524,36 +517,31 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int *left, int *right)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (kctl->get(kctl, uctl))
- goto __unalloc;
+ return;
if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
- goto __unalloc;
+ return;
*left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
if (uinfo->count > 1)
*right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -562,26 +550,25 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
int *left, int *right,
int route)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (kctl->get(kctl, uctl))
- goto __unalloc;
+ return;
if (!uctl->value.integer.value[0]) {
*left = 0;
if (uinfo->count == 1)
@@ -589,10 +576,6 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
}
if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
*right = 0;
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
@@ -626,39 +609,35 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int left, int right)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
- goto __unalloc;
+ return;
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)
- goto __unalloc;
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
+ return;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -667,25 +646,24 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
int left, int right,
int route)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (uinfo->count > 1) {
uctl->value.integer.value[0] = left > 0 ? 1 : 0;
uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
@@ -696,14 +674,11 @@ 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)
- goto __unalloc;
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
+ return;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
@@ -808,26 +783,24 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot;
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err, idx;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
- if (uinfo == NULL || uctl == NULL) {
- err = -ENOMEM;
- goto __free_only;
- }
- down_read(&card->controls_rwsem);
+ if (uinfo == NULL || uctl == NULL)
+ return -ENOMEM;
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
- if (! kctl) {
- err = -ENOENT;
- goto __unlock;
- }
- if ((err = kctl->info(kctl, uinfo)) < 0)
- goto __unlock;
- if ((err = kctl->get(kctl, uctl)) < 0)
- goto __unlock;
+ if (!kctl)
+ return -ENOENT;
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
+ return err;
+ err = kctl->get(kctl, uctl);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
@@ -842,13 +815,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
break;
}
}
- err = 0;
- __unlock:
- up_read(&card->controls_rwsem);
- __free_only:
- kfree(uctl);
- kfree(uinfo);
- return err;
+ return 0;
}
static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)
@@ -858,25 +825,22 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot = NULL;
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err;
unsigned int idx;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
- if (uinfo == NULL || uctl == NULL) {
- err = -ENOMEM;
- goto __free_only;
- }
- down_read(&card->controls_rwsem);
+ if (uinfo == NULL || uctl == NULL)
+ return -ENOMEM;
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
- if (! kctl) {
- err = -ENOENT;
- goto __unlock;
- }
- if ((err = kctl->info(kctl, uinfo)) < 0)
- goto __unlock;
+ if (!kctl)
+ return -ENOENT;
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
@@ -890,20 +854,14 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
break;
slot = NULL;
}
- if (! slot)
- goto __unlock;
+ if (!slot)
+ return 0;
for (idx = 0; idx < uinfo->count; idx++)
uctl->value.enumerated.item[idx] = slot->capture_item;
err = kctl->put(kctl, uctl);
if (err > 0)
snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- err = 0;
- __unlock:
- up_read(&card->controls_rwsem);
- __free_only:
- kfree(uctl);
- kfree(uinfo);
- return err;
+ return 0;
}
struct snd_mixer_oss_assign_table {
@@ -914,33 +872,26 @@ struct snd_mixer_oss_assign_table {
static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)
{
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info *info __free(kfree) = NULL;
struct snd_kcontrol *kcontrol;
struct snd_card *card = mixer->card;
int err;
- down_read(&card->controls_rwsem);
- kcontrol = snd_mixer_oss_test_id(mixer, name, index);
- if (kcontrol == NULL) {
- up_read(&card->controls_rwsem);
- return 0;
- }
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (! info) {
- up_read(&card->controls_rwsem);
- return -ENOMEM;
- }
- if ((err = kcontrol->info(kcontrol, info)) < 0) {
- up_read(&card->controls_rwsem);
- kfree(info);
- return err;
+ scoped_guard(rwsem_read, &card->controls_rwsem) {
+ kcontrol = snd_mixer_oss_test_id(mixer, name, index);
+ if (kcontrol == NULL)
+ return 0;
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ err = kcontrol->info(kcontrol, info);
+ if (err < 0)
+ return err;
+ slot->numid[item] = kcontrol->id.numid;
}
- slot->numid[item] = kcontrol->id.numid;
- up_read(&card->controls_rwsem);
if (info->count > slot->channels)
slot->channels = info->count;
slot->present |= 1 << item;
- kfree(info);
return 0;
}
@@ -968,7 +919,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];
@@ -1032,13 +983,15 @@ 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;
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *rslot;
- char str[64];
+ const char *str;
/* check if already assigned */
if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
@@ -1048,46 +1001,39 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
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) {
- struct snd_ctl_elem_info *uinfo;
+ guard(rwsem_read)(&mixer->card->controls_rwsem);
+ kctl = NULL;
+ if (!ptr->index)
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ if (kctl) {
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- if (! uinfo) {
- up_read(&mixer->card->controls_rwsem);
+ if (!uinfo)
return -ENOMEM;
- }
- if (kctl->info(kctl, uinfo)) {
- up_read(&mixer->card->controls_rwsem);
- kfree(uinfo);
+ if (kctl->info(kctl, uinfo))
return 0;
- }
- strcpy(str, ptr->name);
+ str = ptr->name;
if (!strcmp(str, "Master"))
- strcpy(str, "Mix");
- if (!strcmp(str, "Master Mono"))
- strcpy(str, "Mix Mono");
+ str = "Mix";
+ else if (!strcmp(str, "Master Mono"))
+ str = "Mix Mono";
slot.capture_item = 0;
if (!strcmp(uinfo->value.enumerated.name, str)) {
slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
} else {
for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
uinfo->value.enumerated.item = slot.capture_item;
- if (kctl->info(kctl, uinfo)) {
- up_read(&mixer->card->controls_rwsem);
- kfree(uinfo);
+ if (kctl->info(kctl, uinfo))
return 0;
- }
if (!strcmp(uinfo->value.enumerated.name, str)) {
slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
break;
}
}
}
- kfree(uinfo);
}
- up_read(&mixer->card->controls_rwsem);
if (slot.present != 0) {
pslot = kmalloc(sizeof(slot), GFP_KERNEL);
if (! pslot)
@@ -1122,7 +1068,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
/*
*/
#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),
@@ -1160,7 +1106,7 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
int i;
- mutex_lock(&mixer->reg_mutex);
+ guard(mutex)(&mixer->reg_mutex);
for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
struct slot *p;
@@ -1175,7 +1121,6 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
else
snd_iprintf(buffer, "\"\" 0\n");
}
- mutex_unlock(&mixer->reg_mutex);
}
static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
@@ -1202,9 +1147,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
cptr = snd_info_get_str(str, cptr, sizeof(str));
if (! *str) {
/* remove the entry */
- mutex_lock(&mixer->reg_mutex);
- mixer_slot_clear(&mixer->slots[ch]);
- mutex_unlock(&mixer->reg_mutex);
+ scoped_guard(mutex, &mixer->reg_mutex)
+ mixer_slot_clear(&mixer->slots[ch]);
continue;
}
snd_info_get_str(idxstr, cptr, sizeof(idxstr));
@@ -1213,28 +1157,27 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
pr_err("ALSA: mixer_oss: invalid index %d\n", idx);
continue;
}
- mutex_lock(&mixer->reg_mutex);
- slot = (struct slot *)mixer->slots[ch].private_data;
- if (slot && slot->assigned &&
- slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
- /* not changed */
- goto __unlock;
- tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
- if (!tbl)
- goto __unlock;
- tbl->oss_id = ch;
- tbl->name = kstrdup(str, GFP_KERNEL);
- if (! tbl->name) {
- kfree(tbl);
- goto __unlock;
- }
- tbl->index = idx;
- if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
- kfree(tbl->name);
- kfree(tbl);
+ scoped_guard(mutex, &mixer->reg_mutex) {
+ slot = (struct slot *)mixer->slots[ch].private_data;
+ if (slot && slot->assigned &&
+ slot->assigned->index == idx && !strcmp(slot->assigned->name, str))
+ /* not changed */
+ break;
+ tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+ if (!tbl)
+ break;
+ tbl->oss_id = ch;
+ tbl->name = kstrdup(str, GFP_KERNEL);
+ if (!tbl->name) {
+ kfree(tbl);
+ break;
+ }
+ tbl->index = idx;
+ if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
+ kfree(tbl->name);
+ kfree(tbl);
+ }
}
- __unlock:
- mutex_unlock(&mixer->reg_mutex);
}
}
@@ -1247,7 +1190,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;
@@ -1270,7 +1213,7 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
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 },
@@ -1356,9 +1299,10 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
if (mixer == NULL)
return -ENOMEM;
mutex_init(&mixer->reg_mutex);
- if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
- card, 0,
- &snd_mixer_oss_f_ops, card)) < 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);
@@ -1368,7 +1312,7 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
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
snprintf(mixer->name, sizeof(mixer->name),
"mixer%i", card->number);
@@ -1403,24 +1347,32 @@ 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);
+ }
}
}
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 7915564bd394..fe27034f2846 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -269,6 +269,8 @@ 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;
@@ -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 3ebba9c7f86e..a82dd155e1d3 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
@@ -162,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)
{
@@ -392,7 +377,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
snd_pcm_hw_param_t var, unsigned int best,
int *dir)
{
- struct snd_pcm_hw_params *save = NULL;
+ struct snd_pcm_hw_params *save __free(kfree) = NULL;
int v;
unsigned int saved_min;
int last = 0;
@@ -419,38 +404,30 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
saved_min = min;
min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
if (min >= 0) {
- struct snd_pcm_hw_params *params1;
+ struct snd_pcm_hw_params *params1 __free(kfree) = NULL;
if (max < 0)
goto _end;
if ((unsigned int)min == saved_min && mindir == valdir)
goto _end;
params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
- if (params1 == NULL) {
- kfree(save);
+ if (params1 == NULL)
return -ENOMEM;
- }
*params1 = *save;
max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
- if (max < 0) {
- kfree(params1);
+ if (max < 0)
goto _end;
- }
if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
*params = *params1;
last = 1;
}
- kfree(params1);
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
- if (max < 0) {
- kfree(save);
+ if (max < 0)
return max;
- }
last = 1;
}
_end:
- kfree(save);
if (last)
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
@@ -697,17 +674,25 @@ 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 * 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)
@@ -743,17 +728,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 = roundup_pow_of_two(min_period_size);
- 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 = rounddown_pow_of_two(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;
@@ -761,7 +750,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;
@@ -777,6 +766,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;
@@ -787,7 +781,7 @@ static int choose_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, unsigned int best_rate)
{
const struct snd_interval *it;
- struct snd_pcm_hw_params *save;
+ struct snd_pcm_hw_params *save __free(kfree) = NULL;
unsigned int rate, prev;
save = kmalloc(sizeof(*save), GFP_KERNEL);
@@ -806,10 +800,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_param_set(substream, params,
SNDRV_PCM_HW_PARAM_RATE,
rate, 0);
- if (ret == (int)rate) {
- kfree(save);
+ if (ret == (int)rate)
return rate;
- }
*params = *save;
}
prev = rate;
@@ -819,12 +811,39 @@ static int choose_rate(struct snd_pcm_substream *substream,
}
/* not found, use the nearest rate */
- kfree(save);
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,
- bool trylock)
+/* 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;
@@ -838,11 +857,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
const struct snd_mask *sformat_mask;
struct snd_mask mask;
- if (trylock) {
- if (!(mutex_trylock(&runtime->oss.params_lock)))
- return -EAGAIN;
- } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
- return -ERESTARTSYS;
+ 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);
@@ -873,8 +889,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
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);
@@ -885,20 +908,17 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
sformat = snd_pcm_plug_slave_format(format, sformat_mask);
if ((__force int)sformat < 0 ||
- !snd_mask_test(sformat_mask, (__force int)sformat)) {
- for (sformat = (__force snd_pcm_format_t)0;
- (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST;
- sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {
- if (snd_mask_test(sformat_mask, (__force int)sformat) &&
+ !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 ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) {
- pcm_dbg(substream->pcm, "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;
}
+ format_found:
err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
if (err < 0)
goto failure;
@@ -926,25 +946,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) {
+ 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);
- snd_pcm_oss_plugin_clear(substream);
goto failure;
}
if (runtime->oss.plugin_first) {
struct snd_pcm_plugin *plugin;
- if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
+ 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);
- snd_pcm_oss_plugin_clear(substream);
goto failure;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -952,35 +991,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) {
- pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);
- goto failure;
- }
-
if (runtime->oss.trigger) {
sw_params->start_threshold = 1;
} else {
@@ -1008,7 +1024,8 @@ 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) {
+ 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;
}
@@ -1026,10 +1043,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;
}
@@ -1048,8 +1064,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;
@@ -1058,16 +1074,34 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
runtime->oss.params = 0;
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
- if (runtime->dma_area)
- snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
+ snd_pcm_runtime_buffer_set_silence(runtime);
runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
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;
}
@@ -1096,6 +1130,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;
@@ -1120,8 +1158,6 @@ 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, false);
@@ -1129,6 +1165,29 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
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;
+ }
+ if (runtime->oss.prepare) {
err = snd_pcm_oss_prepare(substream);
if (err < 0)
return err;
@@ -1166,25 +1225,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
pcm_dbg(substream->pcm,
"pcm_oss: write: recovering from %s\n",
- runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
"XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
+ 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;
@@ -1196,18 +1257,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
pcm_dbg(substream->pcm,
"pcm_oss: read: recovering from %s\n",
- runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ 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;
@@ -1215,10 +1276,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;
+ 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;
@@ -1237,12 +1300,12 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void
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
pcm_dbg(substream->pcm,
"pcm_oss: writev: recovering from %s\n",
- runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
"XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
@@ -1255,7 +1318,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void
/* 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;
@@ -1266,18 +1329,18 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void *
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
pcm_dbg(substream->pcm,
"pcm_oss: readv: recovering from %s\n",
- runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ 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;
@@ -1326,19 +1389,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;
+ 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)
@@ -1394,6 +1459,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
}
tmp = 0;
}
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1433,19 +1499,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;
+ 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);
@@ -1486,6 +1554,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
}
tmp = 0;
}
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1501,10 +1570,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;
}
@@ -1516,7 +1587,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);
}
@@ -1550,9 +1622,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
break;
result = 0;
set_current_state(TASK_INTERRUPTIBLE);
- snd_pcm_stream_lock_irq(substream);
- state = runtime->status->state;
- snd_pcm_stream_unlock_irq(substream);
+ scoped_guard(pcm_stream_lock_irq, substream)
+ state = runtime->state;
if (state != SNDRV_PCM_STATE_RUNNING) {
set_current_state(TASK_RUNNING);
break;
@@ -1588,11 +1659,16 @@ 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
pcm_dbg(substream->pcm, "sync: buffer_used\n");
@@ -1602,10 +1678,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
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
pcm_dbg(substream->pcm, "sync: period_ptr\n");
@@ -1615,10 +1689,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
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.
@@ -1632,7 +1704,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
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
*/
@@ -1643,19 +1719,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;
}
@@ -1667,6 +1748,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;
@@ -1674,10 +1757,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);
}
@@ -1687,7 +1774,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;
}
@@ -1702,13 +1790,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);
}
@@ -1718,7 +1812,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;
}
@@ -1728,7 +1823,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;
}
@@ -1738,12 +1834,13 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
int direct;
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
unsigned int formats = 0;
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;
@@ -1762,23 +1859,24 @@ 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_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
- kfree(params);
if (err < 0)
return err;
+ 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);
+ int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt);
if (f >= 0)
formats |= f;
}
}
+
return 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);
@@ -1792,10 +1890,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);
@@ -1806,7 +1908,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;
}
@@ -1815,8 +1918,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;
@@ -1840,9 +1941,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;
@@ -1851,13 +1960,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;
@@ -1873,9 +1984,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;
@@ -1883,9 +2002,8 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
static int snd_pcm_oss_nonblock(struct file * file)
{
- spin_lock(&file->f_lock);
+ guard(spinlock)(&file->f_lock);
file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
return 0;
}
@@ -1943,22 +2061,27 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
int err, cmd;
#ifdef OSS_DEBUG
- pcm_dbg(substream->pcm, "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;
@@ -1976,13 +2099,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;
@@ -1997,11 +2126,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;
}
@@ -2029,7 +2161,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)
@@ -2056,7 +2189,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) {
@@ -2127,9 +2261,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, false)) < 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;
@@ -2195,7 +2331,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
{
struct snd_pcm_oss_setup *setup;
- mutex_lock(&pcm->streams[stream].oss.setup_mutex);
+ guard(mutex)(&pcm->streams[stream].oss.setup_mutex);
do {
for (setup = pcm->streams[stream].oss.setup_list; setup;
setup = setup->next) {
@@ -2206,18 +2342,11 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
out:
if (setup)
*rsetup = *setup;
- mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
}
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;
}
@@ -2253,6 +2382,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)
@@ -2311,7 +2441,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);
}
@@ -2489,7 +2618,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:
@@ -2501,7 +2631,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:
@@ -2616,6 +2747,10 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
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
@@ -2701,22 +2836,23 @@ static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)
if (psubstream != NULL) {
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 ||
- snd_pcm_oss_playback_ready(psubstream)))
- mask |= POLLOUT | POLLWRNORM;
- snd_pcm_stream_unlock_irq(psubstream);
+ scoped_guard(pcm_stream_lock_irq, psubstream) {
+ if (runtime->state != SNDRV_PCM_STATE_DRAINING &&
+ (runtime->state != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_playback_ready(psubstream)))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
}
if (csubstream != NULL) {
struct snd_pcm_runtime *runtime = csubstream->runtime;
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 ||
- snd_pcm_oss_capture_ready(csubstream))
- mask |= POLLIN | POLLRDNORM;
- snd_pcm_stream_unlock_irq(csubstream);
+ scoped_guard(pcm_stream_lock_irq, csubstream) {
+ ostate = runtime->state;
+ if (ostate != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_capture_ready(csubstream))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
struct snd_pcm_oss_file ofile;
memset(&ofile, 0, sizeof(ofile));
@@ -2745,7 +2881,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;
@@ -2757,7 +2893,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;
@@ -2770,7 +2906,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
if (runtime->oss.params) {
/* use mutex_trylock() for params_lock for avoiding a deadlock
- * between mmap_sem and params_lock taken by
+ * 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);
@@ -2811,7 +2947,7 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
{
struct snd_pcm_str *pstr = entry->private_data;
struct snd_pcm_oss_setup *setup = pstr->oss.setup_list;
- mutex_lock(&pstr->oss.setup_mutex);
+ guard(mutex)(&pstr->oss.setup_mutex);
while (setup) {
snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
setup->task_name,
@@ -2825,7 +2961,6 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
setup->nosilence ? " no-silence" : "");
setup = setup->next;
}
- mutex_unlock(&pstr->oss.setup_mutex);
}
static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)
@@ -2851,12 +2986,11 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
struct snd_pcm_oss_setup *setup, *setup1, template;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
- mutex_lock(&pstr->oss.setup_mutex);
+ guard(mutex)(&pstr->oss.setup_mutex);
memset(&template, 0, sizeof(template));
ptr = snd_info_get_str(task_name, line, sizeof(task_name));
if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
snd_pcm_oss_proc_free_setup_list(pstr);
- mutex_unlock(&pstr->oss.setup_mutex);
continue;
}
for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
@@ -2896,7 +3030,6 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
setup = kmalloc(sizeof(*setup), GFP_KERNEL);
if (! setup) {
buffer->error = -ENOMEM;
- mutex_unlock(&pstr->oss.setup_mutex);
return;
}
if (pstr->oss.setup_list == NULL)
@@ -2910,12 +3043,10 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
if (! template.task_name) {
kfree(setup);
buffer->error = -ENOMEM;
- mutex_unlock(&pstr->oss.setup_mutex);
return;
}
}
*setup = template;
- mutex_unlock(&pstr->oss.setup_mutex);
}
}
@@ -2927,9 +3058,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;
@@ -2953,8 +3085,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 */
/*
@@ -2968,7 +3104,6 @@ static const struct file_operations snd_pcm_oss_f_reg =
.write = snd_pcm_oss_write,
.open = snd_pcm_oss_open,
.release = snd_pcm_oss_release,
- .llseek = no_llseek,
.poll = snd_pcm_oss_poll,
.unlocked_ioctl = snd_pcm_oss_ioctl,
.compat_ioctl = snd_pcm_oss_ioctl_compat,
@@ -3071,7 +3206,8 @@ static int __init alsa_pcm_oss_init(void)
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 85a56af104bd..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,79 +195,83 @@ 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;
+ struct snd_pcm_plugin *plugin, *plugin_next;
- if (snd_BUG_ON(!plug))
- return -ENXIO;
- if (drv_frames == 0)
- return 0;
- stream = snd_pcm_plug_stream(plug);
- 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;
-
if (snd_BUG_ON(!plug))
return -ENXIO;
- if (clt_frames == 0)
- return 0;
- frames = clt_frames;
- stream = snd_pcm_plug_stream(plug);
- 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(const struct snd_mask *mask,
@@ -281,14 +289,14 @@ static int snd_pcm_plug_formats(const struct snd_mask *mask,
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
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);
+ 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 snd_pcm_format_t 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,
@@ -353,6 +361,7 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
if (snd_mask_test(format_mask, (__force int)format1))
return format1;
}
+ fallthrough;
default:
return (__force snd_pcm_format_t)-EINVAL;
}
@@ -567,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 &&
@@ -595,16 +605,17 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
while (plugin) {
if (frames <= 0)
return frames;
- if ((next = plugin->next) != NULL) {
+ next = plugin->next;
+ if (next) {
snd_pcm_sframes_t frames1 = frames;
if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
if (frames1 <= 0)
return frames1;
}
- if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
+ err = next->client_channels(next, frames1, &dst_channels);
+ if (err < 0)
return err;
- }
if (err != frames1) {
frames = err;
if (plugin->src_frames) {
@@ -616,12 +627,13 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
} 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)
@@ -631,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;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index c9cd29d86efd..7b76cf64157e 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
@@ -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,
@@ -89,7 +74,6 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
size_t extra,
struct snd_pcm_plugin **ret);
int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin);
-int snd_pcm_plugin_clear(struct snd_pcm_plugin **first);
int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames);
snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size);
snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size);
@@ -154,8 +138,14 @@ int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel,
size_t dst_offset,
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);
+#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, const struct snd_mask *format_mask) { return format; }
+
+#endif
+
snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream,
const char *ptr, snd_pcm_uframes_t size,
int in_kernel);
@@ -166,16 +156,8 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream,
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream,
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, const struct snd_mask *format_mask) { return format; }
-
-#endif
-
#ifdef PLUGIN_DEBUG
-#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args)
+#define pdprintf(fmt, args...) pr_debug("plugin: " fmt, ##args)
#else
#define pdprintf(fmt, args...)
#endif
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 2fa9299a440d..b56eeda5e30e 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);
}
@@ -294,7 +294,7 @@ static int rate_action(struct snd_pcm_plugin *plugin,
default:
break;
}
- return 0; /* silenty ignore other actions */
+ return 0; /* silently ignore other actions */
}
int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
@@ -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 c8171f5783c8..72dea04197ef 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -57,6 +57,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
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 09ee8c6b9f75..283aac441fa0 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.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/init.h>
@@ -25,9 +10,11 @@
#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>
@@ -104,9 +91,8 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
- mutex_lock(&register_mutex);
- device = snd_pcm_next(card, device);
- mutex_unlock(&register_mutex);
+ scoped_guard(mutex, &register_mutex)
+ device = snd_pcm_next(card, device);
if (put_user(device, (int __user *)arg))
return -EFAULT;
return 0;
@@ -119,7 +105,6 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
struct snd_pcm *pcm;
struct snd_pcm_str *pstr;
struct snd_pcm_substream *substream;
- int err;
info = (struct snd_pcm_info __user *)arg;
if (get_user(device, &info->device))
@@ -128,37 +113,26 @@ 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);
+ guard(mutex)(&register_mutex);
pcm = snd_pcm_get(card, device);
- if (pcm == NULL) {
- err = -ENXIO;
- goto _error;
- }
+ if (pcm == NULL)
+ return -ENXIO;
pstr = &pcm->streams[stream];
- if (pstr->substream_count == 0) {
- err = -ENOENT;
- goto _error;
- }
- if (subdevice >= pstr->substream_count) {
- err = -ENXIO;
- goto _error;
- }
+ if (pstr->substream_count == 0)
+ return -ENOENT;
+ if (subdevice >= pstr->substream_count)
+ return -ENXIO;
for (substream = pstr->substream; substream;
substream = substream->next)
if (substream->number == (int)subdevice)
break;
- if (substream == NULL) {
- 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;
+ if (substream == NULL)
+ return -ENXIO;
+ guard(mutex)(&pcm->open_mutex);
+ return snd_pcm_info_user(substream, info);
}
case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
{
@@ -175,7 +149,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),
@@ -223,17 +197,25 @@ static char *snd_pcm_format_names[] = {
FORMAT(DSD_U32_LE),
FORMAT(DSD_U16_BE),
FORMAT(DSD_U32_BE),
+ FORMAT(S20_LE),
+ FORMAT(S20_BE),
+ FORMAT(U20_LE),
+ FORMAT(U20_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 ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
+ unsigned int format_num = (__force unsigned int)format;
+
+ if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num])
return "Unknown";
- return snd_pcm_format_names[(__force unsigned int)format];
+ return snd_pcm_format_names[format_num];
}
EXPORT_SYMBOL_GPL(snd_pcm_format_name);
@@ -249,12 +231,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),
@@ -263,9 +245,10 @@ static char *snd_pcm_state_names[] = {
STATE(DRAINING),
STATE(PAUSED),
STATE(SUSPENDED),
+ STATE(DISCONNECTED),
};
-static char *snd_pcm_access_names[] = {
+static const char * const snd_pcm_access_names[] = {
ACCESS(MMAP_INTERLEAVED),
ACCESS(MMAP_NONINTERLEAVED),
ACCESS(MMAP_COMPLEX),
@@ -273,11 +256,14 @@ 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),
+ SUBFORMAT(MSBITS_MAX),
+ SUBFORMAT(MSBITS_20),
+ SUBFORMAT(MSBITS_24),
};
-static char *snd_pcm_tstamp_mode_names[] = {
+static const char * const snd_pcm_tstamp_mode_names[] = {
TSTAMP(NONE),
TSTAMP(ENABLE),
};
@@ -342,7 +328,7 @@ static const char *snd_pcm_oss_format_name(int format)
static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
struct snd_info_buffer *buffer)
{
- struct snd_pcm_info *info;
+ struct snd_pcm_info *info __free(kfree) = NULL;
int err;
if (! substream)
@@ -355,7 +341,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
err = snd_pcm_info(substream, info);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
- kfree(info);
return;
}
snd_iprintf(buffer, "card: %d\n", info->card);
@@ -369,7 +354,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass);
snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count);
snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail);
- kfree(info);
}
static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
@@ -391,15 +375,15 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- mutex_lock(&substream->pcm->open_mutex);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- goto unlock;
+ return;
}
snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
@@ -418,8 +402,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
@@ -428,15 +410,15 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- mutex_lock(&substream->pcm->open_mutex);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- goto unlock;
+ return;
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
@@ -446,8 +428,6 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
@@ -455,35 +435,36 @@ 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);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
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;
+ return;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
- snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
- status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
- snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
- 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);
snd_iprintf(buffer, "-----\n");
snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr);
snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr);
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ snd_iprintf(buffer, "xrun_counter: %d\n", substream->xrun_counter);
+#endif
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -491,13 +472,8 @@ 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;
- struct snd_pcm_runtime *runtime;
- snd_pcm_stream_lock_irq(substream);
- runtime = substream->runtime;
- if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock_irq(substream);
+ snd_pcm_stop_xrun(substream);
}
static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
@@ -529,53 +505,45 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
pcm->card->proc_root);
if (!entry)
return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
pstr->proc_root = entry;
entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root);
- if (entry) {
+ 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
entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
pstr->proc_root);
if (entry) {
- entry->c.text.read = snd_pcm_xrun_debug_read;
+ 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;
@@ -589,102 +557,60 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
substream->pstr->proc_root);
if (!entry)
return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
substream->proc_root = entry;
- entry = snd_info_create_card_entry(card, "info", substream->proc_root);
- if (entry) {
- 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;
- entry = snd_info_create_card_entry(card, "hw_params",
- substream->proc_root);
- if (entry) {
- 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;
- entry = snd_info_create_card_entry(card, "sw_params",
- substream->proc_root);
- if (entry) {
- 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;
- entry = snd_info_create_card_entry(card, "status",
- substream->proc_root);
- if (entry) {
- 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;
- }
- }
- substream->proc_status_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);
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- entry = snd_info_create_card_entry(card, "xrun_injection",
- substream->proc_root);
+ entry = create_substream_info_entry(substream, "xrun_injection", NULL);
if (entry) {
- entry->private_data = substream;
- entry->c.text.read = NULL;
entry->c.text.write = snd_pcm_xrun_injection_write;
- entry->mode = S_IFREG | S_IWUSR;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+ entry->mode = S_IFREG | 0200;
}
- substream->proc_xrun_injection_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;
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- snd_info_free_entry(substream->proc_xrun_injection_entry);
- substream->proc_xrun_injection_entry = NULL;
-#endif
- 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
+ */
+static int do_pcm_suspend(struct device *dev)
+{
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
+
+ if (!pstr->pcm->no_device_suspend)
+ snd_pcm_suspend_all(pstr->pcm);
+ return 0;
+}
+
+static const struct dev_pm_ops pcm_dev_pm_ops = {
+ 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
@@ -713,10 +639,14 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
if (!substream_count)
return 0;
- snd_device_initialize(&pstr->dev, pcm->card);
- pstr->dev.groups = pcm_dev_attr_groups;
- dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
+ err = snd_device_alloc(&pstr->dev, pcm->card);
+ if (err < 0)
+ return err;
+ dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+ pstr->dev->groups = pcm_dev_attr_groups;
+ pstr->dev->type = &pcm_dev_type;
+ dev_set_drvdata(pstr->dev, pstr);
if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);
@@ -755,9 +685,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
}
}
substream->group = &substream->self_group;
- spin_lock_init(&substream->self_group.lock);
- mutex_init(&substream->self_group.mutex);
- 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;
@@ -772,12 +700,12 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
{
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 struct snd_device_ops internal_ops = {
+ static const struct snd_device_ops internal_ops = {
.dev_free = snd_pcm_dev_free,
};
@@ -795,7 +723,7 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
init_waitqueue_head(&pcm->open_wait);
INIT_LIST_HEAD(&pcm->list);
if (id)
- strlcpy(pcm->id, id, sizeof(pcm->id));
+ strscpy(pcm->id, id, sizeof(pcm->id));
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
playback_count);
@@ -876,7 +804,9 @@ EXPORT_SYMBOL(snd_pcm_new_internal);
static void free_chmap(struct snd_pcm_str *pstr)
{
if (pstr->chmap_kctl) {
- snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+ struct snd_card *card = pstr->pcm->card;
+
+ snd_ctl_remove(card, pstr->chmap_kctl);
pstr->chmap_kctl = NULL;
}
}
@@ -887,15 +817,17 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
#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 IS_ENABLED(CONFIG_SND_PCM_OSS)
for (setup = pstr->oss.setup_list; setup; setup = setupn) {
setupn = setup->next;
@@ -905,7 +837,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
#endif
free_chmap(pstr);
if (pstr->substream_count)
- put_device(&pstr->dev);
+ put_device(pstr->dev);
}
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
@@ -1008,27 +940,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;
@@ -1037,6 +971,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ substream->xrun_counter = 0;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
return 0;
}
@@ -1049,22 +986,30 @@ 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);
+ /* Avoid concurrent access to runtime via PCM timer interface */
+ if (substream->timer) {
+ scoped_guard(spinlock_irq, &substream->timer->lock)
+ substream->runtime = NULL;
+ } 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_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
@@ -1078,10 +1023,10 @@ static ssize_t show_pcm_class(struct device *dev,
str = "none";
else
str = strs[pcm->dev_class];
- return snprintf(buf, PAGE_SIZE, "%s\n", str);
+ return sysfs_emit(buf, "%s\n", str);
}
-static DEVICE_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
@@ -1106,10 +1051,10 @@ static int snd_pcm_dev_register(struct snd_device *device)
return -ENXIO;
pcm = device->device_data;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
err = snd_pcm_add(pcm);
if (err)
- goto unlock;
+ return err;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
@@ -1125,10 +1070,10 @@ static int snd_pcm_dev_register(struct snd_device *device)
/* register pcm */
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
- &pcm->streams[cidx].dev);
+ pcm->streams[cidx].dev);
if (err < 0) {
list_del_init(&pcm->list);
- goto unlock;
+ return err;
}
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
@@ -1136,9 +1081,6 @@ static int snd_pcm_dev_register(struct snd_device *device)
}
pcm_call_notify(pcm, n_register);
-
- unlock:
- mutex_unlock(&register_mutex);
return err;
}
@@ -1148,33 +1090,34 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
struct snd_pcm_substream *substream;
int cidx;
- mutex_lock(&register_mutex);
- mutex_lock(&pcm->open_mutex);
+ guard(mutex)(&register_mutex);
+ guard(mutex)(&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) {
- 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 */
- substream->runtime->status->state = 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_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++) {
- snd_unregister_device(&pcm->streams[cidx].dev);
+ if (pcm->streams[cidx].dev)
+ snd_unregister_device(pcm->streams[cidx].dev);
free_chmap(&pcm->streams[cidx]);
}
- mutex_unlock(&pcm->open_mutex);
- mutex_unlock(&register_mutex);
return 0;
}
@@ -1187,6 +1130,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
* 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)
{
@@ -1197,7 +1142,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
!notify->n_unregister ||
!notify->n_disconnect))
return -EINVAL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
if (nfree) {
list_del(&notify->list);
list_for_each_entry(pcm, &snd_pcm_devices, list)
@@ -1207,7 +1152,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
list_for_each_entry(pcm, &snd_pcm_devices, list)
notify->n_register(pcm);
}
- mutex_unlock(&register_mutex);
return 0;
}
EXPORT_SYMBOL(snd_pcm_notify);
@@ -1223,7 +1167,7 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
{
struct snd_pcm *pcm;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(pcm, &snd_pcm_devices, list) {
snd_iprintf(buffer, "%02i-%02i: %s : %s",
pcm->card->number, pcm->device, pcm->id, pcm->name);
@@ -1235,7 +1179,6 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
snd_iprintf(buffer, "\n");
}
- mutex_unlock(&register_mutex);
}
static struct snd_info_entry *snd_pcm_proc_entry;
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index b719d0bd833e..54eb9bd8eb21 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,10 +13,11 @@ static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
s32 __user *src)
{
snd_pcm_sframes_t delay;
+ int err;
- delay = snd_pcm_delay(substream);
- if (delay < 0)
- return delay;
+ err = snd_pcm_delay(substream, &delay);
+ if (err)
+ return err;
if (put_user(delay, src))
return -EFAULT;
return 0;
@@ -44,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;
@@ -61,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;
@@ -102,19 +83,6 @@ struct snd_pcm_sw_params32 {
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)
{
@@ -179,105 +147,47 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
return err;
}
-#ifdef CONFIG_X86_X32
+#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 */
-
-struct snd_pcm_status32 {
- s32 state;
- struct compat_timespec trigger_tstamp;
- struct compat_timespec tstamp;
+#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;
+ snd_pcm_state_t suspended_state;
u32 audio_tstamp_data;
- struct compat_timespec audio_tstamp;
- struct compat_timespec driver_tstamp;
+ s64 audio_tstamp_sec;
+ s64 audio_tstamp_nsec;
+ s64 driver_tstamp_sec;
+ s64 driver_tstamp_nsec;
u32 audio_tstamp_accuracy;
- unsigned char reserved[52-2*sizeof(struct compat_timespec)];
-} __attribute__((packed));
-
-
-static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
- struct snd_pcm_status32 __user *src,
- bool ext)
-{
- struct snd_pcm_status status;
- int err;
-
- memset(&status, 0, sizeof(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 *)(&src->audio_tstamp_data)))
- return -EFAULT;
- err = snd_pcm_status(substream, &status);
- if (err < 0)
- return err;
-
- if (clear_user(src, sizeof(*src)))
- return -EFAULT;
- if (put_user(status.state, &src->state) ||
- compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
- compat_put_timespec(&status.tstamp, &src->tstamp) ||
- 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) ||
- put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
- compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
- compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
- put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
- return -EFAULT;
-
- return err;
-}
-
-#ifdef CONFIG_X86_X32
-/* X32 ABI has 64bit timespec and 64bit alignment */
-struct snd_pcm_status_x32 {
- s32 state;
- u32 rsvd; /* alignment */
- struct timespec trigger_tstamp;
- struct timespec tstamp;
- u32 appl_ptr;
- u32 hw_ptr;
- s32 delay;
- u32 avail;
- u32 avail_max;
- u32 overrange;
- s32 suspended_state;
- u32 audio_tstamp_data;
- struct timespec audio_tstamp;
- struct timespec driver_tstamp;
- u32 audio_tstamp_accuracy;
- unsigned char reserved[52-2*sizeof(struct timespec)];
+ unsigned char reserved[52-4*sizeof(s64)];
} __packed;
-#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
-
-static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
- struct snd_pcm_status_x32 __user *src,
- bool ext)
+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;
memset(&status, 0, sizeof(status));
+ memset(&compat_status64, 0, sizeof(compat_status64));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
@@ -286,42 +196,51 @@ static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&src->audio_tstamp_data)))
return -EFAULT;
- err = snd_pcm_status(substream, &status);
+ err = snd_pcm_status64(substream, &status);
if (err < 0)
return err;
if (clear_user(src, sizeof(*src)))
return -EFAULT;
- if (put_user(status.state, &src->state) ||
- put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
- put_timespec(&status.tstamp, &src->tstamp) ||
- 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) ||
- put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
- put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
- put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
- put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
+
+ 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;
}
-#endif /* CONFIG_X86_X32 */
/* both for HW_PARAMS and HW_REFINE */
static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
int refine,
struct snd_pcm_hw_params32 __user *data32)
{
- struct snd_pcm_hw_params *data;
+ struct snd_pcm_hw_params *data __free(kfree) = NULL;
struct snd_pcm_runtime *runtime;
int err;
- if (! (runtime = substream->runtime))
+ runtime = substream->runtime;
+ if (!runtime)
return -ENOTTY;
data = kmalloc(sizeof(*data), GFP_KERNEL);
@@ -329,30 +248,28 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
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 (copy_from_user(data, data32, sizeof(*data32)))
+ return -EFAULT;
- if (refine)
+ if (refine) {
err = snd_pcm_hw_refine(substream, data);
- else
+ if (err < 0)
+ return err;
+ err = fixup_unreferenced_params(substream, data);
+ } else {
err = snd_pcm_hw_params(substream, data);
+ }
if (err < 0)
- goto error;
+ return err;
if (copy_to_user(data32, data, sizeof(*data32)) ||
- put_user(data->fifo_size, &data32->fifo_size)) {
- err = -EFAULT;
- goto error;
- }
+ put_user(data->fifo_size, &data32->fifo_size))
+ return -EFAULT;
if (! refine) {
unsigned int new_boundary = recalculate_boundary(runtime);
if (new_boundary)
runtime->boundary = new_boundary;
}
- error:
- kfree(data);
return err;
}
@@ -376,7 +293,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) ||
@@ -415,29 +332,30 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
compat_caddr_t buf;
compat_caddr_t __user *bufptr;
u32 frames;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
int err, ch, i;
if (! substream->runtime)
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++) {
u32 ptr;
- if (get_user(ptr, bufptr)) {
- kfree(bufs);
+ if (get_user(ptr, bufptr))
return -EFAULT;
- }
bufs[i] = compat_ptr(ptr);
bufptr++;
}
@@ -447,41 +365,44 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
err = snd_pcm_lib_readv(substream, bufs, frames);
if (err >= 0) {
if (put_user(err, &data32->result))
- err = -EFAULT;
+ return -EFAULT;
}
- kfree(bufs);
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;
- struct compat_timespec audio_tstamp;
-} __attribute__((packed));
+ u32 pad2; /* alignment */
+ struct __snd_timespec64 tstamp;
+ snd_pcm_state_t suspended_state;
+ s32 pad3;
+ struct __snd_timespec64 audio_tstamp;
+} __packed;
-struct snd_pcm_mmap_control32 {
+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;
@@ -495,9 +416,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
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))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
return -EFAULT;
if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
@@ -507,125 +426,102 @@ 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 */
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ /* FIXME: we should consider the boundary for the sync from app */
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ control->appl_ptr = scontrol.appl_ptr;
+ 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;
+ }
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
- control->appl_ptr = scontrol.appl_ptr;
- 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 (put_user(sstatus.state, &src->s.status.state) ||
- put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
- put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
- compat_put_timespec(&sstatus.audio_tstamp,
- &src->s.status.audio_tstamp) ||
- put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- put_user(scontrol.avail_min, &src->c.control.avail_min))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
return -EFAULT;
return 0;
}
-
-#ifdef CONFIG_X86_X32
-/* X32 ABI has 64bit timespec and 64bit alignment */
-struct snd_pcm_mmap_status_x32 {
- s32 state;
- s32 pad1;
- u32 hw_ptr;
- u32 pad2; /* alignment */
- struct timespec tstamp;
- s32 suspended_state;
- s32 pad3;
- struct timespec audio_tstamp;
-} __packed;
-
-struct snd_pcm_mmap_control_x32 {
- u32 appl_ptr;
- u32 avail_min;
+#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;
};
-struct snd_pcm_sync_ptr_x32 {
- u32 flags;
- u32 rsvd; /* alignment */
- union {
- struct snd_pcm_mmap_status_x32 status;
- unsigned char reserved[64];
- } s;
- union {
- struct snd_pcm_mmap_control_x32 control;
- unsigned char reserved[64];
- } c;
-} __packed;
-
-static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
- struct snd_pcm_sync_ptr_x32 __user *src)
+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;
- 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))
+ 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 (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ 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;
}
- 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))
- control->appl_ptr = scontrol.appl_ptr;
- 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 (put_user(sstatus.state, &src->s.status.state) ||
- put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
- put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
- put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
- put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- put_user(scontrol.avail_min, &src->c.control.avail_min))
+ scoped_guard(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)
+ 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;
+ }
+ 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;
}
-#endif /* CONFIG_X86_X32 */
/*
*/
@@ -633,8 +529,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_EXT32 = _IOWR('A', 0x24, 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),
@@ -643,13 +539,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),
-#ifdef CONFIG_X86_X32
+ 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_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
- SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
-#endif /* CONFIG_X86_X32 */
+#endif /* CONFIG_X86_X32_ABI */
};
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -667,8 +562,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;
@@ -690,19 +585,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:
+ 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, false);
- case SNDRV_PCM_IOCTL_STATUS_EXT32:
- return snd_pcm_status_user_compat(substream, argp, true);
- 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:
@@ -719,16 +619,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);
-#ifdef CONFIG_X86_X32
- case SNDRV_PCM_IOCTL_STATUS_X32:
- return snd_pcm_status_user_x32(substream, argp, false);
- case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
- return snd_pcm_status_user_x32(substream, argp, true);
- case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
- return snd_pcm_ioctl_sync_ptr_x32(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 */
+#endif /* CONFIG_X86_X32_ABI */
}
return -ENOIOCTLCMD;
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 8eb58c709b14..f0c17503df42 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012, Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -7,16 +8,6 @@
* 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
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * You 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/init.h>
@@ -57,6 +48,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
*
* 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,
@@ -100,8 +93,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
* @dma_data: DAI DMA data
* @slave_config: DMA slave configuration
*
- * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and
- * slave_id fields of the DMA slave config from the same fields of the DAI DMA
+ * 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
@@ -118,6 +111,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config->dst_addr = dma_data->addr;
slave_config->dst_maxburst = dma_data->maxburst;
+ slave_config->dst_port_window_size = dma_data->port_window_size;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->dst_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
@@ -126,6 +120,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
} else {
slave_config->src_addr = dma_data->addr;
slave_config->src_maxburst = dma_data->maxburst;
+ slave_config->src_port_window_size = dma_data->port_window_size;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->src_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
@@ -133,18 +128,21 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
slave_config->src_addr_width = dma_data->addr_width;
}
- slave_config->slave_id = dma_data->slave_id;
+ 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);
- prtd->pos += snd_pcm_lib_period_bytes(substream);
- if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
- prtd->pos = 0;
+ 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);
}
@@ -183,10 +181,10 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
* @substream: PCM substream
* @cmd: Trigger command
*
- * Returns 0 on success, a negative error code otherwise.
- *
* 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)
{
@@ -231,6 +229,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
*
* 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)
{
@@ -245,10 +245,13 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
*
* 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;
@@ -259,9 +262,12 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
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(substream->runtime, pos);
+ return bytes_to_frames(runtime, pos);
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
@@ -270,9 +276,9 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
* @filter_fn: Filter function used to request the DMA channel
* @filter_data: Data passed to the DMA filter function
*
- * Returns NULL or the requested DMA channel.
- *
* 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)
@@ -292,11 +298,11 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
* @substream: PCM substream
* @chan: DMA channel to use for data transfers
*
- * Returns 0 on success, a negative error code otherwise.
- *
* 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)
@@ -324,58 +330,148 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
}
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
- *
- * Returns 0 on success, a negative error code otherwise.
- *
- * 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.
- */
-int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
- dma_filter_fn filter_fn, void *filter_data)
+int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream)
{
- return snd_dmaengine_pcm_open(substream,
- snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status != DMA_PAUSED)
+ dmaengine_synchronize(prtd->dma_chan);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
-/**
- * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
- * @substream: PCM substream
- */
-int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
+static void __snd_dmaengine_pcm_close(struct snd_pcm_substream *substream,
+ bool release_channel)
{
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_PAUSED)
+ dmaengine_terminate_async(prtd->dma_chan);
dmaengine_synchronize(prtd->dma_chan);
+ if (release_channel)
+ dma_release_channel(prtd->dma_chan);
kfree(prtd);
+}
+/**
+ * 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)
+{
+ __snd_dmaengine_pcm_close(substream, false);
return 0;
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
/**
- * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel
+ * 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);
-
+ __snd_dmaengine_pcm_close(substream, true);
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_DESCRIPTION("PCM dmaengine helper APIs");
MODULE_LICENSE("GPL");
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
index 9881d087756f..688eefce82fa 100644
--- a/sound/core/pcm_drm_eld.c
+++ b/sound/core/pcm_drm_eld.c
@@ -1,15 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM DRM helpers
- *
- * 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/bitfield.h>
#include <linux/export.h>
+#include <linux/hdmi.h>
+#include <linux/unaligned.h>
#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
+#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_drm_eld.h>
+#define SAD0_CHANNELS_MASK GENMASK(2, 0) /* max number of channels - 1 */
+#define SAD0_FORMAT_MASK GENMASK(6, 3) /* audio format */
+
+#define SAD1_RATE_MASK GENMASK(6, 0) /* bitfield of supported rates */
+#define SAD1_RATE_32000_MASK BIT(0)
+#define SAD1_RATE_44100_MASK BIT(1)
+#define SAD1_RATE_48000_MASK BIT(2)
+#define SAD1_RATE_88200_MASK BIT(3)
+#define SAD1_RATE_96000_MASK BIT(4)
+#define SAD1_RATE_176400_MASK BIT(5)
+#define SAD1_RATE_192000_MASK BIT(6)
+
static const unsigned int eld_rates[] = {
32000,
44100,
@@ -20,9 +34,62 @@ static const unsigned int eld_rates[] = {
192000,
};
+static unsigned int map_rate_families(const u8 *sad,
+ unsigned int mask_32000,
+ unsigned int mask_44100,
+ unsigned int mask_48000)
+{
+ unsigned int rate_mask = 0;
+
+ if (sad[1] & SAD1_RATE_32000_MASK)
+ rate_mask |= mask_32000;
+ if (sad[1] & (SAD1_RATE_44100_MASK | SAD1_RATE_88200_MASK | SAD1_RATE_176400_MASK))
+ rate_mask |= mask_44100;
+ if (sad[1] & (SAD1_RATE_48000_MASK | SAD1_RATE_96000_MASK | SAD1_RATE_192000_MASK))
+ rate_mask |= mask_48000;
+ return rate_mask;
+}
+
+static unsigned int sad_rate_mask(const u8 *sad)
+{
+ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
+ case HDMI_AUDIO_CODING_TYPE_PCM:
+ return sad[1] & SAD1_RATE_MASK;
+ case HDMI_AUDIO_CODING_TYPE_AC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS:
+ return map_rate_families(sad,
+ SAD1_RATE_32000_MASK,
+ SAD1_RATE_44100_MASK,
+ SAD1_RATE_48000_MASK);
+ case HDMI_AUDIO_CODING_TYPE_EAC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ return map_rate_families(sad,
+ 0,
+ SAD1_RATE_176400_MASK,
+ SAD1_RATE_192000_MASK);
+ default:
+ /* TODO adjust for other compressed formats as well */
+ return sad[1] & SAD1_RATE_MASK;
+ }
+}
+
static unsigned int sad_max_channels(const u8 *sad)
{
- return 1 + (sad[0] & 7);
+ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
+ case HDMI_AUDIO_CODING_TYPE_PCM:
+ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
+ case HDMI_AUDIO_CODING_TYPE_AC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS:
+ case HDMI_AUDIO_CODING_TYPE_EAC3:
+ return 2;
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ return 8;
+ default:
+ /* TODO adjust for other compressed formats as well */
+ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
+ }
}
static int eld_limit_rates(struct snd_pcm_hw_params *params,
@@ -45,7 +112,7 @@ static int eld_limit_rates(struct snd_pcm_hw_params *params,
* requested number of channels.
*/
if (c->min <= max_channels)
- rate_mask |= sad[1];
+ rate_mask |= sad_rate_mask(sad);
}
}
@@ -73,7 +140,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params,
rate_mask |= BIT(i);
for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
- if (rate_mask & sad[1])
+ if (rate_mask & sad_rate_mask(sad))
t.max = max(t.max, sad_max_channels(sad));
}
@@ -97,3 +164,388 @@ int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
return ret;
}
EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
+
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+
+static const char * const eld_connection_type_names[4] = {
+ "HDMI",
+ "DisplayPort",
+ "2-reserved",
+ "3-reserved"
+};
+
+static const char * const cea_audio_coding_type_names[] = {
+ /* 0 */ "undefined",
+ /* 1 */ "LPCM",
+ /* 2 */ "AC-3",
+ /* 3 */ "MPEG1",
+ /* 4 */ "MP3",
+ /* 5 */ "MPEG2",
+ /* 6 */ "AAC-LC",
+ /* 7 */ "DTS",
+ /* 8 */ "ATRAC",
+ /* 9 */ "DSD (One Bit Audio)",
+ /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+ /* 11 */ "DTS-HD",
+ /* 12 */ "MLP (Dolby TrueHD)",
+ /* 13 */ "DST",
+ /* 14 */ "WMAPro",
+ /* 15 */ "HE-AAC",
+ /* 16 */ "HE-AACv2",
+ /* 17 */ "MPEG Surround",
+};
+
+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",
+};
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static const int cea_sample_sizes[4] = {
+ 0, /* 0: Refer to Stream Header */
+ ELD_PCM_BITS_16, /* 1: 16 bits */
+ ELD_PCM_BITS_20, /* 2: 20 bits */
+ ELD_PCM_BITS_24, /* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+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 */
+ SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
+ SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
+ SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
+ SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
+ SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
+};
+
+#define GRAB_BITS(buf, byte, lowbit, bits) \
+({ \
+ BUILD_BUG_ON(lowbit > 7); \
+ BUILD_BUG_ON(bits > 8); \
+ BUILD_BUG_ON(bits <= 0); \
+ \
+ (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
+})
+
+static void hdmi_update_short_audio_desc(struct device *dev,
+ struct snd_cea_sad *a,
+ const unsigned char *buf)
+{
+ int i;
+ int val;
+
+ val = GRAB_BITS(buf, 1, 0, 7);
+ a->rates = 0;
+ for (i = 0; i < 7; i++)
+ if (val & (1 << i))
+ a->rates |= cea_sampling_frequencies[i + 1];
+
+ a->channels = GRAB_BITS(buf, 0, 0, 3);
+ a->channels++;
+
+ a->sample_bits = 0;
+ a->max_bitrate = 0;
+
+ a->format = GRAB_BITS(buf, 0, 3, 4);
+ switch (a->format) {
+ case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+ dev_info(dev, "HDMI: audio coding type 0 not expected\n");
+ break;
+
+ case AUDIO_CODING_TYPE_LPCM:
+ val = GRAB_BITS(buf, 2, 0, 3);
+ for (i = 0; i < 3; i++)
+ if (val & (1 << i))
+ a->sample_bits |= cea_sample_sizes[i + 1];
+ break;
+
+ case AUDIO_CODING_TYPE_AC3:
+ case AUDIO_CODING_TYPE_MPEG1:
+ case AUDIO_CODING_TYPE_MP3:
+ case AUDIO_CODING_TYPE_MPEG2:
+ case AUDIO_CODING_TYPE_AACLC:
+ case AUDIO_CODING_TYPE_DTS:
+ case AUDIO_CODING_TYPE_ATRAC:
+ a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+ a->max_bitrate *= 8000;
+ break;
+
+ case AUDIO_CODING_TYPE_SACD:
+ break;
+
+ case AUDIO_CODING_TYPE_EAC3:
+ break;
+
+ case AUDIO_CODING_TYPE_DTS_HD:
+ break;
+
+ case AUDIO_CODING_TYPE_MLP:
+ break;
+
+ case AUDIO_CODING_TYPE_DST:
+ break;
+
+ case AUDIO_CODING_TYPE_WMAPRO:
+ a->profile = GRAB_BITS(buf, 2, 0, 3);
+ break;
+
+ case AUDIO_CODING_TYPE_REF_CXT:
+ a->format = GRAB_BITS(buf, 2, 3, 5);
+ if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+ a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+ dev_info(dev,
+ "HDMI: audio coding xtype %d not expected\n",
+ a->format);
+ a->format = 0;
+ } else
+ a->format += AUDIO_CODING_TYPE_HE_AAC -
+ AUDIO_CODING_XTYPE_HE_AAC;
+ break;
+ }
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+int snd_parse_eld(struct device *dev, struct snd_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) {
+ dev_info(dev, "HDMI: Unknown ELD version %d\n", e->eld_ver);
+ goto out_fail;
+ }
+
+ 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);
+
+ e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
+ e->support_ai = GRAB_BITS(buf, 5, 1, 1);
+ e->conn_type = GRAB_BITS(buf, 5, 2, 2);
+ e->sad_count = GRAB_BITS(buf, 5, 4, 4);
+
+ e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+ e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
+
+ e->port_id = get_unaligned_le64(buf + 8);
+
+ /* not specified, but the spec's tendency is little endian */
+ e->manufacture_id = get_unaligned_le16(buf + 16);
+ e->product_id = get_unaligned_le16(buf + 18);
+
+ if (mnl > ELD_MAX_MNL) {
+ dev_info(dev, "HDMI: MNL is reserved value %d\n", mnl);
+ goto out_fail;
+ } else if (ELD_FIXED_BYTES + mnl > size) {
+ dev_info(dev, "HDMI: out of range MNL %d\n", mnl);
+ goto out_fail;
+ } else
+ 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) {
+ dev_info(dev, "HDMI: out of range SAD %d\n", i);
+ goto out_fail;
+ }
+ hdmi_update_short_audio_desc(dev, 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:
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_parse_eld);
+
+/*
+ * 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;
+
+ 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 eld_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+ 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 & (ELD_PCM_BITS_8 << i))
+ j += scnprintf(buf + j, buflen - j, " %d", bits[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+static void hdmi_show_short_audio_desc(struct device *dev,
+ struct snd_cea_sad *a)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
+
+ if (!a->format)
+ return;
+
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM)
+ eld_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';
+
+ dev_dbg(dev,
+ "HDMI: supports coding type %s: channels = %d, rates =%s%s\n",
+ cea_audio_coding_type_names[a->format],
+ a->channels, buf, buf2);
+}
+
+static void snd_eld_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 */
+}
+
+void snd_show_eld(struct device *dev, struct snd_parsed_hdmi_eld *e)
+{
+ int i;
+
+ dev_dbg(dev, "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_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ dev_dbg(dev, "HDMI: available speakers:%s\n", buf);
+ }
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_show_short_audio_desc(dev, e->sad + i);
+}
+EXPORT_SYMBOL_GPL(snd_show_eld);
+
+#ifdef CONFIG_SND_PROC_FS
+static void hdmi_print_sad_info(int i, struct snd_cea_sad *a,
+ struct snd_info_buffer *buffer)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+ snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+ i, a->format, cea_audio_coding_type_names[a->format]);
+ snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+ 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) {
+ eld_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
+ i, a->sample_bits, buf);
+ }
+
+ if (a->max_bitrate)
+ snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+ i, a->max_bitrate);
+
+ if (a->profile)
+ snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+void snd_print_eld_info(struct snd_parsed_hdmi_eld *e,
+ struct snd_info_buffer *buffer)
+{
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+ int i;
+ static const char * const eld_version_names[32] = {
+ "reserved",
+ "reserved",
+ "CEA-861D or below",
+ [3 ... 30] = "reserved",
+ [31] = "partial"
+ };
+ static const char * const cea_edid_version_names[8] = {
+ "no CEA EDID Timing Extension block present",
+ "CEA-861",
+ "CEA-861-A",
+ "CEA-861-B, C or D",
+ [4 ... 7] = "reserved"
+ };
+
+ 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_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);
+ snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+ snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+ snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+ 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_eld_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);
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+EXPORT_SYMBOL_GPL(snd_print_eld_info);
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c
index 5e6aed64f451..7a1b816f67cc 100644
--- a/sound/core/pcm_iec958.c
+++ b/sound/core/pcm_iec958.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM DRM helpers
- *
- * 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/export.h>
#include <linux/types.h>
@@ -12,41 +9,85 @@
#include <sound/pcm_params.h>
#include <sound/pcm_iec958.h>
-static int create_iec958_consumer(uint rate, uint sample_width,
- u8 *cs, size_t len)
+/**
+ * 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)
{
- unsigned int fs, ws;
-
if (len < 4)
return -EINVAL;
- 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:
+ 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) {
+ 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;
@@ -67,20 +108,57 @@ static int create_iec958_consumer(uint rate, uint sample_width,
default:
return -EINVAL;
}
- }
- memset(cs, 0, len);
+ cs[4] &= ~IEC958_AES4_CON_WORDLEN;
+ cs[4] |= ws;
+ }
- 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 | fs;
+ return len;
+}
- if (len > 4)
- cs[4] = ws;
+/**
+ * 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);
- return len;
+/**
+ * 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
@@ -98,15 +176,19 @@ static int create_iec958_consumer(uint rate, uint sample_width,
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{
- return create_iec958_consumer(runtime->rate,
- snd_pcm_format_width(runtime->format),
- cs, 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
- * @hw_params: the hw_params instance for extracting rate and sample format
+ * @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
*
@@ -120,7 +202,12 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
- return create_iec958_consumer(params_rate(params), params_width(params),
- cs, 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 a83152e7d387..6eaa950504cf 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1,23 +1,8 @@
+// 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>
@@ -48,6 +33,25 @@
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
@@ -64,63 +68,70 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
int err;
if (runtime->silence_size < runtime->boundary) {
- snd_pcm_sframes_t noise_dist, n;
+ snd_pcm_sframes_t noise_dist;
snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
- if (runtime->silence_start != appl_ptr) {
- n = 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 = appl_ptr;
- }
- if (runtime->silence_filled >= runtime->buffer_size)
- return;
- noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+ 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;
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);
}
#ifdef CONFIG_SND_DEBUG
@@ -153,13 +164,19 @@ EXPORT_SYMBOL(snd_pcm_debug_name);
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;
trace_xrun(substream);
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ 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];
@@ -167,6 +184,9 @@ static void xrun(struct snd_pcm_substream *substream)
pcm_warn(substream->pcm, "XRUN: %s\n", name);
dump_stack_on_xrun(substream);
}
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ substream->xrun_counter++;
+#endif
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -191,20 +211,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;
}
}
@@ -217,12 +234,12 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
}
static void update_audio_tstamp(struct snd_pcm_substream *substream,
- struct timespec *curr_tstamp,
- struct timespec *audio_tstamp)
+ struct timespec64 *curr_tstamp,
+ struct timespec64 *audio_tstamp)
{
struct snd_pcm_runtime *runtime = substream->runtime;
u64 audio_frames, audio_nsecs;
- struct timespec driver_tstamp;
+ struct timespec64 driver_tstamp;
if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
return;
@@ -246,18 +263,23 @@ static void update_audio_tstamp(struct snd_pcm_substream *substream,
}
audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate);
- *audio_tstamp = ns_to_timespec(audio_nsecs);
+ *audio_tstamp = ns_to_timespec64(audio_nsecs);
}
- if (!timespec_equal(&runtime->status->audio_tstamp, audio_tstamp)) {
- runtime->status->audio_tstamp = *audio_tstamp;
- runtime->status->tstamp = *curr_tstamp;
+
+ 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, (struct timespec *)&driver_tstamp);
+ snd_pcm_gettime(substream->runtime, &driver_tstamp);
runtime->driver_tstamp = driver_tstamp;
}
@@ -270,8 +292,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta;
unsigned long curr_jiffies;
- struct timespec curr_tstamp;
- struct timespec audio_tstamp;
+ struct timespec64 curr_tstamp;
+ struct timespec64 audio_tstamp;
int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr;
@@ -294,13 +316,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* 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, (struct timespec *)&curr_tstamp);
+ snd_pcm_gettime(runtime, &curr_tstamp);
} else
- snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
+ 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) {
@@ -440,6 +462,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
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;
}
@@ -496,21 +519,38 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
EXPORT_SYMBOL(snd_pcm_set_ops);
/**
- * snd_pcm_sync - set the PCM sync id
+ * snd_pcm_set_sync_per_card - set the PCM sync id with card number
* @substream: the pcm substream
+ * @params: modified hardware parameters
+ * @id: identifier (max 12 bytes)
+ * @len: identifier length (max 12 bytes)
+ *
+ * Sets the PCM sync identifier for the card with zero padding.
+ *
+ * User space or any user should use this 16-byte identifier for a comparison only
+ * to check if two IDs are similar or different. Special case is the identifier
+ * containing only zeros. Interpretation for this combination is - empty (not set).
+ * The contents of the identifier should not be interpreted in any other way.
*
- * Sets the PCM sync identifier for the card.
+ * The synchronization ID must be unique per clock source (usually one sound card,
+ * but multiple soundcard may use one PCM word clock source which means that they
+ * are fully synchronized).
+ *
+ * This routine composes this ID using card number in first four bytes and
+ * 12-byte additional ID. When other ID composition is used (e.g. for multiple
+ * sound cards), make sure that the composition does not clash with this
+ * composition scheme.
*/
-void snd_pcm_set_sync(struct snd_pcm_substream *substream)
+void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ const unsigned char *id, unsigned int len)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->sync.id32[0] = substream->pcm->card->number;
- runtime->sync.id32[1] = -1;
- runtime->sync.id32[2] = -1;
- runtime->sync.id32[3] = -1;
+ *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number);
+ len = min(12, len);
+ memcpy(params->sync + 4, id, len);
+ memset(params->sync + 4 + len, 0, 12 - len);
}
-EXPORT_SYMBOL(snd_pcm_set_sync);
+EXPORT_SYMBOL_GPL(snd_pcm_set_sync_per_card);
/*
* Standard ioctl routine
@@ -629,27 +669,33 @@ 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;
}
@@ -1129,16 +1175,12 @@ 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);
+ 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;
}
@@ -1457,7 +1499,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
- static 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,
@@ -1684,12 +1726,46 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
}
EXPORT_SYMBOL(snd_pcm_hw_param_last);
+/**
+ * snd_pcm_hw_params_bits - Get the number of bits per the sample.
+ * @p: hardware parameters
+ *
+ * Return: The number of bits per sample based on the format,
+ * subformat and msbits the specified hw params has.
+ */
+int snd_pcm_hw_params_bits(const struct snd_pcm_hw_params *p)
+{
+ snd_pcm_subformat_t subformat = params_subformat(p);
+ snd_pcm_format_t format = params_format(p);
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ switch (subformat) {
+ case SNDRV_PCM_SUBFORMAT_MSBITS_20:
+ return 20;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_24:
+ return 24;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_MAX:
+ case SNDRV_PCM_SUBFORMAT_STD:
+ default:
+ break;
+ }
+ fallthrough;
+ default:
+ return snd_pcm_format_width(format);
+ }
+}
+EXPORT_SYMBOL(snd_pcm_hw_params_bits);
+
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- snd_pcm_stream_lock_irqsave(substream, flags);
+
+ guard(pcm_stream_lock_irqsave)(substream);
if (snd_pcm_running(substream) &&
snd_pcm_update_hw_ptr(substream) >= 0)
runtime->status->hw_ptr %= runtime->buffer_size;
@@ -1697,7 +1773,6 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
runtime->status->hw_ptr = 0;
runtime->hw_ptr_wrap = 0;
}
- snd_pcm_stream_unlock_irqrestore(substream, flags);
return 0;
}
@@ -1750,11 +1825,23 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
channels = params_channels(params);
frame_size = snd_pcm_format_size(format, channels);
if (frame_size > 0)
- params->fifo_size /= (unsigned)frame_size;
+ params->fifo_size /= frame_size;
}
return 0;
}
+static int snd_pcm_lib_ioctl_sync_id(struct snd_pcm_substream *substream,
+ void *arg)
+{
+ static const unsigned char id[12] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff };
+
+ if (substream->runtime->std_sync_id)
+ snd_pcm_set_sync_per_card(substream, arg, id, sizeof(id));
+ return 0;
+}
+
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance
@@ -1776,32 +1863,48 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
return snd_pcm_lib_ioctl_channel_info(substream, arg);
case SNDRV_PCM_IOCTL1_FIFO_SIZE:
return snd_pcm_lib_ioctl_fifo_size(substream, arg);
+ case SNDRV_PCM_IOCTL1_SYNC_ID:
+ return snd_pcm_lib_ioctl_sync_id(substream, arg);
}
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;
- snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
@@ -1811,8 +1914,29 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
snd_timer_interrupt(substream->timer, 1);
#endif
_end:
- kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
- snd_pcm_stream_unlock_irqrestore(substream, flags);
+ 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)
+{
+ if (snd_BUG_ON(!substream))
+ return;
+
+ guard(pcm_stream_lock_irqsave)(substream);
+ snd_pcm_period_elapsed_under_stream_lock(substream);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
@@ -1839,12 +1963,18 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
if (runtime->no_period_wakeup)
wait_time = MAX_SCHEDULE_TIMEOUT;
else {
- wait_time = 10;
- if (runtime->rate) {
- long t = runtime->period_size * 2 / runtime->rate;
- wait_time = max(t, wait_time);
+ /* 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 * 1000);
+ wait_time = msecs_to_jiffies(wait_time);
}
for (;;) {
@@ -1860,10 +1990,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
* This check must happen after been added to the waitqueue
* and having current state be INTERRUPTIBLE.
*/
- if (is_playback)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
+ avail = snd_pcm_avail(substream);
if (avail >= runtime->twake)
break;
snd_pcm_stream_unlock_irq(substream);
@@ -1872,7 +1999,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
snd_pcm_stream_lock_irq(substream);
set_current_state(TASK_INTERRUPTIBLE);
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _endloop;
@@ -1895,8 +2022,8 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
}
if (!tout) {
pcm_dbg(substream->pcm,
- "%s write error (DMA or IRQ trouble?)\n",
- is_playback ? "playback" : "capture");
+ "%s timeout (DMA or IRQ trouble?)\n",
+ is_playback ? "playback write" : "capture read");
err = -EIO;
break;
}
@@ -1910,10 +2037,11 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes);
+ struct iov_iter *iter, 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);
+ snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f,
+ bool);
/* calculate the target DMA-buffer position to be written/read */
static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
@@ -1923,32 +2051,24 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
channel * (runtime->dma_bytes / runtime->channels);
}
-/* default copy_user ops for write; used for both interleaved and non- modes */
+/* default copy 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)
+ struct iov_iter *iter, unsigned long bytes)
{
- if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
- (void __user *)buf, bytes))
+ if (copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter) != 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;
-}
-
/* 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)
+ unsigned long hwoff, struct iov_iter *iter,
+ unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1964,25 +2084,41 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel,
return 0;
}
-/* default copy_user ops for read; used for both interleaved and non- modes */
+/* default copy 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)
+ struct iov_iter *iter, unsigned long bytes)
{
- if (copy_to_user((void __user *)buf,
- get_dma_ptr(substream->runtime, channel, hwoff),
- bytes))
+ if (copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter) != bytes)
return -EFAULT;
return 0;
}
-/* 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)
+/* call transfer with the filled iov_iter */
+static int do_transfer(struct snd_pcm_substream *substream, int c,
+ unsigned long hwoff, void *data, unsigned long bytes,
+ pcm_transfer_f transfer, bool in_kernel)
{
- memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes);
- return 0;
+ struct iov_iter iter;
+ int err, type;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ type = ITER_SOURCE;
+ else
+ type = ITER_DEST;
+
+ if (in_kernel) {
+ struct kvec kvec = { data, bytes };
+
+ iov_iter_kvec(&iter, type, &kvec, 1, bytes);
+ return transfer(substream, c, hwoff, &iter, bytes);
+ }
+
+ err = import_ubuf(type, (__force void __user *)data, bytes, &iter);
+ if (err)
+ return err;
+ return transfer(substream, c, hwoff, &iter, bytes);
}
/* call transfer function with the converted pointers and sizes;
@@ -1992,7 +2128,8 @@ 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)
+ pcm_transfer_f transfer,
+ bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2000,7 +2137,9 @@ static int interleaved_copy(struct snd_pcm_substream *substream,
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);
+
+ return do_transfer(substream, 0, hwoff, data + off, frames, transfer,
+ in_kernel);
}
/* call transfer function with the converted pointers and sizes for each
@@ -2010,7 +2149,8 @@ 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)
+ pcm_transfer_f transfer,
+ bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int channels = runtime->channels;
@@ -2028,8 +2168,8 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream,
if (!data || !*bufs)
err = fill_silence(substream, c, hwoff, NULL, frames);
else
- err = transfer(substream, c, hwoff, *bufs + off,
- frames);
+ err = do_transfer(substream, c, hwoff, *bufs + off,
+ frames, transfer, in_kernel);
if (err < 0)
return err;
}
@@ -2045,10 +2185,10 @@ static int fill_silence_frames(struct snd_pcm_substream *substream,
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);
+ fill_silence, true);
else
return noninterleaved_copy(substream, off, NULL, 0, frames,
- fill_silence);
+ fill_silence, true);
}
/* sanity-check for read/write methods */
@@ -2058,16 +2198,16 @@ 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_user && !runtime->dma_area))
+ if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
return -EINVAL;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
return 0;
}
static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
{
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PAUSED:
@@ -2089,16 +2229,35 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
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;
+ }
+ }
+
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;
}
}
@@ -2144,15 +2303,9 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
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;
+ if (substream->ops->copy)
+ transfer = substream->ops->copy;
else
transfer = is_playback ?
default_write_copy : default_read_copy;
@@ -2168,27 +2321,30 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
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->status->state == SNDRV_PCM_STATE_PREPARED &&
+ runtime->state == SNDRV_PCM_STATE_PREPARED &&
size >= runtime->start_threshold) {
err = snd_pcm_start(substream);
if (err < 0)
goto _end_unlock;
}
- runtime->twake = runtime->control->avail_min ? : 1;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- if (is_playback)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
+ avail = snd_pcm_avail(substream);
+
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
snd_pcm_uframes_t cont;
if (!avail) {
if (!is_playback &&
- runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ runtime->state == SNDRV_PCM_STATE_DRAINING) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock;
}
@@ -2211,14 +2367,22 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
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;
}
snd_pcm_stream_unlock_irq(substream);
+ if (!is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
err = writer(substream, appl_ofs, data, offset, frames,
- transfer);
+ transfer, in_kernel);
+ 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;
err = pcm_accessible_state(runtime);
@@ -2236,7 +2400,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
xfer += frames;
avail -= frames;
if (is_playback &&
- runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
+ 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)
@@ -2313,7 +2477,6 @@ static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 0;
uinfo->count = info->max_channels;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = SNDRV_CHMAP_LAST;
@@ -2337,7 +2500,7 @@ static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
if (!substream)
return -ENODEV;
memset(ucontrol->value.integer.value, 0,
- sizeof(ucontrol->value.integer.value));
+ sizeof(long) * info->max_channels);
if (!substream->runtime)
return 0; /* no channels set */
for (map = info->chmap; map->channels; map++) {
@@ -2427,6 +2590,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
.info = pcm_chmap_ctl_info,
diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h
index 16f254732b2a..ecb21697ae3a 100644
--- a/sound/core/pcm_local.h
+++ b/sound/core/pcm_local.h
@@ -1,9 +1,8 @@
+/* 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef __SOUND_CORE_PCM_LOCAL_H
@@ -21,9 +20,6 @@ void snd_interval_muldivk(const struct snd_interval *a,
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_constraints_init(struct snd_pcm_substream *substream);
-int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream);
-
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime,
snd_pcm_hw_param_t var, u_int32_t mask);
@@ -36,6 +32,24 @@ 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);
@@ -47,4 +61,23 @@ 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 ae33e456708c..56725d36825b 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.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/io.h>
@@ -24,12 +9,12 @@
#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);
@@ -41,6 +26,68 @@ 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 void __update_allocated_size(struct snd_card *card, ssize_t bytes)
+{
+ card->total_pcm_alloc_bytes += bytes;
+}
+
+static void update_allocated_size(struct snd_card *card, ssize_t bytes)
+{
+ guard(mutex)(&card->memory_mutex);
+ __update_allocated_size(card, bytes);
+}
+
+static void decrease_allocated_size(struct snd_card *card, size_t bytes)
+{
+ guard(mutex)(&card->memory_mutex);
+ WARN_ON(card->total_pcm_alloc_bytes < bytes);
+ __update_allocated_size(card, -(ssize_t)bytes);
+}
+
+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;
+
+ /* check and reserve the requested size */
+ scoped_guard(mutex, &card->memory_mutex) {
+ if (max_alloc_per_card &&
+ card->total_pcm_alloc_bytes + size > max_alloc_per_card)
+ return -ENOMEM;
+ __update_allocated_size(card, size);
+ }
+
+ 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) {
+ /* the actual allocation size might be bigger than requested,
+ * and we need to correct the account
+ */
+ if (dmab->bytes != size)
+ update_allocated_size(card, dmab->bytes - size);
+ } else {
+ /* take back on allocation failure */
+ decrease_allocated_size(card, size);
+ }
+ return err;
+}
+
+static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
+{
+ if (!dmab->area)
+ return;
+ decrease_allocated_size(card, dmab->bytes);
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+}
/*
* try to allocate as the large pages as possible.
@@ -48,19 +95,21 @@ 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;
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 */
@@ -68,18 +117,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz
substream->pcm->card->number, substream->pcm->device,
substream->stream ? 'c' : 'p', substream->number,
substream->pcm->name, orig_size);
- 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;
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
+ return -ENOMEM;
}
/**
@@ -87,19 +125,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.
- *
- * Return: 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);
}
/**
@@ -107,18 +136,14 @@ 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.
- *
- * Return: 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);
@@ -156,17 +181,22 @@ 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;
+ unsigned long size;
struct snd_dma_buffer new_dmab;
+ guard(mutex)(&substream->pcm->open_mutex);
if (substream->runtime) {
buffer->error = -EBUSY;
return;
}
if (!snd_info_get_line(buffer, line, sizeof(line))) {
snd_info_get_str(str, line, sizeof(str));
- size = simple_strtoul(str, NULL, 10) * 1024;
+ buffer->error = kstrtoul(str, 10, &size);
+ if (buffer->error != 0)
+ return;
+ size *= 1024;
if ((size != 0 && size < 8192) || size > substream->dma_max) {
buffer->error = -EINVAL;
return;
@@ -176,10 +206,16 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
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;
+ pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %lu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
return;
}
substream->buffer_bytes_max = size;
@@ -187,7 +223,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
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;
@@ -198,49 +234,80 @@ 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;
+
+ substream->dma_buffer.dev.type = type;
+ substream->dma_buffer.dev.dev = data;
- if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
- preallocate_pcm_pages(substream, size);
+ 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
@@ -251,16 +318,12 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation for the given DMA buffer type.
- *
- * Return: 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);
@@ -274,45 +337,74 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
*
* Do pre-allocation to all substreams of the given pcm for the
* specified DMA type.
- *
- * Return: 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().
*
- * Used as the page callback of PCM ops.
+ * 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.
*
- * Return: The page struct at the given buffer offset. %NULL on failure.
+ * Upon successful buffer allocation and setup, the function returns 0.
+ *
+ * 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);
+ return preallocate_pages(substream, type, data, size, max, true);
+}
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer);
- unsigned int idx = offset >> PAGE_SHIFT;
- if (idx >= (unsigned int)sgbuf->pages)
- return NULL;
- return sgbuf->page_table[idx];
+/**
+ * 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
+ */
+int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
+ struct device *data,
+ size_t size, size_t max)
+{
+ return preallocate_pages_for_all(pcm, type, data, size, max, true);
}
-EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-#endif /* CONFIG_SND_DMA_SGBUF */
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
@@ -327,6 +419,7 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
*/
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;
@@ -336,6 +429,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
@@ -351,14 +445,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 allocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
return -ENOMEM;
}
}
@@ -386,69 +489,13 @@ 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,
- size_t size, gfp_t gfp_flags)
-{
- struct snd_pcm_runtime *runtime;
-
- if (PCM_RUNTIME_CHECK(substream))
- return -EINVAL;
- runtime = substream->runtime;
- if (runtime->dma_area) {
- if (runtime->dma_bytes >= size)
- return 0; /* already large enough */
- vfree(runtime->dma_area);
- }
- runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
- if (!runtime->dma_area)
- return -ENOMEM;
- runtime->dma_bytes = size;
- return 1;
-}
-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)
-{
- struct snd_pcm_runtime *runtime;
-
- if (PCM_RUNTIME_CHECK(substream))
- return -EINVAL;
- runtime = substream->runtime;
- vfree(runtime->dma_area);
- runtime->dma_area = NULL;
- return 0;
-}
-EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
-
-/**
- * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
- * @substream: the substream with a buffer allocated by
- * snd_pcm_lib_alloc_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)
-{
- return vmalloc_to_page(substream->runtime->dma_area + offset);
-}
-EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index c4eb561d2008..71eec32a7a0a 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -42,7 +42,12 @@ struct pcm_format_data {
/* we do lots of calculations on snd_pcm_format_t; shut up sparse */
#define INT __force int
-static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
+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 = {},
@@ -259,9 +264,10 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
int snd_pcm_format_signed(snd_pcm_format_t format)
{
int val;
- if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[(INT)format].signd) < 0)
+ val = pcm_formats[(INT)format].signd;
+ if (val < 0)
return -EINVAL;
return val;
}
@@ -307,9 +313,10 @@ EXPORT_SYMBOL(snd_pcm_format_linear);
int snd_pcm_format_little_endian(snd_pcm_format_t format)
{
int val;
- if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[(INT)format].le) < 0)
+ val = pcm_formats[(INT)format].le;
+ if (val < 0)
return -EINVAL;
return val;
}
@@ -343,9 +350,10 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian);
int snd_pcm_format_width(snd_pcm_format_t format)
{
int val;
- if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[(INT)format].width) == 0)
+ val = pcm_formats[(INT)format].width;
+ if (!val)
return -EINVAL;
return val;
}
@@ -361,9 +369,10 @@ EXPORT_SYMBOL(snd_pcm_format_width);
int snd_pcm_format_physical_width(snd_pcm_format_t format)
{
int val;
- if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[(INT)format].phys) == 0)
+ val = pcm_formats[(INT)format].phys;
+ if (!val)
return -EINVAL;
return val;
}
@@ -394,7 +403,7 @@ EXPORT_SYMBOL(snd_pcm_format_size);
*/
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
{
- if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return NULL;
if (! pcm_formats[(INT)format].phys)
return NULL;
@@ -415,16 +424,17 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64);
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 ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
if (samples == 0)
return 0;
width = pcm_formats[(INT)format].phys; /* physical width */
- pat = pcm_formats[(INT)format].silence;
- if (! width)
+ if (!width)
return -EINVAL;
+ pat = pcm_formats[(INT)format].silence;
/* signed or 1 byte data */
if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
unsigned int bytes = samples * width / 8;
@@ -473,32 +483,34 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
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.
*
* 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;
+ unsigned int rmin, rmax;
+
+ rmin = UINT_MAX;
+ rmax = 0;
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];
- 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];
- break;
+ if (hw->rates & (1 << i)) {
+ rmin = min(rmin, snd_pcm_known_rates.list[i]);
+ rmax = max(rmax, snd_pcm_known_rates.list[i]);
}
}
+ if (rmin > rmax)
+ return -EINVAL;
+ hw->rate_min = rmin;
+ hw->rate_max = rmax;
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
@@ -574,33 +586,3 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int 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 51104df924e1..68bee40c9ada 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1,24 +1,10 @@
+// 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>
@@ -28,6 +14,7 @@
#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>
@@ -36,6 +23,8 @@
#include <sound/timer.h>
#include <sound/minors.h>
#include <linux/uio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
#include "pcm_local.h"
@@ -84,21 +73,36 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
*
*/
-static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem);
-/* Writer in rwsem may block readers even during its waiting in queue,
- * and this may lead to a deadlock when the code path takes read sem
- * twice (e.g. one in snd_pcm_action_nonatomic() and another in
- * snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to
- * spin until it gets the lock.
- */
-static inline void down_write_nonblock(struct rw_semaphore *lock)
+void snd_pcm_group_init(struct snd_pcm_group *group)
{
- while (!down_write_trylock(lock))
- cond_resched();
+ 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, bh_lock, bh_unlock, mutex_action) \
+static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
+{ \
+ if (nonatomic) { \
+ mutex_ ## mutex_action(&group->mutex); \
+ } else { \
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && bh_lock) \
+ local_bh_disable(); \
+ spin_ ## action(&group->lock); \
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && bh_unlock) \
+ local_bh_enable(); \
+ } \
}
+DEFINE_PCM_GROUP_LOCK(lock, false, false, lock);
+DEFINE_PCM_GROUP_LOCK(unlock, false, false, unlock);
+DEFINE_PCM_GROUP_LOCK(lock_irq, true, false, lock);
+DEFINE_PCM_GROUP_LOCK(unlock_irq, false, true, unlock);
+
/**
* snd_pcm_stream_lock - Lock the PCM stream
* @substream: PCM substream
@@ -109,31 +113,19 @@ static inline void down_write_nonblock(struct rw_semaphore *lock)
*/
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
- if (substream->pcm->nonatomic) {
- down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
- mutex_lock(&substream->self_group.mutex);
- } else {
- read_lock(&snd_pcm_link_rwlock);
- spin_lock(&substream->self_group.lock);
- }
+ snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
/**
- * snd_pcm_stream_lock - Unlock the PCM stream
+ * 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)
{
- if (substream->pcm->nonatomic) {
- mutex_unlock(&substream->self_group.mutex);
- up_read(&snd_pcm_link_rwsem);
- } else {
- spin_unlock(&substream->self_group.lock);
- read_unlock(&snd_pcm_link_rwlock);
- }
+ snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
@@ -147,12 +139,21 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
*/
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{
- if (!substream->pcm->nonatomic)
- local_irq_disable();
- snd_pcm_stream_lock(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)
+{
+ 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);
+}
+
/**
* snd_pcm_stream_unlock_irq - Unlock the PCM stream
* @substream: PCM substream
@@ -161,22 +162,35 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
*/
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
- snd_pcm_stream_unlock(substream);
- if (!substream->pcm->nonatomic)
- local_irq_enable();
+ 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)
- local_irq_save(flags);
- snd_pcm_stream_lock(substream);
+ 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
@@ -187,12 +201,23 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags)
{
- snd_pcm_stream_unlock(substream);
- if (!substream->pcm->nonatomic)
- local_irq_restore(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 *pcm = substream->pcm;
@@ -203,13 +228,13 @@ 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));
+ strscpy(info->subname, substream->name, sizeof(info->subname));
return 0;
}
@@ -217,7 +242,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
int snd_pcm_info_user(struct snd_pcm_substream *substream,
struct snd_pcm_info __user * _info)
{
- struct snd_pcm_info *info;
+ struct snd_pcm_info *info __free(kfree) = NULL;
int err;
info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -228,21 +253,37 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
if (copy_to_user(_info, info, sizeof(*info)))
err = -EFAULT;
}
- kfree(info);
return err;
}
+/* macro for simplified cast */
+#define PARAM_MASK_BIT(b) (1U << (__force int)(b))
+
static bool hw_support_mmap(struct snd_pcm_substream *substream)
{
+ struct snd_dma_buffer *dmabuf;
+
if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
return false;
- /* architecture supports dma_mmap_coherent()? */
-#if defined(CONFIG_ARCH_NO_COHERENT_DMA_MMAP) || !defined(CONFIG_HAS_DMA)
- if (!substream->ops->mmap &&
- substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return false;
-#endif
- return true;
+
+ 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,
@@ -252,7 +293,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
&substream->runtime->hw_constraints;
struct snd_mask *m;
unsigned int k;
- struct snd_mask old_mask;
+ 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++) {
@@ -261,7 +302,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* This parameter is not requested to change by a caller. */
- if (!(params->rmask & (1 << k)))
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
if (trace_hw_mask_param_enabled())
@@ -275,7 +316,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
/* Set corresponding flag so that the caller gets it. */
trace_hw_mask_param(substream, k, 0, &old_mask, m);
- params->cmask |= 1 << k;
+ params->cmask |= PARAM_MASK_BIT(k);
}
return 0;
@@ -288,7 +329,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
&substream->runtime->hw_constraints;
struct snd_interval *i;
unsigned int k;
- struct snd_interval old_interval;
+ 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++) {
@@ -297,7 +338,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* This parameter is not requested to change by a caller. */
- if (!(params->rmask & (1 << k)))
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
if (trace_hw_interval_param_enabled())
@@ -311,7 +352,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
/* Set corresponding flag so that the caller gets it. */
trace_hw_interval_param(substream, k, 0, &old_interval, i);
- params->cmask |= 1 << k;
+ params->cmask |= PARAM_MASK_BIT(k);
}
return 0;
@@ -323,15 +364,15 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
struct snd_pcm_hw_constraints *constrs =
&substream->runtime->hw_constraints;
unsigned int k;
- unsigned int rstamps[constrs->rules_num];
+ unsigned int *rstamps __free(kfree) = NULL;
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;
- struct snd_interval old_interval;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
bool again;
- int changed;
+ int changed, err = 0;
/*
* Each application of rule has own sequence number.
@@ -339,8 +380,9 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
* Each member of 'rstamps' array represents the sequence number of
* recent application of corresponding rule.
*/
- for (k = 0; k < constrs->rules_num; k++)
- rstamps[k] = 0;
+ rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
+ if (!rstamps)
+ return -ENOMEM;
/*
* Each member of 'vstamps' array represents the sequence number of
@@ -352,7 +394,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
* 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 & (1 << k)) ? 1 : 0;
+ vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0;
/* Due to the above design, actual sequence number starts at 2. */
stamp = 2;
@@ -372,8 +414,8 @@ retry:
continue;
/*
- * The 'deps' array includes maximum three dependencies
- * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth
+ * 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.
*
@@ -418,7 +460,7 @@ retry:
hw_param_interval(params, r->var));
}
- params->cmask |= (1 << r->var);
+ params->cmask |= PARAM_MASK_BIT(r->var);
vstamps[r->var] = stamp;
again = true;
}
@@ -430,7 +472,7 @@ retry:
if (again)
goto retry;
- return 0;
+ return err;
}
static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
@@ -438,12 +480,34 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
{
const struct snd_interval *i;
const struct snd_mask *m;
+ struct snd_mask *m_rw;
int err;
if (!params->msbits) {
i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
if (snd_interval_single(i))
params->msbits = snd_interval_value(i);
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+ params->msbits = snd_pcm_format_width(format);
+ }
+ }
+
+ if (params->msbits) {
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+
+ if (snd_pcm_format_linear(format) &&
+ snd_pcm_format_width(format) != params->msbits) {
+ m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ snd_mask_reset(m_rw,
+ (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ if (snd_mask_empty(m_rw))
+ return -EINVAL;
+ }
+ }
}
if (!params->rate_den) {
@@ -458,8 +522,9 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
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 = substream->ops->ioctl(substream,
- SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_FIFO_SIZE,
+ params);
if (err < 0)
return err;
}
@@ -474,6 +539,12 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
SNDRV_PCM_INFO_MMAP_VALID);
}
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_SYNC_ID,
+ params);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -484,9 +555,9 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
params->info = 0;
params->fifo_size = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
params->msbits = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) {
params->rate_num = 0;
params->rate_den = 0;
}
@@ -512,7 +583,7 @@ EXPORT_SYMBOL(snd_pcm_hw_refine);
static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
params = memdup_user(_params, sizeof(*params));
@@ -521,17 +592,15 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
err = snd_pcm_hw_refine(substream, params);
if (err < 0)
- goto end;
+ return err;
err = fixup_unreferenced_params(substream, params);
if (err < 0)
- goto end;
+ return err;
if (copy_to_user(_params, params, sizeof(*params)))
- err = -EFAULT;
-end:
- kfree(params);
- return err;
+ return -EFAULT;
+ return 0;
}
static int period_to_usecs(struct snd_pcm_runtime *runtime)
@@ -549,12 +618,12 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
return usecs;
}
-static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
+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->status->state != SNDRV_PCM_STATE_DISCONNECTED)
- substream->runtime->status->state = state;
- snd_pcm_stream_unlock_irq(substream);
+ guard(pcm_stream_lock_irq)(substream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED)
+ __snd_pcm_set_state(substream->runtime, state);
}
static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
@@ -567,8 +636,19 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
#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_param_choose - choose a configuration defined by @params
+ * snd_pcm_hw_params_choose - choose a configuration defined by @params
* @pcm: PCM instance
* @params: the hw_params instance
*
@@ -594,8 +674,8 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
-1
};
const int *v;
- struct snd_mask old_mask;
- struct snd_interval old_interval;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
int changed;
for (v = vars; *v != -1; v++) {
@@ -612,7 +692,7 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
changed = snd_pcm_hw_param_first(pcm, params, *v, NULL);
else
changed = snd_pcm_hw_param_last(pcm, params, *v, NULL);
- if (snd_BUG_ON(changed < 0))
+ if (changed < 0)
return changed;
if (changed == 0)
continue;
@@ -631,6 +711,41 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
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);
+}
+
+/* fill the PCM buffer with the current silence format; called from pcm_oss.c */
+void snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_buffer_access_lock(runtime);
+ if (runtime->dma_area)
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+ bytes_to_samples(runtime, runtime->dma_bytes));
+ snd_pcm_buffer_access_unlock(runtime);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_runtime_buffer_set_silence);
+
+#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)
{
@@ -642,22 +757,27 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_OPEN:
- case SNDRV_PCM_STATE_SETUP:
- case SNDRV_PCM_STATE_PREPARED:
- break;
- default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ err = snd_pcm_buffer_access_lock(runtime);
+ if (err < 0)
+ return err;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ 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:
+ err = -EBADFD;
+ break;
+ }
}
- snd_pcm_stream_unlock_irq(substream);
-#if IS_ENABLED(CONFIG_SND_PCM_OSS)
- 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);
@@ -672,6 +792,14 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
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)
@@ -717,29 +845,46 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
+ /* clear the buffer for avoiding possible kernel info leaks */
+ if (runtime->dma_area && !substream->ops->copy) {
+ size_t size = runtime->dma_bytes;
+
+ 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 (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 (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 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 (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;
}
static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
params = memdup_user(_params, sizeof(*params));
@@ -748,15 +893,25 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
err = snd_pcm_hw_params(substream, params);
if (err < 0)
- goto end;
+ return err;
if (copy_to_user(_params, params, sizeof(*params)))
- err = -EFAULT;
-end:
- kfree(params);
+ return -EFAULT;
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;
@@ -765,22 +920,28 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_SETUP:
- case SNDRV_PCM_STATE_PREPARED:
- break;
- default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ result = snd_pcm_buffer_access_lock(runtime);
+ if (result < 0)
+ return result;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ if (atomic_read(&substream->mmap_count))
+ result = -EBADFD;
+ break;
+ default:
+ 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);
+ if (result)
+ goto unlock;
+ result = do_hw_free(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
+ unlock:
+ snd_pcm_buffer_access_unlock(runtime);
return result;
}
@@ -793,12 +954,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
if (params->tstamp_mode < 0 ||
params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
@@ -818,24 +977,24 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
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;
- runtime->stop_threshold = params->stop_threshold;
- runtime->silence_threshold = params->silence_threshold;
- runtime->silence_size = params->silence_size;
- params->boundary = runtime->boundary;
- if (snd_pcm_running(substream)) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- runtime->silence_size > 0)
- snd_pcm_playback_silence(substream, ULONG_MAX);
- err = snd_pcm_update_state(substream, runtime);
+ scoped_guard(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;
+ runtime->stop_threshold = params->stop_threshold;
+ runtime->silence_threshold = params->silence_threshold;
+ runtime->silence_size = params->silence_size;
+ params->boundary = runtime->boundary;
+ if (snd_pcm_running(substream)) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ runtime->silence_size > 0)
+ snd_pcm_playback_silence(substream, ULONG_MAX);
+ err = snd_pcm_update_state(substream, runtime);
+ }
}
- snd_pcm_stream_unlock_irq(substream);
return err;
}
@@ -852,12 +1011,24 @@ 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);
+ guard(pcm_stream_lock_irq)(substream);
snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
&runtime->audio_tstamp_config);
@@ -875,18 +1046,26 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
} else
runtime->audio_tstamp_report.valid = 1;
- status->state = runtime->status->state;
- status->suspended_state = runtime->status->suspended_state;
+ 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;
+ return 0;
+ 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->driver_tstamp = runtime->driver_tstamp;
- status->audio_tstamp =
- runtime->status->audio_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,
@@ -897,41 +1076,32 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
}
} else {
/* get tstamp only in fallback mode and only if enabled */
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, &status->tstamp);
+ 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;
+ }
}
_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;
runtime->overrange = 0;
- _end:
- snd_pcm_stream_unlock_irq(substream);
return 0;
}
-static int snd_pcm_status_user(struct snd_pcm_substream *substream,
- struct snd_pcm_status __user * _status,
- bool ext)
+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));
@@ -943,7 +1113,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&_status->audio_tstamp_data)))
return -EFAULT;
- res = snd_pcm_status(substream, &status);
+ res = snd_pcm_status64(substream, &status);
if (res < 0)
return res;
if (copy_to_user(_status, &status, sizeof(status)))
@@ -951,6 +1121,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)
{
@@ -959,17 +1178,15 @@ 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) {
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
if (channel >= runtime->channels)
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,
@@ -1003,11 +1220,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);
};
/*
@@ -1017,15 +1240,18 @@ struct action_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, depth = 1;
snd_pcm_group_for_each_entry(s, substream) {
- if (do_lock && s != substream) {
- if (s->pcm->nonatomic)
+ 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);
@@ -1053,18 +1279,18 @@ static int snd_pcm_action_group(const 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) {
- if (s1->pcm->nonatomic)
- mutex_unlock(&s1->self_group.mutex);
- else
- spin_unlock(&s1->self_group.lock);
- }
- if (s1 == s) /* end */
- break;
+ /* 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;
}
return res;
}
@@ -1074,7 +1300,7 @@ static int snd_pcm_action_group(const 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;
@@ -1089,35 +1315,83 @@ static int snd_pcm_action_single(const 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(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))
- return snd_pcm_action_single(ops, substream, state);
-
- if (substream->pcm->nonatomic) {
- if (!mutex_trylock(&substream->group->mutex)) {
- mutex_unlock(&substream->self_group.mutex);
- mutex_lock(&substream->group->mutex);
- mutex_lock(&substream->self_group.mutex);
- }
- res = snd_pcm_action_group(ops, substream, state, 1);
- mutex_unlock(&substream->group->mutex);
- } else {
- 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);
- }
+ 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;
}
@@ -1126,40 +1400,41 @@ static int snd_pcm_action(const 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;
-
- snd_pcm_stream_lock_irq(substream);
- res = snd_pcm_action(ops, substream, state);
- snd_pcm_stream_unlock_irq(substream);
- return res;
+ guard(pcm_stream_lock_irq)(substream);
+ return snd_pcm_action(ops, substream, state);
}
/*
*/
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;
- down_read(&snd_pcm_link_rwsem);
+ /* Guarantee the group members won't change during non-atomic action */
+ guard(rwsem_read)(&snd_pcm_link_rwsem);
+ res = snd_pcm_buffer_access_lock(substream->runtime);
+ if (res < 0)
+ return res;
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);
- up_read(&snd_pcm_link_rwsem);
+ snd_pcm_buffer_access_unlock(substream->runtime);
return res;
}
/*
* 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))
@@ -1169,27 +1444,38 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
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);
@@ -1226,29 +1512,34 @@ static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream)
/*
* 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);
- runtime->status->state = state;
+ __snd_pcm_set_state(runtime, state);
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
@@ -1283,7 +1574,7 @@ 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 succesful, or a negative error code.
+ * Return: Zero if successful, or a negative error code.
*/
int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
@@ -1302,71 +1593,71 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
*/
int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
{
- unsigned long flags;
- int ret = 0;
-
- snd_pcm_stream_lock_irqsave(substream, flags);
- if (snd_pcm_running(substream))
- ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock_irqrestore(substream, flags);
- return ret;
+ guard(pcm_stream_lock_irqsave)(substream);
+ if (substream->runtime && snd_pcm_running(substream))
+ __snd_pcm_xrun(substream);
+ 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)
- 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
* delta, effectively to skip the check once.
*/
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 (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;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_RUNNING);
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
@@ -1381,24 +1672,41 @@ static const 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)
@@ -1406,15 +1714,18 @@ 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);
- 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);
@@ -1426,29 +1737,20 @@ static const struct action_ops snd_pcm_action_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 @substream is %NULL), or a negative error
- * code.
+ * 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);
- snd_pcm_stream_unlock_irqrestore(substream, flags);
- return err;
+ guard(pcm_stream_lock_irqsave)(substream);
+ return snd_pcm_action(&snd_pcm_action_suspend, substream,
+ ACTION_ARG_IGNORE);
}
-EXPORT_SYMBOL(snd_pcm_suspend);
/**
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
@@ -1466,57 +1768,72 @@ 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->state != SNDRV_PCM_STATE_SUSPENDED)
+ return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
return -ENOSYS;
runtime->trigger_master = substream;
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);
- runtime->status->state = runtime->status->suspended_state;
+ __snd_pcm_set_state(runtime, runtime->suspended_state);
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
@@ -1529,7 +1846,8 @@ static const struct action_ops snd_pcm_action_resume = {
static int snd_pcm_resume(struct snd_pcm_substream *substream)
{
- return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream,
+ ACTION_ARG_IGNORE);
}
#else
@@ -1549,30 +1867,28 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
static int snd_pcm_xrun(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int result;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ guard(pcm_stream_lock_irq)(substream);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_XRUN:
- result = 0; /* already there */
- break;
+ return 0; /* already there */
case SNDRV_PCM_STATE_RUNNING:
- result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- break;
+ __snd_pcm_xrun(substream);
+ return 0;
default:
- result = -EBADFD;
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
- 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:
@@ -1583,12 +1899,14 @@ 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;
+ guard(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;
@@ -1597,9 +1915,11 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
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;
+ guard(pcm_stream_lock_irq)(substream);
runtime->control->appl_ptr = runtime->status->hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@@ -1614,19 +1934,22 @@ static const 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;
@@ -1634,16 +1957,19 @@ 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;
@@ -1673,29 +1999,32 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
else
f_flags = substream->f_flags;
- snd_pcm_stream_lock_irq(substream);
- switch (substream->runtime->status->state) {
- case SNDRV_PCM_STATE_PAUSED:
- snd_pcm_pause(substream, 0);
- /* fallthru */
- case SNDRV_PCM_STATE_SUSPENDED:
- snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
- break;
+ scoped_guard(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, f_flags);
+ 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)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_DISCONNECTED:
case SNDRV_PCM_STATE_SUSPENDED:
@@ -1705,40 +2034,43 @@ static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state
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 {
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ __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:
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ __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->status->state == SNDRV_PCM_STATE_DRAINING &&
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING &&
runtime->trigger_master == substream &&
(runtime->hw.info & SNDRV_PCM_INFO_DRAIN_TRIGGER))
return substream->ops->trigger(substream,
@@ -1747,7 +2079,8 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
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)
{
}
@@ -1757,8 +2090,6 @@ static const struct action_ops snd_pcm_action_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
@@ -1772,6 +2103,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
+ struct snd_pcm_group *group;
wait_queue_entry_t wait;
int result = 0;
int nonblock = 0;
@@ -1779,7 +2111,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
card = substream->pcm->card;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (file) {
@@ -1788,14 +2120,14 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
} 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 */
@@ -1813,45 +2145,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);
+ set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&to_check->sleep, &wait);
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
if (runtime->no_period_wakeup)
tout = MAX_SCHEDULE_TIMEOUT;
else {
- tout = 10;
+ tout = 100;
if (runtime->rate) {
- long t = runtime->period_size * 2 / runtime->rate;
+ long t = runtime->buffer_size * 1100 / runtime->rate;
tout = max(t, tout);
}
- tout = msecs_to_jiffies(tout * 1000);
+ tout = msecs_to_jiffies(tout);
}
- tout = schedule_timeout_interruptible(tout);
- down_read(&snd_pcm_link_rwsem);
+ 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 {
dev_dbg(substream->pcm->card->dev,
- "playback drain error (DMA or IRQ trouble?)\n");
+ "playback drain timeout (DMA or IRQ trouble?)\n");
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
result = -EIO;
}
@@ -1861,7 +2203,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
unlock:
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
return result;
}
@@ -1880,18 +2221,17 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- snd_pcm_stream_lock_irq(substream);
+ guard(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; */
- snd_pcm_stream_unlock_irq(substream);
return result;
}
@@ -1900,13 +2240,19 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
static bool is_pcm_file(struct file *file)
{
struct inode *inode = file_inode(file);
+ struct snd_pcm *pcm;
unsigned int minor;
if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
return false;
minor = iminor(inode);
- return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) ||
- snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+ 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;
}
/*
@@ -1914,92 +2260,90 @@ static bool is_pcm_file(struct file *file)
*/
static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
{
- int res = 0;
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1;
- struct snd_pcm_group *group;
- struct fd f = fdget(fd);
+ struct snd_pcm_group *group __free(kfree) = NULL;
+ struct snd_pcm_group *target_group;
+ bool nonatomic = substream->pcm->nonatomic;
+ CLASS(fd, f)(fd);
- if (!f.file)
+ if (fd_empty(f))
return -EBADFD;
- if (!is_pcm_file(f.file)) {
- res = -EBADFD;
- goto _badf;
- }
- pcm_file = f.file->private_data;
+ if (!is_pcm_file(fd_file(f)))
+ return -EBADFD;
+
+ pcm_file = fd_file(f)->private_data;
substream1 = pcm_file->substream;
- group = kmalloc(sizeof(*group), GFP_KERNEL);
- if (!group) {
- res = -ENOMEM;
- goto _nolock;
- }
- down_write_nonblock(&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 ||
- substream->pcm->nonatomic != substream1->pcm->nonatomic) {
- res = -EBADFD;
- goto _end;
- }
- if (snd_pcm_stream_linked(substream1)) {
- res = -EALREADY;
- goto _end;
- }
- if (!snd_pcm_stream_linked(substream)) {
- substream->group = group;
- group = NULL;
- spin_lock_init(&substream->group->lock);
- mutex_init(&substream->group->mutex);
- 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;
- _end:
- write_unlock_irq(&snd_pcm_link_rwlock);
- up_write(&snd_pcm_link_rwsem);
- _nolock:
- snd_card_unref(substream1->pcm->card);
- kfree(group);
- _badf:
- fdput(f);
- return res;
+
+ if (substream == substream1)
+ return -EINVAL;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+ snd_pcm_group_init(group);
+
+ guard(rwsem_write)(&snd_pcm_link_rwsem);
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN ||
+ substream->runtime->state != substream1->runtime->state ||
+ substream->pcm->nonatomic != substream1->pcm->nonatomic)
+ return -EBADFD;
+ if (snd_pcm_stream_linked(substream1))
+ return -EALREADY;
+
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (!snd_pcm_stream_linked(substream)) {
+ snd_pcm_group_assign(substream, group);
+ group = NULL; /* assigned, don't free this one below */
+ }
+ target_group = substream->group;
+ }
+
+ 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);
+ return 0;
}
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;
- int res = 0;
+ struct snd_pcm_group *group;
+ bool nonatomic = substream->pcm->nonatomic;
+ bool do_free = false;
+
+ guard(rwsem_write)(&snd_pcm_link_rwsem);
+
+ if (!snd_pcm_stream_linked(substream))
+ return -EALREADY;
+
+ group = substream->group;
+ snd_pcm_group_lock_irq(group, nonatomic);
- down_write_nonblock(&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);
- }
relink_to_local(substream);
- _end:
- write_unlock_irq(&snd_pcm_link_rwlock);
- up_write(&snd_pcm_link_rwsem);
- return res;
+ 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);
+ return 0;
}
/*
@@ -2046,21 +2390,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;
+ 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);
}
@@ -2069,14 +2413,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)
@@ -2090,13 +2435,17 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ||\
+ SNDRV_PCM_RATE_128000 != 1 << 19
#error "Change this table"
#endif
+/* NOTE: the list is unsorted! */
static const unsigned int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000
+ 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000,
+ /* extended */
+ 12000, 24000, 128000
};
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
@@ -2126,7 +2475,42 @@ 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_rule_subformats(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_mask *sfmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ u32 *subformats = rule->private;
+ snd_pcm_format_t f;
+ struct snd_mask m;
+
+ snd_mask_none(&m);
+ /* All PCMs support at least the default STD subformat. */
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD);
+
+ pcm_for_each_format(f) {
+ if (!snd_mask_test(fmask, (__force unsigned)f))
+ continue;
+
+ if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
+ m.bits[0] |= *subformats;
+ else if (snd_pcm_format_linear(f))
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ }
+
+ return snd_mask_refine(sfmask, &m);
+}
+
+static int snd_pcm_hw_constraint_subformats(struct snd_pcm_runtime *runtime,
+ unsigned int cond, u32 *subformats)
+{
+ return snd_pcm_hw_rule_add(runtime, cond, -1,
+ snd_pcm_hw_rule_subformats, (void *)subformats,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+}
+
+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;
@@ -2250,7 +2634,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;
@@ -2258,16 +2642,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;
+ 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)
@@ -2277,7 +2661,7 @@ 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_subformats(runtime, 0, &hw->subformats);
if (err < 0)
return err;
@@ -2335,7 +2719,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)
@@ -2346,14 +2731,13 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free &&
- substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- 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;
@@ -2383,7 +2767,8 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
goto error;
}
- if ((err = substream->ops->open(substream)) < 0)
+ err = substream->ops->open(substream);
+ if (err < 0)
goto error;
substream->hw_opened = 1;
@@ -2394,6 +2779,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
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;
@@ -2421,10 +2813,8 @@ static int snd_pcm_open_file(struct file *file,
return -ENOMEM;
}
pcm_file->substream = substream;
- if (substream->ref_count == 1) {
- substream->file = pcm_file;
+ if (substream->ref_count == 1)
substream->pcm_release = pcm_release_private;
- }
file->private_data = pcm_file;
return 0;
@@ -2526,10 +2916,14 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
if (snd_BUG_ON(!substream))
return -ENXIO;
pcm = substream->pcm;
- mutex_lock(&pcm->open_mutex);
- snd_pcm_release_substream(substream);
- kfree(pcm_file);
- mutex_unlock(&pcm->open_mutex);
+
+ /* block until the device gets woken up as it may touch the hardware */
+ snd_power_wait(pcm->card);
+
+ scoped_guard(mutex, &pcm->open_mutex) {
+ snd_pcm_release_substream(substream);
+ kfree(pcm_file);
+ }
wake_up(&pcm->open_wait);
module_put(pcm->card->module);
snd_card_file_remove(pcm->card, file);
@@ -2541,11 +2935,11 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
*/
static int do_pcm_hwsync(struct snd_pcm_substream *substream)
{
- switch (substream->runtime->status->state) {
+ switch (substream->runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return -EBADFD;
- /* Fall through */
+ fallthrough;
case SNDRV_PCM_STATE_RUNNING:
return snd_pcm_update_hw_ptr(substream);
case SNDRV_PCM_STATE_PREPARED:
@@ -2605,152 +2999,252 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
return ret < 0 ? 0 : frames;
}
-static snd_pcm_sframes_t snd_pcm_playback_rewind(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 ret;
if (frames == 0)
return 0;
- snd_pcm_stream_lock_irq(substream);
- ret = do_pcm_hwsync(substream);
- if (!ret)
- ret = rewind_appl_ptr(substream, frames,
- snd_pcm_playback_hw_avail(runtime));
- snd_pcm_stream_unlock_irq(substream);
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = rewind_appl_ptr(substream, frames,
+ snd_pcm_hw_avail(substream));
+ }
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static snd_pcm_sframes_t snd_pcm_capture_rewind(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 ret;
if (frames == 0)
return 0;
- snd_pcm_stream_lock_irq(substream);
- ret = do_pcm_hwsync(substream);
- if (!ret)
- ret = rewind_appl_ptr(substream, frames,
- snd_pcm_capture_hw_avail(runtime));
- snd_pcm_stream_unlock_irq(substream);
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = forward_appl_ptr(substream, frames,
+ snd_pcm_avail(substream));
+ }
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static int snd_pcm_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *delay)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t ret;
+ int err;
- if (frames == 0)
- return 0;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ err = do_pcm_hwsync(substream);
+ if (delay && !err)
+ *delay = snd_pcm_calc_delay(substream);
+ }
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
- snd_pcm_stream_lock_irq(substream);
- ret = do_pcm_hwsync(substream);
- if (!ret)
- ret = forward_appl_ptr(substream, frames,
- snd_pcm_playback_avail(runtime));
- snd_pcm_stream_unlock_irq(substream);
- return ret;
+ return err;
}
+
+static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_delay(substream, NULL);
+}
+
+#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (!user_read_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_get_user(__f, &__src->flags, failed); \
+ unsafe_get_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_get_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_read_access_end(); \
+failed_begin: \
+ __err; \
+})
+
+#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (!user_write_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_put_user(__s.state, &__src->s.status.state, failed); \
+ unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \
+ unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp.tv_nsec, failed); \
+ unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp.tv_nsec, failed);\
+ unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_write_access_end(); \
+failed_begin: \
+ __err; \
+})
-static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr __user *_sync_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t ret;
+ 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;
+ int err;
- if (frames == 0)
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, _sync_ptr))
+ return -EFAULT;
+ status = runtime->status;
+ control = runtime->control;
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr);
+ if (err < 0)
+ return err;
+ } else {
+ scontrol.appl_ptr = control->appl_ptr;
+ }
+ 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;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ }
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, _sync_ptr))
+ return -EFAULT;
+ return 0;
+}
+
+struct snd_pcm_mmap_status32 {
+ snd_pcm_state_t state;
+ s32 pad1;
+ u32 hw_ptr;
+ struct __snd_timespec tstamp;
+ snd_pcm_state_t suspended_state;
+ struct __snd_timespec audio_tstamp;
+} __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;
+} __packed;
+
+/* recalculate the boundary within 32bit */
+static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_uframes_t boundary;
+ snd_pcm_uframes_t border;
+ int order;
+
+ if (! runtime->buffer_size)
return 0;
- snd_pcm_stream_lock_irq(substream);
- ret = do_pcm_hwsync(substream);
- if (!ret)
- ret = forward_appl_ptr(substream, frames,
- snd_pcm_capture_avail(runtime));
- snd_pcm_stream_unlock_irq(substream);
- return ret;
-}
+ border = 0x7fffffffUL - runtime->buffer_size;
+ if (runtime->buffer_size > border)
+ return runtime->buffer_size;
-static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
-{
- int err;
+ order = __fls(border) - __fls(runtime->buffer_size);
+ boundary = runtime->buffer_size << order;
- snd_pcm_stream_lock_irq(substream);
- err = do_pcm_hwsync(substream);
- snd_pcm_stream_unlock_irq(substream);
- return err;
+ if (boundary <= border)
+ return boundary;
+ else
+ return boundary / 2;
}
-
-static snd_pcm_sframes_t snd_pcm_delay(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);
- err = do_pcm_hwsync(substream);
- if (!err) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- n = snd_pcm_playback_hw_avail(runtime);
- else
- n = snd_pcm_capture_avail(runtime);
- n += runtime->delay;
- }
- snd_pcm_stream_unlock_irq(substream);
- return err < 0 ? err : n;
-}
-
-static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
- struct snd_pcm_sync_ptr __user *_sync_ptr)
+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;
- struct snd_pcm_sync_ptr sync_ptr;
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;
- memset(&sync_ptr, 0, sizeof(sync_ptr));
- if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
+
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
return -EFAULT;
- if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
- return -EFAULT;
- status = runtime->status;
- control = runtime->control;
- if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ if (sflags & 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_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;
+ status = runtime->status;
+ control = runtime->control;
+ boundary = recalculate_boundary(runtime);
+ if (! boundary)
+ boundary = 0x7fffffff;
+ scoped_guard(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)
+ 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;
}
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
- control->avail_min = sync_ptr.c.control.avail_min;
- else
- sync_ptr.c.control.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;
- snd_pcm_stream_unlock_irq(substream);
- if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
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)
{
@@ -2772,7 +3266,7 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (put_user(0, &_xferi->result))
return -EFAULT;
@@ -2782,7 +3276,8 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
else
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
+ if (put_user(result, &_xferi->result))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -2791,10 +3286,10 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
{
struct snd_xfern xfern;
struct snd_pcm_runtime *runtime = substream->runtime;
- void *bufs;
+ void *bufs __free(kfree) = NULL;
snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (runtime->channels > 128)
return -EINVAL;
@@ -2803,15 +3298,15 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;
- bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels);
+ bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *));
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);
- __put_user(result, &_xfern->result);
+ if (put_user(result, &_xfern->result))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -2825,11 +3320,9 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- result = snd_pcm_playback_rewind(substream, frames);
- else
- result = snd_pcm_capture_rewind(substream, frames);
- __put_user(result, _frames);
+ result = snd_pcm_rewind(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -2843,11 +3336,9 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- result = snd_pcm_playback_forward(substream, frames);
- else
- result = snd_pcm_capture_forward(substream, frames);
- __put_user(result, _frames);
+ result = snd_pcm_forward(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -2861,7 +3352,10 @@ static int snd_pcm_common_ioctl(struct file *file,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
- res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0);
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ res = snd_power_wait(substream->pcm->card);
if (res < 0)
return res;
@@ -2887,10 +3381,14 @@ static int snd_pcm_common_ioctl(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, false);
- case SNDRV_PCM_IOCTL_STATUS_EXT:
- return snd_pcm_status_user(substream, arg, true);
+ 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:
@@ -2911,16 +3409,20 @@ static int snd_pcm_common_ioctl(struct file *file,
return snd_pcm_hwsync(substream);
case SNDRV_PCM_IOCTL_DELAY:
{
- snd_pcm_sframes_t delay = snd_pcm_delay(substream);
+ snd_pcm_sframes_t delay = 0;
snd_pcm_sframes_t __user *res = arg;
+ int err;
- if (delay < 0)
- return delay;
+ err = snd_pcm_delay(substream, &delay);
+ if (err)
+ return err;
if (put_user(delay, res))
return -EFAULT;
return 0;
}
- case SNDRV_PCM_IOCTL_SYNC_PTR:
+ 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:
@@ -2933,9 +3435,7 @@ static int snd_pcm_common_ioctl(struct file *file,
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
- return snd_pcm_action_lock_irq(&snd_pcm_action_pause,
- substream,
- (int)(unsigned long)arg);
+ return snd_pcm_pause_lock_irq(substream, (unsigned long)arg);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
case SNDRV_PCM_IOCTL_READI_FRAMES:
return snd_pcm_xferi_frames_ioctl(substream, arg);
@@ -2974,6 +3474,8 @@ static long snd_pcm_ioctl(struct file *file, unsigned int cmd,
* 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)
@@ -2981,13 +3483,16 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
snd_pcm_uframes_t *frames = arg;
snd_pcm_sframes_t result;
+ 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_capture_forward(substream, *frames);
+ result = snd_pcm_forward(substream, *frames);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_HW_PARAMS:
@@ -3003,13 +3508,7 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_DELAY:
- {
- result = snd_pcm_delay(substream);
- if (result < 0)
- return result;
- *frames = result;
- return 0;
- }
+ return snd_pcm_delay(substream, frames);
default:
return -EINVAL;
}
@@ -3029,7 +3528,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;
@@ -3053,7 +3553,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;
@@ -3071,32 +3572,35 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
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 (!iter_is_iovec(to))
+ if (!user_backed_iter(to))
return -EINVAL;
if (to->nr_segs > 1024 || to->nr_segs != runtime->channels)
return -EINVAL;
- if (!frame_aligned(runtime, to->iov->iov_len))
+ if (!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
- frames = bytes_to_samples(runtime, to->iov->iov_len);
- bufs = kmalloc(sizeof(void *) * to->nr_segs, GFP_KERNEL);
+ frames = bytes_to_samples(runtime, iov->iov_len);
+ bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < to->nr_segs; ++i)
- bufs[i] = to->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);
- kfree(bufs);
return result;
}
@@ -3107,113 +3611,82 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
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 (!iter_is_iovec(from))
+ if (!user_backed_iter(from))
return -EINVAL;
if (from->nr_segs > 128 || from->nr_segs != runtime->channels ||
- !frame_aligned(runtime, from->iov->iov_len))
+ !frame_aligned(runtime, iov->iov_len))
return -EINVAL;
- frames = bytes_to_samples(runtime, from->iov->iov_len);
- bufs = kmalloc(sizeof(void *) * from->nr_segs, GFP_KERNEL);
+ frames = bytes_to_samples(runtime, iov->iov_len);
+ bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < from->nr_segs; ++i)
- bufs[i] = from->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);
- kfree(bufs);
return result;
}
-static __poll_t 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;
- __poll_t 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 POLLOUT | POLLWRNORM | POLLERR;
- runtime = substream->runtime;
+ return ok | EPOLLERR;
- poll_wait(file, &runtime->sleep, wait);
-
- 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 __poll_t 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;
- __poll_t mask;
- snd_pcm_uframes_t avail;
-
- pcm_file = file->private_data;
-
- substream = pcm_file->substream;
- if (PCM_RUNTIME_CHECK(substream))
- return POLLIN | POLLRDNORM | POLLERR;
runtime = substream->runtime;
+ if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return ok | EPOLLERR;
poll_wait(file, &runtime->sleep, wait);
- snd_pcm_stream_lock_irq(substream);
- avail = snd_pcm_capture_avail(runtime);
- switch (runtime->status->state) {
+ mask = 0;
+ guard(pcm_stream_lock_irq)(substream);
+ 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);
return mask;
}
@@ -3229,7 +3702,7 @@ static __poll_t snd_pcm_capture_poll(struct file *file, poll_table * wait)
/*
* mmap status record
*/
-static int snd_pcm_mmap_status_fault(struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_status_fault(struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
@@ -3258,14 +3731,16 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_status;
area->vm_private_data = substream;
- area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ 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_fault *vmf)
+static vm_fault_t snd_pcm_mmap_control_fault(struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
@@ -3294,13 +3769,17 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_control;
area->vm_private_data = substream;
- area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
return 0;
}
static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
{
- if (pcm_file->no_compat_mmap)
+ /* 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
@@ -3316,6 +3795,9 @@ 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.
@@ -3344,17 +3826,30 @@ 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)
+/*
+ * snd_pcm_mmap_data_open - increase the mmap counter
+ */
+static void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
- void *vaddr = substream->runtime->dma_area + ofs;
- return virt_to_page(vaddr);
+ struct snd_pcm_substream *substream = area->vm_private_data;
+
+ atomic_inc(&substream->mmap_count);
+}
+
+/*
+ * snd_pcm_mmap_data_close - decrease the mmap counter
+ */
+static void snd_pcm_mmap_data_close(struct vm_area_struct *area)
+{
+ struct snd_pcm_substream *substream = area->vm_private_data;
+
+ atomic_dec(&substream->mmap_count);
}
/*
* fault callback for mmapping a RAM page
*/
-static int snd_pcm_mmap_data_fault(struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
@@ -3371,8 +3866,12 @@ static int snd_pcm_mmap_data_fault(struct vm_fault *vmf)
return VM_FAULT_SIGBUS;
if (substream->ops->page)
page = substream->ops->page(substream, offset);
- else
- page = snd_pcm_default_page_ops(substream, offset);
+ else if (!snd_pcm_get_dma_buf(substream)) {
+ if (WARN_ON_ONCE(!runtime->dma_area))
+ return VM_FAULT_SIGBUS;
+ page = virt_to_page(runtime->dma_area + offset);
+ } else
+ page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset);
if (!page)
return VM_FAULT_SIGBUS;
get_page(page);
@@ -3402,28 +3901,16 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
*
* 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_DONTEXPAND | VM_DONTDUMP;
-#ifdef CONFIG_GENERIC_ALLOCATOR
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
- area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
- return remap_pfn_range(area, area->vm_start,
- substream->dma_buffer.addr >> PAGE_SHIFT,
- area->vm_end - area->vm_start, area->vm_page_prot);
- }
-#endif /* CONFIG_GENERIC_ALLOCATOR */
-#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
- if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
- substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return dma_mmap_coherent(substream->dma_buffer.dev.dev,
- area,
- substream->runtime->dma_area,
- substream->runtime->dma_addr,
- area->vm_end - area->vm_start);
-#endif /* CONFIG_X86 */
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
+ if (!substream->ops->page &&
+ !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;
@@ -3442,6 +3929,8 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
* 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)
@@ -3474,7 +3963,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;
@@ -3511,14 +4000,24 @@ 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:
+ 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:
+ 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);
@@ -3539,7 +4038,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);
}
/*
@@ -3600,8 +4101,8 @@ static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *opara
static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params;
- struct snd_pcm_hw_params_old *oparams = NULL;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
+ struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -3609,34 +4110,28 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
return -ENOMEM;
oparams = memdup_user(_oparams, sizeof(*oparams));
- if (IS_ERR(oparams)) {
- err = PTR_ERR(oparams);
- goto out;
- }
+ if (IS_ERR(oparams))
+ return PTR_ERR(oparams);
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_refine(substream, params);
if (err < 0)
- goto out_old;
+ return err;
err = fixup_unreferenced_params(substream, params);
if (err < 0)
- goto out_old;
+ return err;
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);
- return err;
+ return -EFAULT;
+ return 0;
}
static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params;
- struct snd_pcm_hw_params_old *oparams = NULL;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
+ struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -3644,24 +4139,18 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
return -ENOMEM;
oparams = memdup_user(_oparams, sizeof(*oparams));
- if (IS_ERR(oparams)) {
- err = PTR_ERR(oparams);
- goto out;
- }
+ if (IS_ERR(oparams))
+ return PTR_ERR(oparams);
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_params(substream, params);
if (err < 0)
- goto out_old;
+ return err;
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);
- return err;
+ return -EFAULT;
+ return 0;
}
#endif /* CONFIG_SND_SUPPORT_OLD_API */
@@ -3678,9 +4167,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;
@@ -3701,8 +4190,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
.write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
- .llseek = no_llseek,
- .poll = snd_pcm_playback_poll,
+ .poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
@@ -3715,8 +4203,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
.read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
- .llseek = no_llseek,
- .poll = snd_pcm_capture_poll,
+ .poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 11389f13de73..ab0e5bd70f8f 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>
@@ -90,7 +75,7 @@ static int snd_pcm_timer_stop(struct snd_timer * timer)
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,
@@ -123,8 +108,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0)
return;
sprintf(timer->name, "PCM %s %i-%i-%i",
- substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
- "capture" : "playback",
+ snd_pcm_direction_name(substream->stream),
tid.card, tid.device, tid.subdevice);
timer->hw = snd_pcm_timer;
if (snd_device_register(timer->card, timer) < 0) {
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
index f18da2050772..adb9b1f3bbfa 100644
--- a/sound/core/pcm_trace.h
+++ b/sound/core/pcm_trace.h
@@ -88,19 +88,19 @@ TRACE_EVENT(hw_ptr_error,
__field( unsigned int, device )
__field( unsigned int, number )
__field( unsigned int, stream )
- __field( const char *, reason )
+ __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;
- __entry->reason = (why);
+ __assign_str(reason);
),
TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s",
__entry->card, __entry->device,
__entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
- __entry->number, __entry->reason)
+ __entry->number, __get_str(reason))
);
TRACE_EVENT(applptr,
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index fae21311723f..20d36a346cca 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1,22 +1,7 @@
+// 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>
@@ -29,11 +14,14 @@
#include <linux/mutex.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>
#include <sound/minors.h>
#include <sound/initval.h>
+#include <sound/ump.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
@@ -48,7 +36,6 @@ 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_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);
@@ -57,11 +44,37 @@ static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex);
#define rmidi_err(rmidi, fmt, args...) \
- dev_err(&(rmidi)->dev, fmt, ##args)
+ dev_err((rmidi)->dev, fmt, ##args)
#define rmidi_warn(rmidi, fmt, args...) \
- dev_warn(&(rmidi)->dev, fmt, ##args)
+ dev_warn((rmidi)->dev, fmt, ##args)
#define rmidi_dbg(rmidi, fmt, args...) \
- dev_dbg(&(rmidi)->dev, 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)
+
+#define rawmidi_is_ump(rmidi) \
+ (IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP))
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
{
@@ -85,16 +98,22 @@ 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)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ return __snd_rawmidi_ready(substream->runtime);
+}
+
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);
}
@@ -103,18 +122,47 @@ static void snd_rawmidi_input_event_work(struct work_struct *work)
{
struct snd_rawmidi_runtime *runtime =
container_of(work, struct snd_rawmidi_runtime, event_work);
+
if (runtime->event)
runtime->event(runtime->substream);
}
+/* buffer refcount management: call with substream->lock held */
+static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
+{
+ 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;
runtime->substream = substream;
- spin_lock_init(&runtime->lock);
init_waitqueue_head(&runtime->sleep);
INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
@@ -124,26 +172,41 @@ 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;
}
runtime->appl_ptr = runtime->hw_ptr = 0;
substream->runtime = runtime;
+ if (rawmidi_is_ump(substream->rmidi))
+ runtime->align = 3;
return 0;
}
+/* get the current alignment (either 0 or 3) */
+static inline int get_align(struct snd_rawmidi_runtime *runtime)
+{
+ if (IS_ENABLED(CONFIG_SND_UMP))
+ return runtime->align;
+ else
+ return 0;
+}
+
+/* get the trimmed size with the current alignment */
+#define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
+
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;
@@ -159,41 +222,60 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i
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)
{
- unsigned long flags;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
-
- 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);
+ runtime->avail = is_input ? 0 : runtime->buffer_size;
+}
+
+static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
+ bool is_input)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ if (substream->opened && substream->runtime)
+ __reset_runtime_ptrs(substream->runtime, is_input);
+}
+
+int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
+{
+ snd_rawmidi_output_trigger(substream, 0);
+ 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;
+
+ scoped_guard(spinlock_irq, &substream->lock) {
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer)
+ return -EINVAL;
+ snd_rawmidi_buffer_ref(runtime);
+ runtime->drain = 1;
+ }
- err = 0;
- runtime->drain = 1;
timeout = wait_event_interruptible_timeout(runtime->sleep,
(runtime->avail >= runtime->buffer_size),
10*HZ);
- if (signal_pending(current))
- err = -ERESTARTSYS;
- if (runtime->avail < runtime->buffer_size && !timeout) {
- rmidi_warn(substream->rmidi,
- "rawmidi drain error (avail = %li, buffer_size = %li)\n",
- (long)runtime->avail, (long)runtime->buffer_size);
- err = -EIO;
+
+ scoped_guard(spinlock_irq, &substream->lock) {
+ if (signal_pending(current))
+ err = -ERESTARTSYS;
+ if (runtime->avail < runtime->buffer_size && !timeout) {
+ 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;
}
- runtime->drain = 0;
+
if (err != -ERESTARTSYS) {
/* we need wait a while to make sure that Tx FIFOs are empty */
if (substream->ops->drain)
@@ -202,21 +284,18 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
msleep(50);
snd_rawmidi_drop_output(substream);
}
+
+ scoped_guard(spinlock_irq, &substream->lock)
+ snd_rawmidi_buffer_unref(runtime);
+
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);
@@ -230,7 +309,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,
};
@@ -271,6 +350,7 @@ static int open_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
return err;
}
+ guard(spinlock_irq)(&substream->lock);
substream->opened = 1;
substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
@@ -329,30 +409,18 @@ 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 snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
+ int mode, struct snd_rawmidi_file *rfile)
{
- struct snd_rawmidi *rmidi;
int err;
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);
+ if (!try_module_get(rmidi->card->module))
return -ENXIO;
- }
- mutex_unlock(&register_mutex);
- mutex_lock(&rmidi->open_mutex);
+ guard(mutex)(&rmidi->open_mutex);
err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
- mutex_unlock(&rmidi->open_mutex);
if (err < 0)
module_put(rmidi->card->module);
return err;
@@ -370,10 +438,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
struct snd_rawmidi_file *rawmidi_file = NULL;
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;
@@ -409,6 +477,7 @@ 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) {
@@ -482,13 +551,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);
+ }
+ scoped_guard(spinlock_irq, &substream->lock) {
+ substream->opened = 0;
+ substream->append = 0;
}
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--;
@@ -499,7 +571,7 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
struct snd_rawmidi *rmidi;
rmidi = rfile->rmidi;
- mutex_lock(&rmidi->open_mutex);
+ guard(mutex)(&rmidi->open_mutex);
if (rfile->input) {
close_substream(rmidi, rfile->input, 1);
rfile->input = NULL;
@@ -509,7 +581,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
rfile->output = NULL;
}
rfile->rmidi = NULL;
- mutex_unlock(&rmidi->open_mutex);
wake_up(&rmidi->open_wait);
}
@@ -520,7 +591,7 @@ 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);
@@ -548,7 +619,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;
@@ -558,21 +629,26 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
info->subdevice = substream->number;
info->stream = substream->stream;
info->flags = rmidi->info_flags;
- strcpy(info->id, rmidi->id);
- strcpy(info->name, rmidi->name);
- strcpy(info->subname, substream->name);
+ if (substream->inactive)
+ info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE;
+ strscpy(info->id, rmidi->id);
+ strscpy(info->name, rmidi->name);
+ strscpy(info->subname, substream->name);
info->subdevices_count = substream->pstr->substream_count;
info->subdevices_avail = (substream->pstr->substream_count -
substream->pstr->substream_opened);
+ info->tied_device = rmidi->tied_device;
return 0;
}
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;
@@ -591,6 +667,7 @@ static int __snd_rawmidi_info_select(struct snd_card *card,
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;
@@ -605,12 +682,8 @@ static int __snd_rawmidi_info_select(struct snd_card *card,
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;
+ guard(mutex)(&register_mutex);
+ return __snd_rawmidi_info_select(card, info);
}
EXPORT_SYMBOL(snd_rawmidi_info_select);
@@ -619,106 +692,204 @@ static int snd_rawmidi_info_select_user(struct snd_card *card,
{
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 & get_align(runtime))
return -EINVAL;
- }
if (params->buffer_size != runtime->buffer_size) {
- newbuf = krealloc(runtime->buffer, params->buffer_size,
- GFP_KERNEL);
+ newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
+ 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);
+ guard(mutex)(&substream->rmidi->open_mutex);
+ if (substream->append && substream->use_count > 1)
+ return -EBUSY;
+ err = resize_runtime_buffer(substream, params, false);
+ if (!err)
+ substream->active_sensing = !params->no_active_sensing;
+ 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 = krealloc(runtime->buffer, params->buffer_size,
- GFP_KERNEL);
- if (!newbuf)
- return -ENOMEM;
- runtime->buffer = newbuf;
- runtime->buffer_size = params->buffer_size;
+ guard(mutex)(&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;
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);
+ guard(spinlock_irq)(&substream->lock);
status->avail = runtime->avail;
- spin_unlock_irq(&runtime->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);
+ guard(spinlock_irq)(&substream->lock);
status->avail = runtime->avail;
status->xruns = runtime->xruns;
runtime->xruns = 0;
- spin_unlock_irq(&runtime->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;
}
static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_rawmidi_file *rfile;
+ struct snd_rawmidi *rmidi;
void __user *argp = (void __user *)arg;
rfile = file->private_data;
@@ -731,6 +902,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) {
@@ -742,11 +914,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)
@@ -760,35 +942,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) {
@@ -803,6 +964,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) {
@@ -819,12 +981,65 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
}
}
default:
- rmidi_dbg(rfile->rmidi,
- "rawmidi: unknown command = 0x%x\n", cmd);
+ rmidi = rfile->rmidi;
+ if (rmidi->ops && rmidi->ops->ioctl)
+ return rmidi->ops->ioctl(rmidi, cmd, argp);
+ rmidi_dbg(rmidi, "rawmidi: unknown command = 0x%x\n", cmd);
}
return -ENOTTY;
}
+/* ioctl to find the next device; either legacy or UMP depending on @find_ump */
+static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
+ bool find_ump)
+
+{
+ struct snd_rawmidi *rmidi;
+ int device;
+ bool is_ump;
+
+ if (get_user(device, argp))
+ return -EFAULT;
+ if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
+ device = SNDRV_RAWMIDI_DEVICES - 1;
+ scoped_guard(mutex, &register_mutex) {
+ device = device < 0 ? 0 : device + 1;
+ for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
+ rmidi = snd_rawmidi_search(card, device);
+ if (!rmidi)
+ continue;
+ is_ump = rawmidi_is_ump(rmidi);
+ if (find_ump == is_ump)
+ break;
+ }
+ if (device == SNDRV_RAWMIDI_DEVICES)
+ device = -1;
+ }
+ if (put_user(device, argp))
+ return -EFAULT;
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SND_UMP)
+/* inquiry of UMP endpoint and block info via control API */
+static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
+ void __user *argp)
+{
+ struct snd_ump_endpoint_info __user *info = argp;
+ struct snd_rawmidi *rmidi;
+ int device;
+
+ if (get_user(device, &info->device))
+ return -EFAULT;
+ guard(mutex)(&register_mutex);
+ rmidi = snd_rawmidi_search(card, device);
+ if (rmidi && rmidi->ops && rmidi->ops->ioctl)
+ return rmidi->ops->ioctl(rmidi, cmd, argp);
+ else
+ return -ENXIO;
+}
+#endif
+
static int snd_rawmidi_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd,
@@ -834,31 +1049,19 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
switch (cmd) {
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 */
- device = SNDRV_RAWMIDI_DEVICES - 1;
- mutex_lock(&register_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_RAWMIDI_DEVICES) {
- if (snd_rawmidi_search(card, device))
- break;
- device++;
- }
- if (device == SNDRV_RAWMIDI_DEVICES)
- device = -1;
- mutex_unlock(&register_mutex);
- if (put_user(device, (int __user *)argp))
- return -EFAULT;
- return 0;
- }
+ return snd_rawmidi_next_device(card, argp, false);
+#if IS_ENABLED(CONFIG_SND_UMP)
+ case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
+ return snd_rawmidi_next_device(card, argp, true);
+ case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
+ return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
+ case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
+ return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
+#endif
case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
{
int val;
-
+
if (get_user(val, (int __user *)argp))
return -EFAULT;
control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val;
@@ -870,6 +1073,63 @@ 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);
+ int align = get_align(runtime);
+
+ BUILD_BUG_ON(frame_size != 0x20);
+ if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
+ return -EINVAL;
+
+ while (src_count > align) {
+ 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 = get_aligned_size(runtime, src_count);
+ if (!frame.length)
+ break;
+ 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
@@ -883,19 +1143,27 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
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;
+ guard(spinlock_irqsave)(&substream->lock);
if (!substream->opened)
return -EBADFD;
- if (runtime->buffer == NULL) {
+ runtime = substream->runtime;
+ if (!runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi,
"snd_rawmidi_receive: input is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
- if (count == 1) { /* special case, faster code */
+
+ count = get_aligned_size(runtime, count);
+ if (!count)
+ return result;
+
+ 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];
@@ -912,6 +1180,9 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (int)(runtime->buffer_size - runtime->avail))
count1 = runtime->buffer_size - runtime->avail;
+ count1 = get_aligned_size(runtime, count1);
+ if (!count1)
+ return result;
memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
runtime->hw_ptr += count1;
runtime->hw_ptr %= runtime->buffer_size;
@@ -936,10 +1207,9 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
if (result > 0) {
if (runtime->event)
schedule_work(&runtime->event_work);
- else if (snd_rawmidi_ready(substream))
+ else if (__snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_receive);
@@ -952,8 +1222,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long appl_ptr;
+ int err = 0;
- spin_lock_irqsave(&runtime->lock, flags);
+ 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)
@@ -970,18 +1242,21 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
if (kernelbuf)
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 + 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;
}
result += count1;
count -= count1;
}
- spin_unlock_irqrestore(&runtime->lock, flags);
- 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,
@@ -1009,28 +1284,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)) {
+ 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*/,
@@ -1052,23 +1330,20 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
*/
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
- int result;
- unsigned long flags;
+ struct snd_rawmidi_runtime *runtime;
- if (runtime->buffer == NULL) {
+ guard(spinlock_irqsave)(&substream->lock);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi,
"snd_rawmidi_transmit_empty: output is not active!!!\n");
return 1;
}
- spin_lock_irqsave(&runtime->lock, flags);
- result = runtime->avail >= runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ return (runtime->avail >= runtime->buffer_size);
}
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-/**
+/*
* __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
@@ -1076,8 +1351,8 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
*
* 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)
{
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1101,12 +1376,18 @@ int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (int)(runtime->buffer_size - runtime->avail))
count1 = runtime->buffer_size - runtime->avail;
+ count1 = get_aligned_size(runtime, count1);
+ if (!count1)
+ goto __skip;
memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
count -= count1;
result += count1;
if (count > 0) {
if (count > (int)(runtime->buffer_size - runtime->avail - count1))
count = runtime->buffer_size - runtime->avail - count1;
+ count = get_aligned_size(runtime, count);
+ if (!count)
+ goto __skip;
memcpy(buffer + count1, runtime->buffer, count);
result += count;
}
@@ -1114,7 +1395,6 @@ int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
__skip:
return result;
}
-EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
/**
* snd_rawmidi_transmit_peek - copy data from the internal buffer
@@ -1133,25 +1413,22 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
- int result;
- unsigned long flags;
-
- spin_lock_irqsave(&runtime->lock, flags);
- result = __snd_rawmidi_transmit_peek(substream, buffer, count);
- spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ guard(spinlock_irqsave)(&substream->lock);
+ if (!substream->opened || !substream->runtime)
+ return -EBADFD;
+ return __snd_rawmidi_transmit_peek(substream, buffer, count);
}
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.
*/
-int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
+ int count)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1161,17 +1438,17 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun
return -EINVAL;
}
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
+ count = get_aligned_size(runtime, count);
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);
}
return count;
}
-EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
/**
* snd_rawmidi_transmit_ack - acknowledge the transmission
@@ -1186,14 +1463,10 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
*/
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
- int result;
- unsigned long flags;
-
- spin_lock_irqsave(&runtime->lock, flags);
- result = __snd_rawmidi_transmit_ack(substream, count);
- spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ guard(spinlock_irqsave)(&substream->lock);
+ if (!substream->opened || !substream->runtime)
+ return -EBADFD;
+ return __snd_rawmidi_transmit_ack(substream, count);
}
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
@@ -1202,7 +1475,7 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
* @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.
*
* Return: The copied size if successful, or a negative error code on failure.
@@ -1210,25 +1483,38 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
- int result;
- unsigned long flags;
-
- spin_lock_irqsave(&runtime->lock, flags);
+ guard(spinlock_irqsave)(&substream->lock);
if (!substream->opened)
- 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(&runtime->lock, flags);
- return result;
+ return -EBADFD;
+ count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ if (count <= 0)
+ return count;
+ return __snd_rawmidi_transmit_ack(substream, count);
}
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;
+ int count = 0;
+
+ guard(spinlock_irqsave)(&substream->lock);
+ runtime = substream->runtime;
+ if (substream->opened && runtime &&
+ runtime->avail < runtime->buffer_size) {
+ count = runtime->buffer_size - runtime->avail;
+ __snd_rawmidi_transmit_ack(substream, count);
+ }
+ return count;
+}
+EXPORT_SYMBOL(snd_rawmidi_proceed);
+
static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
const unsigned char __user *userbuf,
const unsigned char *kernelbuf,
@@ -1245,13 +1531,14 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
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)
@@ -1269,21 +1556,22 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ 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);
}
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;
@@ -1313,28 +1601,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_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;
@@ -1345,28 +1636,29 @@ 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_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 __poll_t 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;
@@ -1385,11 +1677,11 @@ static __poll_t 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;
}
@@ -1403,7 +1695,6 @@ static __poll_t snd_rawmidi_poll(struct file *file, poll_table * wait)
#endif
/*
-
*/
static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
@@ -1412,10 +1703,18 @@ 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);
- mutex_lock(&rmidi->open_mutex);
+ if (IS_ENABLED(CONFIG_SND_UMP))
+ snd_iprintf(buffer, "Type: %s\n",
+ rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
+ if (rmidi->ops && rmidi->ops->proc_read)
+ rmidi->ops->proc_read(entry, buffer);
+ guard(mutex)(&rmidi->open_mutex);
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
list_for_each_entry(substream,
&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
@@ -1430,13 +1729,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ scoped_guard(spinlock_irq, &substream->lock) {
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ }
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);
}
}
}
@@ -1454,31 +1756,39 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ scoped_guard(spinlock_irq, &substream->lock) {
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ xruns = runtime->xruns;
+ }
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]);
+ }
}
}
}
- mutex_unlock(&rmidi->open_mutex);
}
/*
* 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,
.open = snd_rawmidi_open,
.release = snd_rawmidi_release,
- .llseek = no_llseek,
.poll = snd_rawmidi_poll,
.unlocked_ioctl = snd_rawmidi_ioctl,
.compat_ioctl = snd_rawmidi_ioctl_compat,
@@ -1500,16 +1810,63 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
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)
+/* used for both rawmidi and ump */
+int snd_rawmidi_init(struct snd_rawmidi *rmidi,
+ struct snd_card *card, char *id, int device,
+ int output_count, int input_count,
+ unsigned int info_flags)
{
- kfree(container_of(dev, struct snd_rawmidi, dev));
+ int err;
+ 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,
+ };
+
+ rmidi->card = card;
+ rmidi->device = device;
+ mutex_init(&rmidi->open_mutex);
+ init_waitqueue_head(&rmidi->open_wait);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
+ rmidi->info_flags = info_flags;
+
+ if (id != NULL)
+ strscpy(rmidi->id, id, sizeof(rmidi->id));
+
+ err = snd_device_alloc(&rmidi->dev, card);
+ if (err < 0)
+ return err;
+ if (rawmidi_is_ump(rmidi))
+ dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device);
+ else
+ 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)
+ return err;
+ err = snd_rawmidi_alloc_substreams(rmidi,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ output_count);
+ if (err < 0)
+ return err;
+ err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
+ if (err < 0)
+ return err;
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_rawmidi_init);
/**
* snd_rawmidi_new - create a rawmidi instance
@@ -1527,52 +1884,19 @@ static void release_rawmidi_device(struct device *dev)
*/
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 = {
- .dev_free = snd_rawmidi_dev_free,
- .dev_register = snd_rawmidi_dev_register,
- .dev_disconnect = snd_rawmidi_dev_disconnect,
- };
- if (snd_BUG_ON(!card))
- return -ENXIO;
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
if (!rmidi)
return -ENOMEM;
- rmidi->card = card;
- rmidi->device = device;
- mutex_init(&rmidi->open_mutex);
- init_waitqueue_head(&rmidi->open_wait);
- INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
- INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
-
- if (id != NULL)
- strlcpy(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);
-
- 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) {
+ err = snd_rawmidi_init(rmidi, card, id, device,
+ output_count, input_count, 0);
+ if (err < 0) {
snd_rawmidi_free(rmidi);
return err;
}
@@ -1593,29 +1917,31 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
}
}
-static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
+/* called from ump.c, too */
+int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{
if (!rmidi)
return 0;
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);
- put_device(&rmidi->dev);
+ put_device(rmidi->dev);
+ kfree(rmidi);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_rawmidi_free);
static int snd_rawmidi_dev_free(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
+
return snd_rawmidi_free(rmidi);
}
@@ -1623,6 +1949,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device)
static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
{
struct snd_rawmidi *rmidi = device->private_data;
+
rmidi->seq_dev = NULL;
}
#endif
@@ -1636,34 +1963,32 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
return -ENOMEM;
- mutex_lock(&register_mutex);
- if (snd_rawmidi_search(rmidi->card, rmidi->device)) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ err = 0;
+ scoped_guard(mutex, &register_mutex) {
+ if (snd_rawmidi_search(rmidi->card, rmidi->device))
+ err = -EBUSY;
+ else
+ list_add_tail(&rmidi->list, &snd_rawmidi_devices);
}
- 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);
+ &snd_rawmidi_f_ops, rmidi, rmidi->dev);
if (err < 0) {
rmidi_err(rmidi, "unable to register\n");
- mutex_lock(&register_mutex);
- list_del(&rmidi->list);
- mutex_unlock(&register_mutex);
- return err;
+ goto error;
}
- if (rmidi->ops && rmidi->ops->dev_register &&
- (err = rmidi->ops->dev_register(rmidi)) < 0) {
- snd_unregister_device(&rmidi->dev);
- mutex_lock(&register_mutex);
- 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 (!rawmidi_is_ump(rmidi) &&
+ (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) < 0) {
@@ -1677,7 +2002,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
#endif
}
}
- if ((int)rmidi->device == amidi_map[rmidi->card->number]) {
+ if (!rawmidi_is_ump(rmidi) &&
+ (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) < 0) {
@@ -1701,7 +2027,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
rmidi->proc_entry = entry;
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
- if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
+ /* no own registration mechanism? */
+ if (!rmidi->ops || !rmidi->ops->dev_register) {
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;
rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
@@ -1711,6 +2038,13 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
#endif
return 0;
+
+ error_unregister:
+ snd_unregister_device(rmidi->dev);
+ error:
+ scoped_guard(mutex, &register_mutex)
+ list_del(&rmidi->list);
+ return err;
}
static int snd_rawmidi_dev_disconnect(struct snd_device *device)
@@ -1718,12 +2052,13 @@ 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);
+ guard(mutex)(&register_mutex);
+ guard(mutex)(&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);
@@ -1743,9 +2078,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
rmidi->ossreg = 0;
}
#endif /* CONFIG_SND_OSSEMUL */
- snd_unregister_device(&rmidi->dev);
- mutex_unlock(&rmidi->open_mutex);
- mutex_unlock(&register_mutex);
+ snd_unregister_device(rmidi->dev);
return 0;
}
@@ -1761,7 +2094,7 @@ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
const struct snd_rawmidi_ops *ops)
{
struct snd_rawmidi_substream *substream;
-
+
list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
substream->ops = ops;
}
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index f69764d7cdd7..2c6de6e113e4 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,8 +13,9 @@ struct snd_rawmidi_params32 {
u32 buffer_size;
u32 avail_min;
unsigned int no_active_sensing; /* avoid bit-field */
- unsigned char reserved[16];
-} __attribute__((packed));
+ unsigned int mode;
+ unsigned char reserved[12];
+} __packed;
static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_params32 __user *src)
@@ -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));
+} __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,66 +80,24 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
if (err < 0)
return err;
- if (compat_put_timespec(&status.tstamp, &src->tstamp) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.xruns, &src->xruns))
- return -EFAULT;
-
- return 0;
-}
-
-#ifdef CONFIG_X86_X32
-/* X32 ABI has 64bit timespec and 64bit alignment */
-struct snd_rawmidi_status_x32 {
- s32 stream;
- u32 rsvd; /* alignment */
- struct timespec tstamp;
- u32 avail;
- u32 xruns;
- unsigned char reserved[16];
-} __attribute__((packed));
-
-#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
-
-static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
- struct snd_rawmidi_status_x32 __user *src)
-{
- int err;
- struct snd_rawmidi_status status;
-
- if (rfile->output == NULL)
- return -EINVAL;
- if (get_user(status.stream, &src->stream))
- return -EFAULT;
-
- switch (status.stream) {
- case SNDRV_RAWMIDI_STREAM_OUTPUT:
- err = snd_rawmidi_output_status(rfile->output, &status);
- break;
- case SNDRV_RAWMIDI_STREAM_INPUT:
- err = snd_rawmidi_input_status(rfile->input, &status);
- break;
- default:
- return -EINVAL;
- }
- if (err < 0)
- return err;
+ 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 (put_timespec(&status.tstamp, &src->tstamp) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.xruns, &src->xruns))
+ if (copy_to_user(src, &compat_status, sizeof(*src)))
return -EFAULT;
return 0;
}
-#endif /* CONFIG_X86_X32 */
enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
- SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
-#ifdef CONFIG_X86_X32
- SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32),
-#endif /* CONFIG_X86_X32 */
+ 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)
@@ -158,15 +111,17 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
case SNDRV_RAWMIDI_IOCTL_INFO:
case SNDRV_RAWMIDI_IOCTL_DROP:
case SNDRV_RAWMIDI_IOCTL_DRAIN:
+#if IS_ENABLED(CONFIG_SND_UMP)
+ case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+ case SNDRV_UMP_IOCTL_BLOCK_INFO:
+#endif
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);
-#ifdef CONFIG_X86_X32
- case SNDRV_RAWMIDI_IOCTL_STATUS_X32:
- return snd_rawmidi_ioctl_status_x32(rfile, argp);
-#endif /* CONFIG_X86_X32 */
+ 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/seq/Kconfig b/sound/core/seq/Kconfig
index 45c1336c6597..e4f58cb985d4 100644
--- a/sound/core/seq/Kconfig
+++ b/sound/core/seq/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_SEQUENCER
tristate "Sequencer support"
select SND_TIMER
@@ -59,4 +60,17 @@ config SND_SEQ_MIDI_EMUL
config SND_SEQ_VIRMIDI
tristate
+config SND_SEQ_UMP
+ bool "Support for UMP events"
+ default SND_UMP
+ help
+ Say Y here to enable the support for handling UMP (Universal MIDI
+ Packet) events via ALSA sequencer infrastructure, which is an
+ essential feature for enabling MIDI 2.0 support.
+ It includes the automatic conversion of ALSA sequencer events
+ among legacy and UMP clients.
+
+config SND_SEQ_UMP_CLIENT
+ def_tristate SND_UMP && SND_SEQ_UMP
+
endif # SND_SEQUENCER
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 3a2177a7e50c..0904aa48d88b 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -4,21 +4,24 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+snd-seq-y := 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
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
+snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.o
+snd-seq-midi-y := seq_midi.o
+snd-seq-midi-emul-y := seq_midi_emul.o
+snd-seq-midi-event-y := seq_midi_event.o
+snd-seq-dummy-y := seq_dummy.o
+snd-seq-virmidi-y := seq_virmidi.o
+snd-seq-ump-client-y := seq_ump_client.o
obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o
obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/
obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
obj-$(CONFIG_SND_SEQ_MIDI) += snd-seq-midi.o
+obj-$(CONFIG_SND_SEQ_UMP_CLIENT) += snd-seq-ump-client.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 4ea4e3eea6b7..4e4741834208 100644
--- a/sound/core/seq/oss/Makefile
+++ b/sound/core/seq/oss/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
+snd-seq-oss-y := 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
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 5f64d0d88320..02d30d8b6c3a 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -1,23 +1,10 @@
+// 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>
@@ -80,13 +67,16 @@ static int __init alsa_seq_oss_init(void)
{
int rc;
- 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;
@@ -127,18 +117,15 @@ static DEFINE_MUTEX(register_mutex);
static int
odev_open(struct inode *inode, struct file *file)
{
- int level, rc;
+ int level;
if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
level = SNDRV_SEQ_OSS_MODE_MUSIC;
else
level = SNDRV_SEQ_OSS_MODE_SYNTH;
- mutex_lock(&register_mutex);
- rc = snd_seq_oss_open(file, level);
- mutex_unlock(&register_mutex);
-
- return rc;
+ guard(mutex)(&register_mutex);
+ return snd_seq_oss_open(file, level);
}
static int
@@ -146,13 +133,12 @@ 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;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
snd_seq_oss_release(dp);
- mutex_unlock(&register_mutex);
-
return 0;
}
@@ -181,10 +167,19 @@ 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
@@ -203,7 +198,7 @@ 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,35 +224,33 @@ 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)) < 0) {
+ guard(mutex)(&register_mutex);
+ 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)) < 0) {
+ 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;
}
- mutex_unlock(&register_mutex);
return 0;
}
static void
unregister_device(void)
{
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
pr_err("ALSA: seq_oss: error unregister device music\n");
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
pr_err("ALSA: seq_oss: error unregister device seq\n");
- mutex_unlock(&register_mutex);
}
/*
@@ -271,12 +264,11 @@ static struct snd_info_entry *info_entry;
static void
info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf)
{
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
snd_seq_oss_system_info_read(buf);
snd_seq_oss_synth_info_read(buf);
snd_seq_oss_midi_info_read(buf);
- mutex_unlock(&register_mutex);
}
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index 2d0e9eaf13aa..935cf3df0b30 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
@@ -30,6 +17,7 @@
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
#include <sound/info.h>
+#include "../seq_clientmgr.h"
/* max. applications */
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
@@ -67,7 +55,6 @@ struct seq_oss_chinfo {
struct seq_oss_synthinfo {
struct snd_seq_oss_arg arg;
struct seq_oss_chinfo *ch;
- struct seq_oss_synth_sysex *sysex;
int nr_voices;
int opened;
int is_midi;
@@ -128,10 +115,6 @@ __poll_t snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_ta
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
-/* */
-void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
-
-
/* proc interface */
void snd_seq_oss_system_info_read(struct snd_info_buffer *buf);
void snd_seq_oss_midi_info_read(struct snd_info_buffer *buf);
@@ -150,11 +133,11 @@ 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);
+ return snd_seq_kernel_client_ioctl(dp->cseq, type, arg);
}
/* fill the addresses in header */
@@ -168,8 +151,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
ev->dest.port = dest_port;
}
-
-/* misc. functions for proc interface */
-char *enabled_str(int bool);
-
#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 c3908862bc8b..76fb81077eef 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>
/*
@@ -287,10 +275,10 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
{
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;
- info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -298,19 +286,18 @@ 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;
- //if (! vel)
- /* set volume to zero -- note off */
- // type = SNDRV_SEQ_EVENT_NOTEOFF;
- //else
- if (info->ch[ch].vel)
+
+ if (info->ch[ch].vel)
/* sample already started -- volume change */
type = SNDRV_SEQ_EVENT_KEYPRESS;
else
/* sample not started -- start now */
type = SNDRV_SEQ_EVENT_NOTEON;
+
info->ch[ch].vel = vel;
return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
} else if (note >= 128)
@@ -347,10 +334,10 @@ note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, s
{
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;
- info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -358,6 +345,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;
@@ -381,7 +369,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;
@@ -399,7 +387,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 92c96a95a903..973f057eb731 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"
@@ -76,26 +63,23 @@ int __init
snd_seq_oss_create_client(void)
{
int rc;
- struct snd_seq_port_info *port;
+ struct snd_seq_port_info *port __free(kfree) = NULL;
struct snd_seq_port_callback port_callback;
- port = kmalloc(sizeof(*port), GFP_KERNEL);
- if (!port) {
- rc = -ENOMEM;
- goto __error;
- }
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
/* create ALSA client */
rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
"OSS sequencer");
if (rc < 0)
- goto __error;
+ return rc;
system_client = rc;
- /* create annoucement receiver port */
- memset(port, 0, sizeof(*port));
- strcpy(port->name, "Receiver");
+ /* create announcement receiver port */
+ strscpy(port->name, "Receiver");
port->addr.client = system_client;
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
port->type = 0;
@@ -107,10 +91,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;
@@ -118,19 +102,16 @@ snd_seq_oss_create_client(void)
subs.dest.port = system_port;
call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
}
- rc = 0;
/* look up midi devices */
schedule_work(&async_lookup_work);
- __error:
- kfree(port);
- return rc;
+ return 0;
}
/*
- * receive annoucement from system port, and check the midi device
+ * receive announcement from system port, and check the midi device
*/
static int
receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
@@ -366,8 +347,9 @@ alloc_seq_queue(struct seq_oss_devinfo *dp)
memset(&qinfo, 0, sizeof(qinfo));
qinfo.owner = system_client;
qinfo.locked = 1;
- strcpy(qinfo.name, "OSS Sequencer Emulation");
- if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
+ strscpy(qinfo.name, "OSS Sequencer Emulation");
+ rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo);
+ if (rc < 0)
return rc;
dp->queue = qinfo.queue;
return 0;
@@ -467,16 +449,10 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
/*
* misc. functions for proc interface
*/
-char *
-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];
@@ -498,7 +474,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;
}
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
index 5b8520177b0e..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);
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index b30b2139e3f0..023e5d0a4351 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>
/*
@@ -49,8 +37,10 @@ struct seq_oss_midi {
struct snd_midi_event *coder; /* MIDI event coder */
struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */
snd_use_lock_t use_lock;
+ struct mutex open_mutex;
};
+DEFINE_FREE(seq_oss_midi, struct seq_oss_midi *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock))
/*
* midi device table
@@ -75,16 +65,13 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
int
snd_seq_oss_midi_lookup_ports(int client)
{
- struct snd_seq_client_info *clinfo;
- struct snd_seq_port_info *pinfo;
+ struct snd_seq_client_info *clinfo __free(kfree) = NULL;
+ struct snd_seq_port_info *pinfo __free(kfree) = NULL;
clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (! clinfo || ! pinfo) {
- kfree(clinfo);
- kfree(pinfo);
+ if (!clinfo || !pinfo)
return -ENOMEM;
- }
clinfo->client = -1;
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
if (clinfo->client == client)
@@ -94,8 +81,6 @@ snd_seq_oss_midi_lookup_ports(int client)
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
snd_seq_oss_midi_check_new_port(pinfo);
}
- kfree(clinfo);
- kfree(pinfo);
return 0;
}
@@ -106,13 +91,11 @@ static struct seq_oss_midi *
get_mdev(int dev)
{
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
mdev = midi_devs[dev];
if (mdev)
snd_use_lock_use(&mdev->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
@@ -124,19 +107,16 @@ find_slot(int client, int port)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
mdev = midi_devs[i];
if (mdev && mdev->client == client && mdev->port == port) {
/* found! */
snd_use_lock_use(&mdev->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
}
- spin_unlock_irqrestore(&register_lock, flags);
return NULL;
}
@@ -151,7 +131,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
/* the port must include generic midi */
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
@@ -164,7 +143,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;
@@ -183,9 +163,10 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
mdev->flags = pinfo->capability;
mdev->opened = 0;
snd_use_lock_init(&mdev->use_lock);
+ mutex_init(&mdev->open_mutex);
/* 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) {
@@ -199,14 +180,13 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* look for en empty slot
*/
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
if (midi_devs[i] == NULL)
break;
}
if (i >= max_midi_devs) {
if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
- spin_unlock_irqrestore(&register_lock, flags);
snd_midi_event_free(mdev->coder);
kfree(mdev);
return -ENOMEM;
@@ -215,7 +195,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
}
mdev->seq_device = i;
midi_devs[mdev->seq_device] = mdev;
- spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
@@ -227,25 +206,24 @@ int
snd_seq_oss_midi_check_exit_port(int client, int port)
{
struct seq_oss_midi *mdev;
- unsigned long flags;
int index;
- if ((mdev = find_slot(client, port)) != NULL) {
- spin_lock_irqsave(&register_lock, flags);
- midi_devs[mdev->seq_device] = NULL;
- spin_unlock_irqrestore(&register_lock, flags);
+ mdev = find_slot(client, port);
+ if (mdev) {
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ midi_devs[mdev->seq_device] = NULL;
+ }
snd_use_lock_free(&mdev->use_lock);
snd_use_lock_sync(&mdev->use_lock);
snd_midi_event_free(mdev->coder);
kfree(mdev);
}
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (index = max_midi_devs - 1; index >= 0; index--) {
if (midi_devs[index])
break;
}
max_midi_devs = index + 1;
- spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
@@ -258,18 +236,17 @@ snd_seq_oss_midi_clear_all(void)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
- if ((mdev = midi_devs[i]) != NULL) {
+ mdev = midi_devs[i];
+ if (mdev) {
snd_midi_event_free(mdev->coder);
kfree(mdev);
midi_devs[i] = NULL;
}
}
max_midi_devs = 0;
- spin_unlock_irqrestore(&register_lock, flags);
}
@@ -279,6 +256,7 @@ snd_seq_oss_midi_clear_all(void)
void
snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)
{
+ guard(spinlock_irq)(&register_lock);
dp->max_mididev = max_midi_devs;
}
@@ -315,6 +293,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);
}
@@ -326,17 +305,17 @@ int
snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
{
int perm;
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
struct snd_seq_port_subscribe subs;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
+ guard(mutex)(&mdev->open_mutex);
/* already used? */
- if (mdev->opened && mdev->devinfo != dp) {
- snd_use_lock_free(&mdev->use_lock);
+ if (mdev->opened && mdev->devinfo != dp)
return -EBUSY;
- }
perm = 0;
if (is_write_mode(fmode))
@@ -344,16 +323,12 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
if (is_read_mode(fmode))
perm |= PERM_READ;
perm &= mdev->flags;
- if (perm == 0) {
- snd_use_lock_free(&mdev->use_lock);
+ if (perm == 0)
return -ENXIO;
- }
/* already opened? */
- if ((mdev->opened & perm) == perm) {
- snd_use_lock_free(&mdev->use_lock);
+ if ((mdev->opened & perm) == perm)
return 0;
- }
perm &= ~mdev->opened;
@@ -376,13 +351,10 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
mdev->opened |= PERM_READ;
}
- if (! mdev->opened) {
- snd_use_lock_free(&mdev->use_lock);
+ if (!mdev->opened)
return -ENXIO;
- }
mdev->devinfo = dp;
- snd_use_lock_free(&mdev->use_lock);
return 0;
}
@@ -392,15 +364,15 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
int
snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
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);
+ guard(mutex)(&mdev->open_mutex);
+ if (!mdev->opened || mdev->devinfo != dp)
return 0;
- }
memset(&subs, 0, sizeof(subs));
if (mdev->opened & PERM_WRITE) {
@@ -418,8 +390,6 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
mdev->opened = 0;
mdev->devinfo = NULL;
-
- snd_use_lock_free(&mdev->use_lock);
return 0;
}
@@ -429,10 +399,11 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
int
snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
int mode;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return 0;
mode = 0;
@@ -441,7 +412,6 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
if (mdev->opened & PERM_READ)
mode |= SNDRV_SEQ_OSS_FILE_READ;
- snd_use_lock_free(&mdev->use_lock);
return mode;
}
@@ -452,14 +422,13 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- 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);
+ if (!mdev->opened)
return;
- }
if (mdev->opened & PERM_WRITE) {
struct snd_seq_event ev;
@@ -490,7 +459,6 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
}
}
// snd_seq_oss_midi_close(dp, dev);
- snd_use_lock_free(&mdev->use_lock);
}
@@ -500,13 +468,13 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return;
addr->client = mdev->client;
addr->port = mdev->port;
- snd_use_lock_free(&mdev->use_lock);
}
@@ -517,25 +485,20 @@ int
snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
- struct seq_oss_midi *mdev;
- int rc;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
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);
+ if (!(mdev->opened & PERM_READ))
return 0;
- }
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- rc = send_synth_event(dp, ev, mdev->seq_device);
+ return send_synth_event(dp, ev, mdev->seq_device);
else
- rc = send_midi_event(dp, ev, mdev);
-
- snd_use_lock_free(&mdev->use_lock);
- return rc;
+ return send_midi_event(dp, ev, mdev);
}
/*
@@ -613,6 +576,7 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
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)
@@ -631,16 +595,15 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
int
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- 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;
}
- snd_use_lock_free(&mdev->use_lock);
return -EINVAL;
}
@@ -650,15 +613,15 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
int
snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- 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));
- snd_use_lock_free(&mdev->use_lock);
+ strscpy(inf->name, mdev->name, sizeof(inf->name));
return 0;
}
@@ -685,10 +648,11 @@ void
snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
{
int i;
- struct seq_oss_midi *mdev;
snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
for (i = 0; i < max_midi_devs; i++) {
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+
snd_iprintf(buf, "\nmidi %d: ", i);
mdev = get_mdev(i);
if (mdev == NULL) {
@@ -700,7 +664,6 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, " capability %s / opened %s\n",
capmode_str(mdev->flags),
capmode_str(mdev->opened));
- snd_use_lock_free(&mdev->use_lock);
}
}
#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 06b21226b4e7..bbaf72e70b35 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"
@@ -153,13 +140,9 @@ int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
int
snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->lock, flags);
- if (q->qlen >= q->maxlen - 1) {
- spin_unlock_irqrestore(&q->lock, flags);
+ guard(spinlock_irqsave)(&q->lock);
+ if (q->qlen >= q->maxlen - 1)
return -ENOMEM;
- }
memcpy(&q->q[q->tail], ev, sizeof(*ev));
q->tail = (q->tail + 1) % q->maxlen;
@@ -168,8 +151,6 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
/* wake up sleeper */
wake_up(&q->midi_sleep);
- spin_unlock_irqrestore(&q->lock, flags);
-
return 0;
}
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
index 8d033ca2d23f..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
diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
index c538e78ca310..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;
}
@@ -204,13 +189,13 @@ snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wai
/* 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 cd0e0ebbfdb1..8c4e5913c7e6 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -1,23 +1,10 @@
+// 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"
@@ -26,6 +13,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
* constants
@@ -38,13 +26,6 @@
* definition of synth info records
*/
-/* sysex buffer */
-struct seq_oss_synth_sysex {
- int len;
- int skip;
- unsigned char buf[MAX_SYSEX_BUFLEN];
-};
-
/* synth info */
struct seq_oss_synth {
int seq_device;
@@ -63,6 +44,7 @@ struct seq_oss_synth {
snd_use_lock_t use_lock;
};
+DEFINE_FREE(seq_oss_synth, struct seq_oss_synth *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock))
/*
* device table
@@ -104,7 +86,6 @@ snd_seq_oss_synth_probe(struct device *_dev)
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
- unsigned long flags;
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
if (!rec)
@@ -119,26 +100,25 @@ snd_seq_oss_synth_probe(struct 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);
- for (i = 0; i < max_synth_devs; i++) {
- if (synth_devs[i] == NULL)
- break;
- }
- if (i >= max_synth_devs) {
- if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
- spin_unlock_irqrestore(&register_lock, flags);
- pr_err("ALSA: seq_oss: no more synth slot\n");
- kfree(rec);
- return -ENOMEM;
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ for (i = 0; i < max_synth_devs; i++) {
+ if (synth_devs[i] == NULL)
+ break;
+ }
+ if (i >= max_synth_devs) {
+ if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
+ pr_err("ALSA: seq_oss: no more synth slot\n");
+ kfree(rec);
+ return -ENOMEM;
+ }
+ max_synth_devs++;
}
- max_synth_devs++;
+ rec->seq_device = i;
+ synth_devs[i] = rec;
}
- rec->seq_device = i;
- synth_devs[i] = rec;
- spin_unlock_irqrestore(&register_lock, flags);
dev->driver_data = rec;
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (i < SNDRV_CARDS)
@@ -154,27 +134,25 @@ 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;
- spin_lock_irqsave(&register_lock, flags);
- for (index = 0; index < max_synth_devs; index++) {
- if (synth_devs[index] == rec)
- break;
- }
- if (index >= max_synth_devs) {
- spin_unlock_irqrestore(&register_lock, flags);
- pr_err("ALSA: seq_oss: can't unregister synth\n");
- return -EINVAL;
- }
- synth_devs[index] = NULL;
- if (index == max_synth_devs - 1) {
- for (index--; index >= 0; index--) {
- if (synth_devs[index])
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ for (index = 0; index < max_synth_devs; index++) {
+ if (synth_devs[index] == rec)
break;
}
- max_synth_devs = index + 1;
+ if (index >= max_synth_devs) {
+ pr_err("ALSA: seq_oss: can't unregister synth\n");
+ return -EINVAL;
+ }
+ synth_devs[index] = NULL;
+ if (index == max_synth_devs - 1) {
+ for (index--; index >= 0; index--) {
+ if (synth_devs[index])
+ break;
+ }
+ max_synth_devs = index + 1;
+ }
}
- spin_unlock_irqrestore(&register_lock, flags);
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (rec->seq_device < SNDRV_CARDS)
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
@@ -193,13 +171,11 @@ static struct seq_oss_synth *
get_sdev(int dev)
{
struct seq_oss_synth *rec;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
rec = synth_devs[dev];
if (rec)
snd_use_lock_use(&rec->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return rec;
}
@@ -212,20 +188,18 @@ void
snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
{
int i;
- struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
dp->max_synthdev = max_synth_devs;
dp->synth_opened = 0;
memset(dp->synths, 0, sizeof(dp->synths));
for (i = 0; i < dp->max_synthdev; i++) {
- rec = get_sdev(i);
+ struct seq_oss_synth *rec __free(seq_oss_synth) = get_sdev(i);
+
if (rec == NULL)
continue;
- if (rec->oper.open == NULL || rec->oper.close == NULL) {
- snd_use_lock_free(&rec->use_lock);
+ if (rec->oper.open == NULL || rec->oper.close == NULL)
continue;
- }
info = &dp->synths[i];
info->arg.app_index = dp->port;
info->arg.file_mode = dp->file_mode;
@@ -235,13 +209,10 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
else
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
info->opened = 0;
- if (!try_module_get(rec->oper.owner)) {
- snd_use_lock_free(&rec->use_lock);
+ if (!try_module_get(rec->oper.owner))
continue;
- }
if (rec->oper.open(&info->arg, rec->private_data) < 0) {
module_put(rec->oper.owner);
- snd_use_lock_free(&rec->use_lock);
continue;
}
info->nr_voices = rec->nr_voices;
@@ -250,7 +221,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
if (!info->ch) {
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
- snd_use_lock_free(&rec->use_lock);
continue;
}
reset_channels(info);
@@ -258,7 +228,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
info->opened++;
rec->opened++;
dp->synth_opened++;
- snd_use_lock_free(&rec->use_lock);
}
}
@@ -305,7 +274,6 @@ void
snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
{
int i;
- struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
@@ -320,7 +288,9 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
midi_synth_dev.opened--;
}
} else {
- rec = get_sdev(i);
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_sdev(i);
+
if (rec == NULL)
continue;
if (rec->opened > 0) {
@@ -328,10 +298,7 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
module_put(rec->oper.owner);
rec->opened = 0;
}
- snd_use_lock_free(&rec->use_lock);
}
- kfree(info->sysex);
- info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
@@ -339,17 +306,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];
}
/*
@@ -359,14 +322,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;
@@ -399,16 +368,12 @@ reset_channels(struct seq_oss_synthinfo *info)
void
snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_synth *rec;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
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 */
reset_channels(info);
if (info->is_midi) {
if (midi_synth_dev.opened <= 0)
@@ -420,8 +385,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
dp->file_mode) < 0) {
midi_synth_dev.opened--;
info->opened = 0;
- kfree(info->sysex);
- info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
@@ -441,7 +404,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
ev.type = SNDRV_SEQ_EVENT_RESET;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
- snd_use_lock_free(&rec->use_lock);
}
@@ -453,98 +415,62 @@ int
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;
- int rc;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+ struct seq_oss_synthinfo *info;
- 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;
+ return -ENXIO;
else
- rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
- snd_use_lock_free(&rec->use_lock);
- return rc;
+ return rec->oper.load_patch(&info->arg, fmt, buf, p, c);
}
/*
- * 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;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+
rec = get_synthdev(dp, dev);
- if (rec) {
- snd_use_lock_free(&rec->use_lock);
- return 1;
- }
- return 0;
+ if (rec)
+ return get_synthinfo_nospec(dp, dev);
+ return NULL;
}
/*
* receive OSS 6 byte sysex packet:
- * the full sysex message will be sent if it reaches to the end of data
- * (0xff).
+ * the event is filled and prepared for sending immediately
+ * (i.e. sysex messages are fragmented)
*/
int
snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)
{
- int i, send;
- unsigned char *dest;
- struct seq_oss_synth_sysex *sysex;
-
- if (! snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
+ unsigned char *p;
+ int len = 6;
- sysex = dp->synths[dev].sysex;
- if (sysex == NULL) {
- sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
- if (sysex == NULL)
- return -ENOMEM;
- dp->synths[dev].sysex = sysex;
- }
+ p = memchr(buf, 0xff, 6);
+ if (p)
+ len = p - buf + 1;
- send = 0;
- dest = sysex->buf + sysex->len;
- /* copy 6 byte packet to the buffer */
- for (i = 0; i < 6; i++) {
- if (buf[i] == 0xff) {
- send = 1;
- break;
- }
- dest[i] = buf[i];
- sysex->len++;
- if (sysex->len >= MAX_SYSEX_BUFLEN) {
- sysex->len = 0;
- sysex->skip = 1;
- break;
- }
- }
-
- if (sysex->len && send) {
- if (sysex->skip) {
- sysex->skip = 0;
- sysex->len = 0;
- return -EINVAL; /* skip */
- }
- /* copy the data to event record and send it */
- ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
- if (snd_seq_oss_synth_addr(dp, dev, ev))
- return -EINVAL;
- ev->data.ext.len = sysex->len;
- ev->data.ext.ptr = sysex->buf;
- sysex->len = 0;
- return 0;
- }
-
- return -EINVAL; /* skip */
+ /* copy the data to event record and send it */
+ if (snd_seq_oss_synth_addr(dp, dev, ev))
+ return -EINVAL;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+ ev->data.ext.len = len;
+ ev->data.ext.ptr = buf;
+ return 0;
}
/*
@@ -553,10 +479,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;
}
@@ -567,19 +495,19 @@ snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event
int
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
- struct seq_oss_synth *rec;
- int rc;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+ struct seq_oss_synthinfo *info;
- 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;
+ return -ENXIO;
else
- rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
- snd_use_lock_free(&rec->use_lock);
- return rc;
+ return rec->oper.ioctl(&info->arg, cmd, addr);
}
@@ -589,7 +517,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);
@@ -603,28 +534,31 @@ snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *
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)
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ 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));
- snd_use_lock_free(&rec->use_lock);
+ strscpy(inf->name, rec->name, sizeof(inf->name));
}
return 0;
}
@@ -638,10 +572,11 @@ void
snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
{
int i;
- struct seq_oss_synth *rec;
snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
for (i = 0; i < max_synth_devs; i++) {
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+
snd_iprintf(buf, "\nsynth %d: ", i);
rec = get_sdev(i);
if (rec == NULL) {
@@ -653,9 +588,8 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
rec->synth_type, rec->synth_subtype,
rec->nr_voices);
snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
- enabled_str((long)rec->oper.ioctl),
- enabled_str((long)rec->oper.load_patch));
- snd_use_lock_free(&rec->use_lock);
+ str_enabled_disabled((long)rec->oper.ioctl),
+ str_enabled_disabled((long)rec->oper.load_patch));
}
}
#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 74ac55f166b6..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
@@ -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 4f24ea9fad93..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;
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 5e04f4df10e4..a93ff8315b8e 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"
@@ -40,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;
@@ -116,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);
@@ -134,13 +122,10 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
void
snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->sync_lock, flags);
+ guard(spinlock_irqsave)(&q->sync_lock);
q->sync_time = time;
q->sync_event_put = 0;
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 639544b4fb04..00f7342ee839 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -1,22 +1,7 @@
+// 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>
@@ -84,30 +69,32 @@ static int __init alsa_seq_init(void)
{
int err;
- 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:
return err;
}
@@ -126,9 +113,6 @@ 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();
}
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index b611deef81f5..f9a6e497f997 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1,24 +1,9 @@
+// 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>
@@ -29,12 +14,14 @@
#include <linux/kmod.h>
#include <sound/seq_kernel.h>
+#include <sound/ump.h>
#include "seq_clientmgr.h"
#include "seq_memory.h"
#include "seq_queue.h"
#include "seq_timer.h"
#include "seq_info.h"
#include "seq_system.h"
+#include "seq_ump_convert.h"
#include <sound/seq_device.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
@@ -83,7 +70,11 @@ static int bounce_error_event(struct snd_seq_client *client,
int err, int atomic, int hop);
static int snd_seq_deliver_single_event(struct snd_seq_client *client,
struct snd_seq_event *event,
- int filter, int atomic, int hop);
+ int atomic, int hop);
+
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static void free_ump_info(struct snd_seq_client *client);
+#endif
/*
*/
@@ -115,9 +106,8 @@ static struct snd_seq_client *clientptr(int clientid)
return clienttab[clientid];
}
-struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+static struct snd_seq_client *client_use_ptr(int clientid, bool load_module)
{
- unsigned long flags;
struct snd_seq_client *client;
if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
@@ -125,24 +115,22 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
clientid);
return NULL;
}
- spin_lock_irqsave(&clients_lock, flags);
- client = clientptr(clientid);
- if (client)
- goto __lock;
- if (clienttablock[clientid]) {
- spin_unlock_irqrestore(&clients_lock, flags);
- return NULL;
+ scoped_guard(spinlock_irqsave, &clients_lock) {
+ client = clientptr(clientid);
+ if (client)
+ return snd_seq_client_ref(client);
+ if (clienttablock[clientid])
+ return NULL;
}
- 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];
+ if (load_module) {
+ 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;
@@ -157,26 +145,33 @@ 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();
}
}
- spin_lock_irqsave(&clients_lock, flags);
- client = clientptr(clientid);
- if (client)
- goto __lock;
- spin_unlock_irqrestore(&clients_lock, flags);
+ scoped_guard(spinlock_irqsave, &clients_lock) {
+ client = clientptr(clientid);
+ if (client)
+ return snd_seq_client_ref(client);
+ }
}
#endif
return NULL;
+}
- __lock:
- snd_use_lock_use(&client->use_lock);
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
+/* get snd_seq_client object for the given id quickly */
+struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+{
+ return client_use_ptr(clientid, false);
+}
+
+/* get snd_seq_client object for the given id;
+ * if not found, retry after loading the modules
+ */
+static struct snd_seq_client *client_load_and_use_ptr(int clientid)
+{
+ return client_use_ptr(clientid, IS_ENABLED(CONFIG_MODULES));
}
static void usage_alloc(struct snd_seq_usage *res, int num)
@@ -203,7 +198,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;
@@ -222,27 +216,27 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
mutex_init(&client->ports_mutex);
INIT_LIST_HEAD(&client->ports_list_head);
mutex_init(&client->ioctl_mutex);
+ client->ump_endpoint_port = -1;
/* find free slot in the client table */
- spin_lock_irqsave(&clients_lock, flags);
- if (client_index < 0) {
- for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
- c < SNDRV_SEQ_MAX_CLIENTS;
- c++) {
- if (clienttab[c] || clienttablock[c])
- continue;
- clienttab[client->number = c] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
- }
- } else {
- if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
- clienttab[client->number = client_index] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
+ scoped_guard(spinlock_irq, &clients_lock) {
+ if (client_index < 0) {
+ for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
+ c < SNDRV_SEQ_MAX_CLIENTS;
+ c++) {
+ if (clienttab[c] || clienttablock[c])
+ continue;
+ clienttab[client->number = c] = client;
+ return client;
+ }
+ } else {
+ if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
+ clienttab[client->number = client_index] = client;
+ return client;
+ }
}
}
- spin_unlock_irqrestore(&clients_lock, flags);
+
snd_seq_pool_delete(&client->pool);
kfree(client);
return NULL; /* no free slot found or busy, return failure code */
@@ -251,46 +245,43 @@ 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;
+ scoped_guard(spinlock_irq, &clients_lock) {
+ clienttablock[client->number] = 1;
+ clienttab[client->number] = NULL;
+ }
snd_seq_delete_all_ports(client);
snd_seq_queue_client_leave(client->number);
- spin_lock_irqsave(&clients_lock, flags);
- clienttablock[client->number] = 1;
- clienttab[client->number] = NULL;
- spin_unlock_irqrestore(&clients_lock, flags);
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);
- clienttablock[client->number] = 0;
- spin_unlock_irqrestore(&clients_lock, flags);
+ scoped_guard(spinlock_irq, &clients_lock) {
+ clienttablock[client->number] = 0;
+ }
return 0;
}
static void seq_free_client(struct snd_seq_client * client)
{
- mutex_lock(&register_mutex);
- switch (client->type) {
- case NO_CLIENT:
- pr_warn("ALSA: seq: Trying to free unused client %d\n",
- client->number);
- break;
- case USER_CLIENT:
- case KERNEL_CLIENT:
- seq_free_client1(client);
- usage_free(&client_usage, 1);
- break;
+ scoped_guard(mutex, &register_mutex) {
+ switch (client->type) {
+ case NO_CLIENT:
+ pr_warn("ALSA: seq: Trying to free unused client %d\n",
+ client->number);
+ break;
+ case USER_CLIENT:
+ case KERNEL_CLIENT:
+ seq_free_client1(client);
+ usage_free(&client_usage, 1);
+ break;
- default:
- pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n",
- client->number, client->type);
+ default:
+ pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n",
+ client->number, client->type);
+ }
}
- mutex_unlock(&register_mutex);
snd_seq_system_client_ev_client_exit(client->number);
}
@@ -307,42 +298,38 @@ 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;
- client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- return -ENOMEM; /* failure code */
- }
-
- mode = snd_seq_file_flags(file);
- if (mode & SNDRV_SEQ_LFLG_INPUT)
- client->accept_input = 1;
- if (mode & SNDRV_SEQ_LFLG_OUTPUT)
- client->accept_output = 1;
-
- user = &client->data.user;
- user->fifo = NULL;
- user->fifo_pool_size = 0;
-
- if (mode & SNDRV_SEQ_LFLG_INPUT) {
- user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
- user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
- if (user->fifo == NULL) {
- seq_free_client1(client);
- kfree(client);
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ scoped_guard(mutex, &register_mutex) {
+ client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+ if (!client)
+ return -ENOMEM; /* failure code */
+
+ mode = snd_seq_file_flags(file);
+ if (mode & SNDRV_SEQ_LFLG_INPUT)
+ client->accept_input = 1;
+ if (mode & SNDRV_SEQ_LFLG_OUTPUT)
+ client->accept_output = 1;
+
+ user = &client->data.user;
+ user->fifo = NULL;
+ user->fifo_pool_size = 0;
+
+ if (mode & SNDRV_SEQ_LFLG_INPUT) {
+ user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
+ user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
+ if (user->fifo == NULL) {
+ seq_free_client1(client);
+ kfree(client);
+ return -ENOMEM;
+ }
}
- }
- usage_alloc(&client_usage, 1);
- client->type = USER_CLIENT;
- mutex_unlock(&register_mutex);
+ usage_alloc(&client_usage, 1);
+ client->type = USER_CLIENT;
+ }
c = client->number;
file->private_data = client;
@@ -367,6 +354,9 @@ 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);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ free_ump_info(client);
+#endif
put_pid(client->data.user.owner);
kfree(client);
}
@@ -374,6 +364,15 @@ static int snd_seq_release(struct inode *inode, struct file *file)
return 0;
}
+static bool event_is_compatible(const struct snd_seq_client *client,
+ const struct snd_seq_event *ev)
+{
+ if (snd_seq_ev_is_ump(ev) && !client->midi_version)
+ return false;
+ if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev))
+ return false;
+ return true;
+}
/* handle client read() */
/* possible error values:
@@ -387,6 +386,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
{
struct snd_seq_client *client = file->private_data;
struct snd_seq_fifo *fifo;
+ size_t aligned_size;
int err;
long result = 0;
struct snd_seq_event_cell *cell;
@@ -394,14 +394,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) {
@@ -413,45 +416,56 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
cell = NULL;
err = 0;
- snd_seq_fifo_lock(fifo);
+ guard(snd_seq_fifo)(fifo);
+
+ if (IS_ENABLED(CONFIG_SND_SEQ_UMP) && client->midi_version > 0)
+ aligned_size = sizeof(struct snd_seq_ump_event);
+ else
+ aligned_size = sizeof(struct snd_seq_event);
/* while data available in queue */
- while (count >= sizeof(struct snd_seq_event)) {
+ while (count >= aligned_size) {
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 (!event_is_compatible(client, &cell->event)) {
+ snd_seq_cell_free(cell);
+ cell = NULL;
+ continue;
}
if (snd_seq_ev_is_variable(&cell->event)) {
- struct snd_seq_event tmpev;
- tmpev = cell->event;
+ struct snd_seq_ump_event tmpev;
+
+ memcpy(&tmpev, &cell->event, aligned_size);
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
- if (copy_to_user(buf, &tmpev, sizeof(struct snd_seq_event))) {
+ if (copy_to_user(buf, &tmpev, aligned_size)) {
err = -EFAULT;
break;
}
- count -= sizeof(struct snd_seq_event);
- buf += sizeof(struct snd_seq_event);
+ count -= aligned_size;
+ buf += aligned_size;
err = snd_seq_expand_var_event(&cell->event, count,
(char __force *)buf, 0,
- sizeof(struct snd_seq_event));
+ aligned_size);
if (err < 0)
break;
result += err;
count -= err;
buf += err;
} else {
- if (copy_to_user(buf, &cell->event, sizeof(struct snd_seq_event))) {
+ if (copy_to_user(buf, &cell->event, aligned_size)) {
err = -EFAULT;
break;
}
- count -= sizeof(struct snd_seq_event);
- buf += sizeof(struct snd_seq_event);
+ count -= aligned_size;
+ buf += aligned_size;
}
snd_seq_cell_free(cell);
cell = NULL; /* to be sure */
- result += sizeof(struct snd_seq_event);
+ result += aligned_size;
}
if (err < 0) {
@@ -460,7 +474,6 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
if (err == -EAGAIN && result > 0)
err = 0;
}
- snd_seq_fifo_unlock(fifo);
return (err < 0) ? err : result;
}
@@ -478,28 +491,24 @@ static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
/*
* check if the destination client is available, and return the pointer
- * if filter is non-zero, client filter bitmap is tested.
*/
-static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event,
- int filter)
+static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event)
{
- struct snd_seq_client *dest;
+ struct snd_seq_client *dest __free(snd_seq_client) = NULL;
dest = snd_seq_client_use_ptr(event->dest.client);
if (dest == NULL)
return NULL;
if (! dest->accept_input)
- goto __not_avail;
+ return NULL;
+ if (snd_seq_ev_is_ump(event))
+ return no_free_ptr(dest); /* ok - no filter checks */
+
if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
! test_bit(event->type, dest->event_filter))
- goto __not_avail;
- if (filter && !(dest->filter & filter))
- goto __not_avail;
+ return NULL;
- return dest; /* ok - accessible */
-__not_avail:
- snd_seq_client_unlock(dest);
- return NULL;
+ return no_free_ptr(dest); /* ok - accessible */
}
@@ -538,7 +547,7 @@ static int bounce_error_event(struct snd_seq_client *client,
bounce_ev.data.quote.origin = event->dest;
bounce_ev.data.quote.event = event;
bounce_ev.data.quote.value = -err; /* use positive value */
- result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
+ result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1);
if (result < 0) {
client->event_lost++;
return result;
@@ -556,7 +565,7 @@ static int bounce_error_event(struct snd_seq_client *client,
static int update_timestamp_of_queue(struct snd_seq_event *event,
int queue, int real_time)
{
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = queueptr(queue);
if (! q)
@@ -564,78 +573,94 @@ 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);
event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
}
- queuefree(q);
return 1;
}
-
-/*
- * deliver an event to the specified destination.
- * if filter is non-zero, client filter bitmap is tested.
- *
- * RETURN VALUE: 0 : if succeeded
- * <0 : error
- */
-static int snd_seq_deliver_single_event(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int filter, int atomic, int hop)
+/* deliver a single event; called from below and UMP converter */
+int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
{
- struct snd_seq_client *dest = NULL;
- struct snd_seq_client_port *dest_port = NULL;
- int result = -ENOENT;
- int direct;
+ switch (dest->type) {
+ case USER_CLIENT:
+ if (!dest->data.user.fifo)
+ return 0;
+ return snd_seq_fifo_event_in(dest->data.user.fifo, event);
+ case KERNEL_CLIENT:
+ if (!dest_port->event_input)
+ return 0;
+ return dest_port->event_input(event,
+ snd_seq_ev_is_direct(event),
+ dest_port->private_data,
+ atomic, hop);
+ }
+ return 0;
+}
- direct = snd_seq_ev_is_direct(event);
+/* deliver a single event; called from snd_seq_deliver_single_event() */
+static int _snd_seq_deliver_single_event(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_client *dest __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *dest_port __free(snd_seq_port) = NULL;
- dest = get_event_dest_client(event, filter);
+ dest = get_event_dest_client(event);
if (dest == NULL)
- goto __skip;
+ return -ENOENT;
dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
if (dest_port == NULL)
- goto __skip;
+ return -ENOENT;
/* check permission */
- if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
- result = -EPERM;
- goto __skip;
- }
-
+ if (!check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE))
+ return -EPERM;
+
if (dest_port->timestamping)
update_timestamp_of_queue(event, dest_port->time_queue,
dest_port->time_real);
- switch (dest->type) {
- case USER_CLIENT:
- if (dest->data.user.fifo)
- result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
- break;
-
- case KERNEL_CLIENT:
- if (dest_port->event_input == NULL)
- break;
- result = dest_port->event_input(event, direct,
- dest_port->private_data,
- atomic, hop);
- break;
- default:
- break;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (snd_seq_ev_is_ump(event)) {
+ if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT))
+ return snd_seq_deliver_from_ump(client, dest, dest_port,
+ event, atomic, hop);
+ else if (dest->type == USER_CLIENT &&
+ !snd_seq_client_is_ump(dest))
+ return 0; // drop the event
+ } else if (snd_seq_client_is_ump(dest)) {
+ if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT))
+ return snd_seq_deliver_to_ump(client, dest, dest_port,
+ event, atomic, hop);
}
+#endif /* CONFIG_SND_SEQ_UMP */
- __skip:
- if (dest_port)
- snd_seq_port_unlock(dest_port);
- if (dest)
- snd_seq_client_unlock(dest);
+ return __snd_seq_deliver_single_event(dest, dest_port, event,
+ atomic, hop);
+}
- if (result < 0 && !direct) {
- result = bounce_error_event(client, event, result, atomic, hop);
- }
+/*
+ * deliver an event to the specified destination.
+ * if filter is non-zero, client filter bitmap is tested.
+ *
+ * RETURN VALUE: 0 : if succeeded
+ * <0 : error
+ */
+static int snd_seq_deliver_single_event(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ int result = _snd_seq_deliver_single_event(client, event, atomic, hop);
+
+ if (result < 0 && !snd_seq_ev_is_direct(event))
+ return bounce_error_event(client, event, result, atomic, hop);
return result;
}
@@ -643,21 +668,26 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
/*
* send the event to all subscribers:
*/
-static int deliver_to_subscribers(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int atomic, int hop)
+static int __deliver_to_subscribers(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int port, int atomic, int hop)
{
+ struct snd_seq_client_port *src_port __free(snd_seq_port) = NULL;
struct snd_seq_subscribers *subs;
int err, result = 0, num_ev = 0;
- struct snd_seq_event event_saved;
- struct snd_seq_client_port *src_port;
+ union __snd_seq_event event_saved;
+ size_t saved_size;
struct snd_seq_port_subs_info *grp;
- src_port = snd_seq_port_use_ptr(client, event->source.port);
- if (src_port == NULL)
- return -EINVAL; /* invalid source port */
+ if (port < 0)
+ return 0;
+ src_port = snd_seq_port_use_ptr(client, port);
+ if (!src_port)
+ return 0;
+
/* save original event record */
- event_saved = *event;
+ saved_size = snd_seq_event_packet_size(event);
+ memcpy(&event_saved, event, saved_size);
grp = &src_port->c_src;
/* lock list */
@@ -674,8 +704,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
/* convert time according to flag with subscription */
update_timestamp_of_queue(event, subs->info.queue,
subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
- err = snd_seq_deliver_single_event(client, event,
- 0, atomic, hop);
+ err = snd_seq_deliver_single_event(client, event, atomic, hop);
if (err < 0) {
/* save first error that occurs and continue */
if (!result)
@@ -684,103 +713,47 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
}
num_ev++;
/* restore original event record */
- *event = event_saved;
+ memcpy(event, &event_saved, saved_size);
}
if (atomic)
read_unlock(&grp->list_lock);
else
up_read(&grp->list_mutex);
- *event = event_saved; /* restore */
- snd_seq_port_unlock(src_port);
+ memcpy(event, &event_saved, saved_size);
return (result < 0) ? result : num_ev;
}
-
-#ifdef SUPPORT_BROADCAST
-/*
- * broadcast to all ports:
- */
-static int port_broadcast_event(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int atomic, int hop)
-{
- int num_ev = 0, err, result = 0;
- struct snd_seq_client *dest_client;
- struct snd_seq_client_port *port;
-
- dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
- if (dest_client == NULL)
- return 0; /* no matching destination */
-
- read_lock(&dest_client->ports_lock);
- list_for_each_entry(port, &dest_client->ports_list_head, list) {
- event->dest.port = port->addr.port;
- /* pass NULL as source client to avoid error bounce */
- err = snd_seq_deliver_single_event(NULL, event,
- SNDRV_SEQ_FILTER_BROADCAST,
- atomic, hop);
- 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 (result < 0) ? result : num_ev;
-}
-
-/*
- * send the event to all clients:
- * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
- */
-static int broadcast_event(struct snd_seq_client *client,
- struct snd_seq_event *event, int atomic, int hop)
+static int deliver_to_subscribers(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
{
- int err, result = 0, num_ev = 0;
- int dest;
- struct snd_seq_addr addr;
-
- addr = event->dest; /* save */
-
- for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
- /* don't send to itself */
- if (dest == client->number)
- continue;
- event->dest.client = dest;
- event->dest.port = addr.port;
- if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
- err = port_broadcast_event(client, event, atomic, hop);
- else
- /* pass NULL as source client to avoid error bounce */
- err = snd_seq_deliver_single_event(NULL, event,
- SNDRV_SEQ_FILTER_BROADCAST,
- atomic, hop);
- if (err < 0) {
- /* save first error that occurs and continue */
- if (!result)
- result = err;
- continue;
- }
- num_ev += err;
- }
- event->dest = addr; /* restore */
- return (result < 0) ? result : num_ev;
-}
-
+ int ret;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ int ret2;
+#endif
-/* multicast - not supported yet */
-static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event,
- int atomic, int hop)
-{
- pr_debug("ALSA: seq: multicast not supported yet.\n");
- return 0; /* ignored */
+ ret = __deliver_to_subscribers(client, event,
+ event->source.port, atomic, hop);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (!snd_seq_client_is_ump(client) || client->ump_endpoint_port < 0)
+ return ret;
+ /* If it's an event from EP port (and with a UMP group),
+ * deliver to subscribers of the corresponding UMP group port, too.
+ * Or, if it's from non-EP port, deliver to subscribers of EP port, too.
+ */
+ if (event->source.port == client->ump_endpoint_port)
+ ret2 = __deliver_to_subscribers(client, event,
+ snd_seq_ump_group_port(event),
+ atomic, hop);
+ else
+ ret2 = __deliver_to_subscribers(client, event,
+ client->ump_endpoint_port,
+ atomic, hop);
+ if (ret2 < 0)
+ return ret2;
+#endif
+ return ret;
}
-#endif /* SUPPORT_BROADCAST */
-
/* deliver an event to the destination port(s).
* if the event is to subscribers or broadcast, the event is dispatched
@@ -810,17 +783,8 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
result = deliver_to_subscribers(client, event, atomic, hop);
-#ifdef SUPPORT_BROADCAST
- else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
- event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
- result = broadcast_event(client, event, atomic, hop);
- else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
- result = multicast_event(client, event, atomic, hop);
- else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
- result = port_broadcast_event(client, event, atomic, hop);
-#endif
else
- result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
+ result = snd_seq_deliver_single_event(client, event, atomic, hop);
return result;
}
@@ -837,7 +801,7 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
*/
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
- struct snd_seq_client *client;
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
int result;
if (snd_BUG_ON(!cell))
@@ -849,7 +813,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
return -EINVAL;
}
- if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+ if (!snd_seq_ev_is_ump(&cell->event) &&
+ cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
/* NOTE event:
* the event cell is re-used as a NOTE-OFF event and
* enqueued again.
@@ -873,7 +838,7 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
/* add the duration time */
switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
case SNDRV_SEQ_TIME_STAMP_TICK:
- ev->time.tick += ev->data.note.duration;
+ cell->event.time.tick += ev->data.note.duration;
break;
case SNDRV_SEQ_TIME_STAMP_REAL:
/* unit for duration is ms */
@@ -898,7 +863,6 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
snd_seq_cell_free(cell);
}
- snd_seq_client_unlock(client);
return result;
}
@@ -910,7 +874,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;
@@ -919,24 +884,18 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
event->queue = SNDRV_SEQ_QUEUE_DIRECT;
- } else
-#ifdef SUPPORT_BROADCAST
- if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
- event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
- event->queue = SNDRV_SEQ_QUEUE_DIRECT;
- }
-#endif
- if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+ } else if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
/* check presence of source port */
- struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, event->source.port);
- if (src_port == NULL)
+ struct snd_seq_client_port *src_port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(client, event->source.port);
+ if (!src_port)
return -EINVAL;
- snd_seq_port_unlock(src_port);
}
/* direct event processing without enqueued */
if (snd_seq_ev_is_direct(event)) {
- if (event->type == SNDRV_SEQ_EVENT_NOTE)
+ if (!snd_seq_ev_is_ump(event) &&
+ event->type == SNDRV_SEQ_EVENT_NOTE)
return -EINVAL; /* this event must be enqueued! */
return snd_seq_deliver_event(client, event, atomic, hop);
}
@@ -948,12 +907,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;
}
@@ -1003,8 +964,9 @@ 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;
- struct snd_seq_event event;
+ int err, handled;
+ union __snd_seq_event __event;
+ struct snd_seq_event *ev = &__event.legacy;
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
return -ENXIO;
@@ -1016,70 +978,101 @@ 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);
- if (copy_from_user(&event, buf, len)) {
+ len = sizeof(struct snd_seq_event);
+ if (copy_from_user(ev, buf, len)) {
err = -EFAULT;
break;
}
- event.source.client = client->number; /* fill in client number */
+ /* read in the rest bytes for UMP events */
+ if (snd_seq_ev_is_ump(ev)) {
+ if (count < sizeof(struct snd_seq_ump_event))
+ break;
+ if (copy_from_user((char *)ev + len, buf + len,
+ sizeof(struct snd_seq_ump_event) - len)) {
+ err = -EFAULT;
+ break;
+ }
+ len = sizeof(struct snd_seq_ump_event);
+ }
+
+ ev->source.client = client->number; /* fill in client number */
/* Check for extension data length */
- if (check_event_type_and_length(&event)) {
+ if (check_event_type_and_length(ev)) {
err = -EINVAL;
break;
}
- /* check for special events */
- if (event.type == SNDRV_SEQ_EVENT_NONE)
- goto __skip_event;
- else if (snd_seq_ev_is_reserved(&event)) {
+ if (!event_is_compatible(client, ev)) {
err = -EINVAL;
break;
}
- if (snd_seq_ev_is_variable(&event)) {
- int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+ /* check for special events */
+ if (!snd_seq_ev_is_ump(ev)) {
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ goto __skip_event;
+ else if (snd_seq_ev_is_reserved(ev)) {
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (snd_seq_ev_is_variable(ev)) {
+ int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
if ((size_t)(extlen + len) > count) {
/* back out, will get an error this time or next */
err = -EINVAL;
break;
}
/* set user space pointer */
- event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
- event.data.ext.ptr = (char __force *)buf
- + sizeof(struct snd_seq_event);
+ ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+ ev->data.ext.ptr = (char __force *)buf + len;
len += extlen; /* increment data length */
} else {
#ifdef CONFIG_COMPAT
- if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
- void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]);
- event.data.ext.ptr = ptr;
- }
+ if (client->convert32 && snd_seq_ev_is_varusr(ev))
+ ev->data.ext.ptr =
+ (void __force *)compat_ptr(ev->data.raw32.d[1]);
#endif
}
/* ok, enqueue it */
- err = snd_seq_client_enqueue_event(client, &event, file,
+ err = snd_seq_client_enqueue_event(client, ev, 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;
}
@@ -1094,22 +1087,21 @@ static __poll_t snd_seq_poll(struct file *file, poll_table * wait)
/* 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) {
/* 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;
+ if (snd_seq_pool_poll_wait(client->pool, file, wait))
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
@@ -1126,6 +1118,12 @@ static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
return 0;
}
+static int snd_seq_ioctl_user_pversion(struct snd_seq_client *client, void *arg)
+{
+ client->user_pversion = *(unsigned int *)arg;
+ return 0;
+}
+
static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
{
int *client_id = arg;
@@ -1156,34 +1154,24 @@ static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
{
struct snd_seq_running_info *info = arg;
- struct snd_seq_client *cptr;
- int err = 0;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* requested client number */
- cptr = snd_seq_client_use_ptr(info->client);
+ cptr = client_load_and_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
#ifdef SNDRV_BIG_ENDIAN
- if (!info->big_endian) {
- err = -EINVAL;
- goto __err;
- }
+ if (!info->big_endian)
+ return -EINVAL;
#else
- if (info->big_endian) {
- err = -EINVAL;
- goto __err;
- }
-
+ if (info->big_endian)
+ return -EINVAL;
#endif
- if (info->cpu_mode > sizeof(long)) {
- err = -EINVAL;
- goto __err;
- }
+ if (info->cpu_mode > sizeof(long))
+ return -EINVAL;
cptr->convert32 = (info->cpu_mode < sizeof(long));
- __err:
- snd_seq_client_unlock(cptr);
- return err;
+ return 0;
}
/* CLIENT_INFO ioctl() */
@@ -1194,10 +1182,11 @@ static void get_client_info(struct snd_seq_client *cptr,
/* fill the info fields */
info->type = cptr->type;
- strcpy(info->name, cptr->name);
+ strscpy(info->name, cptr->name);
info->filter = cptr->filter;
info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32);
+ info->group_filter = cptr->group_filter;
info->num_ports = cptr->num_ports;
if (cptr->type == USER_CLIENT)
@@ -1210,6 +1199,7 @@ static void get_client_info(struct snd_seq_client *cptr,
else
info->card = -1;
+ info->midi_version = cptr->midi_version;
memset(info->reserved, 0, sizeof(info->reserved));
}
@@ -1217,16 +1207,14 @@ static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_info *client_info = arg;
- struct snd_seq_client *cptr;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* requested client number */
- cptr = snd_seq_client_use_ptr(client_info->client);
+ cptr = client_load_and_use_ptr(client_info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
get_client_info(cptr, client_info);
- snd_seq_client_unlock(cptr);
-
return 0;
}
@@ -1244,13 +1232,30 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
if (client->type != client_info->type)
return -EINVAL;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) {
+ /* check validity of midi_version field */
+ if (client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
+ return -EINVAL;
+
+ /* check if UMP is supported in kernel */
+ if (!IS_ENABLED(CONFIG_SND_SEQ_UMP) &&
+ client_info->midi_version > 0)
+ return -EINVAL;
+ }
+
/* fill the info fields */
if (client_info->name[0])
- strlcpy(client->name, client_info->name, sizeof(client->name));
+ strscpy(client->name, client_info->name, sizeof(client->name));
client->filter = client_info->filter;
client->event_lost = client_info->event_lost;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
+ client->midi_version = client_info->midi_version;
memcpy(client->event_filter, client_info->event_filter, 32);
+ client->group_filter = client_info->group_filter;
+
+ /* notify the change */
+ snd_seq_system_client_ev_client_change(client->number);
return 0;
}
@@ -1264,24 +1269,30 @@ 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_callback *callback;
- int port_idx;
+ int port_idx, err;
/* it is not allowed to create the port for an another client */
if (info->addr.client != client->number)
return -EPERM;
+ if (client->type == USER_CLIENT && info->kernel)
+ return -EINVAL;
+ if ((info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) &&
+ client->ump_endpoint_port >= 0)
+ return -EBUSY;
- 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) {
- port_idx = port->addr.port;
- snd_seq_port_unlock(port);
- snd_seq_delete_port(client, port_idx);
+ if (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT)
+ port_idx = info->addr.port;
+ else
+ port_idx = -1;
+ if (port_idx >= SNDRV_SEQ_ADDRESS_UNKNOWN)
return -EINVAL;
- }
+ err = snd_seq_create_port(client, port_idx, &port);
+ if (err < 0)
+ return err;
+
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;
@@ -1297,6 +1308,8 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
info->addr = port->addr;
snd_seq_set_port_info(port, info);
+ if (info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT)
+ client->ump_endpoint_port = port->addr.port;
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
snd_seq_port_unlock(port);
@@ -1316,8 +1329,11 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
return -EPERM;
err = snd_seq_delete_port(client, info->addr.port);
- if (err >= 0)
+ if (err >= 0) {
+ if (client->ump_endpoint_port == info->addr.port)
+ client->ump_endpoint_port = -1;
snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
+ }
return err;
}
@@ -1328,24 +1344,19 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *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_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- cptr = snd_seq_client_use_ptr(info->addr.client);
+ cptr = client_load_and_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
port = snd_seq_port_use_ptr(cptr, info->addr.port);
- if (port == NULL) {
- snd_seq_client_unlock(cptr);
+ if (port == NULL)
return -ENOENT; /* don't change */
- }
/* get port info */
snd_seq_get_port_info(port, info);
- snd_seq_port_unlock(port);
- snd_seq_client_unlock(cptr);
-
return 0;
}
@@ -1356,14 +1367,16 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *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_client_port *port __free(snd_seq_port) = NULL;
if (info->addr.client != client->number) /* only set our own ports ! */
return -EPERM;
port = snd_seq_port_use_ptr(client, info->addr.port);
if (port) {
snd_seq_set_port_info(port, info);
- snd_seq_port_unlock(port);
+ /* notify the change */
+ snd_seq_system_client_ev_port_change(info->addr.client,
+ info->addr.port);
}
return 0;
}
@@ -1420,7 +1433,7 @@ int snd_seq_client_notify_subscription(int client, int port,
event.data.connect.dest = info->dest;
event.data.connect.sender = info->sender;
- return snd_seq_system_notify(client, port, &event); /* non-atomic */
+ return snd_seq_system_notify(client, port, &event, false); /* non-atomic */
}
@@ -1431,37 +1444,34 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
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;
-
- if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
- goto __end;
- if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
- goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
- goto __end;
+ struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
+ struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
+ int result;
+
+ receiver = client_load_and_use_ptr(subs->dest.client);
+ if (!receiver)
+ return -EINVAL;
+ sender = client_load_and_use_ptr(subs->sender.client);
+ if (!sender)
+ return -EINVAL;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -EINVAL;
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
+ return -EINVAL;
result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
- goto __end;
+ return result;
/* connect them */
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);
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (dport)
- snd_seq_port_unlock(dport);
- if (sender)
- snd_seq_client_unlock(sender);
- if (receiver)
- snd_seq_client_unlock(receiver);
return result;
}
@@ -1473,36 +1483,33 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
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;
-
- if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
- goto __end;
- if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
- goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
- goto __end;
+ struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
+ struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
+ int result;
+
+ receiver = snd_seq_client_use_ptr(subs->dest.client);
+ if (!receiver)
+ return -ENXIO;
+ sender = snd_seq_client_use_ptr(subs->sender.client);
+ if (!sender)
+ return -ENXIO;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -ENXIO;
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
+ return -ENXIO;
result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
- goto __end;
+ return result;
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);
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (dport)
- snd_seq_port_unlock(dport);
- if (sender)
- snd_seq_client_unlock(sender);
- if (receiver)
- snd_seq_client_unlock(receiver);
return result;
}
@@ -1511,7 +1518,7 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
if (IS_ERR(q))
@@ -1524,8 +1531,7 @@ static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
/* 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));
- snd_use_lock_free(&q->use_lock);
+ strscpy(q->name, info->name, sizeof(q->name));
return 0;
}
@@ -1543,7 +1549,7 @@ static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = queueptr(info->queue);
if (q == NULL)
@@ -1553,8 +1559,7 @@ static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
info->queue = q->queue;
info->owner = q->owner;
info->locked = q->locked;
- strlcpy(info->name, q->name, sizeof(info->name));
- queuefree(q);
+ strscpy(info->name, q->name, sizeof(info->name));
return 0;
}
@@ -1564,7 +1569,7 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (info->owner != client->number)
return -EINVAL;
@@ -1582,12 +1587,9 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
q = queueptr(info->queue);
if (! q)
return -EINVAL;
- if (q->owner != client->number) {
- queuefree(q);
+ if (q->owner != client->number)
return -EPERM;
- }
- strlcpy(q->name, info->name, sizeof(q->name));
- queuefree(q);
+ strscpy(q->name, info->name, sizeof(q->name));
return 0;
}
@@ -1597,7 +1599,7 @@ static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = snd_seq_queue_find_name(info->name);
if (q == NULL)
@@ -1605,7 +1607,6 @@ static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
info->queue = q->queue;
info->owner = q->owner;
info->locked = q->locked;
- queuefree(q);
return 0;
}
@@ -1615,7 +1616,7 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_status *status = arg;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
queue = queueptr(status->queue);
@@ -1627,13 +1628,12 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
tmr = queue->timer;
status->events = queue->tickq->cells + queue->timeq->cells;
- status->time = snd_seq_timer_get_cur_time(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->flags = queue->flags;
- queuefree(queue);
return 0;
}
@@ -1644,7 +1644,7 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_tempo *tempo = arg;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
queue = queueptr(tempo->queue);
@@ -1659,7 +1659,8 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
tempo->ppq = tmr->ppq;
tempo->skew_value = tmr->skew;
tempo->skew_base = tmr->skew_base;
- queuefree(queue);
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = tmr->tempo_base;
return 0;
}
@@ -1680,6 +1681,8 @@ static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
struct snd_seq_queue_tempo *tempo = arg;
int result;
+ if (client->user_pversion < SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = 0;
result = snd_seq_set_queue_tempo(client->number, tempo);
return result < 0 ? result : 0;
}
@@ -1690,17 +1693,14 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_timer *timer = arg;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
queue = queueptr(timer->queue);
if (queue == NULL)
return -EINVAL;
- if (mutex_lock_interruptible(&queue->timer_mutex)) {
- queuefree(queue);
- return -ERESTARTSYS;
- }
+ guard(mutex)(&queue->timer_mutex);
tmr = queue->timer;
memset(timer, 0, sizeof(*timer));
timer->queue = queue->queue;
@@ -1710,8 +1710,6 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
timer->u.alsa.id = tmr->alsa_id;
timer->u.alsa.resolution = tmr->preferred_resolution;
}
- mutex_unlock(&queue->timer_mutex);
- queuefree(queue);
return 0;
}
@@ -1728,16 +1726,13 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
return -EINVAL;
if (snd_seq_queue_check_access(timer->queue, client->number)) {
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
q = queueptr(timer->queue);
if (q == NULL)
return -ENXIO;
- if (mutex_lock_interruptible(&q->timer_mutex)) {
- queuefree(q);
- return -ERESTARTSYS;
- }
+ guard(mutex)(&q->timer_mutex);
tmr = q->timer;
snd_seq_queue_timer_close(timer->queue);
tmr->type = timer->type;
@@ -1746,8 +1741,6 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
tmr->preferred_resolution = timer->u.alsa.resolution;
}
result = snd_seq_queue_timer_open(timer->queue);
- mutex_unlock(&q->timer_mutex);
- queuefree(q);
} else {
return -EPERM;
}
@@ -1795,9 +1788,9 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_pool *info = arg;
- struct snd_seq_client *cptr;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
- cptr = snd_seq_client_use_ptr(info->client);
+ cptr = client_load_and_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT;
memset(info, 0, sizeof(*info));
@@ -1809,13 +1802,11 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
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_free = snd_seq_fifo_unused_cells(cptr->data.user.fifo);
} else {
info->input_pool = 0;
info->input_free = 0;
}
- snd_seq_client_unlock(cptr);
return 0;
}
@@ -1834,9 +1825,11 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
(! snd_seq_write_pool_allocated(client) ||
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_pool_mark_closing(client->pool);
- snd_seq_queue_client_leave_cells(client->number);
snd_seq_pool_done(client->pool);
}
client->pool->size = info->output_pool;
@@ -1895,30 +1888,16 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
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_subscribers *p;
-
- result = -EINVAL;
- if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
- goto __end;
- p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest);
- if (p) {
- result = 0;
- *subs = p->info;
- } else
- result = -ENOENT;
-
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (sender)
- snd_seq_client_unlock(sender);
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
- return result;
+ sender = client_load_and_use_ptr(subs->sender.client);
+ if (!sender)
+ return -EINVAL;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -EINVAL;
+ return snd_seq_port_get_subscription(&sport->c_src, &subs->dest, subs);
}
@@ -1928,17 +1907,18 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
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_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
struct snd_seq_port_subs_info *group;
struct list_head *p;
int i;
- if ((cptr = snd_seq_client_use_ptr(subs->root.client)) == NULL)
- goto __end;
- if ((port = snd_seq_port_use_ptr(cptr, subs->root.port)) == NULL)
- goto __end;
+ cptr = client_load_and_use_ptr(subs->root.client);
+ if (!cptr)
+ return -ENXIO;
+ port = snd_seq_port_use_ptr(cptr, subs->root.port);
+ if (!port)
+ return -ENXIO;
switch (subs->type) {
case SNDRV_SEQ_QUERY_SUBS_READ:
@@ -1948,14 +1928,13 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
group = &port->c_dest;
break;
default:
- goto __end;
+ return -ENXIO;
}
- down_read(&group->list_mutex);
+ guard(rwsem_read)(&group->list_mutex);
/* search for the subscriber */
subs->num_subs = group->count;
i = 0;
- result = -ENOENT;
list_for_each(p, &group->list_head) {
if (i++ == subs->index) {
/* found! */
@@ -1969,19 +1948,11 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
}
subs->flags = s->info.flags;
subs->queue = s->info.queue;
- result = 0;
- break;
+ return 0;
}
}
- up_read(&group->list_mutex);
- __end:
- if (port)
- snd_seq_port_unlock(port);
- if (cptr)
- snd_seq_client_unlock(cptr);
-
- return result;
+ return -ENOENT;
}
@@ -1992,24 +1963,21 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_info *info = arg;
- struct snd_seq_client *cptr = NULL;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* search for next client */
- 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 */
+ cptr = client_load_and_use_ptr(info->client);
+ if (cptr) {
+ get_client_info(cptr, info);
+ return 0; /* found */
+ }
}
- if (cptr == NULL)
- return -ENOENT;
-
- get_client_info(cptr, info);
- snd_seq_client_unlock(cptr);
-
- return 0;
+ return -ENOENT;
}
/*
@@ -2019,30 +1987,163 @@ static int snd_seq_ioctl_query_next_port(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 = NULL;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- cptr = snd_seq_client_use_ptr(info->addr.client);
+ cptr = client_load_and_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);
- if (port == NULL) {
- snd_seq_client_unlock(cptr);
+ if (port == NULL)
return -ENOENT;
- }
/* get port info */
info->addr = port->addr;
snd_seq_get_port_info(port, info);
- snd_seq_port_unlock(port);
- snd_seq_client_unlock(cptr);
return 0;
}
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+#define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1)
+
+static void free_ump_info(struct snd_seq_client *client)
+{
+ int i;
+
+ if (!client->ump_info)
+ return;
+ for (i = 0; i < NUM_UMP_INFOS; i++)
+ kfree(client->ump_info[i]);
+ kfree(client->ump_info);
+ client->ump_info = NULL;
+}
+
+static void terminate_ump_info_strings(void *p, int type)
+{
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) {
+ struct snd_ump_endpoint_info *ep = p;
+ ep->name[sizeof(ep->name) - 1] = 0;
+ } else {
+ struct snd_ump_block_info *bp = p;
+ bp->name[sizeof(bp->name) - 1] = 0;
+ }
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void dump_ump_info(struct snd_info_buffer *buffer,
+ struct snd_seq_client *client)
+{
+ struct snd_ump_endpoint_info *ep;
+ struct snd_ump_block_info *bp;
+ int i;
+
+ if (!client->ump_info)
+ return;
+ ep = client->ump_info[SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT];
+ if (ep && *ep->name)
+ snd_iprintf(buffer, " UMP Endpoint: \"%s\"\n", ep->name);
+ for (i = 0; i < SNDRV_UMP_MAX_BLOCKS; i++) {
+ bp = client->ump_info[i + 1];
+ if (bp && *bp->name) {
+ snd_iprintf(buffer, " UMP Block %d: \"%s\" [%s]\n",
+ i, bp->name,
+ bp->active ? "Active" : "Inactive");
+ snd_iprintf(buffer, " Groups: %d-%d\n",
+ bp->first_group + 1,
+ bp->first_group + bp->num_groups);
+ }
+ }
+}
+#endif
+
+/* UMP-specific ioctls -- called directly without data copy */
+static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_seq_client_ump_info __user *argp =
+ (struct snd_seq_client_ump_info __user *)arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ int client, type, err = 0;
+ size_t size;
+ void *p;
+
+ if (get_user(client, &argp->client) || get_user(type, &argp->type))
+ return -EFAULT;
+ if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO &&
+ caller->number != client)
+ return -EPERM;
+ if (type < 0 || type >= NUM_UMP_INFOS)
+ return -EINVAL;
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
+ size = sizeof(struct snd_ump_endpoint_info);
+ else
+ size = sizeof(struct snd_ump_block_info);
+ cptr = client_load_and_use_ptr(client);
+ if (!cptr)
+ return -ENOENT;
+
+ scoped_guard(mutex, &cptr->ioctl_mutex) {
+ if (!cptr->midi_version) {
+ err = -EBADFD;
+ break;
+ }
+
+ if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) {
+ if (!cptr->ump_info)
+ p = NULL;
+ else
+ p = cptr->ump_info[type];
+ if (!p) {
+ err = -ENODEV;
+ break;
+ }
+ if (copy_to_user(argp->info, p, size)) {
+ err = -EFAULT;
+ break;
+ }
+ } else {
+ if (cptr->type != USER_CLIENT) {
+ err = -EBADFD;
+ break;
+ }
+ if (!cptr->ump_info) {
+ cptr->ump_info = kcalloc(NUM_UMP_INFOS,
+ sizeof(void *), GFP_KERNEL);
+ if (!cptr->ump_info) {
+ err = -ENOMEM;
+ break;
+ }
+ }
+ p = memdup_user(argp->info, size);
+ if (IS_ERR(p)) {
+ err = PTR_ERR(p);
+ break;
+ }
+ kfree(cptr->ump_info[type]);
+ terminate_ump_info_strings(p, type);
+ cptr->ump_info[type] = p;
+ }
+
+ }
+ if (!err && cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO) {
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
+ snd_seq_system_ump_notify(client, 0,
+ SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ false);
+ else
+ snd_seq_system_ump_notify(client, type - 1,
+ SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
+ false);
+ }
+ return err;
+}
+#endif
+
/* -------------------------------------------------------- */
static const struct ioctl_handler {
@@ -2050,6 +2151,7 @@ static const struct ioctl_handler {
int (*func)(struct snd_seq_client *client, void *arg);
} ioctl_handlers[] = {
{ SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion },
+ { SNDRV_SEQ_IOCTL_USER_PVERSION, snd_seq_ioctl_user_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 },
@@ -2112,6 +2214,15 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd,
if (snd_BUG_ON(!client))
return -ENXIO;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ /* exception - handling large data */
+ switch (cmd) {
+ case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+ case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
+ return snd_seq_ioctl_client_ump_info(client, cmd, arg);
+ }
+#endif
+
for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
if (handler->cmd == cmd)
break;
@@ -2131,9 +2242,9 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd,
return -EFAULT;
}
- mutex_lock(&client->ioctl_mutex);
- err = handler->func(client, &buf);
- mutex_unlock(&client->ioctl_mutex);
+ scoped_guard(mutex, &client->ioctl_mutex) {
+ err = handler->func(client, &buf);
+ }
if (err >= 0) {
/* Some commands includes a bug in 'dir' field. */
if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
@@ -2170,34 +2281,32 @@ 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;
+ scoped_guard(mutex, &register_mutex) {
- if (card) {
- client_index += SNDRV_SEQ_GLOBAL_CLIENTS
- + card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
- if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
- client_index = -1;
- }
+ if (card) {
+ client_index += SNDRV_SEQ_GLOBAL_CLIENTS
+ + card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
+ if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
+ client_index = -1;
+ }
- /* empty write queue as default */
- client = seq_create_client1(client_index, 0);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- return -EBUSY; /* failure code */
- }
- usage_alloc(&client_usage, 1);
+ /* empty write queue as default */
+ client = seq_create_client1(client_index, 0);
+ if (client == NULL)
+ return -EBUSY; /* failure code */
+ usage_alloc(&client_usage, 1);
- client->accept_input = 1;
- client->accept_output = 1;
- client->data.kernel.card = card;
+ client->accept_input = 1;
+ client->accept_output = 1;
+ client->data.kernel.card = card;
+ client->user_pversion = SNDRV_SEQ_VERSION;
- va_start(args, name_fmt);
- vsnprintf(client->name, sizeof(client->name), name_fmt, args);
- va_end(args);
+ va_start(args, name_fmt);
+ vsnprintf(client->name, sizeof(client->name), name_fmt, args);
+ va_end(args);
- client->type = KERNEL_CLIENT;
- mutex_unlock(&register_mutex);
+ client->type = KERNEL_CLIENT;
+ }
/* make others aware this new client */
snd_seq_system_client_ev_client_start(client->number);
@@ -2225,23 +2334,25 @@ int snd_seq_delete_kernel_client(int client)
}
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;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
- if (ev->type == SNDRV_SEQ_EVENT_NONE)
- return 0; /* ignore this */
- if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
- return -EINVAL; /* quoted events can't be enqueued */
+ if (!snd_seq_ev_is_ump(ev)) {
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0; /* ignore this */
+ if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+ return -EINVAL; /* quoted events can't be enqueued */
+ }
/* fill in client number */
ev->source.client = client;
@@ -2249,44 +2360,21 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
if (check_event_type_and_length(ev))
return -EINVAL;
- cptr = snd_seq_client_use_ptr(client);
+ cptr = client_load_and_use_ptr(client);
if (cptr == NULL)
return -EINVAL;
- if (! cptr->accept_output)
- result = -EPERM;
- else /* send it */
- result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
-
- 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);
+ if (!cptr->accept_output) {
+ return -EPERM;
+ } else { /* send it */
+ guard(mutex)(&cptr->ioctl_mutex);
+ return snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+ false, 0,
+ &cptr->ioctl_mutex);
+ }
}
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.
@@ -2297,8 +2385,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
int atomic, int hop)
{
- struct snd_seq_client *cptr;
- int result;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -2315,15 +2402,27 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
return -EINVAL;
if (!cptr->accept_output)
- result = -EPERM;
+ return -EPERM;
else
- result = snd_seq_deliver_event(cptr, ev, atomic, hop);
-
- snd_seq_client_unlock(cptr);
- return result;
+ return snd_seq_deliver_event(cptr, ev, atomic, hop);
}
EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
+static int call_seq_client_ctl(struct snd_seq_client *client,
+ unsigned int cmd, void *arg)
+{
+ const struct ioctl_handler *handler;
+
+ 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;
+}
+
/**
* snd_seq_kernel_client_ctl - operate a command for a client with data in
* kernel space.
@@ -2338,24 +2437,29 @@ EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
*/
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{
- const struct ioctl_handler *handler;
struct snd_seq_client *client;
client = clientptr(clientid);
if (client == NULL)
return -ENXIO;
- 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;
+ return call_seq_client_ctl(client, cmd, arg);
}
EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
+/* a similar like above but taking locks; used only from OSS sequencer layer */
+int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg)
+{
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
+
+ client = client_load_and_use_ptr(clientid);
+ if (!client)
+ return -ENXIO;
+ guard(mutex)(&client->ioctl_mutex);
+ return call_seq_client_ctl(client, cmd, arg);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_ioctl);
+
/* exported (for OSS emulator) */
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
{
@@ -2365,14 +2469,27 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
if (client == NULL)
return -ENXIO;
- if (! snd_seq_write_pool_allocated(client))
- return 1;
if (snd_seq_pool_poll_wait(client->pool, file, wait))
return 1;
return 0;
}
EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+/* get a sequencer client object; for internal use from a kernel client */
+struct snd_seq_client *snd_seq_kernel_client_get(int id)
+{
+ return snd_seq_client_use_ptr(id);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_get);
+
+/* put a sequencer client object; for internal use from a kernel client */
+void snd_seq_kernel_client_put(struct snd_seq_client *cptr)
+{
+ if (cptr)
+ snd_seq_client_unref(cptr);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_put);
+
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_SND_PROC_FS
@@ -2387,11 +2504,9 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
struct snd_seq_subscribers *s;
int count = 0;
- down_read(&group->list_mutex);
- if (list_empty(&group->list_head)) {
- up_read(&group->list_mutex);
+ guard(rwsem_read)(&group->list_mutex);
+ if (list_empty(&group->list_head))
return;
- }
snd_iprintf(buffer, msg);
list_for_each(p, &group->list_head) {
if (is_src)
@@ -2408,7 +2523,6 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
if (group->exclusive)
snd_iprintf(buffer, "[ex]");
}
- up_read(&group->list_mutex);
snd_iprintf(buffer, "\n");
}
@@ -2418,32 +2532,62 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
+static const char *port_direction_name(unsigned char dir)
+{
+ static const char *names[4] = {
+ "-", "In", "Out", "In/Out"
+ };
+
+ if (dir > SNDRV_SEQ_PORT_DIR_BIDIRECTION)
+ return "Invalid";
+ return names[dir];
+}
+
static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
struct snd_seq_client *client)
{
struct snd_seq_client_port *p;
- mutex_lock(&client->ports_mutex);
+ guard(mutex)(&client->ports_mutex);
list_for_each_entry(p, &client->ports_list_head, list) {
- snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n",
+ if (p->capability & SNDRV_SEQ_PORT_CAP_INACTIVE)
+ continue;
+ snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]",
p->addr.port, p->name,
FLAG_PERM_RD(p->capability),
FLAG_PERM_WR(p->capability),
FLAG_PERM_EX(p->capability),
- FLAG_PERM_DUPLEX(p->capability));
+ FLAG_PERM_DUPLEX(p->capability),
+ port_direction_name(p->direction));
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (snd_seq_client_is_midi2(client) && p->is_midi1)
+ snd_iprintf(buffer, " [MIDI1]");
+#endif
+ snd_iprintf(buffer, "\n");
snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: ");
snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: ");
}
- mutex_unlock(&client->ports_mutex);
}
+static const char *midi_version_string(unsigned int version)
+{
+ switch (version) {
+ case SNDRV_SEQ_CLIENT_LEGACY_MIDI:
+ return "Legacy";
+ case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0:
+ return "UMP MIDI1";
+ case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0:
+ return "UMP MIDI2";
+ default:
+ return "Unknown";
+ }
+}
/* exported to seq_info.c */
void snd_seq_info_clients_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
int c;
- struct snd_seq_client *client;
snd_iprintf(buffer, "Client info\n");
snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur);
@@ -2453,17 +2597,22 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
/* list the client table */
for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
- client = snd_seq_client_use_ptr(c);
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
+
+ client = client_load_and_use_ptr(c);
if (client == NULL)
continue;
- if (client->type == NO_CLIENT) {
- snd_seq_client_unlock(client);
+ if (client->type == NO_CLIENT)
continue;
- }
- snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+ guard(mutex)(&client->ioctl_mutex);
+ snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n",
c, client->name,
- client->type == USER_CLIENT ? "User" : "Kernel");
+ client->type == USER_CLIENT ? "User" : "Kernel",
+ midi_version_string(client->midi_version));
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ dump_ump_info(buffer, client);
+#endif
snd_seq_info_dump_ports(buffer, client);
if (snd_seq_write_pool_allocated(client)) {
snd_iprintf(buffer, " Output pool :\n");
@@ -2474,7 +2623,6 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " Input pool :\n");
snd_seq_info_pool(buffer, client->data.user.fifo->pool, " ");
}
- snd_seq_client_unlock(client);
}
}
#endif /* CONFIG_SND_PROC_FS */
@@ -2493,13 +2641,12 @@ static const struct file_operations snd_seq_f_ops =
.write = snd_seq_write,
.open = snd_seq_open,
.release = snd_seq_release,
- .llseek = no_llseek,
.poll = snd_seq_poll,
.unlocked_ioctl = snd_seq_ioctl,
.compat_ioctl = snd_seq_ioctl_compat,
};
-static struct device seq_dev;
+static struct device *seq_dev;
/*
* register sequencer device
@@ -2508,22 +2655,20 @@ int __init snd_sequencer_device_init(void)
{
int err;
- snd_device_initialize(&seq_dev, NULL);
- dev_set_name(&seq_dev, "seq");
-
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ err = snd_device_alloc(&seq_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(seq_dev, "seq");
- err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
- &snd_seq_f_ops, NULL, &seq_dev);
+ scoped_guard(mutex, &register_mutex) {
+ err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
+ &snd_seq_f_ops, NULL, seq_dev);
+ }
if (err < 0) {
- mutex_unlock(&register_mutex);
- put_device(&seq_dev);
+ put_device(seq_dev);
return err;
}
- mutex_unlock(&register_mutex);
-
return 0;
}
@@ -2532,8 +2677,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(&seq_dev);
- put_device(&seq_dev);
+ 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 0611e1e0ed5b..ece02c58db70 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
@@ -27,7 +12,6 @@
#include "seq_ports.h"
#include "seq_lock.h"
-
/* client manager */
struct snd_seq_user_client {
@@ -50,10 +34,13 @@ struct snd_seq_client {
snd_seq_client_type_t type;
unsigned int accept_input: 1,
accept_output: 1;
+ unsigned int midi_version;
+ unsigned int user_pversion;
char name[64]; /* client name */
int number; /* client number */
unsigned int filter; /* filter flags */
DECLARE_BITMAP(event_filter, 256);
+ unsigned short group_filter;
snd_use_lock_t use_lock;
int event_lost;
/* ports */
@@ -63,6 +50,7 @@ struct snd_seq_client {
struct mutex ports_mutex;
struct mutex ioctl_mutex;
int convert32; /* convert 32->64bit */
+ int ump_endpoint_port;
/* output pool */
struct snd_seq_pool *pool; /* memory pool for this client */
@@ -71,6 +59,9 @@ struct snd_seq_client {
struct snd_seq_user_client user;
struct snd_seq_kernel_client kernel;
} data;
+
+ /* for UMP */
+ void **ump_info;
};
/* usage statistics */
@@ -87,20 +78,50 @@ void snd_sequencer_device_done(void);
/* get locked pointer to client */
struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
+static inline struct snd_seq_client *
+snd_seq_client_ref(struct snd_seq_client *client)
+{
+ snd_use_lock_use(&client->use_lock);
+ return client;
+}
+
/* unlock pointer to client */
-#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
+static inline void snd_seq_client_unref(struct snd_seq_client *client)
+{
+ snd_use_lock_free(&client->use_lock);
+}
+
+DEFINE_FREE(snd_seq_client, struct snd_seq_client *, if (!IS_ERR_OR_NULL(_T)) snd_seq_client_unref(_T))
/* 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);
+int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+
+/* only for OSS sequencer */
+int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg);
+
extern int seq_client_load[15];
+/* for internal use between kernel sequencer clients */
+struct snd_seq_client *snd_seq_kernel_client_get(int client);
+void snd_seq_kernel_client_put(struct snd_seq_client *cptr);
+
+static inline bool snd_seq_client_is_ump(struct snd_seq_client *c)
+{
+ return c->midi_version != SNDRV_SEQ_CLIENT_LEGACY_MIDI;
+}
+
+static inline bool snd_seq_client_is_midi2(struct snd_seq_client *c)
+{
+ return c->midi_version == SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+}
+
#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 8c3507216676..643af4c1e838 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 */
@@ -45,8 +31,8 @@ struct snd_seq_port_info32 {
static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned int cmd,
struct snd_seq_port_info32 __user *data32)
{
- int err = -EFAULT;
- struct snd_seq_port_info *data;
+ struct snd_seq_port_info *data __free(kfree) = NULL;
+ int err;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -55,20 +41,18 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
if (copy_from_user(data, data32, sizeof(*data32)) ||
get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue))
- goto error;
+ return -EFAULT;
data->kernel = NULL;
err = snd_seq_kernel_client_ctl(client->number, cmd, data);
if (err < 0)
- goto error;
+ return err;
if (copy_to_user(data32, data, sizeof(*data32)) ||
put_user(data->flags, &data32->flags) ||
put_user(data->time_queue, &data32->time_queue))
- err = -EFAULT;
+ return -EFAULT;
- error:
- kfree(data);
return err;
}
@@ -95,10 +79,13 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
switch (cmd) {
case SNDRV_SEQ_IOCTL_PVERSION:
+ case SNDRV_SEQ_IOCTL_USER_PVERSION:
case SNDRV_SEQ_IOCTL_CLIENT_ID:
case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
+ case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+ case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index d3a2ec4f0561..783fc72c2ef6 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -1,21 +1,7 @@
+// 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>
@@ -34,15 +20,15 @@
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
@@ -72,6 +58,12 @@ MODULE_PARM_DESC(ports, "number of ports to be created");
module_param(duplex, bool, 0444);
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static int ump;
+module_param(ump, int, 0444);
+MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)");
+#endif
+
struct snd_seq_dummy_port {
int client;
int port;
@@ -123,7 +115,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;
@@ -140,6 +133,7 @@ create_port(int idx, int type)
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
if (duplex)
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ pinfo.direction = SNDRV_SEQ_PORT_DIR_BIDIRECTION;
pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
@@ -164,6 +158,9 @@ static int __init
register_client(void)
{
struct snd_seq_dummy_port *rec1, *rec2;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct snd_seq_client *client;
+#endif
int i;
if (ports < 1) {
@@ -177,6 +174,25 @@ register_client(void)
if (my_client < 0)
return my_client;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ client = snd_seq_kernel_client_get(my_client);
+ if (!client)
+ return -EINVAL;
+ switch (ump) {
+ case 1:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
+ break;
+ case 2:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+ break;
+ default:
+ /* don't convert events but just pass-through */
+ client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
+ break;
+ }
+ snd_seq_kernel_client_put(client);
+#endif
+
/* create ports */
for (i = 0; i < ports; i++) {
rec1 = create_port(i, 0);
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index a8c2822e0198..91cce1890111 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -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
- *
*/
#include <sound/core.h>
@@ -98,18 +83,16 @@ 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);
+ guard(spinlock_irq)(&f->lock);
/* drain the fifo */
while ((cell = fifo_cell_out(f)) != NULL) {
snd_seq_cell_free(cell);
}
- spin_unlock_irqrestore(&f->lock, flags);
}
@@ -118,38 +101,34 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
struct snd_seq_event *event)
{
struct snd_seq_event_cell *cell;
- unsigned long flags;
int err;
if (snd_BUG_ON(!f))
return -EINVAL;
- snd_use_lock_use(&f->use_lock);
- err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+ guard(snd_seq_fifo)(f);
+ err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
- snd_use_lock_free(&f->use_lock);
return err;
}
/* append new cells to fifo */
- spin_lock_irqsave(&f->lock, flags);
- if (f->tail != NULL)
- f->tail->next = cell;
- f->tail = cell;
- if (f->head == NULL)
- f->head = cell;
- cell->next = NULL;
- f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
+ scoped_guard(spinlock_irqsave, &f->lock) {
+ if (f->tail != NULL)
+ f->tail->next = cell;
+ f->tail = cell;
+ if (f->head == NULL)
+ f->head = cell;
+ cell->next = NULL;
+ f->cells++;
+ }
/* wakeup client */
if (waitqueue_active(&f->input_sleep))
wake_up(&f->input_sleep);
- snd_use_lock_free(&f->use_lock);
-
return 0; /* success */
}
@@ -159,7 +138,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 */
@@ -195,9 +175,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);
@@ -214,16 +194,13 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
struct snd_seq_event_cell *cell)
{
- unsigned long flags;
-
if (cell) {
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
cell->next = f->head;
f->head = cell;
if (!f->tail)
f->tail = cell;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
}
}
@@ -233,13 +210,13 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
poll_table *wait)
{
poll_wait(file, &f->input_sleep, wait);
+ guard(spinlock_irq)(&f->lock);
return (f->cells > 0);
}
/* 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;
@@ -255,17 +232,17 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return -ENOMEM;
}
- spin_lock_irqsave(&f->lock, flags);
- /* remember old pool */
- oldpool = f->pool;
- oldhead = f->head;
- /* exchange pools */
- f->pool = newpool;
- f->head = NULL;
- f->tail = NULL;
- f->cells = 0;
- /* NOTE: overflow flag is not cleared */
- spin_unlock_irqrestore(&f->lock, flags);
+ scoped_guard(spinlock_irq, &f->lock) {
+ /* remember old pool */
+ oldpool = f->pool;
+ oldhead = f->head;
+ /* exchange pools */
+ f->pool = newpool;
+ f->head = NULL;
+ f->tail = NULL;
+ f->cells = 0;
+ /* NOTE: overflow flag is not cleared */
+ }
/* close the old pool and wait until all users are gone */
snd_seq_pool_mark_closing(oldpool);
@@ -280,3 +257,14 @@ 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)
+{
+ if (!f)
+ return 0;
+
+ guard(snd_seq_fifo)(f);
+ guard(spinlock_irqsave)(&f->lock);
+ return snd_seq_unused_cells(f->pool);
+}
diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h
index 062c446e7867..4c9c49127746 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
@@ -52,6 +37,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event);
/* lock fifo from release */
#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock)
#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock)
+DEFINE_GUARD(snd_seq_fifo, struct snd_seq_fifo *, snd_seq_fifo_lock(_T), snd_seq_fifo_unlock(_T))
/* get a cell from fifo - fifo should be locked */
int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, struct snd_seq_event_cell **cellp, int nonblock);
@@ -68,5 +54,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 97015447b9b3..3e9fce7bead8 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -1,22 +1,7 @@
+// 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>
@@ -50,7 +35,7 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *,
return entry;
}
-static void free_info_entries(void)
+void snd_seq_info_done(void)
{
snd_info_free_entry(queues_entry);
snd_info_free_entry(clients_entry);
@@ -70,12 +55,6 @@ int __init snd_seq_info_init(void)
return 0;
error:
- free_info_entries();
+ snd_seq_info_done();
return -ENOMEM;
}
-
-int __exit snd_seq_info_done(void)
-{
- free_info_entries();
- return 0;
-}
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
index f8549f81a645..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
@@ -30,11 +15,11 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buff
#ifdef CONFIG_SND_PROC_FS
-int snd_seq_info_init( void );
-int snd_seq_info_done( void );
+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 cda64b489e42..48b4ffb4b76e 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -1,22 +1,7 @@
+// 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>
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index f763682584a8..ccde0ca3d208 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -1,30 +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/sched/signal.h>
-#include <linux/vmalloc.h>
+#include <linux/mm.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
@@ -77,18 +63,26 @@ static int get_var_len(const struct snd_seq_event *event)
return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
}
-int snd_seq_dump_var_event(const struct snd_seq_event *event,
- snd_seq_dump_func_t func, void *private_data)
+static int dump_var_event(const struct snd_seq_event *event,
+ snd_seq_dump_func_t func, void *private_data,
+ int offset, int maxlen)
{
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 (len <= offset)
+ return 0;
+ if (maxlen && len > offset + maxlen)
+ len = offset + maxlen;
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
char buf[32];
char __user *curptr = (char __force __user *)event->data.ext.ptr;
+ curptr += offset;
+ len -= offset;
while (len > 0) {
int size = sizeof(buf);
if (len < size)
@@ -104,20 +98,35 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
return 0;
}
if (!(event->data.ext.len & SNDRV_SEQ_EXT_CHAINED))
- return func(private_data, event->data.ext.ptr, len);
+ return func(private_data, event->data.ext.ptr + offset,
+ len - offset);
cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
for (; len > 0 && cell; cell = cell->next) {
int size = sizeof(struct snd_seq_event);
+ char *curptr = (char *)&cell->event;
+
+ if (offset >= size) {
+ offset -= size;
+ len -= size;
+ continue;
+ }
if (len < size)
size = len;
- err = func(private_data, &cell->event, size);
+ err = func(private_data, curptr + offset, size - offset);
if (err < 0)
return err;
+ offset = 0;
len -= size;
}
return 0;
}
+
+int snd_seq_dump_var_event(const struct snd_seq_event *event,
+ snd_seq_dump_func_t func, void *private_data)
+{
+ return dump_var_event(event, func, private_data, 0, 0);
+}
EXPORT_SYMBOL(snd_seq_dump_var_event);
@@ -126,50 +135,89 @@ 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;
return 0;
}
+static int expand_var_event(const struct snd_seq_event *event,
+ int offset, int size, char *buf, bool in_kernel)
+{
+ if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+ if (! in_kernel)
+ return -EINVAL;
+ if (copy_from_user(buf,
+ (char __force __user *)event->data.ext.ptr + offset,
+ size))
+ return -EFAULT;
+ return 0;
+ }
+ return dump_var_event(event,
+ in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
+ &buf, offset, size);
+}
+
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
int in_kernel, int size_aligned)
{
- int len, newlen;
- int err;
+ int len, newlen, err;
- if ((len = get_var_len(event)) < 0)
+ len = get_var_len(event);
+ if (len < 0)
return len;
newlen = len;
if (size_aligned > 0)
newlen = roundup(len, size_aligned);
if (count < newlen)
return -EAGAIN;
-
- if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
- if (! in_kernel)
- return -EINVAL;
- if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len))
+ err = expand_var_event(event, 0, len, buf, in_kernel);
+ if (err < 0)
+ return err;
+ if (len != newlen) {
+ if (in_kernel)
+ memset(buf + len, 0, newlen - len);
+ else if (clear_user((__force void __user *)buf + len,
+ newlen - 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,
- &buf);
- return err < 0 ? err : newlen;
+ return newlen;
}
EXPORT_SYMBOL(snd_seq_expand_var_event);
+int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
+ char *buf, int offset)
+{
+ int len, err;
+
+ len = get_var_len(event);
+ if (len < 0)
+ return len;
+ if (len <= offset)
+ return 0;
+ len -= offset;
+ if (len > count)
+ len = count;
+ err = expand_var_event(event, offset, count, buf, true);
+ if (err < 0)
+ return err;
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_seq_expand_var_event_at);
+
/*
* release this cell, free extended data if available
*/
@@ -184,7 +232,6 @@ static inline void free_cell(struct snd_seq_pool *pool,
void snd_seq_cell_free(struct snd_seq_event_cell * cell)
{
- unsigned long flags;
struct snd_seq_pool *pool;
if (snd_BUG_ON(!cell))
@@ -193,7 +240,7 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
if (snd_BUG_ON(!pool))
return;
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irqsave)(&pool->lock);
free_cell(pool, cell);
if (snd_seq_ev_is_variable(&cell->event)) {
if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
@@ -211,7 +258,6 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
if (snd_seq_output_ok(pool))
wake_up(&pool->output_sleep);
}
- spin_unlock_irqrestore(&pool->lock, flags);
}
@@ -220,7 +266,8 @@ 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;
@@ -243,9 +290,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,11 +338,12 @@ __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;
struct snd_seq_event_cell *cell;
+ int size;
*cellp = NULL;
@@ -299,17 +351,22 @@ 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;
/* copy the event */
- cell->event = *event;
+ size = snd_seq_event_packet_size(event);
+ memcpy(&cell->ump, event, size);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (size < sizeof(cell->event))
+ cell->ump.raw.extra = 0;
+#endif
/* decompose */
if (snd_seq_ev_is_variable(event)) {
@@ -327,10 +384,11 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
tail = NULL;
while (ncells-- > 0) {
- int size = sizeof(struct snd_seq_event);
+ 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)
@@ -369,6 +427,7 @@ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file,
poll_table *wait)
{
poll_wait(file, &pool->output_sleep, wait);
+ guard(spinlock_irq)(&pool->lock);
return snd_seq_output_ok(pool);
}
@@ -378,20 +437,20 @@ 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;
- cellptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
+ cellptr = kvmalloc_array(pool->size,
+ sizeof(struct snd_seq_event_cell),
+ GFP_KERNEL);
if (!cellptr)
return -ENOMEM;
/* add new cells to the free cell list */
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irq)(&pool->lock);
if (pool->ptr) {
- spin_unlock_irqrestore(&pool->lock, flags);
- vfree(cellptr);
+ kvfree(cellptr);
return 0;
}
@@ -409,26 +468,21 @@ 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);
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);
+ guard(spinlock_irqsave)(&pool->lock);
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;
if (snd_BUG_ON(!pool))
@@ -442,18 +496,17 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
schedule_timeout_uninterruptible(1);
/* release all resources */
- spin_lock_irqsave(&pool->lock, flags);
- ptr = pool->ptr;
- pool->ptr = NULL;
- pool->free = NULL;
- pool->total_elements = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
+ scoped_guard(spinlock_irq, &pool->lock) {
+ ptr = pool->ptr;
+ pool->ptr = NULL;
+ pool->free = NULL;
+ pool->total_elements = 0;
+ }
- vfree(ptr);
+ kvfree(ptr);
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irq)(&pool->lock);
pool->closing = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
return 0;
}
@@ -497,18 +550,6 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool)
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 32f959c17786..7f7a2c0b187d 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
@@ -26,9 +11,26 @@
struct snd_info_buffer;
+/* aliasing for legacy and UMP event packet handling */
+union __snd_seq_event {
+ struct snd_seq_event legacy;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct snd_seq_ump_event ump;
+#endif
+ struct {
+ struct snd_seq_event event;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ u32 extra;
+#endif
+ } __packed raw;
+};
+
/* container for sequencer event (internal use) */
struct snd_seq_event_cell {
- struct snd_seq_event event;
+ union {
+ struct snd_seq_event event;
+ union __snd_seq_event ump;
+ };
struct snd_seq_pool *pool; /* used pool */
struct snd_seq_event_cell *next; /* next cell */
};
@@ -66,7 +68,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)
@@ -93,12 +96,6 @@ 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);
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 5dd0ee258359..581e138a3115 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
- *
*/
/*
@@ -52,6 +38,7 @@ MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
/* data for this midi synth driver */
struct seq_midisynth {
struct snd_card *card;
+ struct snd_rawmidi *rmidi;
int device;
int subdevice;
struct snd_rawmidi_file input_rfile;
@@ -78,7 +65,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 +81,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,7 +102,8 @@ 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())
pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
return -ENOMEM;
@@ -129,6 +113,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
return 0;
}
+/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */
+static int __dump_midi(void *ptr, void *buf, int count)
+{
+ return dump_midi(ptr, buf, count);
+}
+
static int event_process_midi(struct snd_seq_event *ev, int direct,
void *private_data, int atomic, int hop)
{
@@ -148,7 +138,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
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);
+ snd_seq_dump_var_event(ev, __dump_midi, substream);
snd_midi_event_reset_decode(msynth->parser);
} else {
if (msynth->parser == NULL)
@@ -185,10 +175,10 @@ 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) {
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_INPUT,
+ &msynth->input_rfile);
+ if (err < 0) {
pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
return err;
}
@@ -196,7 +186,8 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
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,10 +218,10 @@ 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) {
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &msynth->output_rfile);
+ if (err < 0) {
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
}
@@ -238,7 +229,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
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;
}
@@ -278,8 +270,8 @@ 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;
- struct snd_rawmidi_info *info;
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_rawmidi_info *info __free(kfree) = NULL;
struct snd_rawmidi *rmidi = dev->private_data;
int newclient = 0;
unsigned int p, ports;
@@ -305,31 +297,24 @@ snd_seq_midisynth_probe(struct device *_dev)
ports = output_count;
if (ports < input_count)
ports = input_count;
- if (ports == 0) {
- kfree(info);
+ if (ports == 0)
return -ENODEV;
- }
if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
ports = 256 / SNDRV_RAWMIDI_DEVICES;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
client = synths[card->number];
if (client == NULL) {
newclient = 1;
client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- kfree(info);
+ if (client == NULL)
return -ENOMEM;
- }
client->seq_client =
snd_seq_create_kernel_client(
card, 0, "%s", card->shortname[0] ?
(const char *)card->shortname : "External MIDI");
if (client->seq_client < 0) {
kfree(client);
- mutex_unlock(&register_mutex);
- kfree(info);
return -ENOMEM;
}
}
@@ -341,6 +326,7 @@ snd_seq_midisynth_probe(struct device *_dev)
for (p = 0; p < ports; p++) {
ms = &msynth[p];
+ ms->rmidi = rmidi;
if (snd_seq_midisynth_new(ms, card, device, p) < 0)
goto __nomem;
@@ -358,13 +344,13 @@ snd_seq_midisynth_probe(struct device *_dev)
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
info->subdevice = p;
if (snd_rawmidi_info_select(card, info) >= 0)
- strcpy(port->name, info->subname);
+ strscpy(port->name, info->subname);
if (! port->name[0]) {
if (info->name[0]) {
if (ports > 1)
- snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
+ scnprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
else
- snprintf(port->name, sizeof(port->name), "%s", info->name);
+ scnprintf(port->name, sizeof(port->name), "%s", info->name);
} else {
/* last resort */
if (ports > 1)
@@ -380,6 +366,10 @@ snd_seq_midisynth_probe(struct device *_dev)
if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ if (port->capability & SNDRV_SEQ_PORT_CAP_READ)
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (port->capability & SNDRV_SEQ_PORT_CAP_WRITE)
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_HARDWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
@@ -405,9 +395,6 @@ snd_seq_midisynth_probe(struct device *_dev)
client->num_ports++;
if (newclient)
synths[card->number] = client;
- mutex_unlock(&register_mutex);
- kfree(info);
- kfree(port);
return 0; /* success */
__nomem:
@@ -420,9 +407,6 @@ snd_seq_midisynth_probe(struct device *_dev)
snd_seq_delete_kernel_client(client->seq_client);
kfree(client);
}
- kfree(info);
- kfree(port);
- mutex_unlock(&register_mutex);
return -ENOMEM;
}
@@ -436,12 +420,10 @@ snd_seq_midisynth_remove(struct device *_dev)
struct snd_card *card = dev->card;
int device = dev->device, p, ports;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
client = synths[card->number];
- if (client == NULL || client->ports[device] == NULL) {
- mutex_unlock(&register_mutex);
+ if (client == NULL || client->ports[device] == NULL)
return -ENODEV;
- }
ports = client->ports_per_device[device];
client->ports_per_device[device] = 0;
msynth = client->ports[device];
@@ -455,7 +437,6 @@ snd_seq_midisynth_remove(struct device *_dev)
synths[card->number] = NULL;
kfree(client);
}
- mutex_unlock(&register_mutex);
return 0;
}
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
index 9e2912e3e80f..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.
@@ -44,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);
@@ -80,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)
{
@@ -243,7 +232,8 @@ 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) {
@@ -265,7 +255,8 @@ 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;
@@ -318,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);
@@ -416,7 +407,7 @@ 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;
@@ -456,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 */
@@ -484,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
@@ -501,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*/
};
@@ -598,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;
@@ -616,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;
@@ -657,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);
@@ -728,15 +721,3 @@ void snd_midi_channel_free_set(struct snd_midi_channel_set *chset)
kfree(chset);
}
EXPORT_SYMBOL(snd_midi_channel_free_set);
-
-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)
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index 90bbbdbeba03..fa9dfc53c3fc 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -1,22 +1,9 @@
+// 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>
@@ -157,32 +144,18 @@ static inline void reset_encode(struct snd_midi_event *dev)
void snd_midi_event_reset_encode(struct snd_midi_event *dev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
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)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
dev->lastcmd = 0xff;
- spin_unlock_irqrestore(&dev->lock, flags);
}
EXPORT_SYMBOL(snd_midi_event_reset_decode);
-#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 */
-
void snd_midi_event_no_status(struct snd_midi_event *dev, int on)
{
dev->nostat = on ? 1 : 0;
@@ -190,68 +163,14 @@ void snd_midi_event_no_status(struct snd_midi_event *dev, int on)
EXPORT_SYMBOL(snd_midi_event_no_status);
/*
- * 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_encode);
-
-/*
* 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;
- unsigned long flags;
-
- c &= 0xff;
+ bool rc = false;
if (c >= MIDI_CMD_COMMON_CLOCK) {
/* real-time event */
@@ -261,7 +180,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
return ev->type != SNDRV_SEQ_EVENT_NONE;
}
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if ((c & 0x80) &&
(c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
/* new command */
@@ -293,7 +212,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) {
@@ -306,11 +225,10 @@ 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);
@@ -496,12 +414,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 };
@@ -531,15 +449,3 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
}
return idx;
}
-
-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 d21ece9f8d73..40fa379847e5 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -1,23 +1,8 @@
+// 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>
@@ -63,17 +48,15 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
if (client == NULL)
return NULL;
- read_lock(&client->ports_lock);
+ guard(read_lock)(&client->ports_lock);
list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port == num) {
if (port->closing)
break; /* deleting now */
snd_use_lock_use(&port->use_lock);
- read_unlock(&client->ports_lock);
return port;
}
}
- read_unlock(&client->ports_lock);
return NULL; /* not found */
}
@@ -84,11 +67,15 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
{
int num;
struct snd_seq_client_port *port, *found;
+ bool check_inactive = (pinfo->capability & SNDRV_SEQ_PORT_CAP_INACTIVE);
num = pinfo->addr.port;
found = NULL;
- read_lock(&client->ports_lock);
+ guard(read_lock)(&client->ports_lock);
list_for_each_entry(port, &client->ports_list_head, list) {
+ if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) &&
+ !check_inactive)
+ continue; /* skip inactive ports */
if (port->addr.port < num)
continue;
if (port->addr.port == num) {
@@ -104,7 +91,6 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
else
snd_use_lock_use(&found->use_lock);
}
- read_unlock(&client->ports_lock);
return found;
}
@@ -122,43 +108,47 @@ 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 or a negative error code is returned
* 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)
+int snd_seq_create_port(struct snd_seq_client *client, int port,
+ struct snd_seq_client_port **port_ret)
{
- unsigned long flags;
struct snd_seq_client_port *new_port, *p;
- int num = -1;
+ int num;
+ *port_ret = NULL;
+
/* sanity check */
if (snd_BUG_ON(!client))
- return NULL;
+ return -EINVAL;
if (client->num_ports >= SNDRV_SEQ_MAX_PORTS) {
pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
- return NULL;
+ return -EINVAL;
}
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
if (!new_port)
- return NULL; /* failure, out of memory */
+ return -ENOMEM; /* failure, out of memory */
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;
new_port->owner = THIS_MODULE;
- sprintf(new_port->name, "port-%d", num);
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;
- mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
+ num = max(port, 0);
+ guard(mutex)(&client->ports_mutex);
+ guard(write_lock_irq)(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
+ if (p->addr.port == port) {
+ kfree(new_port);
+ return -EBUSY;
+ }
if (p->addr.port > num)
break;
if (port < 0) /* auto-probe mode */
@@ -169,10 +159,9 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
sprintf(new_port->name, "port-%d", num);
- write_unlock_irqrestore(&client->ports_lock, flags);
- mutex_unlock(&client->ports_mutex);
+ *port_ret = new_port;
- return new_port;
+ return num;
}
/* */
@@ -189,17 +178,10 @@ static int unsubscribe_port(struct snd_seq_client *client,
static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
struct snd_seq_client **cp)
{
- struct snd_seq_client_port *p;
*cp = snd_seq_client_use_ptr(addr->client);
- if (*cp) {
- p = snd_seq_port_use_ptr(*cp, addr->port);
- if (! p) {
- snd_seq_client_unlock(*cp);
- *cp = NULL;
- }
- return p;
- }
- return NULL;
+ if (!*cp)
+ return NULL;
+ return snd_seq_port_use_ptr(*cp, addr->port);
}
static void delete_and_unsubscribe_port(struct snd_seq_client *client,
@@ -229,8 +211,8 @@ static void clear_subscriber_list(struct snd_seq_client *client,
list_for_each_safe(p, n, &grp->list_head) {
struct snd_seq_subscribers *subs;
- struct snd_seq_client *c;
- struct snd_seq_client_port *aport;
+ struct snd_seq_client *c __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *aport __free(snd_seq_port) = NULL;
subs = get_subscriber(p, is_src);
if (is_src)
@@ -252,8 +234,6 @@ static void clear_subscriber_list(struct snd_seq_client *client,
/* 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);
}
}
@@ -283,22 +263,20 @@ 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);
- list_for_each_entry(p, &client->ports_list_head, list) {
- if (p->addr.port == port) {
- /* ok found. delete from the list at first */
- list_del(&p->list);
- client->num_ports--;
- found = p;
- break;
+ scoped_guard(mutex, &client->ports_mutex) {
+ guard(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 */
+ list_del(&p->list);
+ client->num_ports--;
+ found = p;
+ break;
+ }
}
}
- write_unlock_irqrestore(&client->ports_lock, flags);
- mutex_unlock(&client->ports_mutex);
if (found)
return port_delete(client, found);
else
@@ -308,23 +286,22 @@ 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;
/* move the port list to deleted_list, and
* clear the port list in the client data.
*/
- mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
- if (! list_empty(&client->ports_list_head)) {
- list_add(&deleted_list, &client->ports_list_head);
- list_del_init(&client->ports_list_head);
- } else {
- INIT_LIST_HEAD(&deleted_list);
+ guard(mutex)(&client->ports_mutex);
+ scoped_guard(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);
+ } else {
+ INIT_LIST_HEAD(&deleted_list);
+ }
+ client->num_ports = 0;
}
- client->num_ports = 0;
- write_unlock_irqrestore(&client->ports_lock, flags);
/* remove each port in deleted_list */
list_for_each_entry_safe(port, tmp, &deleted_list, list) {
@@ -332,7 +309,6 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
port_delete(client, port);
}
- mutex_unlock(&client->ports_mutex);
return 0;
}
@@ -345,7 +321,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;
@@ -363,6 +339,22 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
port->time_queue = info->time_queue;
+ /* UMP direction and group */
+ port->direction = info->direction;
+ port->ump_group = info->ump_group;
+ if (port->ump_group > SNDRV_UMP_MAX_GROUPS)
+ port->ump_group = 0;
+
+ /* fill default port direction */
+ if (!port->direction) {
+ if (info->capability & SNDRV_SEQ_PORT_CAP_READ)
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (info->capability & SNDRV_SEQ_PORT_CAP_WRITE)
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ }
+
+ port->is_midi1 = !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1);
+
return 0;
}
@@ -374,7 +366,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;
@@ -400,6 +392,13 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
info->time_queue = port->time_queue;
}
+ if (port->is_midi1)
+ info->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
+
+ /* UMP direction and group */
+ info->direction = port->direction;
+ info->ump_group = port->ump_group;
+
return 0;
}
@@ -494,48 +493,44 @@ static int check_and_subscribe_port(struct snd_seq_client *client,
int err;
grp = is_src ? &port->c_src : &port->c_dest;
- err = -EBUSY;
- down_write(&grp->list_mutex);
+ guard(rwsem_write)(&grp->list_mutex);
if (exclusive) {
if (!list_empty(&grp->list_head))
- goto __error;
+ return -EBUSY;
} else {
if (grp->exclusive)
- goto __error;
+ return -EBUSY;
/* check whether already exists */
list_for_each(p, &grp->list_head) {
s = get_subscriber(p, is_src);
if (match_subs_info(&subs->info, &s->info))
- goto __error;
+ return -EBUSY;
}
}
err = subscribe_port(client, port, grp, &subs->info, ack);
if (err < 0) {
grp->exclusive = 0;
- goto __error;
+ return err;
}
/* add to list */
- write_lock_irq(&grp->list_lock);
+ guard(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;
- __error:
- up_write(&grp->list_mutex);
- return err;
+ return 0;
}
-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)
+/* 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;
@@ -543,19 +538,29 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
grp = is_src ? &port->c_src : &port->c_dest;
list = is_src ? &subs->src_list : &subs->dest_list;
- down_write(&grp->list_mutex);
- 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);
- up_write(&grp->list_mutex);
+ scoped_guard(write_lock_irq, &grp->list_lock) {
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
+ grp->exclusive = 0;
+ }
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;
+ guard(rwsem_write)(&grp->list_mutex);
+ __delete_and_unsubscribe_port(client, port, subs, is_src, ack);
+}
+
/* connect two ports */
int snd_seq_port_connect(struct snd_seq_client *connector,
struct snd_seq_client *src_client,
@@ -608,47 +613,52 @@ 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;
- down_write(&src->list_mutex);
- /* look for the connection */
- list_for_each_entry(subs, &src->list_head, src_list) {
- if (match_subs_info(info, &subs->info)) {
- atomic_dec(&subs->ref_count); /* mark as not ready */
- err = 0;
- break;
+ /* always start from deleting the dest port for avoiding concurrent
+ * deletions
+ */
+ scoped_guard(rwsem_write, &dest->list_mutex) {
+ /* look for the connection */
+ list_for_each_entry(subs, &dest->list_head, dest_list) {
+ if (match_subs_info(info, &subs->info)) {
+ __delete_and_unsubscribe_port(dest_client, dest_port,
+ subs, false,
+ connector->number != dest_client->number);
+ err = 0;
+ break;
+ }
}
}
- up_write(&src->list_mutex);
if (err < 0)
return err;
delete_and_unsubscribe_port(src_client, src_port, subs, true,
connector->number != src_client->number);
- delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
- connector->number != dest_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);
+ guard(rwsem_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;
}
/*
@@ -669,7 +679,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;
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
index 26bd71f36c41..40ed6cf7cb90 100644
--- a/sound/core/seq/seq_ports.h
+++ b/sound/core/seq/seq_ports.h
@@ -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>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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
#include <sound/seq_kernel.h>
+#include <sound/ump_convert.h>
#include "seq_lock.h"
/* list of 'exported' ports */
@@ -87,6 +73,15 @@ struct snd_seq_client_port {
int midi_voices;
int synth_voices;
+ /* UMP direction and group */
+ unsigned char direction;
+ unsigned char ump_group;
+
+ bool is_midi1; /* keep MIDI 1.0 protocol */
+
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct ump_cvt_to_ump_bank midi2_bank[16]; /* per channel */
+#endif
};
struct snd_seq_client;
@@ -101,8 +96,11 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
/* unlock the port */
#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
-/* create a port, port number is returned (-1 on failure) */
-struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int port_index);
+DEFINE_FREE(snd_seq_port, struct snd_seq_client_port *, if (!IS_ERR_OR_NULL(_T)) snd_seq_port_unlock(_T))
+
+/* create a port, port number or a negative error code is returned */
+int snd_seq_create_port(struct snd_seq_client *client, int port_index,
+ struct snd_seq_client_port **port_ret);
/* delete a port */
int snd_seq_delete_port(struct snd_seq_client *client, int port);
@@ -135,7 +133,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 bc1c8488fc2a..e649485a8772 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>
@@ -87,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);
@@ -147,7 +132,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
struct snd_seq_event_cell * cell)
{
struct snd_seq_event_cell *cur, *prev;
- unsigned long flags;
int count;
int prior;
@@ -157,7 +141,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
/* check flags */
prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
/* check if this element needs to inserted at the end (ie. ordered
data is inserted) This will be very likeley if a sequencer
@@ -169,7 +153,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
f->tail = cell;
cell->next = NULL;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
return 0;
}
}
@@ -194,7 +177,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
prev = cur;
cur = cur->next;
if (! --count) {
- spin_unlock_irqrestore(&f->lock, flags);
pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
return -EINVAL;
}
@@ -210,23 +192,33 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
if (cur == NULL) /* reached end of the list */
f->tail = cell;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
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) {
pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
return NULL;
}
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
cell = f->head;
+ if (cell && current_time && !event_is_ready(&cell->event, current_time))
+ cell = NULL;
if (cell) {
f->head = cell->next;
@@ -238,7 +230,6 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
f->cells--;
}
- spin_unlock_irqrestore(&f->lock, flags);
return cell;
}
@@ -252,85 +243,43 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f)
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) {
- pr_debug("ALSA: seq: 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)
-{
- if (cell->event.source.client == client ||
- cell->event.dest.client == client)
- return 1;
- if (!timestamp)
- return 0;
- switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
- case SNDRV_SEQ_TIME_STAMP_TICK:
- if (cell->event.time.tick)
- return 1;
- break;
- case SNDRV_SEQ_TIME_STAMP_REAL:
- if (cell->event.time.time.tv_sec ||
- cell->event.time.time.tv_nsec)
- return 1;
- break;
- }
- return 0;
-}
-
-/* remove cells for left client */
-void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
+/* remove cells matching with the condition */
+static void prioq_remove_cells(struct snd_seq_prioq *f,
+ bool (*match)(struct snd_seq_event_cell *cell,
+ void *arg),
+ void *arg)
{
register struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
struct snd_seq_event_cell *prev = NULL;
struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
/* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
- while (cell) {
- next = cell->next;
- if (prioq_match(cell, client, timestamp)) {
+ scoped_guard(spinlock_irqsave, &f->lock) {
+ for (cell = f->head; cell; cell = next) {
+ next = cell->next;
+ if (!match(cell, arg)) {
+ prev = cell;
+ continue;
+ }
+
/* remove cell from prioq */
- if (cell == f->head) {
+ if (cell == f->head)
f->head = cell->next;
- } else {
+ else
prev->next = cell->next;
- }
if (cell == f->tail)
f->tail = cell->next;
f->cells--;
+
/* add cell to free list */
cell->next = NULL;
- if (freefirst == NULL) {
+ if (freefirst == NULL)
freefirst = cell;
- } else {
+ else
freeprev->next = cell;
- }
freeprev = cell;
- } else {
-#if 0
- pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
- "client = %i\n",
- cell->event.type,
- cell->event.source.client,
- cell->event.dest.client,
- client);
-#endif
- prev = cell;
}
- cell = next;
}
- spin_unlock_irqrestore(&f->lock, flags);
/* remove selected cells */
while (freefirst) {
@@ -340,22 +289,68 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
}
}
-static int prioq_remove_match(struct snd_seq_remove_events *info,
- struct snd_seq_event *ev)
+struct prioq_match_arg {
+ int client;
+ int timestamp;
+};
+
+static inline bool prioq_match(struct snd_seq_event_cell *cell, void *arg)
{
+ struct prioq_match_arg *v = arg;
+
+ if (cell->event.source.client == v->client ||
+ cell->event.dest.client == v->client)
+ return true;
+ if (!v->timestamp)
+ return false;
+ switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+ case SNDRV_SEQ_TIME_STAMP_TICK:
+ if (cell->event.time.tick)
+ return true;
+ break;
+ case SNDRV_SEQ_TIME_STAMP_REAL:
+ if (cell->event.time.time.tv_sec ||
+ cell->event.time.time.tv_nsec)
+ return true;
+ break;
+ }
+ return false;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp)
+{
+ struct prioq_match_arg arg = { client, timestamp };
+
+ return prioq_remove_cells(f, prioq_match, &arg);
+}
+
+struct prioq_remove_match_arg {
+ int client;
+ struct snd_seq_remove_events *info;
+};
+
+static bool prioq_remove_match(struct snd_seq_event_cell *cell, void *arg)
+{
+ struct prioq_remove_match_arg *v = arg;
+ struct snd_seq_event *ev = &cell->event;
+ struct snd_seq_remove_events *info = v->info;
int res;
+ if (ev->source.client != v->client)
+ return false;
+
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
if (ev->dest.client != info->dest.client ||
ev->dest.port != info->dest.port)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
if (! snd_seq_ev_is_channel_type(ev))
- return 0;
+ return false;
/* data.note.channel and data.control.channel are identical */
if (ev->data.note.channel != info->channel)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -363,7 +358,7 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
else
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
if (!res)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -371,81 +366,35 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
else
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
if (res)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
if (ev->type != info->type)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
/* Do not remove off events */
switch (ev->type) {
case SNDRV_SEQ_EVENT_NOTEOFF:
/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
- return 0;
+ return false;
default:
break;
}
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
if (info->tag != ev->tag)
- return 0;
+ return false;
}
- return 1;
+ return true;
}
/* remove cells matching remove criteria */
void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
struct snd_seq_remove_events *info)
{
- struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
- struct snd_seq_event_cell *prev = NULL;
- struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
-
- /* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
-
- while (cell) {
- next = cell->next;
- if (cell->event.source.client == client &&
- prioq_remove_match(info, &cell->event)) {
+ struct prioq_remove_match_arg arg = { client, info };
- /* remove cell from prioq */
- if (cell == f->head) {
- f->head = cell->next;
- } else {
- prev->next = cell->next;
- }
-
- if (cell == f->tail)
- f->tail = cell->next;
- f->cells--;
-
- /* add cell to free list */
- cell->next = NULL;
- if (freefirst == NULL) {
- freefirst = cell;
- } else {
- freeprev->next = cell;
- }
-
- freeprev = cell;
- } else {
- prev = cell;
- }
- cell = next;
- }
- spin_unlock_irqrestore(&f->lock, flags);
-
- /* remove selected cells */
- while (freefirst) {
- freenext = freefirst->next;
- snd_seq_cell_free(freefirst);
- freefirst = freenext;
- }
+ return prioq_remove_cells(f, prioq_remove_match, &arg);
}
-
-
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 0428e9061b47..f5c0e401c8ae 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.
@@ -63,43 +50,35 @@ int snd_seq_queue_get_cur_queues(void)
static int queue_list_add(struct snd_seq_queue *q)
{
int i;
- unsigned long flags;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
if (! queue_list[i]) {
queue_list[i] = q;
q->queue = i;
num_queues++;
- spin_unlock_irqrestore(&queue_list_lock, flags);
return i;
}
}
- spin_unlock_irqrestore(&queue_list_lock, flags);
return -1;
}
static struct snd_seq_queue *queue_list_remove(int id, int client)
{
struct snd_seq_queue *q;
- unsigned long flags;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
q = queue_list[id];
if (q) {
- spin_lock(&q->owner_lock);
+ guard(spinlock)(&q->owner_lock);
if (q->owner == client) {
/* found */
q->klocked = 1;
- spin_unlock(&q->owner_lock);
queue_list[id] = NULL;
num_queues--;
- spin_unlock_irqrestore(&queue_list_lock, flags);
return q;
}
- spin_unlock(&q->owner_lock);
}
- spin_unlock_irqrestore(&queue_list_lock, flags);
return NULL;
}
@@ -159,18 +138,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;
@@ -226,15 +195,13 @@ int snd_seq_queue_delete(int client, int queueid)
struct snd_seq_queue *queueptr(int queueid)
{
struct snd_seq_queue *q;
- unsigned long flags;
if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
return NULL;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
q = queue_list[queueid];
if (q)
snd_use_lock_use(&q->use_lock);
- spin_unlock_irqrestore(&queue_list_lock, flags);
return q;
}
@@ -242,13 +209,13 @@ struct snd_seq_queue *queueptr(int queueid)
struct snd_seq_queue *snd_seq_queue_find_name(char *name)
{
int i;
- struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) != NULL) {
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ q = queueptr(i);
+ if (q) {
if (strncmp(q->name, name, sizeof(q->name)) == 0)
- return q;
- queuefree(q);
+ return no_free_ptr(q);
}
}
return NULL;
@@ -257,61 +224,60 @@ 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;
/* make this function non-reentrant */
- spin_lock_irqsave(&q->check_lock, flags);
- if (q->check_blocked) {
- q->check_again = 1;
- spin_unlock_irqrestore(&q->check_lock, flags);
- return; /* other thread is already checking queues */
+ scoped_guard(spinlock_irqsave, &q->check_lock) {
+ if (q->check_blocked) {
+ q->check_again = 1;
+ return; /* other thread is already checking queues */
+ }
+ q->check_blocked = 1;
}
- q->check_blocked = 1;
- spin_unlock_irqrestore(&q->check_lock, flags);
__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;
+ scoped_guard(spinlock_irqsave, &q->check_lock) {
+ if (q->check_again) {
+ q->check_again = 0;
+ if (processed < MAX_CELL_PROCESSES_IN_QUEUE)
+ goto __again;
+ }
+ q->check_blocked = 0;
}
- q->check_blocked = 0;
- spin_unlock_irqrestore(&q->check_lock, flags);
}
@@ -319,7 +285,7 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
int dest, err;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (snd_BUG_ON(!cell))
return -EINVAL;
@@ -354,16 +320,12 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
break;
}
- if (err < 0) {
- queuefree(q); /* unlock */
+ if (err < 0)
return err;
- }
/* trigger dispatching */
snd_seq_check_queue(q, atomic, hop);
- queuefree(q); /* unlock */
-
return 0;
}
@@ -380,41 +342,31 @@ static inline int check_access(struct snd_seq_queue *q, int client)
*/
static int queue_access_lock(struct snd_seq_queue *q, int client)
{
- unsigned long flags;
int access_ok;
- spin_lock_irqsave(&q->owner_lock, flags);
+ guard(spinlock_irqsave)(&q->owner_lock);
access_ok = check_access(q, client);
if (access_ok)
q->klocked = 1;
- spin_unlock_irqrestore(&q->owner_lock, flags);
return access_ok;
}
/* unlock the queue */
static inline void queue_access_unlock(struct snd_seq_queue *q)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->owner_lock, flags);
+ guard(spinlock_irqsave)(&q->owner_lock);
q->klocked = 0;
- spin_unlock_irqrestore(&q->owner_lock, flags);
}
/* exported - only checking permission */
int snd_seq_queue_check_access(int queueid, int client)
{
- struct snd_seq_queue *q = queueptr(queueid);
- int access_ok;
- unsigned long flags;
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
if (! q)
return 0;
- spin_lock_irqsave(&q->owner_lock, flags);
- access_ok = check_access(q, client);
- spin_unlock_irqrestore(&q->owner_lock, flags);
- queuefree(q);
- return access_ok;
+ guard(spinlock_irqsave)(&q->owner_lock);
+ return check_access(q, client);
}
/*----------------------------------------------------------------*/
@@ -424,20 +376,19 @@ 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);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, client)) {
- queuefree(q);
+ if (!queue_access_lock(q, client))
return -EPERM;
- }
- q->locked = locked ? 1 : 0;
- q->owner = client;
+ scoped_guard(spinlock_irqsave, &q->owner_lock) {
+ q->locked = locked ? 1 : 0;
+ q->owner = client;
+ }
queue_access_unlock(q);
- queuefree(q);
return 0;
}
@@ -452,18 +403,18 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
int snd_seq_queue_timer_open(int queueid)
{
int result = 0;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
queue = queueptr(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);
}
- queuefree(queue);
return result;
}
@@ -472,14 +423,13 @@ int snd_seq_queue_timer_open(int queueid)
*/
int snd_seq_queue_timer_close(int queueid)
{
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
int result = 0;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
snd_seq_timer_close(queue);
- queuefree(queue);
return result;
}
@@ -487,22 +437,20 @@ int snd_seq_queue_timer_close(int queueid)
int snd_seq_queue_timer_set_tempo(int queueid, int client,
struct snd_seq_queue_tempo *info)
{
- struct snd_seq_queue *q = queueptr(queueid);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
int result;
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, client)) {
- queuefree(q);
+ if (!queue_access_lock(q, client))
return -EPERM;
- }
- result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq);
+ result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq,
+ info->tempo_base);
if (result >= 0 && info->skew_base > 0)
result = snd_seq_timer_set_skew(q->timer, info->skew_value,
info->skew_base);
queue_access_unlock(q);
- queuefree(q);
return result;
}
@@ -531,15 +479,13 @@ static void queue_use(struct snd_seq_queue *queue, int client, int use)
*/
int snd_seq_queue_use(int queueid, int client, int use)
{
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
- mutex_lock(&queue->timer_mutex);
+ guard(mutex)(&queue->timer_mutex);
queue_use(queue, client, use);
- mutex_unlock(&queue->timer_mutex);
- queuefree(queue);
return 0;
}
@@ -550,45 +496,17 @@ int snd_seq_queue_use(int queueid, int client, int use)
*/
int snd_seq_queue_is_used(int queueid, int client)
{
- struct snd_seq_queue *q;
- int result;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = queueptr(queueid);
if (q == NULL)
return -EINVAL; /* invalid queue */
- result = test_bit(client, q->clients_bitmap) ? 1 : 0;
- queuefree(q);
- return result;
+ return test_bit(client, q->clients_bitmap) ? 1 : 0;
}
/*----------------------------------------------------------------*/
-/* 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)
@@ -596,11 +514,11 @@ void snd_seq_queue_client_termination(int client)
void snd_seq_queue_client_leave(int client)
{
int i;
- struct snd_seq_queue *q;
/* delete own queues from queue list */
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queue_list_remove(i, client)) != NULL)
+ struct snd_seq_queue *q = queue_list_remove(i, client);
+ if (q)
queue_delete(q);
}
@@ -608,14 +526,14 @@ 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)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap)) {
snd_seq_prioq_leave(q->tickq, client, 0);
snd_seq_prioq_leave(q->timeq, client, 0);
snd_seq_queue_use(q->queue, client, 0);
}
- queuefree(q);
}
}
@@ -623,29 +541,14 @@ void snd_seq_queue_client_leave(int client)
/*----------------------------------------------------------------*/
-/* remove cells from all queues */
-void snd_seq_queue_client_leave_cells(int client)
-{
- int i;
- struct snd_seq_queue *q;
-
- for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
- continue;
- snd_seq_prioq_leave(q->tickq, client, 0);
- snd_seq_prioq_leave(q->timeq, client, 0);
- queuefree(q);
- }
-}
-
/* remove cells based on flush criteria */
void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
{
int i;
- struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap) &&
(! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
@@ -653,7 +556,6 @@ void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
snd_seq_prioq_remove_events(q->tickq, client, info);
snd_seq_prioq_remove_events(q->timeq, client, info);
}
- queuefree(q);
}
}
@@ -740,7 +642,7 @@ static void snd_seq_queue_process_event(struct snd_seq_queue *q,
*/
int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
{
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -749,15 +651,12 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, ev->source.client)) {
- queuefree(q);
+ if (!queue_access_lock(q, ev->source.client))
return -EPERM;
- }
snd_seq_queue_process_event(q, ev, atomic, hop);
queue_access_unlock(q);
- queuefree(q);
return 0;
}
@@ -770,32 +669,39 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
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)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
tmr = q->timer;
if (tmr->tempo)
- bpm = 60000000 / tmr->tempo;
+ bpm = (60000 * tmr->tempo_base) / tmr->tempo;
else
bpm = 0;
+ scoped_guard(spinlock_irq, &q->owner_lock) {
+ locked = q->locked;
+ owner = q->owner;
+ }
+
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");
snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
+ snd_iprintf(buffer, "tempo base : %d ns\n", tmr->tempo_base);
snd_iprintf(buffer, "current BPM : %d\n", bpm);
snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
snd_iprintf(buffer, "\n");
- queuefree(q);
}
}
#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index 719093489a2c..afcd3c5484a6 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,9 +49,6 @@ 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);
@@ -76,9 +59,6 @@ struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int f
/* 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);
@@ -86,7 +66,6 @@ void snd_seq_queue_client_leave(int client);
int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop);
/* Remove events */
-void snd_seq_queue_client_leave_cells(int client);
void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info);
/* return pointer to queue structure for specified id */
@@ -94,6 +73,8 @@ struct snd_seq_queue *queueptr(int queueid);
/* unlock */
#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
+DEFINE_FREE(snd_seq_queue, struct snd_seq_queue *, if (!IS_ERR_OR_NULL(_T)) queuefree(_T))
+
/* return the (first) queue matching with the specified name */
struct snd_seq_queue *snd_seq_queue_find_name(char *name);
@@ -104,7 +85,6 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop);
int snd_seq_queue_check_access(int queueid, int client);
int snd_seq_queue_timer_set_tempo(int queueid, int client, struct snd_seq_queue_tempo *info);
int snd_seq_queue_set_owner(int queueid, int client, int locked);
-int snd_seq_queue_set_locked(int queueid, int client, int locked);
int snd_seq_queue_timer_open(int queueid);
int snd_seq_queue_timer_close(int queueid);
int snd_seq_queue_use(int queueid, int client, int use);
@@ -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 8ce1d0b40dce..5b5603e5970b 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -1,22 +1,7 @@
+// 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>
@@ -64,12 +49,14 @@ static int sysclient = -1;
/* port id numbers for this client */
static int announce_port = -1;
+/* number of subscriptions to announce port */
+static int announce_subscribed;
/* fill standard header data, source port & channel are filled in */
static int setheader(struct snd_seq_event * ev, int client, int port)
{
- if (announce_port < 0)
+ if (announce_port < 0 || !announce_subscribed)
return -ENODEV;
memset(ev, 0, sizeof(struct snd_seq_event));
@@ -91,25 +78,27 @@ static int setheader(struct snd_seq_event * ev, int client, int port)
/* entry points for broadcasting system events */
-void snd_seq_system_broadcast(int client, int port, int type)
+void snd_seq_system_broadcast(int client, int port, int type, bool atomic)
{
struct snd_seq_event ev;
if (setheader(&ev, client, port) < 0)
return;
ev.type = type;
- snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
+ snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0);
}
+EXPORT_SYMBOL_GPL(snd_seq_system_broadcast);
/* entry points for broadcasting system events */
-int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
+ bool atomic)
{
ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
ev->source.client = sysclient;
ev->source.port = announce_port;
ev->dest.client = client;
ev->dest.port = port;
- return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
+ return snd_seq_kernel_client_dispatch(sysclient, ev, atomic, 0);
}
/* call-back handler for timer events */
@@ -118,11 +107,28 @@ static int event_input_timer(struct snd_seq_event * ev, int direct, void *privat
return snd_seq_control_queue(ev, atomic, hop);
}
+static int sys_announce_subscribe(void *private_data,
+ struct snd_seq_port_subscribe *info)
+{
+ announce_subscribed++;
+ return 0;
+}
+
+static int sys_announce_unsubscribe(void *private_data,
+ struct snd_seq_port_subscribe *info)
+{
+ if (snd_BUG_ON(!announce_subscribed))
+ return 0;
+ announce_subscribed--;
+ return 0;
+}
+
/* register our internal client */
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)
@@ -134,9 +140,13 @@ 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");
+ strscpy(port->name, "Timer");
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
port->kernel = &pcallbacks;
@@ -144,26 +154,40 @@ 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");
+ strscpy(port->name, "Announce");
port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
- port->kernel = NULL;
+ pcallbacks.event_input = NULL;
+ pcallbacks.subscribe = sys_announce_subscribe;
+ pcallbacks.unsubscribe = sys_announce_unsubscribe;
+ port->kernel = &pcallbacks;
port->type = 0;
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..62e513f74871 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
@@ -25,16 +10,31 @@
/* entry points for broadcasting system events */
-void snd_seq_system_broadcast(int client, int port, int type);
-
-#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
-#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
-#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
-#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
-#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
-#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
-
-int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev);
+void snd_seq_system_broadcast(int client, int port, int type, bool atomic);
+
+/* normal system notification event broadcast */
+#define notify_event(client, port, type) \
+ snd_seq_system_broadcast(client, port, type, false)
+
+/* notify UMP EP/FB change event */
+static inline void snd_seq_system_ump_notify(int client, int block, int type,
+ bool atomic)
+{
+ /* reuse the existing snd_seq_system_broadcast():
+ * struct snd_seq_ev_ump_notify is compatible with struct snd_seq_addr
+ */
+ snd_seq_system_broadcast(client, block, type, atomic);
+}
+
+#define snd_seq_system_client_ev_client_start(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
+#define snd_seq_system_client_ev_client_exit(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
+#define snd_seq_system_client_ev_client_change(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
+#define snd_seq_system_client_ev_port_start(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_START)
+#define snd_seq_system_client_ev_port_exit(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
+#define snd_seq_system_client_ev_port_change(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
+
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
+ bool atomic);
/* register our internal client */
int snd_seq_system_client_init(void);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 23167578231f..29b018a212fc 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>
@@ -35,14 +20,17 @@
static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
{
- if (tmr->tempo < 1000000)
- tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
+ unsigned int threshold =
+ tmr->tempo_base == 1000 ? 1000000 : 10000;
+
+ if (tmr->tempo < threshold)
+ tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
else {
/* might overflow.. */
unsigned int s;
s = tmr->tempo % tmr->ppq;
- s = (s * 1000) / tmr->ppq;
- tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+ s = (s * tmr->tempo_base) / tmr->ppq;
+ tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
tmr->tick.resolution += s;
}
if (tmr->tick.resolution <= 0)
@@ -90,12 +78,11 @@ 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);
+ guard(spinlock_irqsave)(&tmr->lock);
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
+ tmr->tempo_base = 1000; /* 1us */
snd_seq_timer_set_tick_resolution(tmr);
tmr->running = 0;
@@ -108,7 +95,6 @@ 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);
}
static void seq_timer_reset(struct snd_seq_timer *tmr)
@@ -123,11 +109,8 @@ static void seq_timer_reset(struct snd_seq_timer *tmr)
void snd_seq_timer_reset(struct snd_seq_timer *tmr)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
seq_timer_reset(tmr);
- spin_unlock_irqrestore(&tmr->lock, flags);
}
@@ -136,7 +119,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
unsigned long resolution,
unsigned long ticks)
{
- unsigned long flags;
struct snd_seq_queue *q = timeri->callback_data;
struct snd_seq_timer *tmr;
@@ -145,29 +127,27 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
tmr = q->timer;
if (tmr == NULL)
return;
- spin_lock_irqsave(&tmr->lock, flags);
- if (!tmr->running) {
- spin_unlock_irqrestore(&tmr->lock, flags);
- return;
- }
- resolution *= ticks;
- if (tmr->skew != tmr->skew_base) {
- /* FIXME: assuming skew_base = 0x10000 */
- resolution = (resolution >> 16) * tmr->skew +
- (((resolution & 0xffff) * tmr->skew) >> 16);
- }
+ scoped_guard(spinlock_irqsave, &tmr->lock) {
+ if (!tmr->running)
+ return;
- /* update timer */
- snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+ resolution *= ticks;
+ if (tmr->skew != tmr->skew_base) {
+ /* FIXME: assuming skew_base = 0x10000 */
+ resolution = (resolution >> 16) * tmr->skew +
+ (((resolution & 0xffff) * tmr->skew) >> 16);
+ }
- /* calculate current tick */
- snd_seq_timer_update_tick(&tmr->tick, resolution);
+ /* update timer */
+ snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
- /* register actual time of this timer update */
- ktime_get_ts64(&tmr->last_update);
+ /* calculate current tick */
+ snd_seq_timer_update_tick(&tmr->tick, resolution);
- spin_unlock_irqrestore(&tmr->lock, flags);
+ /* register actual time of this timer update */
+ ktime_get_ts64(&tmr->last_update);
+ }
/* check queues and dispatch events */
snd_seq_check_queue(q, 1, 0);
@@ -176,45 +156,44 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
/* set current tempo */
int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
if (tempo <= 0)
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
if ((unsigned int)tempo != tmr->tempo) {
tmr->tempo = tempo;
snd_seq_timer_set_tick_resolution(tmr);
}
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
-/* set current tempo and ppq in a shot */
-int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
+/* set current tempo, ppq and base in a shot */
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base)
{
int changed;
- unsigned long flags;
if (snd_BUG_ON(!tmr))
return -EINVAL;
if (tempo <= 0 || ppq <= 0)
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ /* allow only 10ns or 1us tempo base for now */
+ if (tempo_base && tempo_base != 10 && tempo_base != 1000)
+ return -EINVAL;
+ guard(spinlock_irqsave)(&tmr->lock);
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);
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;
+ tmr->tempo_base = tempo_base ? tempo_base : 1000;
if (changed)
snd_seq_timer_set_tick_resolution(tmr);
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -222,15 +201,12 @@ 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)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->tick.cur_tick = position;
tmr->tick.fraction = 0;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -238,15 +214,12 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
snd_seq_real_time_t position)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
snd_seq_sanity_real_time(&position);
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->cur_time = position;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -254,8 +227,6 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
unsigned int base)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
@@ -264,9 +235,8 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
return -EINVAL;
}
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->skew = skew;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -287,7 +257,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) {
@@ -297,19 +273,25 @@ 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);
+ err = snd_timer_open(t, &tid, q->queue);
}
}
if (err < 0) {
pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
+ snd_timer_instance_free(t);
+ return err;
+ }
+ scoped_guard(spinlock_irq, &tmr->lock) {
+ if (tmr->timeri)
+ err = -EBUSY;
+ else
+ tmr->timeri = t;
+ }
+ if (err < 0) {
+ snd_timer_close(t);
+ snd_timer_instance_free(t);
return err;
}
- t->callback = snd_seq_timer_interrupt;
- t->callback_data = q;
- t->flags |= SNDRV_TIMER_IFLG_AUTO;
- spin_lock_irq(&tmr->lock);
- tmr->timeri = t;
- spin_unlock_irq(&tmr->lock);
return 0;
}
@@ -321,12 +303,14 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
tmr = q->timer;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- spin_lock_irq(&tmr->lock);
- t = tmr->timeri;
- tmr->timeri = NULL;
- spin_unlock_irq(&tmr->lock);
- if (t)
+ scoped_guard(spinlock_irq, &tmr->lock) {
+ t = tmr->timeri;
+ tmr->timeri = NULL;
+ }
+ if (t) {
snd_timer_close(t);
+ snd_timer_instance_free(t);
+ }
return 0;
}
@@ -343,13 +327,8 @@ static int seq_timer_stop(struct snd_seq_timer *tmr)
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;
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_stop(tmr);
}
static int initialize_timer(struct snd_seq_timer *tmr)
@@ -371,9 +350,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)
@@ -401,13 +378,8 @@ static int seq_timer_start(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;
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_start(tmr);
}
static int seq_timer_continue(struct snd_seq_timer *tmr)
@@ -429,24 +401,19 @@ static int seq_timer_continue(struct snd_seq_timer *tmr)
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;
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_continue(tmr);
}
/* 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);
+ guard(spinlock_irqsave)(&tmr->lock);
cur_time = tmr->cur_time;
- if (tmr->running) {
+ if (adjust_ktime && tmr->running) {
struct timespec64 tm;
ktime_get_ts64(&tm);
@@ -455,7 +422,6 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
cur_time.tv_sec += tm.tv_sec;
snd_seq_sanity_real_time(&cur_time);
}
- spin_unlock_irqrestore(&tmr->lock, flags);
return cur_time;
}
@@ -463,6 +429,7 @@ 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)
{
+ guard(spinlock_irqsave)(&tmr->lock);
return tmr->tick.cur_tick;
}
@@ -473,25 +440,27 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
int idx;
- struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
struct snd_timer_instance *ti;
unsigned long resolution;
for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
- q = queueptr(idx);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(idx);
+
if (q == NULL)
continue;
- if ((tmr = q->timer) == NULL ||
- (ti = tmr->timeri) == NULL) {
- queuefree(q);
- continue;
+ scoped_guard(mutex, &q->timer_mutex) {
+ tmr = q->timer;
+ if (!tmr)
+ break;
+ ti = tmr->timeri;
+ if (!ti)
+ break;
+ 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);
}
- 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);
- queuefree(q);
}
}
#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 62f390671096..c8803216a3a4 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
@@ -51,6 +36,7 @@ struct snd_seq_timer {
unsigned int skew;
unsigned int skew_base;
+ unsigned int tempo_base;
struct timespec64 last_update; /* time of last clock update, used for interpolation */
@@ -123,19 +109,19 @@ static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long
struct snd_seq_queue;
int snd_seq_timer_open(struct snd_seq_queue *q);
int snd_seq_timer_close(struct snd_seq_queue *q);
-int snd_seq_timer_midi_open(struct snd_seq_queue *q);
-int snd_seq_timer_midi_close(struct snd_seq_queue *q);
void snd_seq_timer_defaults(struct snd_seq_timer *tmr);
void snd_seq_timer_reset(struct snd_seq_timer *tmr);
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_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base);
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_ump_client.c b/sound/core/seq/seq_ump_client.c
new file mode 100644
index 000000000000..27247babb16d
--- /dev/null
+++ b/sound/core/seq/seq_ump_client.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ALSA sequencer binding for UMP device */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_device.h>
+#include "seq_clientmgr.h"
+#include "seq_system.h"
+
+struct seq_ump_client;
+struct seq_ump_group;
+
+enum {
+ STR_IN = SNDRV_RAWMIDI_STREAM_INPUT,
+ STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT
+};
+
+/* context for UMP input parsing, per EP */
+struct seq_ump_input_buffer {
+ unsigned char len; /* total length in words */
+ unsigned char pending; /* pending words */
+ unsigned char type; /* parsed UMP packet type */
+ unsigned char group; /* parsed UMP packet group */
+ u32 buf[4]; /* incoming UMP packet */
+};
+
+/* sequencer client, per UMP EP (rawmidi) */
+struct seq_ump_client {
+ struct snd_ump_endpoint *ump; /* assigned endpoint */
+ int seq_client; /* sequencer client id */
+ int opened[2]; /* current opens for each direction */
+ struct snd_rawmidi_file out_rfile; /* rawmidi for output */
+ struct seq_ump_input_buffer input; /* input parser context */
+ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */
+ struct work_struct group_notify_work; /* FB change notification */
+};
+
+/* number of 32bit words for each UMP message type */
+static unsigned char ump_packet_words[0x10] = {
+ 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
+};
+
+/* conversion between UMP group and seq port;
+ * assume the port number is equal with UMP group number (1-based)
+ */
+static unsigned char ump_group_to_seq_port(unsigned char group)
+{
+ return group + 1;
+}
+
+/* process the incoming rawmidi stream */
+static void seq_ump_input_receive(struct snd_ump_endpoint *ump,
+ const u32 *val, int words)
+{
+ struct seq_ump_client *client = ump->seq_client;
+ struct snd_seq_ump_event ev = {};
+
+ if (!client->opened[STR_IN])
+ return;
+
+ if (ump_is_groupless_msg(ump_message_type(*val)))
+ ev.source.port = 0; /* UMP EP port */
+ else
+ ev.source.port = ump_group_to_seq_port(ump_message_group(*val));
+ ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+ ev.flags = SNDRV_SEQ_EVENT_UMP;
+ memcpy(ev.ump, val, words << 2);
+ snd_seq_kernel_client_dispatch(client->seq_client,
+ (struct snd_seq_event *)&ev,
+ true, 0);
+}
+
+/* process an input sequencer event; only deal with UMP types */
+static int seq_ump_process_event(struct snd_seq_event *ev, int direct,
+ void *private_data, int atomic, int hop)
+{
+ struct seq_ump_client *client = private_data;
+ struct snd_rawmidi_substream *substream;
+ struct snd_seq_ump_event *ump_ev;
+ unsigned char type;
+ int len;
+
+ substream = client->out_rfile.output;
+ if (!substream)
+ return -ENODEV;
+ if (!snd_seq_ev_is_ump(ev))
+ return 0; /* invalid event, skip */
+ ump_ev = (struct snd_seq_ump_event *)ev;
+ type = ump_message_type(ump_ev->ump[0]);
+ len = ump_packet_words[type];
+ if (len > 4)
+ return 0; // invalid - skip
+ snd_rawmidi_kernel_write(substream, ev->data.raw8.d, len << 2);
+ return 0;
+}
+
+/* open the rawmidi */
+static int seq_ump_client_open(struct seq_ump_client *client, int dir)
+{
+ struct snd_ump_endpoint *ump = client->ump;
+ int err;
+
+ guard(mutex)(&ump->open_mutex);
+ if (dir == STR_OUT && !client->opened[dir]) {
+ err = snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT |
+ SNDRV_RAWMIDI_LFLG_APPEND,
+ &client->out_rfile);
+ if (err < 0)
+ return err;
+ }
+ client->opened[dir]++;
+ return 0;
+}
+
+/* close the rawmidi */
+static int seq_ump_client_close(struct seq_ump_client *client, int dir)
+{
+ struct snd_ump_endpoint *ump = client->ump;
+
+ guard(mutex)(&ump->open_mutex);
+ if (!--client->opened[dir])
+ if (dir == STR_OUT)
+ snd_rawmidi_kernel_release(&client->out_rfile);
+ return 0;
+}
+
+/* sequencer subscription ops for each client */
+static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_open(client, STR_IN);
+}
+
+static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_close(client, STR_IN);
+}
+
+static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_open(client, STR_OUT);
+}
+
+static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_close(client, STR_OUT);
+}
+
+/* fill port_info from the given UMP EP and group info */
+static void fill_port_info(struct snd_seq_port_info *port,
+ struct seq_ump_client *client,
+ struct snd_ump_group *group)
+{
+ unsigned int rawmidi_info = client->ump->core.info_flags;
+
+ port->addr.client = client->seq_client;
+ port->addr.port = ump_group_to_seq_port(group->group);
+ port->capability = 0;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT)
+ port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
+ SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+ SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT)
+ port->capability |= SNDRV_SEQ_PORT_CAP_READ |
+ SNDRV_SEQ_PORT_CAP_SYNC_READ |
+ SNDRV_SEQ_PORT_CAP_SUBS_READ;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
+ port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ if (group->dir_bits & (1 << STR_IN))
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (group->dir_bits & (1 << STR_OUT))
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ port->ump_group = group->group + 1;
+ if (!group->active)
+ port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE;
+ if (group->is_midi1)
+ port->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
+ port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+ SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_PORT;
+ port->midi_channels = 16;
+ if (*group->name)
+ snprintf(port->name, sizeof(port->name), "Group %d (%.53s)",
+ group->group + 1, group->name);
+ else
+ sprintf(port->name, "Group %d", group->group + 1);
+}
+
+/* skip non-existing group for static blocks */
+static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *group)
+{
+ return !group->valid &&
+ (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS);
+}
+
+/* create a new sequencer port per UMP group */
+static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
+{
+ struct snd_ump_group *group = &client->ump->groups[group_index];
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_seq_port_callback pcallbacks;
+
+ if (skip_group(client, group))
+ return 0;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ fill_port_info(port, client, group);
+ port->flags |= SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+ memset(&pcallbacks, 0, sizeof(pcallbacks));
+ pcallbacks.owner = THIS_MODULE;
+ pcallbacks.private_data = client;
+ pcallbacks.subscribe = seq_ump_subscribe;
+ pcallbacks.unsubscribe = seq_ump_unsubscribe;
+ pcallbacks.use = seq_ump_use;
+ pcallbacks.unuse = seq_ump_unuse;
+ pcallbacks.event_input = seq_ump_process_event;
+ port->kernel = &pcallbacks;
+ return snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+}
+
+/* update the sequencer ports; called from notify_fb_change callback */
+static void update_port_infos(struct seq_ump_client *client)
+{
+ struct snd_seq_port_info *old __free(kfree) = NULL;
+ struct snd_seq_port_info *new __free(kfree) = NULL;
+ int i, err;
+
+ old = kzalloc(sizeof(*old), GFP_KERNEL);
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!old || !new)
+ return;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ if (skip_group(client, &client->ump->groups[i]))
+ continue;
+
+ old->addr.client = client->seq_client;
+ old->addr.port = ump_group_to_seq_port(i);
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_GET_PORT_INFO,
+ old);
+ if (err < 0)
+ continue;
+ fill_port_info(new, client, &client->ump->groups[i]);
+ if (old->capability == new->capability &&
+ !strcmp(old->name, new->name))
+ continue;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_SET_PORT_INFO,
+ new);
+ if (err < 0)
+ continue;
+ }
+}
+
+/* create a UMP Endpoint port */
+static int create_ump_endpoint_port(struct seq_ump_client *client)
+{
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_seq_port_callback pcallbacks;
+ unsigned int rawmidi_info = client->ump->core.info_flags;
+ int err;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->addr.client = client->seq_client;
+ port->addr.port = 0; /* fixed */
+ port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+ port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
+ port->capability |= SNDRV_SEQ_PORT_CAP_READ |
+ SNDRV_SEQ_PORT_CAP_SYNC_READ |
+ SNDRV_SEQ_PORT_CAP_SUBS_READ;
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
+ port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
+ SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+ SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
+ port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ port->ump_group = 0; /* no associated group, no conversion */
+ port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_PORT;
+ port->midi_channels = 16;
+ strscpy(port->name, "MIDI 2.0");
+ memset(&pcallbacks, 0, sizeof(pcallbacks));
+ pcallbacks.owner = THIS_MODULE;
+ pcallbacks.private_data = client;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
+ pcallbacks.subscribe = seq_ump_subscribe;
+ pcallbacks.unsubscribe = seq_ump_unsubscribe;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
+ pcallbacks.use = seq_ump_use;
+ pcallbacks.unuse = seq_ump_unuse;
+ pcallbacks.event_input = seq_ump_process_event;
+ }
+ port->kernel = &pcallbacks;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ return err;
+}
+
+/* release the client resources */
+static void seq_ump_client_free(struct seq_ump_client *client)
+{
+ cancel_work_sync(&client->group_notify_work);
+
+ if (client->seq_client >= 0)
+ snd_seq_delete_kernel_client(client->seq_client);
+
+ client->ump->seq_ops = NULL;
+ client->ump->seq_client = NULL;
+
+ kfree(client);
+}
+
+/* update the MIDI version for the given client */
+static void setup_client_midi_version(struct seq_ump_client *client)
+{
+ struct snd_seq_client *cptr;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr)
+ return;
+ if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+ else
+ cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
+ snd_seq_kernel_client_put(cptr);
+}
+
+/* set up client's group_filter bitmap */
+static void setup_client_group_filter(struct seq_ump_client *client)
+{
+ struct snd_seq_client *cptr;
+ unsigned int filter;
+ int p;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr)
+ return;
+ filter = ~(1U << 0); /* always allow groupless messages */
+ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
+ if (client->ump->groups[p].active)
+ filter &= ~(1U << (p + 1));
+ }
+ cptr->group_filter = filter;
+ snd_seq_kernel_client_put(cptr);
+}
+
+/* UMP group change notification */
+static void handle_group_notify(struct work_struct *work)
+{
+ struct seq_ump_client *client =
+ container_of(work, struct seq_ump_client, group_notify_work);
+
+ update_port_infos(client);
+ setup_client_group_filter(client);
+}
+
+/* UMP EP change notification */
+static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump)
+{
+ struct seq_ump_client *client = ump->seq_client;
+ struct snd_seq_client *cptr;
+ int client_id;
+
+ if (!client)
+ return -ENODEV;
+ client_id = client->seq_client;
+ cptr = snd_seq_kernel_client_get(client_id);
+ if (!cptr)
+ return -ENODEV;
+
+ snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ true);
+
+ /* update sequencer client name if needed */
+ if (*ump->core.name && strcmp(ump->core.name, cptr->name)) {
+ strscpy(cptr->name, ump->core.name, sizeof(cptr->name));
+ snd_seq_system_client_ev_client_change(client_id);
+ }
+
+ snd_seq_kernel_client_put(cptr);
+ return 0;
+}
+
+/* UMP FB change notification */
+static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb)
+{
+ struct seq_ump_client *client = ump->seq_client;
+
+ if (!client)
+ return -ENODEV;
+ schedule_work(&client->group_notify_work);
+ snd_seq_system_ump_notify(client->seq_client, fb->info.block_id,
+ SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
+ true);
+ return 0;
+}
+
+/* UMP protocol change notification; just update the midi_version field */
+static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
+{
+ struct seq_ump_client *client = ump->seq_client;
+
+ if (!client)
+ return -ENODEV;
+ setup_client_midi_version(client);
+ snd_seq_system_ump_notify(client->seq_client, 0,
+ SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ true);
+ return 0;
+}
+
+static const struct snd_seq_ump_ops seq_ump_ops = {
+ .input_receive = seq_ump_input_receive,
+ .notify_ep_change = seq_ump_notify_ep_change,
+ .notify_fb_change = seq_ump_notify_fb_change,
+ .switch_protocol = seq_ump_switch_protocol,
+};
+
+/* create a sequencer client and ports for the given UMP endpoint */
+static int snd_seq_ump_probe(struct device *_dev)
+{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
+ struct snd_ump_endpoint *ump = dev->private_data;
+ struct snd_card *card = dev->card;
+ struct seq_ump_client *client;
+ struct snd_ump_block *fb;
+ struct snd_seq_client *cptr;
+ int p, err;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ INIT_WORK(&client->group_notify_work, handle_group_notify);
+ client->ump = ump;
+
+ client->seq_client =
+ snd_seq_create_kernel_client(card, ump->core.device,
+ ump->core.name);
+ if (client->seq_client < 0) {
+ err = client->seq_client;
+ goto error;
+ }
+
+ client->ump_info[0] = &ump->info;
+ list_for_each_entry(fb, &ump->block_list, list)
+ client->ump_info[fb->info.block_id + 1] = &fb->info;
+
+ setup_client_midi_version(client);
+
+ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
+ err = seq_ump_group_init(client, p);
+ if (err < 0)
+ goto error;
+ }
+
+ setup_client_group_filter(client);
+
+ err = create_ump_endpoint_port(client);
+ if (err < 0)
+ goto error;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr) {
+ err = -EINVAL;
+ goto error;
+ }
+ cptr->ump_info = client->ump_info;
+ snd_seq_kernel_client_put(cptr);
+
+ ump->seq_client = client;
+ ump->seq_ops = &seq_ump_ops;
+ return 0;
+
+ error:
+ seq_ump_client_free(client);
+ return err;
+}
+
+/* remove a sequencer client */
+static int snd_seq_ump_remove(struct device *_dev)
+{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
+ struct snd_ump_endpoint *ump = dev->private_data;
+
+ if (ump->seq_client)
+ seq_ump_client_free(ump->seq_client);
+ return 0;
+}
+
+static struct snd_seq_driver seq_ump_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_seq_ump_probe,
+ .remove = snd_seq_ump_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_UMP,
+ .argsize = 0,
+};
+
+module_snd_seq_driver(seq_ump_driver);
+
+MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c
new file mode 100644
index 000000000000..db2f169cae11
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.c
@@ -0,0 +1,1305 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer event conversion between UMP and legacy clients
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+#include "seq_ump_convert.h"
+
+/*
+ * Upgrade / downgrade value bits
+ */
+static u8 downscale_32_to_7bit(u32 src)
+{
+ return src >> 25;
+}
+
+static u16 downscale_32_to_14bit(u32 src)
+{
+ return src >> 18;
+}
+
+static u8 downscale_16_to_7bit(u16 src)
+{
+ return src >> 9;
+}
+
+static u16 upscale_7_to_16bit(u8 src)
+{
+ u16 val, repeat;
+
+ val = (u16)src << 9;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 3) | (repeat >> 3);
+}
+
+static u32 upscale_7_to_32bit(u8 src)
+{
+ u32 val, repeat;
+
+ val = src << 25;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 19) | (repeat << 13) |
+ (repeat << 7) | (repeat << 1) | (repeat >> 5);
+}
+
+static u32 upscale_14_to_32bit(u16 src)
+{
+ u32 val, repeat;
+
+ val = src << 18;
+ if (src <= 0x2000)
+ return val;
+ repeat = src & 0x1fff;
+ return val | (repeat << 5) | (repeat >> 8);
+}
+
+static unsigned char get_ump_group(struct snd_seq_client_port *port)
+{
+ return port->ump_group ? (port->ump_group - 1) : 0;
+}
+
+/* create a UMP header */
+#define make_raw_ump(port, type) \
+ ump_compose(type, get_ump_group(port), 0, 0)
+
+/*
+ * UMP -> MIDI1 sequencer event
+ */
+
+/* MIDI 1.0 CVM */
+
+/* encode note event */
+static void ump_midi1_to_note_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.note.channel = val->note.channel;
+ ev->data.note.note = val->note.note;
+ ev->data.note.velocity = val->note.velocity;
+}
+
+/* encode one parameter controls */
+static void ump_midi1_to_ctrl_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->caf.channel;
+ ev->data.control.value = val->caf.data;
+}
+
+/* encode pitch wheel change */
+static void ump_midi1_to_pitchbend_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->pb.channel;
+ ev->data.control.value = (val->pb.data_msb << 7) | val->pb.data_lsb;
+ ev->data.control.value -= 8192;
+}
+
+/* encode midi control change */
+static void ump_midi1_to_cc_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->cc.channel;
+ ev->data.control.param = val->cc.index;
+ ev->data.control.value = val->cc.data;
+}
+
+/* Encoding MIDI 1.0 UMP packet */
+struct seq_ump_midi1_to_ev {
+ int seq_type;
+ void (*encode)(const union snd_ump_midi1_msg *val, struct snd_seq_event *ev);
+};
+
+/* Encoders for MIDI1 status 0x80-0xe0 */
+static struct seq_ump_midi1_to_ev midi1_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi1_to_note_ev}, /* 0x80 */
+ {SNDRV_SEQ_EVENT_NOTEON, ump_midi1_to_note_ev}, /* 0x90 */
+ {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi1_to_note_ev}, /* 0xa0 */
+ {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi1_to_cc_ev}, /* 0xb0 */
+ {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi1_to_ctrl_ev}, /* 0xc0 */
+ {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi1_to_ctrl_ev}, /* 0xd0 */
+ {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi1_to_pitchbend_ev}, /* 0xe0 */
+};
+
+static int cvt_ump_midi1_to_event(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->note.status;
+
+ if (status < 0x8 || status > 0xe)
+ return 0; /* invalid - skip */
+ status -= 8;
+ ev->type = midi1_msg_encoders[status].seq_type;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ midi1_msg_encoders[status].encode(val, ev);
+ return 1;
+}
+
+/* MIDI System message */
+
+/* encode one parameter value*/
+static void ump_system_to_one_param_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.value = val->system.parm1;
+}
+
+/* encode song position */
+static void ump_system_to_songpos_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.value = (val->system.parm2 << 7) | val->system.parm1;
+}
+
+/* Encoders for 0xf0 - 0xff */
+static struct seq_ump_midi1_to_ev system_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */
+ {SNDRV_SEQ_EVENT_QFRAME, ump_system_to_one_param_ev}, /* 0xf1 */
+ {SNDRV_SEQ_EVENT_SONGPOS, ump_system_to_songpos_ev}, /* 0xf2 */
+ {SNDRV_SEQ_EVENT_SONGSEL, ump_system_to_one_param_ev}, /* 0xf3 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf4 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf5 */
+ {SNDRV_SEQ_EVENT_TUNE_REQUEST, NULL}, /* 0xf6 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf7 */
+ {SNDRV_SEQ_EVENT_CLOCK, NULL}, /* 0xf8 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf9 */
+ {SNDRV_SEQ_EVENT_START, NULL}, /* 0xfa */
+ {SNDRV_SEQ_EVENT_CONTINUE, NULL}, /* 0xfb */
+ {SNDRV_SEQ_EVENT_STOP, NULL}, /* 0xfc */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xfd */
+ {SNDRV_SEQ_EVENT_SENSING, NULL}, /* 0xfe */
+ {SNDRV_SEQ_EVENT_RESET, NULL}, /* 0xff */
+};
+
+static int cvt_ump_system_to_event(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->system.status;
+
+ if ((status & 0xf0) != UMP_MIDI1_MSG_REALTIME)
+ return 0; /* invalid status - skip */
+ status &= 0x0f;
+ ev->type = system_msg_encoders[status].seq_type;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0;
+ if (system_msg_encoders[status].encode)
+ system_msg_encoders[status].encode(val, ev);
+ return 1;
+}
+
+/* MIDI 2.0 CVM */
+
+/* encode note event */
+static int ump_midi2_to_note_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.note.channel = val->note.channel;
+ ev->data.note.note = val->note.note;
+ ev->data.note.velocity = downscale_16_to_7bit(val->note.velocity);
+ /* correct note-on velocity 0 to 1;
+ * it's no longer equivalent as not-off for MIDI 2.0
+ */
+ if (ev->type == SNDRV_SEQ_EVENT_NOTEON &&
+ !ev->data.note.velocity)
+ ev->data.note.velocity = 1;
+ return 1;
+}
+
+/* encode pitch wheel change */
+static int ump_midi2_to_pitchbend_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->pb.channel;
+ ev->data.control.value = downscale_32_to_14bit(val->pb.data);
+ ev->data.control.value -= 8192;
+ return 1;
+}
+
+/* encode midi control change */
+static int ump_midi2_to_cc_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->cc.channel;
+ ev->data.control.param = val->cc.index;
+ ev->data.control.value = downscale_32_to_7bit(val->cc.data);
+ return 1;
+}
+
+/* encode midi program change */
+static int ump_midi2_to_pgm_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ int size = 1;
+
+ ev->data.control.channel = val->pg.channel;
+ if (val->pg.bank_valid) {
+ ev->type = SNDRV_SEQ_EVENT_CONTROL14;
+ ev->data.control.param = UMP_CC_BANK_SELECT;
+ ev->data.control.value = (val->pg.bank_msb << 7) | val->pg.bank_lsb;
+ ev[1] = ev[0];
+ ev++;
+ ev->type = SNDRV_SEQ_EVENT_PGMCHANGE;
+ size = 2;
+ }
+ ev->data.control.value = val->pg.program;
+ return size;
+}
+
+/* encode one parameter controls */
+static int ump_midi2_to_ctrl_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->caf.channel;
+ ev->data.control.value = downscale_32_to_7bit(val->caf.data);
+ return 1;
+}
+
+/* encode RPN/NRPN */
+static int ump_midi2_to_rpn_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->rpn.channel;
+ ev->data.control.param = (val->rpn.bank << 7) | val->rpn.index;
+ ev->data.control.value = downscale_32_to_14bit(val->rpn.data);
+ return 1;
+}
+
+/* Encoding MIDI 2.0 UMP Packet */
+struct seq_ump_midi2_to_ev {
+ int seq_type;
+ int (*encode)(const union snd_ump_midi2_msg *val, struct snd_seq_event *ev);
+};
+
+/* Encoders for MIDI2 status 0x00-0xf0 */
+static struct seq_ump_midi2_to_ev midi2_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x00 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x10 */
+ {SNDRV_SEQ_EVENT_REGPARAM, ump_midi2_to_rpn_ev}, /* 0x20 */
+ {SNDRV_SEQ_EVENT_NONREGPARAM, ump_midi2_to_rpn_ev}, /* 0x30 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x40 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x50 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x60 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x70 */
+ {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi2_to_note_ev}, /* 0x80 */
+ {SNDRV_SEQ_EVENT_NOTEON, ump_midi2_to_note_ev}, /* 0x90 */
+ {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi2_to_note_ev}, /* 0xa0 */
+ {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi2_to_cc_ev}, /* 0xb0 */
+ {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi2_to_pgm_ev}, /* 0xc0 */
+ {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi2_to_ctrl_ev}, /* 0xd0 */
+ {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi2_to_pitchbend_ev}, /* 0xe0 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */
+};
+
+static int cvt_ump_midi2_to_event(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->note.status;
+
+ ev->type = midi2_msg_encoders[status].seq_type;
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0; /* skip */
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ return midi2_msg_encoders[status].encode(val, ev);
+}
+
+/* parse and compose for a sysex var-length event */
+static int cvt_ump_sysex7_to_event(const u32 *data, unsigned char *buf,
+ struct snd_seq_event *ev)
+{
+ unsigned char status;
+ unsigned char bytes;
+ u32 val;
+ int size = 0;
+
+ val = data[0];
+ status = ump_sysex_message_status(val);
+ bytes = ump_sysex_message_length(val);
+ if (bytes > 6)
+ return 0; // skip
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_START) {
+ buf[0] = UMP_MIDI1_MSG_SYSEX_START;
+ size = 1;
+ }
+
+ if (bytes > 0)
+ buf[size++] = (val >> 8) & 0x7f;
+ if (bytes > 1)
+ buf[size++] = val & 0x7f;
+ val = data[1];
+ if (bytes > 2)
+ buf[size++] = (val >> 24) & 0x7f;
+ if (bytes > 3)
+ buf[size++] = (val >> 16) & 0x7f;
+ if (bytes > 4)
+ buf[size++] = (val >> 8) & 0x7f;
+ if (bytes > 5)
+ buf[size++] = val & 0x7f;
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_END)
+ buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
+
+ ev->type = SNDRV_SEQ_EVENT_SYSEX;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+ ev->data.ext.len = size;
+ ev->data.ext.ptr = buf;
+ return 1;
+}
+
+/* convert UMP packet from MIDI 1.0 to MIDI 2.0 and deliver it */
+static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *__event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
+ struct snd_seq_ump_event ev_cvt;
+ const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump;
+ union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump;
+ struct ump_cvt_to_ump_bank *cc;
+
+ ev_cvt = *event;
+ memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
+
+ midi2->note.type = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE;
+ midi2->note.group = midi1->note.group;
+ midi2->note.status = midi1->note.status;
+ midi2->note.channel = midi1->note.channel;
+ switch (midi1->note.status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi2->note.note = midi1->note.note;
+ midi2->note.velocity = upscale_7_to_16bit(midi1->note.velocity);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi2->paf.note = midi1->paf.note;
+ midi2->paf.data = upscale_7_to_32bit(midi1->paf.data);
+ break;
+ case UMP_MSG_STATUS_CC:
+ cc = &dest_port->midi2_bank[midi1->note.channel];
+ switch (midi1->cc.index) {
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = midi1->cc.data;
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = midi1->cc.data;
+ return 0; // skip
+ }
+ midi2->cc.index = midi1->cc.index;
+ midi2->cc.data = upscale_7_to_32bit(midi1->cc.data);
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ midi2->pg.program = midi1->pg.program;
+ cc = &dest_port->midi2_bank[midi1->note.channel];
+ if (cc->bank_set) {
+ midi2->pg.bank_valid = 1;
+ midi2->pg.bank_msb = cc->cc_bank_msb;
+ midi2->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi2->caf.data = upscale_7_to_32bit(midi1->caf.data);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ midi2->pb.data = upscale_14_to_32bit((midi1->pb.data_msb << 7) |
+ midi1->pb.data_lsb);
+ break;
+ default:
+ return 0;
+ }
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+}
+
+/* convert UMP packet from MIDI 2.0 to MIDI 1.0 and deliver it */
+static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *__event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi1_msg *midi1 = (union snd_ump_midi1_msg *)ev_cvt.ump;
+ const union snd_ump_midi2_msg *midi2 = (const union snd_ump_midi2_msg *)event->ump;
+ int err;
+ u16 v;
+
+ ev_cvt = *event;
+ memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
+
+ midi1->note.type = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE;
+ midi1->note.group = midi2->note.group;
+ midi1->note.status = midi2->note.status;
+ midi1->note.channel = midi2->note.channel;
+ switch (midi2->note.status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi1->note.note = midi2->note.note;
+ midi1->note.velocity = downscale_16_to_7bit(midi2->note.velocity);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi1->paf.note = midi2->paf.note;
+ midi1->paf.data = downscale_32_to_7bit(midi2->paf.data);
+ break;
+ case UMP_MSG_STATUS_CC:
+ midi1->cc.index = midi2->cc.index;
+ midi1->cc.data = downscale_32_to_7bit(midi2->cc.data);
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ if (midi2->pg.bank_valid) {
+ midi1->cc.status = UMP_MSG_STATUS_CC;
+ midi1->cc.index = UMP_CC_BANK_SELECT;
+ midi1->cc.data = midi2->pg.bank_msb;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ midi1->cc.index = UMP_CC_BANK_SELECT_LSB;
+ midi1->cc.data = midi2->pg.bank_lsb;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ midi1->note.status = midi2->note.status;
+ }
+ midi1->pg.program = midi2->pg.program;
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi1->caf.data = downscale_32_to_7bit(midi2->caf.data);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ v = downscale_32_to_14bit(midi2->pb.data);
+ midi1->pb.data_msb = v >> 7;
+ midi1->pb.data_lsb = v & 0x7f;
+ break;
+ default:
+ return 0;
+ }
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+}
+
+/* convert UMP to a legacy ALSA seq event and deliver it */
+static int cvt_ump_to_any(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ unsigned char type,
+ int atomic, int hop)
+{
+ struct snd_seq_event ev_cvt[2]; /* up to two events */
+ struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
+ /* use the second event as a temp buffer for saving stack usage */
+ unsigned char *sysex_buf = (unsigned char *)(ev_cvt + 1);
+ unsigned char flags = event->flags & ~SNDRV_SEQ_EVENT_UMP;
+ int i, len, err;
+
+ ev_cvt[0] = ev_cvt[1] = *event;
+ ev_cvt[0].flags = flags;
+ ev_cvt[1].flags = flags;
+ switch (type) {
+ case UMP_MSG_TYPE_SYSTEM:
+ len = cvt_ump_system_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
+ len = cvt_ump_midi1_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
+ len = cvt_ump_midi2_to_event((union snd_ump_midi2_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_DATA:
+ len = cvt_ump_sysex7_to_event(ump_ev->ump, sysex_buf, ev_cvt);
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ &ev_cvt[i], atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Replace UMP group field with the destination and deliver */
+static int deliver_with_group_convert(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_ump_event *ump_ev,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event ev = *ump_ev;
+
+ /* rewrite the group to the destination port */
+ ev.ump[0] &= ~(0xfU << 24);
+ /* fill with the new group; the dest_port->ump_group field is 1-based */
+ ev.ump[0] |= ((dest_port->ump_group - 1) << 24);
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev,
+ atomic, hop);
+}
+
+/* apply the UMP event filter; return true to skip the event */
+static bool ump_event_filtered(struct snd_seq_client *dest,
+ const struct snd_seq_ump_event *ev)
+{
+ unsigned char group;
+
+ group = ump_message_group(ev->ump[0]);
+ if (ump_is_groupless_msg(ump_message_type(ev->ump[0])))
+ return dest->group_filter & (1U << 0);
+ /* check the bitmap for 1-based group number */
+ return dest->group_filter & (1U << (group + 1));
+}
+
+/* Convert from UMP packet and deliver */
+int snd_seq_deliver_from_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
+ unsigned char type;
+
+ if (snd_seq_ev_is_variable(event))
+ return 0; // skip, no variable event for UMP, so far
+ if (ump_event_filtered(dest, ump_ev))
+ return 0; // skip if group filter is set and matching
+ type = ump_message_type(ump_ev->ump[0]);
+
+ if (snd_seq_client_is_ump(dest)) {
+ bool is_midi2 = snd_seq_client_is_midi2(dest) &&
+ !dest_port->is_midi1;
+
+ if (is_midi2 && type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
+ return cvt_ump_midi1_to_midi2(dest, dest_port,
+ event, atomic, hop);
+ else if (!is_midi2 && type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)
+ return cvt_ump_midi2_to_midi1(dest, dest_port,
+ event, atomic, hop);
+ /* non-EP port and different group is set? */
+ if (dest_port->ump_group &&
+ !ump_is_groupless_msg(type) &&
+ ump_message_group(*ump_ev->ump) + 1 != dest_port->ump_group)
+ return deliver_with_group_convert(dest, dest_port,
+ ump_ev, atomic, hop);
+ /* copy as-is */
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+ }
+
+ return cvt_ump_to_any(dest, dest_port, event, type, atomic, hop);
+}
+
+/*
+ * MIDI1 sequencer event -> UMP conversion
+ */
+
+/* Conversion to UMP MIDI 1.0 */
+
+/* convert note on/off event to MIDI 1.0 UMP */
+static int note_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ if (!event->data.note.velocity)
+ status = UMP_MSG_STATUS_NOTE_OFF;
+ data->note.status = status;
+ data->note.channel = event->data.note.channel & 0x0f;
+ data->note.velocity = event->data.note.velocity & 0x7f;
+ data->note.note = event->data.note.note & 0x7f;
+ return 1;
+}
+
+/* convert CC event to MIDI 1.0 UMP */
+static int cc_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->cc.status = status;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data->cc.index = event->data.control.param;
+ data->cc.data = event->data.control.value;
+ return 1;
+}
+
+/* convert one-parameter control event to MIDI 1.0 UMP */
+static int ctrl_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->caf.status = status;
+ data->caf.channel = event->data.control.channel & 0x0f;
+ data->caf.data = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert pitchbend event to MIDI 1.0 UMP */
+static int pitchbend_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ int val = event->data.control.value + 8192;
+
+ val = clamp(val, 0, 0x3fff);
+ data->pb.status = status;
+ data->pb.channel = event->data.control.channel & 0x0f;
+ data->pb.data_msb = (val >> 7) & 0x7f;
+ data->pb.data_lsb = val & 0x7f;
+ return 1;
+}
+
+/* convert 14bit control event to MIDI 1.0 UMP; split to two events */
+static int ctrl14_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data->cc.index = event->data.control.param & 0x7f;
+ if (event->data.control.param < 0x20) {
+ data->cc.data = (event->data.control.value >> 7) & 0x7f;
+ data[1] = data[0];
+ data[1].cc.index = event->data.control.param | 0x20;
+ data[1].cc.data = event->data.control.value & 0x7f;
+ return 2;
+ }
+
+ data->cc.data = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert RPN/NRPN event to MIDI 1.0 UMP; split to four events */
+static int rpn_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ bool is_rpn = (status == UMP_MSG_STATUS_RPN);
+
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data[1] = data[2] = data[3] = data[0];
+
+ data[0].cc.index = is_rpn ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
+ data[0].cc.data = (event->data.control.param >> 7) & 0x7f;
+ data[1].cc.index = is_rpn ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
+ data[1].cc.data = event->data.control.param & 0x7f;
+ data[2].cc.index = UMP_CC_DATA;
+ data[2].cc.data = (event->data.control.value >> 7) & 0x7f;
+ data[3].cc.index = UMP_CC_DATA_LSB;
+ data[3].cc.data = event->data.control.value & 0x7f;
+ return 4;
+}
+
+/* convert system / RT message to UMP */
+static int system_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ return 1;
+}
+
+/* convert system / RT message with 1 parameter to UMP */
+static int system_1p_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ data->system.parm1 = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert system / RT message with two parameters to UMP */
+static int system_2p_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ data->system.parm1 = event->data.control.value & 0x7f;
+ data->system.parm2 = (event->data.control.value >> 7) & 0x7f;
+ return 1;
+}
+
+/* Conversion to UMP MIDI 2.0 */
+
+/* convert note on/off event to MIDI 2.0 UMP */
+static int note_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ if (!event->data.note.velocity)
+ status = UMP_MSG_STATUS_NOTE_OFF;
+ data->note.status = status;
+ data->note.channel = event->data.note.channel & 0x0f;
+ data->note.note = event->data.note.note & 0x7f;
+ data->note.velocity = upscale_7_to_16bit(event->data.note.velocity & 0x7f);
+ return 1;
+}
+
+/* convert PAF event to MIDI 2.0 UMP */
+static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->paf.status = status;
+ data->paf.channel = event->data.note.channel & 0x0f;
+ data->paf.note = event->data.note.note & 0x7f;
+ data->paf.data = upscale_7_to_32bit(event->data.note.velocity & 0x7f);
+ return 1;
+}
+
+static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
+{
+ cc->rpn_set = 0;
+ cc->nrpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+}
+
+/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
+static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
+ union snd_ump_midi2_msg *data,
+ unsigned char channel,
+ bool flush)
+{
+ if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
+ return 0; // skip
+ /* when not flushing, wait for complete data set */
+ if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
+ return 0; // skip
+
+ if (cc->rpn_set) {
+ data->rpn.status = UMP_MSG_STATUS_RPN;
+ data->rpn.bank = cc->cc_rpn_msb;
+ data->rpn.index = cc->cc_rpn_lsb;
+ } else if (cc->nrpn_set) {
+ data->rpn.status = UMP_MSG_STATUS_NRPN;
+ data->rpn.bank = cc->cc_nrpn_msb;
+ data->rpn.index = cc->cc_nrpn_lsb;
+ } else {
+ return 0; // skip
+ }
+
+ data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+ data->rpn.channel = channel;
+
+ reset_rpn(cc);
+ return 1;
+}
+
+/* convert CC event to MIDI 2.0 UMP */
+static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ unsigned char index = event->data.control.param & 0x7f;
+ unsigned char val = event->data.control.value & 0x7f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+ int ret;
+
+ /* process special CC's (bank/rpn/nrpn) */
+ switch (index) {
+ case UMP_CC_RPN_MSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = val;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = val;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = val;
+ return ret;
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = val;
+ return ret;
+ case UMP_CC_DATA:
+ cc->cc_data_msb_set = 1;
+ cc->cc_data_msb = val;
+ return fill_rpn(cc, data, channel, false);
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = val;
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = val;
+ return 0; // skip
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_lsb_set = 1;
+ cc->cc_data_lsb = val;
+ return fill_rpn(cc, data, channel, false);
+ }
+
+ data->cc.status = status;
+ data->cc.channel = channel;
+ data->cc.index = index;
+ data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
+ return 1;
+}
+
+/* convert one-parameter control event to MIDI 2.0 UMP */
+static int ctrl_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->caf.status = status;
+ data->caf.channel = event->data.control.channel & 0x0f;
+ data->caf.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
+ return 1;
+}
+
+/* convert program change event to MIDI 2.0 UMP */
+static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+
+ data->pg.status = status;
+ data->pg.channel = channel;
+ data->pg.program = event->data.control.value & 0x7f;
+ if (cc->bank_set) {
+ data->pg.bank_valid = 1;
+ data->pg.bank_msb = cc->cc_bank_msb;
+ data->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ return 1;
+}
+
+/* convert pitchbend event to MIDI 2.0 UMP */
+static int pitchbend_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ int val = event->data.control.value + 8192;
+
+ val = clamp(val, 0, 0x3fff);
+ data->pb.status = status;
+ data->pb.channel = event->data.control.channel & 0x0f;
+ data->pb.data = upscale_14_to_32bit(val);
+ return 1;
+}
+
+/* convert 14bit control event to MIDI 2.0 UMP; split to two events */
+static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ unsigned char index = event->data.control.param & 0x7f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+ unsigned char msb, lsb;
+ int ret;
+
+ msb = (event->data.control.value >> 7) & 0x7f;
+ lsb = event->data.control.value & 0x7f;
+ /* process special CC's (bank/rpn/nrpn) */
+ switch (index) {
+ case UMP_CC_BANK_SELECT:
+ cc->cc_bank_msb = msb;
+ fallthrough;
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = lsb;
+ return 0; // skip
+ case UMP_CC_RPN_MSB:
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->cc_rpn_msb = msb;
+ cc->cc_rpn_lsb = lsb;
+ cc->rpn_set = 1;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->cc_nrpn_msb = msb;
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = lsb;
+ return ret;
+ case UMP_CC_DATA:
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 1;
+ cc->cc_data_msb = msb;
+ cc->cc_data_lsb = lsb;
+ return fill_rpn(cc, data, channel, false);
+ }
+
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = channel;
+ data->cc.index = index;
+ if (event->data.control.param < 0x20) {
+ data->cc.data = upscale_7_to_32bit(msb);
+ data[1] = data[0];
+ data[1].cc.index = event->data.control.param | 0x20;
+ data[1].cc.data = upscale_7_to_32bit(lsb);
+ return 2;
+ }
+
+ data->cc.data = upscale_7_to_32bit(lsb);
+ return 1;
+}
+
+/* convert RPN/NRPN event to MIDI 2.0 UMP */
+static int rpn_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->rpn.status = status;
+ data->rpn.channel = event->data.control.channel;
+ data->rpn.bank = (event->data.control.param >> 7) & 0x7f;
+ data->rpn.index = event->data.control.param & 0x7f;
+ data->rpn.data = upscale_14_to_32bit(event->data.control.value & 0x3fff);
+ return 1;
+}
+
+/* convert system / RT message to UMP */
+static int system_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+/* convert system / RT message with 1 parameter to UMP */
+static int system_1p_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_1p_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+/* convert system / RT message with two parameters to UMP */
+static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_2p_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+struct seq_ev_to_ump {
+ int seq_type;
+ unsigned char status;
+ int (*midi1_encode)(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status);
+ int (*midi2_encode)(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status);
+};
+
+static const struct seq_ev_to_ump seq_ev_ump_encoders[] = {
+ { SNDRV_SEQ_EVENT_NOTEON, UMP_MSG_STATUS_NOTE_ON,
+ note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_NOTEOFF, UMP_MSG_STATUS_NOTE_OFF,
+ note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_KEYPRESS, UMP_MSG_STATUS_POLY_PRESSURE,
+ note_ev_to_ump_midi1, paf_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTROLLER, UMP_MSG_STATUS_CC,
+ cc_ev_to_ump_midi1, cc_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_PGMCHANGE, UMP_MSG_STATUS_PROGRAM,
+ ctrl_ev_to_ump_midi1, pgm_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CHANPRESS, UMP_MSG_STATUS_CHANNEL_PRESSURE,
+ ctrl_ev_to_ump_midi1, ctrl_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_PITCHBEND, UMP_MSG_STATUS_PITCH_BEND,
+ pitchbend_ev_to_ump_midi1, pitchbend_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTROL14, 0,
+ ctrl14_ev_to_ump_midi1, ctrl14_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_NONREGPARAM, UMP_MSG_STATUS_NRPN,
+ rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_REGPARAM, UMP_MSG_STATUS_RPN,
+ rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_QFRAME, UMP_SYSTEM_STATUS_MIDI_TIME_CODE,
+ system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SONGPOS, UMP_SYSTEM_STATUS_SONG_POSITION,
+ system_2p_ev_to_ump_midi1, system_2p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SONGSEL, UMP_SYSTEM_STATUS_SONG_SELECT,
+ system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_TUNE_REQUEST, UMP_SYSTEM_STATUS_TUNE_REQUEST,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CLOCK, UMP_SYSTEM_STATUS_TIMING_CLOCK,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_START, UMP_SYSTEM_STATUS_START,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTINUE, UMP_SYSTEM_STATUS_CONTINUE,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_STOP, UMP_SYSTEM_STATUS_STOP,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_RESET, UMP_SYSTEM_STATUS_RESET,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+};
+
+static const struct seq_ev_to_ump *find_ump_encoder(int type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seq_ev_ump_encoders); i++)
+ if (seq_ev_ump_encoders[i].seq_type == type)
+ return &seq_ev_ump_encoders[i];
+
+ return NULL;
+}
+
+static void setup_ump_event(struct snd_seq_ump_event *dest,
+ const struct snd_seq_event *src)
+{
+ memcpy(dest, src, sizeof(*src));
+ dest->type = 0;
+ dest->flags |= SNDRV_SEQ_EVENT_UMP;
+ dest->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+ memset(dest->ump, 0, sizeof(dest->ump));
+}
+
+/* Convert ALSA seq event to UMP MIDI 1.0 and deliver it */
+static int cvt_to_ump_midi1(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ const struct seq_ev_to_ump *encoder;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi1_msg data[4];
+ int i, n, err;
+
+ encoder = find_ump_encoder(event->type);
+ if (!encoder)
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+
+ data->raw = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE);
+ n = encoder->midi1_encode(event, dest_port, data, encoder->status);
+ if (!n)
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ for (i = 0; i < n; i++) {
+ ev_cvt.ump[0] = data[i].raw;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Convert ALSA seq event to UMP MIDI 2.0 and deliver it */
+static int cvt_to_ump_midi2(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ const struct seq_ev_to_ump *encoder;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi2_msg data[2];
+ int i, n, err;
+
+ encoder = find_ump_encoder(event->type);
+ if (!encoder)
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+
+ data->raw[0] = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE);
+ data->raw[1] = 0;
+ n = encoder->midi2_encode(event, dest_port, data, encoder->status);
+ if (!n)
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ for (i = 0; i < n; i++) {
+ memcpy(ev_cvt.ump, &data[i], sizeof(data[i]));
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Fill up a sysex7 UMP from the byte stream */
+static void fill_sysex7_ump(struct snd_seq_client_port *dest_port,
+ u32 *val, u8 status, u8 *buf, int len)
+{
+ memset(val, 0, 8);
+ memcpy((u8 *)val + 2, buf, len);
+#ifdef __LITTLE_ENDIAN
+ swab32_array(val, 2);
+#endif
+ val[0] |= ump_compose(UMP_MSG_TYPE_DATA, get_ump_group(dest_port),
+ status, len);
+}
+
+/* Convert sysex var event to UMP sysex7 packets and deliver them */
+static int cvt_sysex_to_ump(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event ev_cvt;
+ unsigned char status;
+ u8 buf[8], *xbuf;
+ int offset = 0;
+ int len, err;
+ bool finished = false;
+
+ if (!snd_seq_ev_is_variable(event))
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ while (!finished) {
+ len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
+ if (len <= 0)
+ break;
+ if (WARN_ON(len > sizeof(buf)))
+ break;
+
+ xbuf = buf;
+ status = UMP_SYSEX_STATUS_CONTINUE;
+ /* truncate the sysex start-marker */
+ if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
+ status = UMP_SYSEX_STATUS_START;
+ len--;
+ offset++;
+ xbuf++;
+ }
+
+ /* if the last of this packet or the 1st byte of the next packet
+ * is the end-marker, finish the transfer with this packet
+ */
+ if (len > 0 && len < 8 &&
+ xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
+ if (status == UMP_SYSEX_STATUS_START)
+ status = UMP_SYSEX_STATUS_SINGLE;
+ else
+ status = UMP_SYSEX_STATUS_END;
+ len--;
+ finished = true;
+ }
+
+ len = min(len, 6);
+ fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ offset += len;
+ }
+ return 0;
+}
+
+/* Convert to UMP packet and deliver */
+int snd_seq_deliver_to_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ if (dest->group_filter & (1U << dest_port->ump_group))
+ return 0; /* group filtered - skip the event */
+ if (event->type == SNDRV_SEQ_EVENT_SYSEX)
+ return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop);
+ else if (snd_seq_client_is_midi2(dest) && !dest_port->is_midi1)
+ return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop);
+ else
+ return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop);
+}
+
+/* return the UMP group-port number of the event;
+ * return -1 if groupless or non-UMP event
+ */
+int snd_seq_ump_group_port(const struct snd_seq_event *event)
+{
+ const struct snd_seq_ump_event *ump_ev =
+ (const struct snd_seq_ump_event *)event;
+ unsigned char type;
+
+ if (!snd_seq_ev_is_ump(event))
+ return -1;
+ type = ump_message_type(ump_ev->ump[0]);
+ if (ump_is_groupless_msg(type))
+ return -1;
+ /* group-port number starts from 1 */
+ return ump_message_group(ump_ev->ump[0]) + 1;
+}
diff --git a/sound/core/seq/seq_ump_convert.h b/sound/core/seq/seq_ump_convert.h
new file mode 100644
index 000000000000..4abf0a7637d7
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer event conversion between UMP and legacy clients
+ */
+#ifndef __SEQ_UMP_CONVERT_H
+#define __SEQ_UMP_CONVERT_H
+
+#include "seq_clientmgr.h"
+#include "seq_ports.h"
+
+int snd_seq_deliver_from_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+int snd_seq_deliver_to_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+int snd_seq_ump_group_port(const struct snd_seq_event *event);
+
+#endif /* __SEQ_UMP_CONVERT_H */
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index f48a4cd24ffc..9e7fd4993a10 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
- *
*/
/*
@@ -76,6 +62,13 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
/*
* decode input event and put to read buffer of each opened file
*/
+
+/* callback for snd_seq_dump_var_event(), bridging to snd_rawmidi_receive() */
+static int dump_to_rawmidi(void *ptr, void *buf, int count)
+{
+ return snd_rawmidi_receive(ptr, buf, count);
+}
+
static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
struct snd_seq_event *ev,
bool atomic)
@@ -89,12 +82,13 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
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_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream);
+ snd_midi_event_reset_decode(vmidi->parser);
} else {
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
if (len > 0)
@@ -110,23 +104,6 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
}
/*
- * 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, true);
-}
-#endif /* 0 */
-
-/*
* event handler of virmidi port
*/
static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
@@ -147,68 +124,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;
- unsigned long flags;
-
- if (up) {
- vmidi->trigger = 1;
- if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
- !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
- while (snd_rawmidi_transmit(substream, buf,
- sizeof(buf)) > 0) {
- /* ignored */
- }
- return;
- }
+ 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;
- }
- spin_lock_irqsave(&substream->runtime->lock, flags);
- 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)
- goto out;
- vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
- }
- }
}
- out:
- spin_unlock_irqrestore(&substream->runtime->lock, flags);
- } 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)
@@ -229,11 +199,10 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
vmidi->client = rdev->client;
vmidi->port = rdev->port;
runtime->private_data = vmidi;
- down_write(&rdev->filelist_sem);
- write_lock_irq(&rdev->filelist_lock);
- list_add_tail(&vmidi->list, &rdev->filelist);
- write_unlock_irq(&rdev->filelist_lock);
- up_write(&rdev->filelist_sem);
+ scoped_guard(rwsem_write, &rdev->filelist_sem) {
+ guard(write_lock_irq)(&rdev->filelist_lock);
+ list_add_tail(&vmidi->list, &rdev->filelist);
+ }
vmidi->rdev = rdev;
return 0;
}
@@ -260,6 +229,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;
}
@@ -272,11 +242,10 @@ 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;
- 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);
+ scoped_guard(rwsem_write, &rdev->filelist_sem) {
+ guard(write_lock_irq)(&rdev->filelist_lock);
+ list_del(&vmidi->list);
+ }
snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
@@ -289,6 +258,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);
@@ -296,6 +268,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,
@@ -369,6 +351,7 @@ 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,
};
/*
@@ -378,26 +361,22 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
{
int client;
struct snd_seq_port_callback pcallbacks;
- struct snd_seq_port_info *pinfo;
+ struct snd_seq_port_info *pinfo __free(kfree) = NULL;
int err;
if (rdev->client >= 0)
return 0;
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo) {
- err = -ENOMEM;
- goto __error;
- }
+ if (!pinfo)
+ return -ENOMEM;
client = snd_seq_create_kernel_client(rdev->card, rdev->device,
"%s %d-%d", rdev->rmidi->name,
rdev->card->number,
rdev->device);
- if (client < 0) {
- err = client;
- goto __error;
- }
+ if (client < 0)
+ return client;
rdev->client = client;
/* create a port */
@@ -407,6 +386,7 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ pinfo->direction = SNDRV_SEQ_PORT_DIR_BIDIRECTION;
pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
@@ -424,15 +404,11 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
if (err < 0) {
snd_seq_delete_kernel_client(client);
rdev->client = -1;
- goto __error;
+ return err;
}
rdev->port = pinfo->addr.port;
- err = 0; /* success */
-
- __error:
- kfree(pinfo);
- return err;
+ return 0; /* success */
}
@@ -515,12 +491,13 @@ 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);
+ strscpy(rmidi->name, rmidi->id);
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
if (rdev == NULL) {
snd_device_free(card, rmidi);
@@ -546,19 +523,3 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
return 0;
}
EXPORT_SYMBOL(snd_virmidi_new);
-
-/*
- * 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)
diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c
index e40a2cba5002..bac9f8603734 100644
--- a/sound/core/seq_device.c
+++ b/sound/core/seq_device.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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
@@ -33,7 +19,6 @@
* 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>
@@ -55,16 +40,16 @@ MODULE_LICENSE("GPL");
/*
* bus definition
*/
-static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
+static int snd_seq_bus_match(struct device *dev, const struct device_driver *drv)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
- struct snd_seq_driver *sdrv = to_seq_drv(drv);
+ const 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 = {
+static const struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
};
@@ -148,10 +133,19 @@ void snd_seq_device_load_drivers(void)
flush_work(&autoload_work);
}
EXPORT_SYMBOL(snd_seq_device_load_drivers);
-#define cancel_autoload_drivers() cancel_work_sync(&autoload_work)
+
+static inline void cancel_autoload_drivers(void)
+{
+ cancel_work_sync(&autoload_work);
+}
#else
-#define queue_autoload_drivers() /* NOP */
-#define cancel_autoload_drivers() /* NOP */
+static inline void queue_autoload_drivers(void)
+{
+}
+
+static inline void cancel_autoload_drivers(void)
+{
+}
#endif
/*
@@ -162,6 +156,8 @@ 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;
}
@@ -189,11 +185,7 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
static void snd_seq_dev_release(struct device *dev)
{
- struct snd_seq_device *sdev = to_seq_dev(dev);
-
- if (sdev->private_free)
- sdev->private_free(sdev);
- kfree(sdev);
+ kfree(to_seq_dev(dev));
}
/*
@@ -208,7 +200,7 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id,
{
struct snd_seq_device *dev;
int err;
- static struct snd_device_ops dops = {
+ 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,
@@ -242,7 +234,7 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id,
put_device(&dev->dev);
return err;
}
-
+
if (result)
*result = dev;
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644
index 84fffabdd129..000000000000
--- a/sound/core/sgbuf.c
+++ /dev/null
@@ -1,164 +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 <linux/export.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;
-
- 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)
- goto _failed;
- 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;
-}
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
- */
-unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
- unsigned int ofs, unsigned int size)
-{
- struct snd_sg_buf *sg = dmab->private_data;
- unsigned int start, end, pg;
-
- start = ofs >> PAGE_SHIFT;
- end = (ofs + size - 1) >> PAGE_SHIFT;
- /* check page continuity */
- pg = sg->table[start].addr >> PAGE_SHIFT;
- for (;;) {
- start++;
- if (start > end)
- break;
- pg++;
- if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
- return (start << PAGE_SHIFT) - ofs;
- }
- /* ok, all on continuous pages */
- return size;
-}
-EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
diff --git a/sound/core/sound.c b/sound/core/sound.c
index b30f027eb0fe..6531a67f13b3 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -1,22 +1,7 @@
+// 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>
@@ -24,6 +9,7 @@
#include <linux/time.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
@@ -54,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);
@@ -112,7 +103,7 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
if (minor >= ARRAY_SIZE(snd_minors))
return NULL;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&sound_mutex);
mreg = snd_minors[minor];
if (mreg && mreg->type == type) {
private_data = mreg->private_data;
@@ -120,7 +111,6 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
get_device(&mreg->card_ptr->card_dev);
} else
private_data = NULL;
- mutex_unlock(&sound_mutex);
return private_data;
}
EXPORT_SYMBOL(snd_lookup_minor_data);
@@ -134,13 +124,16 @@ 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);
}
- mutex_lock(&sound_mutex); /* reacuire lock */
+ mutex_lock(&sound_mutex); /* reacquire lock */
return snd_minors[minor];
}
#else /* !CONFIG_MODULES */
@@ -156,17 +149,15 @@ static int snd_open(struct inode *inode, struct file *file)
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
- mutex_lock(&sound_mutex);
- mptr = snd_minors[minor];
- if (mptr == NULL) {
- mptr = autoload_device(minor);
- if (!mptr) {
- mutex_unlock(&sound_mutex);
- return -ENODEV;
+ scoped_guard(mutex, &sound_mutex) {
+ mptr = snd_minors[minor];
+ if (mptr == NULL) {
+ mptr = autoload_device(minor);
+ if (!mptr)
+ return -ENODEV;
}
+ new_fops = fops_get(mptr->f_ops);
}
- new_fops = fops_get(mptr->f_ops);
- mutex_unlock(&sound_mutex);
if (!new_fops)
return -ENODEV;
replace_fops(file, new_fops);
@@ -275,7 +266,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
preg->f_ops = f_ops;
preg->private_data = private_data;
preg->card_ptr = card;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&sound_mutex);
minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
err = minor;
@@ -290,7 +281,6 @@ int snd_register_device(int type, struct snd_card *card, int dev,
snd_minors[minor] = preg;
error:
- mutex_unlock(&sound_mutex);
if (err < 0)
kfree(preg);
return err;
@@ -311,7 +301,7 @@ int snd_unregister_device(struct device *dev)
int minor;
struct snd_minor *preg;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&sound_mutex);
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
preg = snd_minors[minor];
if (preg && preg->dev == dev) {
@@ -321,7 +311,6 @@ int snd_unregister_device(struct device *dev)
break;
}
}
- mutex_unlock(&sound_mutex);
if (minor >= ARRAY_SIZE(snd_minors))
return -ENOENT;
return 0;
@@ -349,6 +338,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 "?";
}
@@ -359,9 +350,10 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
int minor;
struct snd_minor *mptr;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&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)
@@ -376,7 +368,6 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
snd_iprintf(buffer, "%3i: : %s\n", minor,
snd_device_type_name(mptr->type));
}
- mutex_unlock(&sound_mutex);
}
int __init snd_minor_info_init(void)
@@ -407,6 +398,10 @@ static int __init alsa_sound_init(void)
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
+
+#ifdef CONFIG_SND_DEBUG
+ sound_debugfs_root = debugfs_create_dir("sound", NULL);
+#endif
#ifndef MODULE
pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
@@ -415,6 +410,9 @@ static int __init alsa_sound_init(void)
static void __exit alsa_sound_exit(void)
{
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(sound_debugfs_root);
+#endif
snd_info_done();
unregister_chrdev(major, "alsa");
}
diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c
new file mode 100644
index 000000000000..84e337ecbddd
--- /dev/null
+++ b/sound/core/sound_kunit.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sound core KUnit test
+ * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#define SILENCE_BUFFER_MAX_FRAMES 260
+#define SILENCE_BUFFER_SIZE (sizeof(u64) * SILENCE_BUFFER_MAX_FRAMES)
+#define SILENCE(...) { __VA_ARGS__ }
+#define DEFINE_FORMAT(fmt, pbits, wd, endianness, signd, silence_arr) { \
+ .format = SNDRV_PCM_FORMAT_##fmt, .physical_bits = pbits, \
+ .width = wd, .le = endianness, .sd = signd, .silence = silence_arr, \
+ .name = #fmt, \
+}
+
+#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1)
+#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1
+
+#define VALID_NAME "ValidName"
+#define NAME_W_SPEC_CHARS "In%v@1id name"
+#define NAME_W_SPACE "Test name"
+#define NAME_W_SPACE_REMOVED "Testname"
+
+#define TEST_FIRST_COMPONENT "Component1"
+#define TEST_SECOND_COMPONENT "Component2"
+
+struct snd_format_test_data {
+ snd_pcm_format_t format;
+ int physical_bits;
+ int width;
+ int le;
+ int sd;
+ unsigned char silence[8];
+ unsigned char *name;
+};
+
+struct avail_test_data {
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t hw_ptr;
+ snd_pcm_uframes_t appl_ptr;
+ snd_pcm_uframes_t expected_avail;
+};
+
+static const struct snd_format_test_data valid_fmt[] = {
+ DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()),
+ DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)),
+ DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S16_BE, 16, 16, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U16_LE, 16, 16, 1, 0, SILENCE(0x00, 0x80)),
+ DEFINE_FORMAT(U16_BE, 16, 16, 0, 0, SILENCE(0x80, 0x00)),
+ DEFINE_FORMAT(S24_LE, 32, 24, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S24_BE, 32, 24, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U24_LE, 32, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U24_BE, 32, 24, 0, 0, SILENCE(0x00, 0x80, 0x00, 0x00)),
+ DEFINE_FORMAT(S32_LE, 32, 32, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S32_BE, 32, 32, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U32_LE, 32, 32, 1, 0, SILENCE(0x00, 0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U32_BE, 32, 32, 0, 0, SILENCE(0x80, 0x00, 0x00, 0x00)),
+ DEFINE_FORMAT(FLOAT_LE, 32, 32, 1, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT_BE, 32, 32, 0, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT64_LE, 64, 64, 1, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT64_BE, 64, 64, 0, -1, SILENCE()),
+ DEFINE_FORMAT(IEC958_SUBFRAME_LE, 32, 32, 1, -1, SILENCE()),
+ DEFINE_FORMAT(IEC958_SUBFRAME_BE, 32, 32, 0, -1, SILENCE()),
+ DEFINE_FORMAT(MU_LAW, 8, 8, -1, -1, SILENCE(0x7f)),
+ DEFINE_FORMAT(A_LAW, 8, 8, -1, -1, SILENCE(0x55)),
+ DEFINE_FORMAT(IMA_ADPCM, 4, 4, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_24, 3, 3, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_40, 5, 5, -1, -1, SILENCE()),
+ DEFINE_FORMAT(DSD_U8, 8, 8, 1, 0, SILENCE(0x69)),
+ DEFINE_FORMAT(DSD_U16_LE, 16, 16, 1, 0, SILENCE(0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U32_LE, 32, 32, 1, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U16_BE, 16, 16, 0, 0, SILENCE(0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U32_BE, 32, 32, 0, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+ DEFINE_FORMAT(S20_LE, 32, 20, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S20_BE, 32, 20, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U20_LE, 32, 20, 1, 0, SILENCE(0x00, 0x00, 0x08, 0x00)),
+ DEFINE_FORMAT(U20_BE, 32, 20, 0, 0, SILENCE(0x00, 0x08, 0x00, 0x00)),
+ DEFINE_FORMAT(S24_3LE, 24, 24, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S24_3BE, 24, 24, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U24_3LE, 24, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U24_3BE, 24, 24, 0, 0, SILENCE(0x80, 0x00, 0x00)),
+ DEFINE_FORMAT(S20_3LE, 24, 20, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S20_3BE, 24, 20, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U20_3LE, 24, 20, 1, 0, SILENCE(0x00, 0x00, 0x08)),
+ DEFINE_FORMAT(U20_3BE, 24, 20, 0, 0, SILENCE(0x08, 0x00, 0x00)),
+ DEFINE_FORMAT(S18_3LE, 24, 18, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S18_3BE, 24, 18, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U18_3LE, 24, 18, 1, 0, SILENCE(0x00, 0x00, 0x02)),
+ DEFINE_FORMAT(U18_3BE, 24, 18, 0, 0, SILENCE(0x02, 0x00, 0x00)),
+ DEFINE_FORMAT(G723_24_1B, 8, 3, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_40_1B, 8, 5, -1, -1, SILENCE()),
+};
+
+static void test_phys_format_size(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(valid_fmt[i].format),
+ valid_fmt[i].physical_bits);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_width(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(valid_fmt[i].format),
+ valid_fmt[i].width);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_signed(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_signed(valid_fmt[i].format),
+ valid_fmt[i].sd < 0 ? -EINVAL : valid_fmt[i].sd);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_unsigned(valid_fmt[i].format),
+ valid_fmt[i].sd < 0 ? -EINVAL : 1 - valid_fmt[i].sd);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_endianness(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(valid_fmt[i].format),
+ valid_fmt[i].le < 0 ? -EINVAL : valid_fmt[i].le);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(valid_fmt[i].format),
+ valid_fmt[i].le < 0 ? -EINVAL : 1 - valid_fmt[i].le);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_2), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void _test_fill_silence(struct kunit *test, const struct snd_format_test_data *data,
+ u8 *buffer, size_t samples_count)
+{
+ size_t sample_bytes = data->physical_bits >> 3;
+ u32 i;
+
+ KUNIT_ASSERT_EQ(test, snd_pcm_format_set_silence(data->format, buffer, samples_count), 0);
+ for (i = 0; i < samples_count * sample_bytes; i++)
+ KUNIT_EXPECT_EQ(test, buffer[i], data->silence[i % sample_bytes]);
+}
+
+static void test_format_fill_silence(struct kunit *test)
+{
+ static const u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES };
+ u8 *buffer;
+ u32 i, j;
+
+ buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
+
+ for (i = 0; i < ARRAY_SIZE(buf_samples); i++) {
+ for (j = 0; j < ARRAY_SIZE(valid_fmt); j++)
+ _test_fill_silence(test, &valid_fmt[j], buffer, buf_samples[i]);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(WRONG_FORMAT_1, buffer, 20), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_LAST, buffer, 0), 0);
+}
+
+static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size)
+{
+ snd_pcm_uframes_t boundary = buffer_size;
+
+ while (boundary * 2 <= 0x7fffffffUL - buffer_size)
+ boundary *= 2;
+ return boundary;
+}
+
+static const struct avail_test_data p_avail_data[] = {
+ /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */
+ { 128, 1000, 1129, 1073741824UL - 1 },
+ /*
+ * buf_size + hw_ptr - appl_ptr >= boundary =>
+ * => avail = buf_size + hw_ptr - appl_ptr - boundary
+ */
+ { 128, 1073741824UL, 10, 118 },
+ /* standard case: avail = buf_size + hw_ptr - appl_ptr */
+ { 128, 1000, 1001, 127 },
+};
+
+static void test_playback_avail(struct kunit *test)
+{
+ struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+ u32 i;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r);
+
+ r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+ r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control);
+
+ for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) {
+ r->buffer_size = p_avail_data[i].buffer_size;
+ r->boundary = calculate_boundary(r->buffer_size);
+ r->status->hw_ptr = p_avail_data[i].hw_ptr;
+ r->control->appl_ptr = p_avail_data[i].appl_ptr;
+ KUNIT_EXPECT_EQ(test, snd_pcm_playback_avail(r), p_avail_data[i].expected_avail);
+ }
+}
+
+static const struct avail_test_data c_avail_data[] = {
+ /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */
+ { 128, 1000, 1001, 1073741824UL - 1 },
+ /* standard case: avail = hw_ptr - appl_ptr */
+ { 128, 1001, 1000, 1 },
+};
+
+static void test_capture_avail(struct kunit *test)
+{
+ struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+ u32 i;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r);
+
+ r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+ r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control);
+
+ for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) {
+ r->buffer_size = c_avail_data[i].buffer_size;
+ r->boundary = calculate_boundary(r->buffer_size);
+ r->status->hw_ptr = c_avail_data[i].hw_ptr;
+ r->control->appl_ptr = c_avail_data[i].appl_ptr;
+ KUNIT_EXPECT_EQ(test, snd_pcm_capture_avail(r), c_avail_data[i].expected_avail);
+ }
+}
+
+static void test_card_set_id(struct kunit *test)
+{
+ struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card);
+
+ snd_card_set_id(card, VALID_NAME);
+ KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME);
+
+ /* clear the first id character so we can set it again */
+ card->id[0] = '\0';
+ snd_card_set_id(card, NAME_W_SPEC_CHARS);
+ KUNIT_EXPECT_STRNEQ(test, card->id, NAME_W_SPEC_CHARS);
+
+ card->id[0] = '\0';
+ snd_card_set_id(card, NAME_W_SPACE);
+ kunit_info(test, "%s", card->id);
+ KUNIT_EXPECT_STREQ(test, card->id, NAME_W_SPACE_REMOVED);
+}
+
+static void test_pcm_format_name(struct kunit *test)
+{
+ u32 i;
+ const char *name;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ name = snd_pcm_format_name(valid_fmt[i].format);
+ KUNIT_ASSERT_NOT_NULL_MSG(test, name, "Don't have name for %s", valid_fmt[i].name);
+ KUNIT_EXPECT_STREQ(test, name, valid_fmt[i].name);
+ }
+
+ KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_1), "Unknown");
+ KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_2), "Unknown");
+}
+
+static void test_card_add_component(struct kunit *test)
+{
+ struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card);
+
+ snd_component_add(card, TEST_FIRST_COMPONENT);
+ KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT);
+
+ snd_component_add(card, TEST_SECOND_COMPONENT);
+ KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT " " TEST_SECOND_COMPONENT);
+}
+
+static struct kunit_case sound_utils_cases[] = {
+ KUNIT_CASE(test_phys_format_size),
+ KUNIT_CASE(test_format_width),
+ KUNIT_CASE(test_format_endianness),
+ KUNIT_CASE(test_format_signed),
+ KUNIT_CASE(test_format_fill_silence),
+ KUNIT_CASE(test_playback_avail),
+ KUNIT_CASE(test_capture_avail),
+ KUNIT_CASE(test_card_set_id),
+ KUNIT_CASE(test_pcm_format_name),
+ KUNIT_CASE(test_card_add_component),
+ {},
+};
+
+static struct kunit_suite sound_utils_suite = {
+ .name = "sound-core-test",
+ .test_cases = sound_utils_cases,
+};
+
+kunit_test_suite(sound_utils_suite);
+MODULE_DESCRIPTION("Sound core KUnit test");
+MODULE_AUTHOR("Ivan Orlov");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 0a5c66229a22..d65cc6fee2e6 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -1,22 +1,7 @@
+// 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>
@@ -44,7 +29,7 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
if (minor >= ARRAY_SIZE(snd_oss_minors))
return NULL;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
mreg = snd_oss_minors[minor];
if (mreg && mreg->type == type) {
private_data = mreg->private_data;
@@ -52,7 +37,6 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
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);
@@ -121,7 +105,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
preg->f_ops = f_ops;
preg->private_data = private_data;
preg->card_ptr = card;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
snd_oss_minors[minor] = preg;
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
switch (minor_unit) {
@@ -145,7 +129,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
goto __end;
snd_oss_minors[track2] = preg;
}
- mutex_unlock(&sound_oss_mutex);
return 0;
__end:
@@ -154,7 +137,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
if (register1 >= 0)
unregister_sound_special(register1);
snd_oss_minors[minor] = NULL;
- mutex_unlock(&sound_oss_mutex);
kfree(preg);
return -EBUSY;
}
@@ -171,13 +153,10 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
return 0;
if (minor < 0)
return minor;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
mptr = snd_oss_minors[minor];
- if (mptr == NULL) {
- mutex_unlock(&sound_oss_mutex);
+ if (mptr == NULL)
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);
@@ -189,12 +168,17 @@ 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;
}
@@ -230,9 +214,10 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
int minor;
struct snd_minor *mptr;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&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,
@@ -242,7 +227,6 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%3i: : %s\n", minor,
snd_oss_device_type_name(mptr->type));
}
- mutex_unlock(&sound_oss_mutex);
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index da05e314917f..d9fff5c87613 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>
@@ -28,6 +13,8 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/sched/signal.h>
+#include <linux/anon_inodes.h>
+#include <linux/idr.h>
#include <sound/core.h>
#include <sound/timer.h>
#include <sound/control.h>
@@ -38,6 +25,7 @@
/* 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
@@ -58,6 +46,28 @@ MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for t
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 */
@@ -69,16 +79,50 @@ struct snd_timer_user {
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 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 */
+};
+
+#ifdef CONFIG_SND_UTIMER
+#define SNDRV_UTIMERS_MAX_COUNT 128
+/* Internal data structure for keeping the state of the userspace-driven timer */
+struct snd_utimer {
+ char *name;
+ struct snd_timer *timer;
+ unsigned int id;
+};
+#endif
+
+#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
+
/* list of timers */
static LIST_HEAD(snd_timer_list);
@@ -88,6 +132,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);
@@ -99,12 +146,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;
@@ -119,22 +165,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)
@@ -174,6 +225,26 @@ 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++;
+ guard(spinlock_irq)(&slave_active_lock);
+ guard(spinlock)(&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);
+ 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.
@@ -184,27 +255,18 @@ 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) {
- 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);
- slave->master = master;
- slave->timer = master->timer;
- spin_unlock_irq(&slave_active_lock);
- return 0;
- }
+ err = check_matching_master_slave(master, slave);
+ if (err != 0) /* match found or error */
+ goto out;
}
}
- return 0;
+ out:
+ return err < 0 ? err : 0;
}
/*
@@ -216,74 +278,56 @@ static int snd_timer_check_slave(struct snd_timer_instance *slave)
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) {
- 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);
- }
+ err = check_matching_master_slave(master, slave);
+ if (err < 0)
+ break;
}
- return 0;
+ return err < 0 ? err : 0;
}
-static int snd_timer_close_locked(struct snd_timer_instance *timeri);
+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) {
pr_debug("ALSA: timer: invalid slave class %i\n",
tid->dev_sclass);
- return -EINVAL;
+ 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);
+ num_slaves++;
err = snd_timer_check_slave(timeri);
- if (err < 0) {
- snd_timer_close_locked(timeri);
- timeri = NULL;
- }
- mutex_unlock(&register_mutex);
- *ti = timeri;
- return err;
+ goto list_added;
}
/* open a master instance */
- mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -294,74 +338,102 @@ 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;
}
}
if (timer->num_instances >= timer->max_instances) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ err = -EBUSY;
+ goto unlock;
}
- timeri = snd_timer_instance_new(owner, timer);
- if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ if (!try_module_get(timer->module)) {
+ err = -EBUSY;
+ goto unlock;
}
/* take a card refcount for safe disconnection */
- if (timer->card)
+ if (timer->card) {
get_device(&timer->card->card_dev);
- timeri->slave_class = tid->dev_sclass;
- timeri->slave_id = slave_id;
+ card_dev_to_put = &timer->card->card_dev;
+ }
if (list_empty(&timer->open_list_head) && timer->hw.open) {
- int err = timer->hw.open(timer);
+ err = timer->hw.open(timer);
if (err) {
- kfree(timeri->owner);
- kfree(timeri);
-
- if (timer->card)
- put_device(&timer->card->card_dev);
module_put(timer->module);
- mutex_unlock(&register_mutex);
- return err;
+ goto unlock;
}
}
+ timeri->timer = timer;
+ timeri->slave_class = tid->dev_sclass;
+ timeri->slave_id = slave_id;
+
list_add_tail(&timeri->open_list, &timer->open_list_head);
timer->num_instances++;
err = snd_timer_check_master(timeri);
- if (err < 0) {
- snd_timer_close_locked(timeri);
- timeri = NULL;
- }
+list_added:
+ if (err < 0)
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+
+ unlock:
mutex_unlock(&register_mutex);
- *ti = timeri;
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (err < 0 && card_dev_to_put)
+ put_device(card_dev_to_put);
return err;
}
EXPORT_SYMBOL(snd_timer_open);
+/* remove slave links, called from snd_timer_close_locked() below */
+static void remove_slave_links(struct snd_timer_instance *timeri,
+ struct snd_timer *timer)
+{
+ struct snd_timer_instance *slave, *tmp;
+
+ guard(spinlock_irq)(&slave_active_lock);
+ guard(spinlock)(&timer->lock);
+ timeri->timer = NULL;
+ list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) {
+ list_move_tail(&slave->open_list, &snd_timer_slave_list);
+ timer->num_instances--;
+ slave->master = NULL;
+ slave->timer = NULL;
+ list_del_init(&slave->ack_list);
+ list_del_init(&slave->active_list);
+ }
+}
+
/*
* close a timer instance
* call this with register_mutex down.
*/
-static int snd_timer_close_locked(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_instance *slave, *tmp;
+ struct snd_timer *timer = timeri->timer;
- list_del(&timeri->open_list);
+ if (timer) {
+ guard(spinlock_irq)(&timer->lock);
+ timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
+ }
+
+ 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);
- timer = timeri->timer;
if (timer) {
timer->num_instances--;
/* wait, until the active callback is finished */
@@ -373,105 +445,95 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
}
spin_unlock_irq(&timer->lock);
- /* remove slave links */
- spin_lock_irq(&slave_active_lock);
- spin_lock(&timer->lock);
- list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
- open_list) {
- list_move_tail(&slave->open_list, &snd_timer_slave_list);
- timer->num_instances--;
- slave->master = NULL;
- slave->timer = NULL;
- list_del_init(&slave->ack_list);
- list_del_init(&slave->active_list);
- }
- spin_unlock(&timer->lock);
- spin_unlock_irq(&slave_active_lock);
+ remove_slave_links(timeri, timer);
/* 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 (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
- put_device(&timer->card->card_dev);
+ *card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
}
-
- return 0;
}
/*
* close a timer instance
*/
-int snd_timer_close(struct snd_timer_instance *timeri)
+void snd_timer_close(struct snd_timer_instance *timeri)
{
- int err;
+ struct device *card_dev_to_put = NULL;
if (snd_BUG_ON(!timeri))
- return -ENXIO;
+ return;
- mutex_lock(&register_mutex);
- err = snd_timer_close_locked(timeri);
- mutex_unlock(&register_mutex);
- return err;
+ scoped_guard(mutex, &register_mutex)
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+ /* 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;
if (timeri == NULL)
return 0;
timer = timeri->timer;
if (timer) {
- if (timer->hw.c_resolution)
- return timer->hw.c_resolution(timer);
- return timer->hw.resolution;
+ guard(spinlock_irqsave)(&timer->lock);
+ ret = snd_timer_hw_resolution(timer);
}
- 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;
+ 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)
- ktime_get_ts(&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;
+ event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
- ts->ccallback(ts, event + 100, &tstamp, resolution);
+ ts->ccallback(ts, event, &tstamp, resolution);
}
/* start/continue a master timer */
@@ -480,21 +542,26 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
{
struct snd_timer *timer;
int result;
- unsigned long flags;
timer = timeri->timer;
if (!timer)
return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- if (timer->card && timer->card->shutdown) {
- result = -ENODEV;
- goto unlock;
- }
+ guard(spinlock_irqsave)(&timer->lock);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+ return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
- SNDRV_TIMER_IFLG_START)) {
- result = -EBUSY;
- goto unlock;
+ SNDRV_TIMER_IFLG_START))
+ return -EBUSY;
+
+ /* check the actual time for the start tick;
+ * bail out as error if it's way too low (< 100us)
+ */
+ if (start && !(timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+ if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000)
+ return -EINVAL;
}
if (start)
@@ -521,8 +588,6 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
}
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
SNDRV_TIMER_EVENT_CONTINUE);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
return result;
}
@@ -530,23 +595,19 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
static int snd_timer_start_slave(struct snd_timer_instance *timeri,
bool start)
{
- unsigned long flags;
-
- spin_lock_irqsave(&slave_active_lock, flags);
- if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
- spin_unlock_irqrestore(&slave_active_lock, flags);
+ guard(spinlock_irqsave)(&slave_active_lock);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING)
return -EBUSY;
- }
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
if (timeri->master && timeri->timer) {
- spin_lock(&timeri->timer->lock);
+ guard(spinlock)(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
SNDRV_TIMER_EVENT_CONTINUE);
- spin_unlock(&timeri->timer->lock);
}
- spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
}
@@ -554,22 +615,18 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
{
struct snd_timer *timer;
- int result = 0;
- unsigned long flags;
timer = timeri->timer;
if (!timer)
return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
- SNDRV_TIMER_IFLG_START))) {
- result = -EBUSY;
- goto unlock;
- }
+ guard(spinlock_irqsave)(&timer->lock);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
+ if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)))
+ return -EBUSY;
if (timer->card && timer->card->shutdown)
- goto unlock;
+ return 0;
if (stop) {
timeri->cticks = timeri->ticks;
timeri->pticks = 0;
@@ -592,33 +649,27 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
else
timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
- SNDRV_TIMER_EVENT_CONTINUE);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
- return result;
+ SNDRV_TIMER_EVENT_PAUSE);
+ return 0;
}
/* 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);
- if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return -EBUSY;
- }
+ guard(spinlock_irqsave)(&slave_active_lock);
+ running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
if (timeri->timer) {
- spin_lock(&timeri->timer->lock);
+ guard(spinlock)(&timeri->timer->lock);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
- snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
- SNDRV_TIMER_EVENT_CONTINUE);
- spin_unlock(&timeri->timer->lock);
+ if (running)
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_PAUSE);
}
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return 0;
+ return running ? 0 : -EBUSY;
}
/*
@@ -710,42 +761,58 @@ 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;
-
- if (timer->card && timer->card->shutdown)
- return;
- 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;
+ }
+ }
+}
+
+/* clear pending instances from ack list */
+static void snd_timer_clear_callbacks(struct snd_timer *timer,
+ struct list_head *head)
+{
+ guard(spinlock_irqsave)(&timer->lock);
+ while (!list_empty(head))
+ list_del_init(head->next);
+}
- ticks = ti->pticks;
- ti->pticks = 0;
- resolution = ti->resolution;
+/*
+ * timer work
+ *
+ */
+static void snd_timer_work(struct work_struct *work)
+{
+ struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
- 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;
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->sack_list_head);
+ return;
}
- spin_unlock_irqrestore(&timer->lock, flags);
+
+ guard(spinlock_irqsave)(&timer->lock);
+ snd_timer_process_callbacks(timer, &timer->sack_list_head);
}
/*
@@ -757,24 +824,21 @@ 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 flags;
- int use_tasklet = 0;
+ unsigned long resolution;
+ struct list_head *ack_list_head;
if (timer == NULL)
return;
- if (timer->card && timer->card->shutdown)
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->ack_list_head);
return;
+ }
- spin_lock_irqsave(&timer->lock, flags);
+ guard(spinlock_irqsave)(&timer->lock);
/* 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
@@ -783,6 +847,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;
@@ -800,7 +866,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
--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
@@ -832,30 +898,11 @@ 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);
- spin_unlock_irqrestore(&timer->lock, flags);
-
- if (use_tasklet)
- tasklet_schedule(&timer->task_queue);
+ if (!list_empty(&timer->sack_list_head))
+ queue_work(system_highpri_wq, &timer->task_work);
}
EXPORT_SYMBOL(snd_timer_interrupt);
@@ -868,7 +915,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,
@@ -876,6 +923,11 @@ 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);
@@ -886,7 +938,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
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);
@@ -894,8 +946,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
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;
@@ -916,7 +967,7 @@ static int snd_timer_free(struct snd_timer *timer)
if (!timer)
return 0;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
struct list_head *p, *n;
struct snd_timer_instance *ti;
@@ -928,7 +979,6 @@ static int snd_timer_free(struct snd_timer *timer)
}
}
list_del(&timer->device_list);
- mutex_unlock(&register_mutex);
if (timer->private_free)
timer->private_free(timer);
@@ -953,7 +1003,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
!timer->hw.resolution && timer->hw.c_resolution == NULL)
return -EINVAL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(timer1, &snd_timer_list, device_list) {
if (timer1->tmr_class > timer->tmr_class)
break;
@@ -974,11 +1024,9 @@ static int snd_timer_dev_register(struct snd_device *dev)
if (timer1->tmr_subdevice < timer->tmr_subdevice)
continue;
/* conflicts.. */
- mutex_unlock(&register_mutex);
return -EBUSY;
}
list_add_tail(&timer->device_list, &timer1->device_list);
- mutex_unlock(&register_mutex);
return 0;
}
@@ -987,20 +1035,18 @@ 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);
+ guard(mutex)(&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;
@@ -1011,15 +1057,11 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
event > SNDRV_TIMER_EVENT_MRESUME))
return;
- spin_lock_irqsave(&timer->lock, flags);
+ guard(spinlock_irqsave)(&timer->lock);
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);
@@ -1027,7 +1069,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
if (ts->ccallback)
ts->ccallback(ts, event, tstamp, resolution);
}
- spin_unlock_irqrestore(&timer->lock, flags);
}
EXPORT_SYMBOL(snd_timer_notify);
@@ -1077,8 +1118,8 @@ struct snd_timer_system_private {
static void snd_timer_s_function(struct timer_list *t)
{
- struct snd_timer_system_private *priv = from_timer(priv, t,
- tlist);
+ struct snd_timer_system_private *priv = timer_container_of(priv, t,
+ tlist);
struct snd_timer *timer = priv->snd_timer;
unsigned long jiff = jiffies;
if (time_after(jiff, priv->last_expires))
@@ -1111,7 +1152,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
unsigned long jiff;
priv = (struct snd_timer_system_private *) timer->private_data;
- del_timer(&priv->tlist);
+ timer_delete(&priv->tlist);
jiff = jiffies;
if (time_before(jiff, priv->last_expires))
timer->sticks = priv->last_expires - jiff;
@@ -1126,14 +1167,14 @@ 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);
+ timer_delete_sync(&priv->tlist);
return 0;
}
-static struct snd_timer_hardware snd_timer_system =
+static const struct snd_timer_hardware snd_timer_system =
{
- .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
- .resolution = 1000000000L / HZ,
+ .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK,
+ .resolution = NSEC_PER_SEC / HZ,
.ticks = 10000000L,
.close = snd_timer_s_close,
.start = snd_timer_s_start,
@@ -1154,7 +1195,7 @@ static int snd_timer_register_system(void)
err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
if (err < 0)
return err;
- strcpy(timer->name, "system timer");
+ strscpy(timer->name, "system timer");
timer->hw = snd_timer_system;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL) {
@@ -1178,8 +1219,9 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
{
struct snd_timer *timer;
struct snd_timer_instance *ti;
+ unsigned long resolution;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->card && timer->card->shutdown)
continue;
@@ -1201,10 +1243,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
timer->tmr_device, timer->tmr_subdevice);
}
snd_iprintf(buffer, "%s :", timer->name);
- if (timer->hw.resolution)
+ scoped_guard(spinlock_irq, &timer->lock)
+ resolution = snd_timer_hw_resolution(timer);
+ if (resolution)
snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
- timer->hw.resolution / 1000,
- timer->hw.resolution % 1000,
+ resolution / 1000,
+ resolution % 1000,
timer->hw.ticks);
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
snd_iprintf(buffer, " SLAVE");
@@ -1212,11 +1256,10 @@ 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);
}
static struct snd_info_entry *snd_timer_proc_entry;
@@ -1257,7 +1300,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
struct snd_timer_read *r;
int prev;
- spin_lock(&tu->qlock);
+ guard(spinlock)(&tu->qlock);
if (tu->qused > 0) {
prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
r = &tu->queue[prev];
@@ -1276,13 +1319,12 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
tu->qused++;
}
__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++;
@@ -1295,12 +1337,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;
- unsigned long flags;
+ struct snd_timer_tread64 r1;
if (event >= SNDRV_TIMER_EVENT_START &&
event <= SNDRV_TIMER_EVENT_PAUSE)
@@ -1309,12 +1350,12 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
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);
+ scoped_guard(spinlock_irqsave, &tu->qlock)
+ snd_timer_user_append_to_tqueue(tu, &r1);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
@@ -1331,64 +1372,64 @@ 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) |
- (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
- spin_unlock(&tu->qlock);
- return;
- }
- if (tu->last_resolution != resolution || ticks > 0) {
- if (timer_tstamp_monotonic)
- ktime_get_ts(&tstamp);
- else
- getnstimeofday(&tstamp);
- }
- if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
- tu->last_resolution != resolution) {
- r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
- r1.tstamp = tstamp;
- r1.val = resolution;
- snd_timer_user_append_to_tqueue(tu, &r1);
- tu->last_resolution = resolution;
- append++;
- }
- if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
- goto __wake;
- if (ticks == 0)
- goto __wake;
- if (tu->qused > 0) {
- 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->val += ticks;
+ scoped_guard(spinlock, &tu->qlock) {
+ if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
+ (1 << SNDRV_TIMER_EVENT_TICK))) == 0)
+ return;
+ if (tu->last_resolution != resolution || ticks > 0) {
+ if (timer_tstamp_monotonic)
+ ktime_get_ts64(&tstamp);
+ else
+ 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_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;
append++;
- goto __wake;
}
+ if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
+ break;
+ if (ticks == 0)
+ break;
+ if (tu->qused > 0) {
+ prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
+ r = &tu->tqueue[prev];
+ if (r->event == SNDRV_TIMER_EVENT_TICK) {
+ r->tstamp_sec = tstamp.tv_sec;
+ r->tstamp_nsec = tstamp.tv_nsec;
+ r->val += ticks;
+ append++;
+ break;
+ }
+ }
+ r1.event = SNDRV_TIMER_EVENT_TICK;
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
+ r1.val = ticks;
+ snd_timer_user_append_to_tqueue(tu, &r1);
+ append++;
}
- r1.event = SNDRV_TIMER_EVENT_TICK;
- r1.tstamp = tstamp;
- r1.val = ticks;
- snd_timer_user_append_to_tqueue(tu, &r1);
- append++;
- __wake:
- 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_tread *tqueue = NULL;
+ struct snd_timer_tread64 *tqueue = NULL;
if (tu->tread) {
tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
@@ -1400,14 +1441,13 @@ static int realloc_user_queue(struct snd_timer_user *tu, int size)
return -ENOMEM;
}
- spin_lock_irq(&tu->qlock);
+ guard(spinlock_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;
}
@@ -1417,7 +1457,7 @@ 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;
@@ -1443,10 +1483,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;
- mutex_lock(&tu->ioctl_lock);
- if (tu->timeri)
- snd_timer_close(tu->timeri);
- mutex_unlock(&tu->ioctl_lock);
+ scoped_guard(mutex, &tu->ioctl_lock) {
+ if (tu->timeri) {
+ snd_timer_close(tu->timeri);
+ snd_timer_instance_free(tu->timeri);
+ }
+ }
+ snd_fasync_free(tu->fasync);
kfree(tu->queue);
kfree(tu->tqueue);
kfree(tu);
@@ -1472,92 +1515,97 @@ static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *ti
id->subdevice = timer->tmr_subdevice;
}
-static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
+static void get_next_device(struct snd_timer_id *id)
{
- struct snd_timer_id id;
struct snd_timer *timer;
struct list_head *p;
- if (copy_from_user(&id, _tid, sizeof(id)))
- return -EFAULT;
- mutex_lock(&register_mutex);
- if (id.dev_class < 0) { /* first item */
+ if (id->dev_class < 0) { /* first item */
if (list_empty(&snd_timer_list))
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
else {
timer = list_entry(snd_timer_list.next,
struct snd_timer, device_list);
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
}
} else {
- switch (id.dev_class) {
+ switch (id->dev_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
- id.device = id.device < 0 ? 0 : id.device + 1;
+ id->device = id->device < 0 ? 0 : id->device + 1;
list_for_each(p, &snd_timer_list) {
timer = list_entry(p, struct snd_timer, device_list);
if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_device >= id.device) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_device >= id->device) {
+ snd_timer_user_copy_id(id, timer);
break;
}
}
if (p == &snd_timer_list)
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
break;
case SNDRV_TIMER_CLASS_CARD:
case SNDRV_TIMER_CLASS_PCM:
- if (id.card < 0) {
- id.card = 0;
+ if (id->card < 0) {
+ id->card = 0;
} else {
- if (id.device < 0) {
- id.device = 0;
+ 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) {
timer = list_entry(p, struct snd_timer, device_list);
- if (timer->tmr_class > id.dev_class) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_class > id->dev_class) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_class < id.dev_class)
+ if (timer->tmr_class < id->dev_class)
continue;
- if (timer->card->number > id.card) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->card->number > id->card) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->card->number < id.card)
+ if (timer->card->number < id->card)
continue;
- if (timer->tmr_device > id.device) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_device > id->device) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_device < id.device)
+ if (timer->tmr_device < id->device)
continue;
- if (timer->tmr_subdevice > id.subdevice) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_subdevice > id->subdevice) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_subdevice < id.subdevice)
+ if (timer->tmr_subdevice < id->subdevice)
continue;
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
break;
}
if (p == &snd_timer_list)
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
break;
default:
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
}
}
- mutex_unlock(&register_mutex);
+}
+
+static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
+{
+ struct snd_timer_id id;
+
+ if (copy_from_user(&id, _tid, sizeof(id)))
+ return -EFAULT;
+ scoped_guard(mutex, &register_mutex)
+ get_next_device(&id);
if (copy_to_user(_tid, &id, sizeof(*_tid)))
return -EFAULT;
return 0;
@@ -1566,11 +1614,10 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
static int snd_timer_user_ginfo(struct file *file,
struct snd_timer_ginfo __user *_ginfo)
{
- struct snd_timer_ginfo *ginfo;
+ struct snd_timer_ginfo *ginfo __free(kfree) = NULL;
struct snd_timer_id tid;
struct snd_timer *t;
struct list_head *p;
- int err = 0;
ginfo = memdup_user(_ginfo, sizeof(*ginfo));
if (IS_ERR(ginfo))
@@ -1579,15 +1626,17 @@ static int snd_timer_user_ginfo(struct file *file,
tid = ginfo->tid;
memset(ginfo, 0, sizeof(*ginfo));
ginfo->tid = tid;
- mutex_lock(&register_mutex);
- t = snd_timer_find(&tid);
- if (t != NULL) {
+ scoped_guard(mutex, &register_mutex) {
+ t = snd_timer_find(&tid);
+ if (!t)
+ return -ENODEV;
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));
- ginfo->resolution = t->hw.resolution;
+ strscpy(ginfo->id, t->id, sizeof(ginfo->id));
+ strscpy(ginfo->name, t->name, sizeof(ginfo->name));
+ scoped_guard(spinlock_irq, &t->lock)
+ ginfo->resolution = snd_timer_hw_resolution(t);
if (t->hw.resolution_min > 0) {
ginfo->resolution_min = t->hw.resolution_min;
ginfo->resolution_max = t->hw.resolution_max;
@@ -1595,39 +1644,25 @@ static int snd_timer_user_ginfo(struct file *file,
list_for_each(p, &t->open_list_head) {
ginfo->clients++;
}
- } else {
- err = -ENODEV;
}
- mutex_unlock(&register_mutex);
- if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
- err = -EFAULT;
- kfree(ginfo);
- return err;
+ if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
+ return -EFAULT;
+ return 0;
}
static int timer_set_gparams(struct snd_timer_gparams *gparams)
{
struct snd_timer *t;
- int err;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
t = snd_timer_find(&gparams->tid);
- if (!t) {
- err = -ENODEV;
- goto _error;
- }
- if (!list_empty(&t->open_list_head)) {
- err = -EBUSY;
- goto _error;
- }
- if (!t->hw.set_period) {
- err = -ENOSYS;
- goto _error;
- }
- err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
-_error:
- mutex_unlock(&register_mutex);
- return err;
+ if (!t)
+ return -ENODEV;
+ if (!list_empty(&t->open_list_head))
+ return -EBUSY;
+ if (!t->hw.set_period)
+ return -ENOSYS;
+ return t->hw.set_period(t, gparams->period_num, gparams->period_den);
}
static int snd_timer_user_gparams(struct file *file,
@@ -1646,34 +1681,31 @@ static int snd_timer_user_gstatus(struct file *file,
struct snd_timer_gstatus gstatus;
struct snd_timer_id tid;
struct snd_timer *t;
- int err = 0;
if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
return -EFAULT;
tid = gstatus.tid;
memset(&gstatus, 0, sizeof(gstatus));
gstatus.tid = tid;
- 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;
- if (t->hw.precise_resolution) {
- t->hw.precise_resolution(t, &gstatus.resolution_num,
- &gstatus.resolution_den);
+ scoped_guard(mutex, &register_mutex) {
+ t = snd_timer_find(&tid);
+ if (t != NULL) {
+ guard(spinlock_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);
+ } else {
+ gstatus.resolution_num = gstatus.resolution;
+ gstatus.resolution_den = 1000000000uL;
+ }
} else {
- gstatus.resolution_num = gstatus.resolution;
- gstatus.resolution_den = 1000000000uL;
+ return -ENODEV;
}
- } else {
- err = -ENODEV;
}
- mutex_unlock(&register_mutex);
- if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
- err = -EFAULT;
- return err;
+ if (copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
+ return -EFAULT;
+ return 0;
}
static int snd_timer_user_tselect(struct file *file,
@@ -1687,6 +1719,7 @@ static int snd_timer_user_tselect(struct file *file,
tu = file->private_data;
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))) {
@@ -1696,9 +1729,11 @@ 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;
+ }
tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
tu->timeri->callback = tu->tread
@@ -1707,6 +1742,12 @@ static int snd_timer_user_tselect(struct file *file,
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:
return err;
}
@@ -1715,9 +1756,8 @@ static int snd_timer_user_info(struct file *file,
struct snd_timer_info __user *_info)
{
struct snd_timer_user *tu;
- struct snd_timer_info *info;
+ struct snd_timer_info *info __free(kfree) = NULL;
struct snd_timer *t;
- int err = 0;
tu = file->private_data;
if (!tu->timeri)
@@ -1732,13 +1772,13 @@ 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));
- info->resolution = t->hw.resolution;
+ strscpy(info->id, t->id, sizeof(info->id));
+ strscpy(info->name, t->name, sizeof(info->name));
+ scoped_guard(spinlock_irq, &t->lock)
+ info->resolution = snd_timer_hw_resolution(t);
if (copy_to_user(_info, info, sizeof(*_info)))
- err = -EFAULT;
- kfree(info);
- return err;
+ return -EFAULT;
+ return 0;
}
static int snd_timer_user_params(struct file *file,
@@ -1796,45 +1836,47 @@ static int snd_timer_user_params(struct file *file,
goto _end;
}
snd_timer_stop(tu->timeri);
- spin_lock_irq(&t->lock);
- tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
- SNDRV_TIMER_IFLG_EXCLUSIVE|
- SNDRV_TIMER_IFLG_EARLY_EVENT);
- if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
- if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
- if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
- spin_unlock_irq(&t->lock);
+ scoped_guard(spinlock_irq, &t->lock) {
+ tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
+ SNDRV_TIMER_IFLG_EXCLUSIVE|
+ SNDRV_TIMER_IFLG_EARLY_EVENT);
+ if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+ if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
+ if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
+ }
if (params.queue_size > 0 &&
(unsigned int)tu->queue_size != params.queue_size) {
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;
- memset(&tread, 0, sizeof(tread));
- tread.event = SNDRV_TIMER_EVENT_EARLY;
- tread.tstamp.tv_sec = 0;
- tread.tstamp.tv_nsec = 0;
- tread.val = 0;
- snd_timer_user_append_to_tqueue(tu, &tread);
- } else {
- struct snd_timer_read *r = &tu->queue[0];
- r->resolution = 0;
- r->ticks = 0;
- tu->qused++;
- tu->qtail++;
+ scoped_guard(spinlock_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_tread64 tread;
+
+ memset(&tread, 0, sizeof(tread));
+ tread.event = SNDRV_TIMER_EVENT_EARLY;
+ tread.tstamp_sec = 0;
+ tread.tstamp_nsec = 0;
+ tread.val = 0;
+ snd_timer_user_append_to_tqueue(tu, &tread);
+ } else {
+ struct snd_timer_read *r = &tu->queue[0];
+
+ r->resolution = 0;
+ r->ticks = 0;
+ tu->qused++;
+ tu->qtail++;
+ }
}
+ tu->filter = params.filter;
+ tu->ticks = params.ticks;
}
- tu->filter = params.filter;
- tu->ticks = params.ticks;
- spin_unlock_irq(&tu->qlock);
err = 0;
_end:
if (copy_to_user(_params, &params, sizeof(params)))
@@ -1842,23 +1884,45 @@ 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;
+ scoped_guard(spinlock_irq, &tu->qlock)
+ status.queue = tu->qused;
+ 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;
- spin_lock_irq(&tu->qlock);
- status.queue = tu->qused;
- spin_unlock_irq(&tu->qlock);
+ scoped_guard(spinlock_irq, &tu->qlock)
+ status.queue = tu->qused;
if (copy_to_user(_status, &status, sizeof(status)))
return -EFAULT;
return 0;
@@ -1875,7 +1939,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)
@@ -1886,7 +1953,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)
@@ -1901,7 +1971,10 @@ static int snd_timer_user_continue(struct file *file)
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)
@@ -1912,7 +1985,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 {
@@ -1922,8 +2028,219 @@ enum {
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
};
+#ifdef CONFIG_SND_UTIMER
+/*
+ * Since userspace-driven timers are passed to userspace, we need to have an identifier
+ * which will allow us to use them (basically, the subdevice number of udriven timer).
+ */
+static DEFINE_IDA(snd_utimer_ids);
+
+static void snd_utimer_put_id(struct snd_utimer *utimer)
+{
+ int timer_id = utimer->id;
+
+ snd_BUG_ON(timer_id < 0 || timer_id >= SNDRV_UTIMERS_MAX_COUNT);
+ ida_free(&snd_utimer_ids, timer_id);
+}
+
+static int snd_utimer_take_id(void)
+{
+ return ida_alloc_max(&snd_utimer_ids, SNDRV_UTIMERS_MAX_COUNT - 1, GFP_KERNEL);
+}
+
+static void snd_utimer_free(struct snd_utimer *utimer)
+{
+ snd_timer_free(utimer->timer);
+ snd_utimer_put_id(utimer);
+ kfree(utimer->name);
+ kfree(utimer);
+}
+
+static int snd_utimer_release(struct inode *inode, struct file *file)
+{
+ struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
+
+ snd_utimer_free(utimer);
+ return 0;
+}
+
+static int snd_utimer_trigger(struct file *file)
+{
+ struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
+
+ snd_timer_interrupt(utimer->timer, utimer->timer->sticks);
+ return 0;
+}
+
+static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
+{
+ switch (ioctl) {
+ case SNDRV_TIMER_IOCTL_TRIGGER:
+ return snd_utimer_trigger(file);
+ }
+
+ return -ENOTTY;
+}
+
+static const struct file_operations snd_utimer_fops = {
+ .llseek = noop_llseek,
+ .release = snd_utimer_release,
+ .unlocked_ioctl = snd_utimer_ioctl,
+};
+
+static int snd_utimer_start(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_stop(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_open(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_close(struct snd_timer *t)
+{
+ return 0;
+}
+
+static const struct snd_timer_hardware timer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
+ .open = snd_utimer_open,
+ .close = snd_utimer_close,
+ .start = snd_utimer_start,
+ .stop = snd_utimer_stop,
+};
+
+static int snd_utimer_create(struct snd_timer_uinfo *utimer_info,
+ struct snd_utimer **r_utimer)
+{
+ struct snd_utimer *utimer;
+ struct snd_timer *timer;
+ struct snd_timer_id tid;
+ int utimer_id;
+ int err = 0;
+
+ if (!utimer_info || utimer_info->resolution == 0)
+ return -EINVAL;
+
+ utimer = kzalloc(sizeof(*utimer), GFP_KERNEL);
+ if (!utimer)
+ return -ENOMEM;
+
+ /* We hold the ioctl lock here so we won't get a race condition when allocating id */
+ utimer_id = snd_utimer_take_id();
+ if (utimer_id < 0) {
+ err = utimer_id;
+ goto err_take_id;
+ }
+
+ utimer->id = utimer_id;
+
+ utimer->name = kasprintf(GFP_KERNEL, "snd-utimer%d", utimer_id);
+ if (!utimer->name) {
+ err = -ENOMEM;
+ goto err_get_name;
+ }
+
+ tid.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
+ tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+ tid.card = -1;
+ tid.device = SNDRV_TIMER_GLOBAL_UDRIVEN;
+ tid.subdevice = utimer_id;
+
+ err = snd_timer_new(NULL, utimer->name, &tid, &timer);
+ if (err < 0) {
+ pr_err("Can't create userspace-driven timer\n");
+ goto err_timer_new;
+ }
+
+ timer->module = THIS_MODULE;
+ timer->hw = timer_hw;
+ timer->hw.resolution = utimer_info->resolution;
+ timer->hw.ticks = 1;
+ timer->max_instances = MAX_SLAVE_INSTANCES;
+
+ utimer->timer = timer;
+
+ err = snd_timer_global_register(timer);
+ if (err < 0) {
+ pr_err("Can't register a userspace-driven timer\n");
+ goto err_timer_reg;
+ }
+
+ *r_utimer = utimer;
+ return 0;
+
+err_timer_reg:
+ snd_timer_free(timer);
+err_timer_new:
+ kfree(utimer->name);
+err_get_name:
+ snd_utimer_put_id(utimer);
+err_take_id:
+ kfree(utimer);
+
+ return err;
+}
+
+static int snd_utimer_ioctl_create(struct file *file,
+ struct snd_timer_uinfo __user *_utimer_info)
+{
+ struct snd_utimer *utimer;
+ struct snd_timer_uinfo *utimer_info __free(kfree) = NULL;
+ int err, timer_fd;
+
+ utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info));
+ if (IS_ERR(utimer_info))
+ return PTR_ERR(utimer_info);
+
+ err = snd_utimer_create(utimer_info, &utimer);
+ if (err < 0)
+ return err;
+
+ utimer_info->id = utimer->id;
+
+ timer_fd = anon_inode_getfd(utimer->name, &snd_utimer_fops, utimer, O_RDWR | O_CLOEXEC);
+ if (timer_fd < 0) {
+ snd_utimer_free(utimer);
+ return timer_fd;
+ }
+
+ utimer_info->fd = timer_fd;
+
+ err = copy_to_user(_utimer_info, utimer_info, sizeof(*utimer_info));
+ if (err) {
+ /*
+ * "Leak" the fd, as there is nothing we can do about it.
+ * It might have been closed already since anon_inode_getfd
+ * makes it available for userspace.
+ *
+ * We have to rely on the process exit path to do any
+ * necessary cleanup (e.g. releasing the file).
+ */
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+#else
+
+static int snd_utimer_ioctl_create(struct file *file,
+ struct snd_timer_uinfo __user *_utimer_info)
+{
+ return -ENOTTY;
+}
+
+#endif
+
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg, bool compat)
{
struct snd_timer_user *tu;
void __user *argp = (void __user *)arg;
@@ -1935,23 +2252,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, old_tread;
-
- if (tu->timeri) /* too late */
- return -EBUSY;
- if (get_user(xarg, p))
- return -EFAULT;
- old_tread = tu->tread;
- tu->tread = xarg ? 1 : 0;
- if (tu->tread != old_tread &&
- realloc_user_queue(tu, tu->queue_size) < 0) {
- tu->tread = old_tread;
- return -ENOMEM;
- }
- 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:
@@ -1964,8 +2267,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);
@@ -1978,6 +2283,8 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
return snd_timer_user_pause(file);
+ case SNDRV_TIMER_IOCTL_CREATE:
+ return snd_utimer_ioctl_create(file, argp);
}
return -ENOTTY;
}
@@ -1986,12 +2293,9 @@ 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);
- mutex_unlock(&tu->ioctl_lock);
- return ret;
+ guard(mutex)(&tu->ioctl_lock);
+ return __snd_timer_user_ioctl(file, cmd, arg, false);
}
static int snd_timer_user_fasync(int fd, struct file * file, int on)
@@ -1999,19 +2303,35 @@ 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) {
@@ -2050,14 +2370,34 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
tu->qused--;
spin_unlock_irq(&tu->qlock);
- if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[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;
- } else {
+ 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;
+ 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;
}
spin_lock_irq(&tu->qlock);
@@ -2082,12 +2422,11 @@ static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
poll_wait(file, &tu->qchange_sleep, wait);
mask = 0;
- spin_lock_irq(&tu->qlock);
+ guard(spinlock_irq)(&tu->qlock);
if (tu->qused)
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
if (tu->disconnected)
- mask |= POLLERR;
- spin_unlock_irq(&tu->qlock);
+ mask |= EPOLLERR;
return mask;
}
@@ -2104,7 +2443,6 @@ static const struct file_operations snd_timer_f_ops =
.read = snd_timer_user_read,
.open = snd_timer_user_open,
.release = snd_timer_user_release,
- .llseek = no_llseek,
.poll = snd_timer_user_poll,
.unlocked_ioctl = snd_timer_user_ioctl,
.compat_ioctl = snd_timer_user_ioctl_compat,
@@ -2120,7 +2458,7 @@ static void snd_timer_free_all(void)
snd_timer_free(timer);
}
-static struct device timer_dev;
+static struct device *timer_dev;
/*
* ENTRY functions
@@ -2130,8 +2468,10 @@ static int __init alsa_timer_init(void)
{
int err;
- snd_device_initialize(&timer_dev, NULL);
- dev_set_name(&timer_dev, "timer");
+ err = snd_device_alloc(&timer_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(timer_dev, "timer");
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
@@ -2145,7 +2485,7 @@ static int __init alsa_timer_init(void)
}
err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
- &snd_timer_f_ops, NULL, &timer_dev);
+ &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();
@@ -2156,15 +2496,15 @@ static int __init alsa_timer_init(void)
return 0;
put_timer:
- put_device(&timer_dev);
+ put_device(timer_dev);
return err;
}
static void __exit alsa_timer_exit(void)
{
- snd_unregister_device(&timer_dev);
+ snd_unregister_device(timer_dev);
snd_timer_free_all();
- put_device(&timer_dev);
+ 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);
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index e00f7e399e46..4ae9eaeb5afb 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -1,21 +1,7 @@
+// 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 */
@@ -75,62 +61,19 @@ static int snd_timer_user_info_compat(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)))
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_status32 status;
-
- tu = file->private_data;
- if (!tu->timeri)
- return -EBADFD;
- memset(&status, 0, sizeof(status));
- status.tstamp.tv_sec = tu->tstamp.tv_sec;
- status.tstamp.tv_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;
-}
-
-#ifdef CONFIG_X86_X32
-/* X32 ABI has the same struct as x86-64 */
-#define snd_timer_user_status_x32(file, s) \
- snd_timer_user_status(file, s)
-#endif /* CONFIG_X86_X32 */
-
-/*
- */
-
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),
-#ifdef CONFIG_X86_X32
- SNDRV_TIMER_IOCTL_STATUS_X32 = _IOW('T', 0x14, struct snd_timer_status),
-#endif /* CONFIG_X86_X32 */
+ 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,
@@ -140,7 +83,8 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
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_GSTATUS:
case SNDRV_TIMER_IOCTL_SELECT:
@@ -154,17 +98,15 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
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);
-#ifdef CONFIG_X86_X32
- case SNDRV_TIMER_IOCTL_STATUS_X32:
- return snd_timer_user_status_x32(file, argp);
-#endif /* CONFIG_X86_X32 */
+ 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;
}
@@ -173,10 +115,7 @@ 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;
+ guard(mutex)(&tu->ioctl_lock);
+ return __snd_timer_user_ioctl_compat(file, cmd, arg);
}
diff --git a/sound/core/ump.c b/sound/core/ump.c
new file mode 100644
index 000000000000..8d8681a42ca5
--- /dev/null
+++ b/sound/core/ump.c
@@ -0,0 +1,1394 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal MIDI Packet (UMP) support
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/ump.h>
+#include <sound/ump_convert.h>
+
+#define ump_err(ump, fmt, args...) dev_err((ump)->core.dev, fmt, ##args)
+#define ump_warn(ump, fmt, args...) dev_warn((ump)->core.dev, fmt, ##args)
+#define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args)
+#define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args)
+
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
+static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
+ void __user *argp);
+static void snd_ump_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer);
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up);
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
+
+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size);
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+static int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count);
+static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
+ int words);
+static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump);
+static void update_legacy_names(struct snd_ump_endpoint *ump);
+#else
+static inline int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count)
+{
+ return 0;
+}
+static inline void process_legacy_input(struct snd_ump_endpoint *ump,
+ const u32 *src, int words)
+{
+}
+static inline void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+}
+static inline void update_legacy_names(struct snd_ump_endpoint *ump)
+{
+}
+#endif
+
+/* copy a string safely with stripping non-printable letters */
+static void safe_copy_string(void *dst, size_t max_dst_size,
+ const void *src, size_t max_src_size)
+{
+ const unsigned char *s = src;
+ unsigned char *d = dst;
+
+ if (!max_dst_size--)
+ return;
+ for (s = src; max_dst_size && *s && max_src_size--; s++) {
+ if (!isascii(*s) || !isprint(*s))
+ continue;
+ *d++ = *s;
+ max_dst_size--;
+ }
+ *d = 0;
+}
+
+/* append a string safely with stripping non-printable letters */
+static void safe_append_string(void *dst, size_t max_dst_size,
+ const void *src, size_t max_src_size)
+{
+ unsigned char *d = dst;
+ size_t len = strlen(d);
+
+ safe_copy_string(d + len, max_dst_size - len, src, max_src_size);
+}
+
+static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
+ .dev_register = snd_ump_dev_register,
+ .dev_unregister = snd_ump_dev_unregister,
+ .ioctl = snd_ump_ioctl,
+ .proc_read = snd_ump_proc_read,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+ .drain = snd_ump_rawmidi_drain,
+};
+
+static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ struct snd_ump_block *fb;
+
+ while (!list_empty(&ump->block_list)) {
+ fb = list_first_entry(&ump->block_list, struct snd_ump_block,
+ list);
+ list_del(&fb->list);
+ if (fb->private_free)
+ fb->private_free(fb);
+ kfree(fb);
+ }
+
+ if (ump->private_free)
+ ump->private_free(ump);
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ kfree(ump->out_cvts);
+#endif
+}
+
+/**
+ * snd_ump_endpoint_new - create a UMP Endpoint object
+ * @card: the card instance
+ * @id: the id string for rawmidi
+ * @device: the device index for rawmidi
+ * @output: 1 for enabling output
+ * @input: 1 for enabling input
+ * @ump_ret: the pointer to store the new UMP instance
+ *
+ * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
+ * instance with one input and/or one output rawmidi stream (either uni-
+ * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
+ * that consist of one or multiple UMP Groups.
+ *
+ * Use snd_rawmidi_set_ops() to set the operators to the new instance.
+ * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
+ * depending on the given @output and @input.
+ *
+ * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
+ * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
+ * created.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
+ int output, int input,
+ struct snd_ump_endpoint **ump_ret)
+{
+ unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
+ struct snd_ump_endpoint *ump;
+ int err;
+
+ if (input)
+ info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ if (output)
+ info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ if (input && output)
+ info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ ump = kzalloc(sizeof(*ump), GFP_KERNEL);
+ if (!ump)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&ump->block_list);
+ mutex_init(&ump->open_mutex);
+ init_waitqueue_head(&ump->stream_wait);
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ spin_lock_init(&ump->legacy_locks[0]);
+ spin_lock_init(&ump->legacy_locks[1]);
+#endif
+ err = snd_rawmidi_init(&ump->core, card, id, device,
+ output, input, info_flags);
+ if (err < 0) {
+ snd_rawmidi_free(&ump->core);
+ return err;
+ }
+
+ ump->info.card = card->number;
+ ump->info.device = device;
+
+ ump->core.private_free = snd_ump_endpoint_free;
+ ump->core.ops = &snd_ump_rawmidi_ops;
+ if (input)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_ump_rawmidi_input_ops);
+ if (output)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_ump_rawmidi_output_ops);
+
+ ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
+ *ump_ret = ump;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
+
+/*
+ * Device register / unregister hooks;
+ * do nothing, placeholders for avoiding the default rawmidi handling
+ */
+
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+static void snd_ump_dev_seq_free(struct snd_seq_device *device)
+{
+ struct snd_ump_endpoint *ump = device->private_data;
+
+ ump->seq_dev = NULL;
+}
+#endif
+
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ int err;
+
+ err = snd_seq_device_new(ump->core.card, ump->core.device,
+ SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
+ if (err < 0)
+ return err;
+ ump->seq_dev->private_data = ump;
+ ump->seq_dev->private_free = snd_ump_dev_seq_free;
+ snd_device_register(ump->core.card, ump->seq_dev);
+#endif
+ return 0;
+}
+
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
+{
+ return 0;
+}
+
+static struct snd_ump_block *
+snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
+{
+ struct snd_ump_block *fb;
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ if (fb->info.block_id == id)
+ return fb;
+ }
+ return NULL;
+}
+
+/*
+ * rawmidi ops for UMP endpoint
+ */
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+ int err;
+
+ if (ump->substreams[dir])
+ return -EBUSY;
+ err = ump->ops->open(ump, dir);
+ if (err < 0)
+ return err;
+ ump->substreams[dir] = substream;
+ return 0;
+}
+
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->substreams[dir] = NULL;
+ ump->ops->close(ump, dir);
+ return 0;
+}
+
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+
+ if (ump->ops->drain)
+ ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+/* number of 32bit words per message type */
+static unsigned char ump_packet_words[0x10] = {
+ 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
+};
+
+/**
+ * snd_ump_receive_ump_val - parse the UMP packet data
+ * @ump: UMP endpoint
+ * @val: UMP packet data
+ *
+ * The data is copied onto ump->input_buf[].
+ * When a full packet is completed, returns the number of words (from 1 to 4).
+ * OTOH, if the packet is incomplete, returns 0.
+ */
+int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
+{
+ int words;
+
+ if (!ump->input_pending)
+ ump->input_pending = ump_packet_words[ump_message_type(val)];
+
+ ump->input_buf[ump->input_buf_head++] = val;
+ ump->input_pending--;
+ if (!ump->input_pending) {
+ words = ump->input_buf_head;
+ ump->input_buf_head = 0;
+ return words;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive_ump_val);
+
+/**
+ * snd_ump_receive - transfer UMP packets from the device
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to submit the received UMP packets from the device
+ * to user-space. It's essentially a wrapper of rawmidi_receive().
+ * The data to receive is in CPU-native endianness.
+ */
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream;
+ const u32 *p = buffer;
+ int n, words = count >> 2;
+
+ while (words--) {
+ n = snd_ump_receive_ump_val(ump, *p++);
+ if (!n)
+ continue;
+ ump_handle_stream_msg(ump, ump->input_buf, n);
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops)
+ ump->seq_ops->input_receive(ump, ump->input_buf, n);
+#endif
+ process_legacy_input(ump, ump->input_buf, n);
+ }
+
+ substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
+ if (!substream)
+ return 0;
+ return snd_rawmidi_receive(substream, (const char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive);
+
+/**
+ * snd_ump_transmit - transmit UMP packets
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to obtain the UMP packets from user-space to the
+ * device. It's essentially a wrapper of rawmidi_transmit().
+ * The data to transmit is in CPU-native endianness.
+ */
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream =
+ ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ int err;
+
+ if (!substream)
+ return -ENODEV;
+ err = snd_rawmidi_transmit(substream, (char *)buffer, count);
+ /* received either data or an error? */
+ if (err)
+ return err;
+ return process_legacy_output(ump, buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_transmit);
+
+/**
+ * snd_ump_block_new - Create a UMP block
+ * @ump: UMP object
+ * @blk: block ID number to create
+ * @direction: direction (in/out/bidirection)
+ * @first_group: the first group ID (0-based)
+ * @num_groups: the number of groups in this block
+ * @blk_ret: the pointer to store the resultant block object
+ */
+int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
+ unsigned int direction, unsigned int first_group,
+ unsigned int num_groups, struct snd_ump_block **blk_ret)
+{
+ struct snd_ump_block *fb, *p;
+
+ if (blk >= SNDRV_UMP_MAX_BLOCKS)
+ return -EINVAL;
+
+ if (snd_ump_get_block(ump, blk))
+ return -EBUSY;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb)
+ return -ENOMEM;
+
+ fb->ump = ump;
+ fb->info.card = ump->info.card;
+ fb->info.device = ump->info.device;
+ fb->info.block_id = blk;
+ if (blk >= ump->info.num_blocks)
+ ump->info.num_blocks = blk + 1;
+ fb->info.direction = direction;
+ fb->info.active = 1;
+ fb->info.first_group = first_group;
+ fb->info.num_groups = num_groups;
+ /* fill the default name, may be overwritten to a better name */
+ snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u",
+ first_group + 1, first_group + num_groups);
+
+ /* put the entry in the ordered list */
+ list_for_each_entry(p, &ump->block_list, list) {
+ if (p->info.block_id > blk) {
+ list_add_tail(&fb->list, &p->list);
+ goto added;
+ }
+ }
+ list_add_tail(&fb->list, &ump->block_list);
+
+ added:
+ ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
+ *blk_ret = fb;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_block_new);
+
+static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
+ struct snd_ump_block_info __user *argp)
+{
+ struct snd_ump_block *fb;
+ unsigned char id;
+
+ if (get_user(id, &argp->block_id))
+ return -EFAULT;
+ fb = snd_ump_get_block(ump, id);
+ if (!fb)
+ return -ENOENT;
+ if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
+ */
+static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
+ void __user *argp)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+
+ switch (cmd) {
+ case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+ if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
+ return -EFAULT;
+ return 0;
+ case SNDRV_UMP_IOCTL_BLOCK_INFO:
+ return snd_ump_ioctl_block(ump, argp);
+ default:
+ ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
+ return -ENOTTY;
+ }
+}
+
+static const char *ump_direction_string(int dir)
+{
+ switch (dir) {
+ case SNDRV_UMP_DIR_INPUT:
+ return "input";
+ case SNDRV_UMP_DIR_OUTPUT:
+ return "output";
+ case SNDRV_UMP_DIR_BIDIRECTION:
+ return "bidirection";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *ump_ui_hint_string(int dir)
+{
+ switch (dir) {
+ case SNDRV_UMP_BLOCK_UI_HINT_RECEIVER:
+ return "receiver";
+ case SNDRV_UMP_BLOCK_UI_HINT_SENDER:
+ return "sender";
+ case SNDRV_UMP_BLOCK_UI_HINT_BOTH:
+ return "both";
+ default:
+ return "unknown";
+ }
+}
+
+/* Additional proc file output */
+static void snd_ump_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_rawmidi *rmidi = entry->private_data;
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ struct snd_ump_block *fb;
+
+ snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
+ snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
+ snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
+ snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
+ snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
+ if (ump->info.version) {
+ snd_iprintf(buffer, "Manufacturer ID: 0x%08x\n",
+ ump->info.manufacturer_id);
+ snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id);
+ snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id);
+ snd_iprintf(buffer, "SW Revision: 0x%4phN\n", ump->info.sw_revision);
+ }
+ snd_iprintf(buffer, "Static Blocks: %s\n",
+ (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No");
+ snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
+ fb->info.name);
+ snd_iprintf(buffer, " Direction: %s\n",
+ ump_direction_string(fb->info.direction));
+ snd_iprintf(buffer, " Active: %s\n",
+ fb->info.active ? "Yes" : "No");
+ snd_iprintf(buffer, " Groups: %d-%d\n",
+ fb->info.first_group + 1,
+ fb->info.first_group + fb->info.num_groups);
+ snd_iprintf(buffer, " Is MIDI1: %s%s\n",
+ (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
+ (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
+ if (ump->info.version) {
+ snd_iprintf(buffer, " MIDI-CI Version: %d\n",
+ fb->info.midi_ci_version);
+ snd_iprintf(buffer, " Sysex8 Streams: %d\n",
+ fb->info.sysex8_streams);
+ snd_iprintf(buffer, " UI Hint: %s\n",
+ ump_ui_hint_string(fb->info.ui_hint));
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+/* update dir_bits and active flag for all groups in the client */
+void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
+{
+ struct snd_ump_block *fb;
+ struct snd_ump_group *group;
+ int i;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ group = &ump->groups[i];
+ *group->name = 0;
+ group->dir_bits = 0;
+ group->active = 0;
+ group->group = i;
+ group->valid = false;
+ group->is_midi1 = false;
+ }
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
+ break;
+ group = &ump->groups[fb->info.first_group];
+ for (i = 0; i < fb->info.num_groups; i++, group++) {
+ group->valid = true;
+ if (fb->info.active)
+ group->active = 1;
+ if (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1)
+ group->is_midi1 = true;
+ switch (fb->info.direction) {
+ case SNDRV_UMP_DIR_INPUT:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT);
+ break;
+ case SNDRV_UMP_DIR_OUTPUT:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
+ break;
+ case SNDRV_UMP_DIR_BIDIRECTION:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) |
+ (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
+ break;
+ }
+ if (!*fb->info.name)
+ continue;
+ if (*group->name)
+ strlcat(group->name, ", ", sizeof(group->name));
+ safe_append_string(group->name, sizeof(group->name),
+ fb->info.name, sizeof(fb->info.name));
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs);
+
+/*
+ * UMP endpoint and function block handling
+ */
+
+/* open / close UMP streams for the internal stream msg communication */
+static int ump_request_open(struct snd_ump_endpoint *ump)
+{
+ return snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &ump->stream_rfile);
+}
+
+static void ump_request_close(struct snd_ump_endpoint *ump)
+{
+ snd_rawmidi_kernel_release(&ump->stream_rfile);
+}
+
+/* request a command and wait for the given response;
+ * @req1 and @req2 are u32 commands
+ * @reply is the expected UMP stream status
+ */
+static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
+ u32 reply)
+{
+ u32 buf[4];
+
+ ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
+ __func__, req1, req2, reply);
+ memset(buf, 0, sizeof(buf));
+ buf[0] = req1;
+ buf[1] = req2;
+ ump->stream_finished = 0;
+ ump->stream_wait_for = reply;
+ snd_rawmidi_kernel_write(ump->stream_rfile.output,
+ (unsigned char *)&buf, 16);
+ wait_event_timeout(ump->stream_wait, ump->stream_finished,
+ msecs_to_jiffies(500));
+ if (!READ_ONCE(ump->stream_finished)) {
+ ump_dbg(ump, "%s: request timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+ ump->stream_finished = 0;
+ ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+ return 0;
+}
+
+/* append the received letters via UMP packet to the given string buffer;
+ * return 1 if the full string is received or 0 to continue
+ */
+static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
+ int maxsize, const u32 *buf, int offset)
+{
+ unsigned char format;
+ int c;
+
+ format = ump_stream_message_format(buf[0]);
+ if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_START) {
+ c = 0;
+ } else {
+ c = strlen(dest);
+ if (c >= maxsize - 1)
+ return 1;
+ }
+
+ for (; offset < 16; offset++) {
+ dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
+ if (!dest[c])
+ break;
+ if (++c >= maxsize - 1)
+ break;
+ }
+ dest[c] = 0;
+ return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_END);
+}
+
+/* Choose the default protocol */
+static void choose_default_protocol(struct snd_ump_endpoint *ump)
+{
+ if (ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK)
+ return;
+ if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ else
+ ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+}
+
+/* notify the EP info/name change to sequencer */
+static void seq_notify_ep_change(struct snd_ump_endpoint *ump)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change)
+ ump->seq_ops->notify_ep_change(ump);
+#endif
+}
+
+/* handle EP info stream message; update the UMP attributes */
+static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.version = (buf->ep_info.ump_version_major << 8) |
+ buf->ep_info.ump_version_minor;
+ ump->info.num_blocks = buf->ep_info.num_function_blocks;
+ if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
+ ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
+ ump->info.num_blocks);
+ ump->info.num_blocks = 1;
+ }
+
+ if (buf->ep_info.static_function_block)
+ ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+
+ ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
+ buf->ep_info.jrts;
+
+ ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
+ ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
+
+ ump->info.protocol &= ump->info.protocol_caps;
+ choose_default_protocol(ump);
+ seq_notify_ep_change(ump);
+
+ return 1; /* finished */
+}
+
+/* handle EP device info stream message; update the UMP attributes */
+static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
+ ump->info.family_id = (buf->device_info.family_msb << 8) |
+ buf->device_info.family_lsb;
+ ump->info.model_id = (buf->device_info.model_msb << 8) |
+ buf->device_info.model_lsb;
+ ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
+ ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
+ ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
+ ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
+ ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%4phN\n",
+ ump->info.manufacturer_id,
+ ump->info.family_id,
+ ump->info.model_id,
+ ump->info.sw_revision);
+ seq_notify_ep_change(ump);
+ return 1; /* finished */
+}
+
+/* set up the core rawmidi name from UMP EP name string */
+static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+ safe_copy_string(ump->core.name, sizeof(ump->core.name),
+ ump->info.name, sizeof(ump->info.name));
+}
+
+/* handle EP name stream message; update the UMP name string */
+static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ int ret;
+
+ ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
+ buf->raw, 2);
+ if (ret && ump->parsed) {
+ ump_set_rawmidi_name(ump);
+ ump_legacy_set_rawmidi_name(ump);
+ seq_notify_ep_change(ump);
+ }
+
+ return ret;
+}
+
+/* handle EP product id stream message; update the UMP product_id string */
+static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ int ret;
+
+ ret = ump_append_string(ump, ump->info.product_id,
+ sizeof(ump->info.product_id),
+ buf->raw, 2);
+ if (ret)
+ seq_notify_ep_change(ump);
+ return ret;
+}
+
+/* notify the protocol change to sequencer */
+static void seq_notify_protocol(struct snd_ump_endpoint *ump)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops && ump->seq_ops->switch_protocol)
+ ump->seq_ops->switch_protocol(ump);
+#endif /* CONFIG_SND_SEQUENCER */
+}
+
+/**
+ * snd_ump_switch_protocol - switch MIDI protocol
+ * @ump: UMP endpoint
+ * @protocol: protocol to switch to
+ *
+ * Returns 1 if the protocol is actually switched, 0 if unchanged
+ */
+int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol)
+{
+ unsigned int type;
+
+ protocol &= ump->info.protocol_caps;
+ if (protocol == ump->info.protocol)
+ return 0;
+
+ type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK;
+ if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 &&
+ type != SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ return 0;
+
+ ump->info.protocol = protocol;
+ ump_dbg(ump, "New protocol = %x (caps = %x)\n",
+ protocol, ump->info.protocol_caps);
+ seq_notify_protocol(ump);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_ump_switch_protocol);
+
+/* handle EP stream config message; update the UMP protocol */
+static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned int protocol =
+ (buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
+
+ snd_ump_switch_protocol(ump, protocol);
+ return 1; /* finished */
+}
+
+/* Extract Function Block info from UMP packet */
+static void fill_fb_info(struct snd_ump_endpoint *ump,
+ struct snd_ump_block_info *info,
+ const union snd_ump_stream_msg *buf)
+{
+ info->direction = buf->fb_info.direction;
+ info->ui_hint = buf->fb_info.ui_hint;
+ info->first_group = buf->fb_info.first_group;
+ info->num_groups = buf->fb_info.num_groups;
+ if (buf->fb_info.midi_10 < 2)
+ info->flags = buf->fb_info.midi_10;
+ else
+ info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED;
+ info->active = buf->fb_info.active;
+ info->midi_ci_version = buf->fb_info.midi_ci_version;
+ info->sysex8_streams = buf->fb_info.sysex8_streams;
+
+ ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
+ info->block_id, info->direction, info->active,
+ info->first_group, info->num_groups, info->midi_ci_version,
+ info->sysex8_streams, info->flags);
+
+ if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) {
+ info->num_groups = 1;
+ ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n",
+ info->block_id);
+ }
+}
+
+/* check whether the FB info gets updated by the current message */
+static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb,
+ const union snd_ump_stream_msg *buf)
+{
+ char tmpbuf[offsetof(struct snd_ump_block_info, name)];
+
+ if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
+ ump_info(ump, "Skipping static FB info update (blk#%d)\n",
+ fb->info.block_id);
+ return 0;
+ }
+
+ memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
+ fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
+ return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
+}
+
+/* notify the FB info/name change to sequencer */
+static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops && ump->seq_ops->notify_fb_change)
+ ump->seq_ops->notify_fb_change(ump, fb);
+#endif
+}
+
+/* handle FB info message; update FB info if the block is present */
+static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+
+ blk = buf->fb_info.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+
+ /* complain only if updated after parsing */
+ if (!fb && ump->parsed) {
+ ump_info(ump, "Function Block Info Update for non-existing block %d\n",
+ blk);
+ return -ENODEV;
+ }
+
+ /* When updated after the initial parse, check the FB info update */
+ if (ump->parsed && !is_fb_info_updated(ump, fb, buf))
+ return 1; /* no content change */
+
+ if (fb) {
+ fill_fb_info(ump, &fb->info, buf);
+ if (ump->parsed) {
+ snd_ump_update_group_attrs(ump);
+ update_legacy_names(ump);
+ seq_notify_fb_change(ump, fb);
+ }
+ }
+
+ return 1; /* finished */
+}
+
+/* handle FB name message; update the FB name string */
+static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+ int ret;
+
+ blk = buf->fb_name.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+ if (!fb)
+ return -ENODEV;
+
+ if (ump->parsed &&
+ (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) {
+ ump_dbg(ump, "Skipping static FB name update (blk#%d)\n",
+ fb->info.block_id);
+ return 0;
+ }
+
+ ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
+ buf->raw, 3);
+ /* notify the FB name update to sequencer, too */
+ if (ret > 0 && ump->parsed) {
+ snd_ump_update_group_attrs(ump);
+ update_legacy_names(ump);
+ seq_notify_fb_change(ump, fb);
+ }
+ return ret;
+}
+
+static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
+{
+ struct snd_ump_block *fb;
+ unsigned char direction, first_group, num_groups;
+ const union snd_ump_stream_msg *buf =
+ (const union snd_ump_stream_msg *)ump->input_buf;
+ u32 msg;
+ int err;
+
+ /* query the FB info once */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get FB info for block %d\n", blk);
+ return err;
+ }
+
+ /* the last input must be the FB info */
+ if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
+ ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw);
+ return -EINVAL;
+ }
+
+ direction = buf->fb_info.direction;
+ first_group = buf->fb_info.first_group;
+ num_groups = buf->fb_info.num_groups;
+
+ err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
+ &fb);
+ if (err < 0)
+ return err;
+
+ fill_fb_info(ump, &fb->info, buf);
+
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
+ if (err)
+ ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
+
+ return 0;
+}
+
+/* handle stream messages, called from snd_ump_receive() */
+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size)
+{
+ const union snd_ump_stream_msg *msg;
+ unsigned int status;
+ int ret;
+
+ /* UMP stream message suppressed (for gadget UMP)? */
+ if (ump->no_process_stream)
+ return;
+
+ BUILD_BUG_ON(sizeof(*msg) != 16);
+ ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
+ buf[0], buf[1], buf[2], buf[3]);
+
+ if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
+ return;
+
+ msg = (const union snd_ump_stream_msg *)buf;
+ status = ump_stream_message_status(*buf);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_INFO:
+ ret = ump_handle_ep_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
+ ret = ump_handle_device_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_EP_NAME:
+ ret = ump_handle_ep_name_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
+ ret = ump_handle_product_id_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG:
+ ret = ump_handle_stream_cfg_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_INFO:
+ ret = ump_handle_fb_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_NAME:
+ ret = ump_handle_fb_name_msg(ump, msg);
+ break;
+ default:
+ return;
+ }
+
+ /* when the message has been processed fully, wake up */
+ if (ret > 0 && ump->stream_wait_for == status) {
+ WRITE_ONCE(ump->stream_finished, 1);
+ wake_up(&ump->stream_wait);
+ }
+}
+
+/**
+ * snd_ump_parse_endpoint - parse endpoint and create function blocks
+ * @ump: UMP object
+ *
+ * Returns 0 for successful parse, -ENODEV if device doesn't respond
+ * (or the query is unsupported), or other error code for serious errors.
+ */
+int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
+{
+ int blk, err;
+ u32 msg;
+
+ if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
+ return -ENODEV;
+
+ err = ump_request_open(ump);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to open rawmidi device: %d\n", err);
+ return err;
+ }
+
+ /* Check Endpoint Information */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
+ 0x0101; /* UMP version 1.1 */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
+ UMP_STREAM_MSG_STATUS_EP_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get UMP EP info\n");
+ goto error;
+ }
+
+ /* Request Endpoint Device Info */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
+ UMP_STREAM_MSG_STATUS_DEVICE_INFO);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP device info\n");
+
+ /* Request Endpoint Name */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
+ UMP_STREAM_MSG_STATUS_EP_NAME);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP name string\n");
+
+ ump_set_rawmidi_name(ump);
+
+ /* Request Endpoint Product ID */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP product ID string\n");
+
+ /* Get the current stream configuration */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP stream config\n");
+
+ /* If no protocol is set by some reason, assume the valid one */
+ choose_default_protocol(ump);
+
+ /* Query and create blocks from Function Blocks */
+ for (blk = 0; blk < ump->info.num_blocks; blk++) {
+ err = create_block_from_fb_info(ump, blk);
+ if (err < 0)
+ continue;
+ }
+
+ /* initialize group attributions */
+ snd_ump_update_group_attrs(ump);
+
+ error:
+ ump->parsed = true;
+ ump_request_close(ump);
+ if (err == -ETIMEDOUT)
+ err = -ENODEV;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+/*
+ * Legacy rawmidi support
+ */
+static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+ int group = ump->legacy_mapping[substream->number];
+ int err;
+
+ guard(mutex)(&ump->open_mutex);
+ if (ump->legacy_substreams[dir][group])
+ return -EBUSY;
+ if (!ump->groups[group].active)
+ return -ENODEV;
+ if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ if (!ump->legacy_out_opens) {
+ err = snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT |
+ SNDRV_RAWMIDI_LFLG_APPEND,
+ &ump->legacy_out_rfile);
+ if (err < 0)
+ return err;
+ }
+ ump->legacy_out_opens++;
+ snd_ump_convert_reset(&ump->out_cvts[group]);
+ }
+ guard(spinlock_irq)(&ump->legacy_locks[dir]);
+ ump->legacy_substreams[dir][group] = substream;
+ return 0;
+}
+
+static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+ int group = ump->legacy_mapping[substream->number];
+
+ guard(mutex)(&ump->open_mutex);
+ scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
+ ump->legacy_substreams[dir][group] = NULL;
+ if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ if (!--ump->legacy_out_opens)
+ snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
+ }
+ return 0;
+}
+
+static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+
+ ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+
+ if (ump->ops->drain)
+ ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
+{
+ /* dummy, just for avoiding create superfluous seq clients */
+ return 0;
+}
+
+static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
+ .open = snd_ump_legacy_open,
+ .close = snd_ump_legacy_close,
+ .trigger = snd_ump_legacy_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
+ .open = snd_ump_legacy_open,
+ .close = snd_ump_legacy_close,
+ .trigger = snd_ump_legacy_trigger,
+ .drain = snd_ump_legacy_drain,
+};
+
+static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
+ .dev_register = snd_ump_legacy_dev_register,
+};
+
+static int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream;
+ struct ump_cvt_to_ump *ctx;
+ const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
+ unsigned int protocol;
+ unsigned char c;
+ int group, size = 0;
+
+ if (!ump->out_cvts || !ump->legacy_out_opens)
+ return 0;
+
+ guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
+ for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
+ substream = ump->legacy_substreams[dir][group];
+ if (!substream)
+ continue;
+ ctx = &ump->out_cvts[group];
+ protocol = ump->info.protocol;
+ if ((protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) &&
+ ump->groups[group].is_midi1)
+ protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ while (!ctx->ump_bytes &&
+ snd_rawmidi_transmit(substream, &c, 1) > 0)
+ snd_ump_convert_to_ump(ctx, group, protocol, c);
+ if (ctx->ump_bytes && ctx->ump_bytes <= count) {
+ size = ctx->ump_bytes;
+ memcpy(buffer, ctx->ump, size);
+ ctx->ump_bytes = 0;
+ break;
+ }
+ }
+ return size;
+}
+
+static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
+ int words)
+{
+ struct snd_rawmidi_substream *substream;
+ unsigned char buf[16];
+ unsigned char group;
+ const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
+ int size;
+
+ size = snd_ump_convert_from_ump(src, buf, &group);
+ if (size <= 0)
+ return;
+ guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
+ substream = ump->legacy_substreams[dir][group];
+ if (substream)
+ snd_rawmidi_receive(substream, buf, size);
+}
+
+/* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
+static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
+{
+ struct snd_ump_block *fb;
+ unsigned int group_maps = 0;
+ int i, num;
+
+ if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
+ list_for_each_entry(fb, &ump->block_list, list) {
+ for (i = 0; i < fb->info.num_groups; i++)
+ group_maps |= 1U << (fb->info.first_group + i);
+ }
+ if (!group_maps)
+ ump_info(ump, "No UMP Group is found in FB\n");
+ }
+
+ /* use all groups for non-static case */
+ if (!group_maps)
+ group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
+
+ num = 0;
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++)
+ if (group_maps & (1U << i))
+ ump->legacy_mapping[num++] = i;
+
+ return num;
+}
+
+static void update_legacy_substreams(struct snd_ump_endpoint *ump,
+ struct snd_rawmidi *rmidi, int dir)
+{
+ struct snd_rawmidi_substream *s;
+ const char *name;
+ int idx;
+
+ list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
+ idx = ump->legacy_mapping[s->number];
+ name = ump->groups[idx].name;
+ if (!*name)
+ name = ump->core.name;
+ scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
+ idx + 1, name,
+ ump->groups[idx].active ? "" : " [Inactive]");
+ s->inactive = !ump->groups[idx].active;
+ }
+}
+
+static void update_legacy_names(struct snd_ump_endpoint *ump)
+{
+ struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+
+ update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
+ update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+ struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+
+ snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
+ ump->core.name);
+}
+
+int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
+ char *id, int device)
+{
+ struct snd_rawmidi *rmidi;
+ bool input, output;
+ int err, num;
+
+ ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
+ sizeof(*ump->out_cvts), GFP_KERNEL);
+ if (!ump->out_cvts)
+ return -ENOMEM;
+
+ num = fill_legacy_mapping(ump);
+
+ input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
+ output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
+ err = snd_rawmidi_new(ump->core.card, id, device,
+ output ? num : 0, input ? num : 0,
+ &rmidi);
+ if (err < 0) {
+ kfree(ump->out_cvts);
+ return err;
+ }
+
+ if (input)
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_ump_legacy_input_ops);
+ if (output)
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_ump_legacy_output_ops);
+ rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
+ rmidi->ops = &snd_ump_legacy_ops;
+ rmidi->private_data = ump;
+ ump->legacy_rmidi = rmidi;
+ ump_legacy_set_rawmidi_name(ump);
+ update_legacy_names(ump);
+
+ snd_rawmidi_tie_devices(rmidi, &ump->core);
+
+ ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
+#endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
+
+MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c
new file mode 100644
index 000000000000..0fe13d031656
--- /dev/null
+++ b/sound/core/ump_convert.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Helpers for UMP <-> MIDI 1.0 byte stream conversion
+ */
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <sound/ump.h>
+#include <sound/ump_convert.h>
+
+/*
+ * Upgrade / downgrade value bits
+ */
+static u8 downscale_32_to_7bit(u32 src)
+{
+ return src >> 25;
+}
+
+static u16 downscale_32_to_14bit(u32 src)
+{
+ return src >> 18;
+}
+
+static u8 downscale_16_to_7bit(u16 src)
+{
+ return src >> 9;
+}
+
+static u16 upscale_7_to_16bit(u8 src)
+{
+ u16 val, repeat;
+
+ val = (u16)src << 9;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 3) | (repeat >> 3);
+}
+
+static u32 upscale_7_to_32bit(u8 src)
+{
+ u32 val, repeat;
+
+ val = src << 25;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 19) | (repeat << 13) |
+ (repeat << 7) | (repeat << 1) | (repeat >> 5);
+}
+
+static u32 upscale_14_to_32bit(u16 src)
+{
+ u32 val, repeat;
+
+ val = src << 18;
+ if (src <= 0x2000)
+ return val;
+ repeat = src & 0x1fff;
+ return val | (repeat << 5) | (repeat >> 8);
+}
+
+/*
+ * UMP -> MIDI 1 byte stream conversion
+ */
+/* convert a UMP System message to MIDI 1.0 byte stream */
+static int cvt_ump_system_to_legacy(u32 data, unsigned char *buf)
+{
+ buf[0] = ump_message_status_channel(data);
+ switch (ump_message_status_code(data)) {
+ case UMP_SYSTEM_STATUS_MIDI_TIME_CODE:
+ case UMP_SYSTEM_STATUS_SONG_SELECT:
+ buf[1] = (data >> 8) & 0x7f;
+ return 2;
+ case UMP_SYSTEM_STATUS_SONG_POSITION:
+ buf[1] = (data >> 8) & 0x7f;
+ buf[2] = data & 0x7f;
+ return 3;
+ default:
+ return 1;
+ }
+}
+
+/* convert a UMP MIDI 1.0 Channel Voice message to MIDI 1.0 byte stream */
+static int cvt_ump_midi1_to_legacy(u32 data, unsigned char *buf)
+{
+ buf[0] = ump_message_status_channel(data);
+ buf[1] = (data >> 8) & 0xff;
+ switch (ump_message_status_code(data)) {
+ case UMP_MSG_STATUS_PROGRAM:
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ return 2;
+ default:
+ buf[2] = data & 0xff;
+ return 3;
+ }
+}
+
+/* convert a UMP MIDI 2.0 Channel Voice message to MIDI 1.0 byte stream */
+static int cvt_ump_midi2_to_legacy(const union snd_ump_midi2_msg *midi2,
+ unsigned char *buf)
+{
+ unsigned char status = midi2->note.status;
+ unsigned char channel = midi2->note.channel;
+ u16 v;
+
+ buf[0] = (status << 4) | channel;
+ switch (status) {
+ case UMP_MSG_STATUS_NOTE_OFF:
+ case UMP_MSG_STATUS_NOTE_ON:
+ buf[1] = midi2->note.note;
+ buf[2] = downscale_16_to_7bit(midi2->note.velocity);
+ if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
+ buf[2] = 1;
+ return 3;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ buf[1] = midi2->paf.note;
+ buf[2] = downscale_32_to_7bit(midi2->paf.data);
+ return 3;
+ case UMP_MSG_STATUS_CC:
+ buf[1] = midi2->cc.index;
+ buf[2] = downscale_32_to_7bit(midi2->cc.data);
+ return 3;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ buf[1] = downscale_32_to_7bit(midi2->caf.data);
+ return 2;
+ case UMP_MSG_STATUS_PROGRAM:
+ if (midi2->pg.bank_valid) {
+ buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[1] = UMP_CC_BANK_SELECT;
+ buf[2] = midi2->pg.bank_msb;
+ buf[3] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[4] = UMP_CC_BANK_SELECT_LSB;
+ buf[5] = midi2->pg.bank_lsb;
+ buf[6] = channel | (UMP_MSG_STATUS_PROGRAM << 4);
+ buf[7] = midi2->pg.program;
+ return 8;
+ }
+ buf[1] = midi2->pg.program;
+ return 2;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ v = downscale_32_to_14bit(midi2->pb.data);
+ buf[1] = v & 0x7f;
+ buf[2] = v >> 7;
+ return 3;
+ case UMP_MSG_STATUS_RPN:
+ case UMP_MSG_STATUS_NRPN:
+ buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[1] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
+ buf[2] = midi2->rpn.bank;
+ buf[3] = buf[0];
+ buf[4] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
+ buf[5] = midi2->rpn.index;
+ buf[6] = buf[0];
+ buf[7] = UMP_CC_DATA;
+ v = downscale_32_to_14bit(midi2->rpn.data);
+ buf[8] = v >> 7;
+ buf[9] = buf[0];
+ buf[10] = UMP_CC_DATA_LSB;
+ buf[11] = v & 0x7f;
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+/* convert a UMP 7-bit SysEx message to MIDI 1.0 byte stream */
+static int cvt_ump_sysex7_to_legacy(const u32 *data, unsigned char *buf)
+{
+ unsigned char status;
+ unsigned char bytes;
+ int size, offset;
+
+ status = ump_sysex_message_status(*data);
+ if (status > UMP_SYSEX_STATUS_END)
+ return 0; // unsupported, skip
+ bytes = ump_sysex_message_length(*data);
+ if (bytes > 6)
+ return 0; // skip
+
+ size = 0;
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_START) {
+ buf[0] = UMP_MIDI1_MSG_SYSEX_START;
+ size = 1;
+ }
+
+ offset = 8;
+ for (; bytes; bytes--, size++) {
+ buf[size] = (*data >> offset) & 0x7f;
+ if (!offset) {
+ offset = 24;
+ data++;
+ } else {
+ offset -= 8;
+ }
+ }
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_END)
+ buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
+
+ return size;
+}
+
+/**
+ * snd_ump_convert_from_ump - convert from UMP to legacy MIDI
+ * @data: UMP packet
+ * @buf: buffer to store legacy MIDI data
+ * @group_ret: pointer to store the target group
+ *
+ * Convert from a UMP packet @data to MIDI 1.0 bytes at @buf.
+ * The target group is stored at @group_ret.
+ *
+ * The function returns the number of bytes of MIDI 1.0 stream.
+ */
+int snd_ump_convert_from_ump(const u32 *data,
+ unsigned char *buf,
+ unsigned char *group_ret)
+{
+ *group_ret = ump_message_group(*data);
+
+ switch (ump_message_type(*data)) {
+ case UMP_MSG_TYPE_SYSTEM:
+ return cvt_ump_system_to_legacy(*data, buf);
+ case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
+ return cvt_ump_midi1_to_legacy(*data, buf);
+ case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
+ return cvt_ump_midi2_to_legacy((const union snd_ump_midi2_msg *)data,
+ buf);
+ case UMP_MSG_TYPE_DATA:
+ return cvt_ump_sysex7_to_legacy(data, buf);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_convert_from_ump);
+
+/*
+ * MIDI 1 byte stream -> UMP conversion
+ */
+/* convert MIDI 1.0 SysEx to a UMP packet */
+static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group, u32 *data, bool finish)
+{
+ unsigned char status;
+ bool start = cvt->in_sysex == 1;
+ int i, offset;
+
+ if (start && finish)
+ status = UMP_SYSEX_STATUS_SINGLE;
+ else if (start)
+ status = UMP_SYSEX_STATUS_START;
+ else if (finish)
+ status = UMP_SYSEX_STATUS_END;
+ else
+ status = UMP_SYSEX_STATUS_CONTINUE;
+ *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len);
+ offset = 8;
+ for (i = 0; i < cvt->len; i++) {
+ *data |= cvt->buf[i] << offset;
+ if (!offset) {
+ offset = 24;
+ data++;
+ } else
+ offset -= 8;
+ }
+ cvt->len = 0;
+ if (finish)
+ cvt->in_sysex = 0;
+ else
+ cvt->in_sysex++;
+ return 8;
+}
+
+/* convert to a UMP System message */
+static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group, u32 *data)
+{
+ data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, cvt->buf[0]);
+ if (cvt->cmd_bytes > 1)
+ data[0] |= cvt->buf[1] << 8;
+ if (cvt->cmd_bytes > 2)
+ data[0] |= cvt->buf[2];
+ return 4;
+}
+
+static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
+{
+ cc->rpn_set = 0;
+ cc->nrpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+}
+
+static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
+ union snd_ump_midi2_msg *midi2,
+ bool flush)
+{
+ if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
+ return 0; // skip
+ /* when not flushing, wait for complete data set */
+ if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
+ return 0; // skip
+
+ if (cc->rpn_set) {
+ midi2->rpn.status = UMP_MSG_STATUS_RPN;
+ midi2->rpn.bank = cc->cc_rpn_msb;
+ midi2->rpn.index = cc->cc_rpn_lsb;
+ } else if (cc->nrpn_set) {
+ midi2->rpn.status = UMP_MSG_STATUS_NRPN;
+ midi2->rpn.bank = cc->cc_nrpn_msb;
+ midi2->rpn.index = cc->cc_nrpn_lsb;
+ } else {
+ return 0; // skip
+ }
+
+ midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+
+ reset_rpn(cc);
+ return 1;
+}
+
+/* convert to a MIDI 1.0 Channel Voice message */
+static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group,
+ unsigned int protocol,
+ u32 *data, unsigned char bytes)
+{
+ const unsigned char *buf = cvt->buf;
+ struct ump_cvt_to_ump_bank *cc;
+ union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
+ unsigned char status, channel;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
+ BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
+
+ /* for MIDI 1.0 UMP, it's easy, just pack it into UMP */
+ if (protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI1) {
+ data[0] = ump_compose(UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE,
+ group, 0, buf[0]);
+ data[0] |= buf[1] << 8;
+ if (bytes > 2)
+ data[0] |= buf[2];
+ return 4;
+ }
+
+ status = *buf >> 4;
+ channel = *buf & 0x0f;
+ cc = &cvt->bank[channel];
+
+ /* special handling: treat note-on with 0 velocity as note-off */
+ if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
+ status = UMP_MSG_STATUS_NOTE_OFF;
+
+ /* initialize the packet */
+ data[0] = ump_compose(UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE,
+ group, status, channel);
+ data[1] = 0;
+
+ switch (status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi2->note.note = buf[1];
+ midi2->note.velocity = upscale_7_to_16bit(buf[2]);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi2->paf.note = buf[1];
+ midi2->paf.data = upscale_7_to_32bit(buf[2]);
+ break;
+ case UMP_MSG_STATUS_CC:
+ switch (buf[1]) {
+ case UMP_CC_RPN_MSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = buf[2];
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = buf[2];
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = buf[2];
+ return ret;
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = buf[2];
+ return ret;
+ case UMP_CC_DATA:
+ cc->cc_data_msb_set = 1;
+ cc->cc_data_msb = buf[2];
+ return fill_rpn(cc, midi2, false);
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = buf[2];
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = buf[2];
+ return 0; // skip
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_lsb_set = 1;
+ cc->cc_data_lsb = buf[2];
+ return fill_rpn(cc, midi2, false);
+ default:
+ midi2->cc.index = buf[1];
+ midi2->cc.data = upscale_7_to_32bit(buf[2]);
+ break;
+ }
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ midi2->pg.program = buf[1];
+ if (cc->bank_set) {
+ midi2->pg.bank_valid = 1;
+ midi2->pg.bank_msb = cc->cc_bank_msb;
+ midi2->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi2->caf.data = upscale_7_to_32bit(buf[1]);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ midi2->pb.data = upscale_14_to_32bit(buf[1] | (buf[2] << 7));
+ break;
+ default:
+ return 0;
+ }
+
+ return 8;
+}
+
+static int do_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
+ unsigned int protocol, unsigned char c, u32 *data)
+{
+ /* bytes for 0x80-0xf0 */
+ static unsigned char cmd_bytes[8] = {
+ 3, 3, 3, 3, 2, 2, 3, 0
+ };
+ /* bytes for 0xf0-0xff */
+ static unsigned char system_bytes[16] = {
+ 0, 2, 3, 2, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1
+ };
+ unsigned char bytes;
+
+ if (c == UMP_MIDI1_MSG_SYSEX_START) {
+ cvt->in_sysex = 1;
+ cvt->len = 0;
+ return 0;
+ }
+ if (c == UMP_MIDI1_MSG_SYSEX_END) {
+ if (!cvt->in_sysex)
+ return 0; /* skip */
+ return cvt_legacy_sysex_to_ump(cvt, group, data, true);
+ }
+
+ if ((c & 0xf0) == UMP_MIDI1_MSG_REALTIME) {
+ bytes = system_bytes[c & 0x0f];
+ if (!bytes)
+ return 0; /* skip */
+ if (bytes == 1) {
+ data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, c);
+ return 4;
+ }
+ cvt->buf[0] = c;
+ cvt->len = 1;
+ cvt->cmd_bytes = bytes;
+ cvt->in_sysex = 0; /* abort SysEx */
+ return 0;
+ }
+
+ if (c & 0x80) {
+ bytes = cmd_bytes[(c >> 4) & 7];
+ cvt->buf[0] = c;
+ cvt->len = 1;
+ cvt->cmd_bytes = bytes;
+ cvt->in_sysex = 0; /* abort SysEx */
+ return 0;
+ }
+
+ if (cvt->in_sysex) {
+ cvt->buf[cvt->len++] = c;
+ if (cvt->len == 6)
+ return cvt_legacy_sysex_to_ump(cvt, group, data, false);
+ return 0;
+ }
+
+ if (!cvt->len)
+ return 0;
+
+ cvt->buf[cvt->len++] = c;
+ if (cvt->len < cvt->cmd_bytes)
+ return 0;
+ cvt->len = 1;
+ if ((cvt->buf[0] & 0xf0) == UMP_MIDI1_MSG_REALTIME)
+ return cvt_legacy_system_to_ump(cvt, group, data);
+ return cvt_legacy_cmd_to_ump(cvt, group, protocol, data, cvt->cmd_bytes);
+}
+
+/**
+ * snd_ump_convert_to_ump - convert legacy MIDI byte to UMP packet
+ * @cvt: converter context
+ * @group: target UMP group
+ * @protocol: target UMP protocol
+ * @c: MIDI 1.0 byte data
+ *
+ * Feed a MIDI 1.0 byte @c and convert to a UMP packet if completed.
+ * The result is stored in the buffer in @cvt.
+ */
+void snd_ump_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
+ unsigned int protocol, unsigned char c)
+{
+ cvt->ump_bytes = do_convert_to_ump(cvt, group, protocol, c, cvt->ump);
+}
+EXPORT_SYMBOL_GPL(snd_ump_convert_to_ump);
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 8632301489fa..c657659b236c 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -1,12 +1,8 @@
+// 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>
@@ -25,15 +21,15 @@ struct link_ctl_info {
};
/*
- * 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];
@@ -42,89 +38,86 @@ struct link_master {
};
/*
- * 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 *kctl; /* original kcontrol pointer */
- struct snd_kcontrol slave; /* the copy of original control entry */
+ 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;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
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];
- kfree(uctl);
+ uctl->id = follower->follower.id;
+ err = follower->follower.get(&follower->follower, uctl);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < follower->info.count; ch++)
+ follower->vals[ch] = uctl->value.integer.value[ch];
return 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;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
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);
- if (err < 0) {
- kfree(uinfo);
+ uinfo->id = follower->follower.id;
+ err = follower->follower.info(&follower->follower, uinfo);
+ if (err < 0)
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)) {
- pr_err("ALSA: vmaster: invalid slave element\n");
- kfree(uinfo);
+ 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");
return -EINVAL;
}
- slave->info.min_val = uinfo->value.integer.min;
- slave->info.max_val = uinfo->value.integer.max;
- kfree(uinfo);
+ follower->info.min_val = uinfo->value.integer.min;
+ follower->info.max_val = uinfo->value.integer.max;
- 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;
@@ -135,113 +128,119 @@ static int master_init(struct link_master *master)
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 (ucontrol->value.integer.value[ch] < follower->info.min_val ||
+ ucontrol->value.integer.value[ch] > follower->info.max_val)
+ return -EINVAL;
+ }
+
+ 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;
- err = 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
+ * 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.
*
@@ -250,35 +249,64 @@ 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->kctl = slave;
- 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_follower);
+
+/**
+ * snd_ctl_add_followers - add multiple followers to vmaster
+ * @card: card instance
+ * @master: the target vmaster kcontrol object
+ * @list: NULL-terminated list of name strings of followers to be added
+ *
+ * Adds the multiple follower kcontrols with the given names.
+ * Returns 0 for success or a negative error code.
+ */
+int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,
+ const char * const *list)
+{
+ struct snd_kcontrol *follower;
+ int err;
+
+ for (; *list; list++) {
+ follower = snd_ctl_find_id_mixer(card, *list);
+ if (follower) {
+ err = snd_ctl_add_follower(master, follower);
+ if (err < 0)
+ return err;
+ }
+ }
+
return 0;
}
-EXPORT_SYMBOL(_snd_ctl_add_slave);
+EXPORT_SYMBOL_GPL(snd_ctl_add_followers);
/*
* ctl callbacks for master controls
@@ -310,22 +338,21 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int sync_slaves(struct link_master *master, int old_val, int new_val)
+static int sync_followers(struct link_master *master, int old_val, int new_val)
{
- struct link_slave *slave;
- struct snd_ctl_elem_value *uval;
+ struct link_follower *follower;
+ struct snd_ctl_elem_value *uval __free(kfree) = NULL;
uval = kmalloc(sizeof(*uval), GFP_KERNEL);
if (!uval)
return -ENOMEM;
- list_for_each_entry(slave, &master->slaves, list) {
+ list_for_each_entry(follower, &master->followers, list) {
master->val = old_val;
- uval->id = slave->slave.id;
- slave_get_val(slave, uval);
+ uval->id = follower->follower.id;
+ follower_get_val(follower, uval);
master->val = new_val;
- slave_put_val(slave, uval);
+ follower_put_val(follower, uval);
}
- kfree(uval);
return 0;
}
@@ -344,8 +371,10 @@ static int master_put(struct snd_kcontrol *kcontrol,
new_val = ucontrol->value.integer.value[0];
if (new_val == old_val)
return 0;
+ if (new_val < master->info.min_val || new_val > master->info.max_val)
+ return -EINVAL;
- err = sync_slaves(master, old_val, new_val);
+ err = sync_followers(master, old_val, new_val);
if (err < 0)
return err;
if (master->hook && !first_init)
@@ -356,17 +385,17 @@ static int master_put(struct snd_kcontrol *kcontrol,
static void master_free(struct snd_kcontrol *kcontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
- struct link_slave *slave, *n;
+ struct link_follower *follower, *n;
- /* free all slave links and retore the original slave kctls */
- list_for_each_entry_safe(slave, n, &master->slaves, list) {
- struct snd_kcontrol *sctl = slave->kctl;
+ /* 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, &slave->slave, sizeof(*sctl));
- memcpy(sctl->vd, slave->slave.vd,
+ 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(slave);
+ kfree(follower);
}
kfree(master);
}
@@ -379,8 +408,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
*
* 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
@@ -404,7 +433,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) {
@@ -418,13 +447,15 @@ 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;
@@ -454,11 +485,11 @@ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
/**
- * snd_ctl_sync_vmaster - Sync the vmaster slaves and 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 slave and call the hook function
+ * 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.
*/
@@ -475,7 +506,7 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
if (err < 0)
return;
first_init = err;
- err = sync_slaves(master, master->val, master->val);
+ err = sync_followers(master, master->val, master->val);
if (err < 0)
return;
}
@@ -486,34 +517,35 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
/**
- * snd_ctl_apply_vmaster_slaves - Apply function to each vmaster slave
+ * 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 slave kctl of the given vmaster kctl.
- * Returns 0 if successful, or a negative error code.
+ * 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_slaves(struct snd_kcontrol *kctl,
- int (*func)(struct snd_kcontrol *vslave,
- struct snd_kcontrol *slave,
- void *arg),
- void *arg)
+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_slave *slave;
+ struct link_follower *follower;
int err;
master = snd_kcontrol_chip(kctl);
err = master_init(master);
if (err < 0)
return err;
- list_for_each_entry(slave, &master->slaves, list) {
- err = func(slave->kctl, &slave->slave, arg);
+ 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_slaves);
+EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 7144cc36e8ae..6debd8e95cb7 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_MPU401_UART
- tristate
- select SND_RAWMIDI
+ tristate
+ select SND_RAWMIDI
config SND_OPL3_LIB
tristate
@@ -89,24 +90,42 @@ 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 loopback device allows 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).
To compile this driver as a module, choose M here: the module
will be called snd-aloop.
+config SND_PCMTEST
+ tristate "Virtual PCM test driver"
+ depends on DEBUG_FS
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for the Virtual PCM test driver.
+ This driver is aimed at extended testing of the userspace applications
+ which use the ALSA API, as well as the PCM middle layer testing.
+
+ It can generate random or pattern-based data into the capture stream,
+ check the playback stream for containing the selected pattern, inject
+ time delays during capture/playback, redefine the RESET ioctl operation
+ to perform the PCM middle layer testing and inject errors during the
+ PCM callbacks. It supports both interleaved and non-interleaved access
+ modes. You can find the corresponding selftest in the 'alsa'
+ selftests folder.
+
config SND_VIRMIDI
tristate "Virtual MIDI soundcard"
depends on SND_SEQUENCER
@@ -126,6 +145,7 @@ config SND_VIRMIDI
config SND_MTPAV
tristate "MOTU MidiTimePiece AV multiport MIDI"
+ depends on HAS_IOPORT
select SND_RAWMIDI
help
To use a MOTU MidiTimePiece AV multiport MIDI adapter
@@ -141,19 +161,20 @@ 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"
+ depends on HAS_IOPORT
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
@@ -163,8 +184,27 @@ 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"
+ depends on HAS_IOPORT
select SND_MPU401_UART
help
Say Y here to include support for MIDI ports compatible with
@@ -184,18 +224,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
@@ -223,7 +251,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 615558a281c8..a08bdd70ec9c 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -4,23 +4,25 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-dummy-objs := dummy.o
-snd-aloop-objs := aloop.o
-snd-mtpav-objs := mtpav.o
-snd-mts64-objs := mts64.o
-snd-portman2x4-objs := portman2x4.o
-snd-serial-u16550-objs := serial-u16550.o
-snd-virmidi-objs := virmidi.o
-snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
+snd-dummy-y := dummy.o
+snd-aloop-y := aloop.o
+snd-mtpav-y := mtpav.o
+snd-mts64-y := mts64.o
+snd-pcmtest-y := pcmtest.o
+snd-portman2x4-y := portman2x4.o
+snd-serial-u16550-y := serial-u16550.o
+snd-serial-generic-y := serial-generic.o
+snd-virmidi-y := virmidi.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_PCMTEST) += snd-pcmtest.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 0333143a1fa7..64ef03b2d579 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,26 +13,12 @@
*
* 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>
#include <linux/jiffies.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/module.h>
@@ -42,11 +29,11 @@
#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
@@ -55,6 +42,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
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.");
@@ -66,11 +54,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];
@@ -79,18 +104,29 @@ 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;
+ snd_pcm_access_t access;
unsigned int channels;
struct snd_ctl_elem_id active_id;
struct snd_ctl_elem_id format_id;
struct snd_ctl_elem_id rate_id;
struct snd_ctl_elem_id channels_id;
+ struct snd_ctl_elem_id access_id;
};
struct loopback {
@@ -99,6 +135,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 {
@@ -116,11 +153,17 @@ 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;
+
+ /* size of per channel buffer in case of non-interleaved access */
+ unsigned int channel_buf_n;
};
static struct platform_device *devices[SNDRV_CARDS];
@@ -167,7 +210,7 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
}
/* call in cable->lock */
-static void loopback_timer_start(struct loopback_pcm *dpcm)
+static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
{
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
@@ -181,20 +224,115 @@ 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;
+ tick = DIV_ROUND_UP(tick, dpcm->pcm_bps);
mod_timer(&dpcm->timer, jiffies + tick);
+
+ return 0;
}
/* call in cable->lock */
-static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
{
- del_timer(&dpcm->timer);
+ 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)
+{
+ timer_delete(&dpcm->timer);
dpcm->timer.expires = 0;
+
+ return 0;
+}
+
+/* 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;
}
-#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)
+static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+ timer_delete_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 bool is_access_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ return false;
+ }
+};
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
@@ -214,7 +352,9 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
substream->runtime;
check = runtime->format != cruntime->format ||
runtime->rate != cruntime->rate ||
- runtime->channels != cruntime->channels;
+ runtime->channels != cruntime->channels ||
+ is_access_interleaved(runtime->access) !=
+ is_access_interleaved(cruntime->access);
if (!check)
return 0;
if (stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -242,6 +382,12 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
&setup->channels_id);
setup->channels = runtime->channels;
}
+ if (is_access_interleaved(setup->access) !=
+ is_access_interleaved(runtime->access)) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->access_id);
+ setup->access = runtime->access;
+ }
}
return 0;
}
@@ -258,7 +404,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:
@@ -268,42 +414,46 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
dpcm->last_jiffies = jiffies;
dpcm->pcm_rate_shift = 0;
dpcm->last_drift = 0;
- spin_lock(&cable->lock);
- cable->running |= stream;
- cable->pause &= ~stream;
- loopback_timer_start(dpcm);
- spin_unlock(&cable->lock);
+ scoped_guard(spinlock, &cable->lock) {
+ cable->running |= stream;
+ cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_STOP:
- spin_lock(&cable->lock);
- cable->running &= ~stream;
- cable->pause &= ~stream;
- loopback_timer_stop(dpcm);
- spin_unlock(&cable->lock);
+ scoped_guard(spinlock, &cable->lock) {
+ cable->running &= ~stream;
+ cable->pause &= ~stream;
+ err = cable->ops->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;
- loopback_timer_stop(dpcm);
- spin_unlock(&cable->lock);
+ scoped_guard(spinlock, &cable->lock) {
+ cable->pause |= stream;
+ err = cable->ops->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;
- loopback_timer_start(dpcm);
- spin_unlock(&cable->lock);
+ scoped_guard(spinlock, &cable->lock) {
+ dpcm->last_jiffies = jiffies;
+ cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
default:
return -EINVAL;
}
- return 0;
+ return err;
}
static void params_change(struct snd_pcm_substream *substream)
@@ -317,6 +467,13 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
+
+ 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)
@@ -324,9 +481,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)
@@ -334,6 +497,7 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
dpcm->buf_pos = 0;
dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+ dpcm->channel_buf_n = dpcm->pcm_buffer_size / runtime->channels;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
/* clear capture buffer */
dpcm->silent_size = dpcm->pcm_buffer_size;
@@ -347,13 +511,12 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
dpcm->pcm_salign = salign;
dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
- mutex_lock(&dpcm->loopback->cable_lock);
+ guard(mutex)(&dpcm->loopback->cable_lock);
if (!(cable->valid & ~(1 << substream->stream)) ||
(get_setup(dpcm)->notify &&
substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
params_change(substream);
cable->valid |= 1 << substream->stream;
- mutex_unlock(&dpcm->loopback->cable_lock);
return 0;
}
@@ -384,6 +547,22 @@ static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
}
}
+static void copy_play_buf_part_n(struct loopback_pcm *play, struct loopback_pcm *capt,
+ unsigned int size, unsigned int src_off, unsigned int dst_off)
+{
+ unsigned int channels = capt->substream->runtime->channels;
+ unsigned int size_p_ch = size / channels;
+ unsigned int src_off_ch = src_off / channels;
+ unsigned int dst_off_ch = dst_off / channels;
+ int i;
+
+ for (i = 0; i < channels; i++) {
+ memcpy(capt->substream->runtime->dma_area + capt->channel_buf_n * i + dst_off_ch,
+ play->substream->runtime->dma_area + play->channel_buf_n * i + src_off_ch,
+ size_p_ch);
+ }
+}
+
static void copy_play_buf(struct loopback_pcm *play,
struct loopback_pcm *capt,
unsigned int bytes)
@@ -397,7 +576,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;
@@ -418,7 +597,10 @@ static void copy_play_buf(struct loopback_pcm *play,
size = play->pcm_buffer_size - src_off;
if (dst_off + size > capt->pcm_buffer_size)
size = capt->pcm_buffer_size - dst_off;
- memcpy(dst + dst_off, src + src_off, size);
+ if (!is_access_interleaved(runtime->access))
+ copy_play_buf_part_n(play, capt, size, src_off, dst_off);
+ else
+ memcpy(dst + dst_off, src + src_off, size);
capt->silent_size = 0;
bytes -= size;
if (!bytes)
@@ -460,23 +642,25 @@ static inline void bytepos_finish(struct loopback_pcm *dpcm,
}
/* call in cable->lock */
-static unsigned int loopback_pos_update(struct loopback_cable *cable)
+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 long delta_play = 0, delta_capt = 0, cur_jiffies;
unsigned int running, count1, count2;
+ 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;
}
@@ -513,23 +697,211 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
return running;
}
-static void loopback_timer_function(struct timer_list *t)
+static void loopback_jiffies_timer_function(struct timer_list *t)
{
- struct loopback_pcm *dpcm = from_timer(dpcm, t, timer);
- unsigned long flags;
+ struct loopback_pcm *dpcm = timer_container_of(dpcm, t, timer);
+ bool period_elapsed = false;
+
+ scoped_guard(spinlock_irqsave, &dpcm->cable->lock) {
+ 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;
+ period_elapsed = true;
+ break;
+ }
+ }
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(dpcm->substream);
+}
+
+/* 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;
+}
- spin_lock_irqsave(&dpcm->cable->lock, flags);
- if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
- loopback_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);
+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;
+ bool xrun = false;
+
+ scoped_guard(spinlock_irqsave, &cable->lock) {
+ running = cable->running ^ cable->pause;
+ /* no need to do anything if no stream is running */
+ if (!running)
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)
+ 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) {
+ xrun = true;
+ break;
+ }
}
+
+ 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);
+ }
+ }
+
+ if (xrun) {
+ if (substream_play)
+ snd_pcm_stop_xrun(substream_play);
+ if (substream_capt)
+ snd_pcm_stop_xrun(substream_capt);
+ return;
+ }
+
+ 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);
}
- spin_unlock_irqrestore(&dpcm->cable->lock, flags);
+}
+
+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",
+ snd_pcm_direction_name(cable->snd_timer.stream));
}
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
@@ -538,10 +910,10 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
struct loopback_pcm *dpcm = runtime->private_data;
snd_pcm_uframes_t pos;
- spin_lock(&dpcm->cable->lock);
- loopback_pos_update(dpcm->cable);
+ guard(spinlock)(&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);
}
@@ -549,13 +921,18 @@ 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_RESUME),
+ SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NONINTERLEAVED),
.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,
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE |
+ SNDRV_PCM_FMTBIT_DSD_U8 |
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U16_BE |
+ SNDRV_PCM_FMTBIT_DSD_U32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_768000,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 768000,
.channels_min = 1,
.channels_max = 32,
.buffer_bytes_max = 2 * 1024 * 1024,
@@ -574,23 +951,15 @@ 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_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(params));
-}
-
static int loopback_hw_free(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;
- mutex_lock(&dpcm->loopback->cable_lock);
+ guard(mutex)(&dpcm->loopback->cable_lock);
cable->valid &= ~(1 << substream->stream);
- mutex_unlock(&dpcm->loopback->cable_lock);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static unsigned int get_cable_index(struct snd_pcm_substream *substream)
@@ -609,10 +978,10 @@ static int rule_format(struct snd_pcm_hw_params *params,
struct snd_mask m;
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);
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ m.bits[0] = (u_int32_t)cable->hw.formats;
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+ }
return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
@@ -623,10 +992,10 @@ static int rule_rate(struct snd_pcm_hw_params *params,
struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- mutex_lock(&dpcm->loopback->cable_lock);
- t.min = cable->hw.rate_min;
- t.max = cable->hw.rate_max;
- mutex_unlock(&dpcm->loopback->cable_lock);
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.rate_min;
+ t.max = cable->hw.rate_max;
+ }
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -639,15 +1008,32 @@ static int rule_channels(struct snd_pcm_hw_params *params,
struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- mutex_lock(&dpcm->loopback->cable_lock);
- t.min = cable->hw.channels_min;
- t.max = cable->hw.channels_max;
- mutex_unlock(&dpcm->loopback->cable_lock);
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.channels_min;
+ t.max = cable->hw.channels_max;
+ }
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;
+
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.period_bytes_min;
+ t.max = cable->hw.period_bytes_max;
+ }
+ 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;
@@ -659,14 +1045,187 @@ static void free_cable(struct snd_pcm_substream *substream)
return;
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
+ guard(spinlock_irq)(&cable->lock);
cable->streams[substream->stream] = NULL;
} 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 (card_idx == -1)
+ tid->dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+ 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;
@@ -676,15 +1235,12 @@ static int loopback_open(struct snd_pcm_substream *substream)
int err = 0;
int dev = get_cable_index(substream);
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
- if (!dpcm) {
- err = -ENOMEM;
- goto unlock;
- }
+ if (!dpcm)
+ return -ENOMEM;
dpcm->loopback = loopback;
dpcm->substream = substream;
- timer_setup(&dpcm->timer, loopback_timer_function, 0);
cable = loopback->cables[substream->number][dev];
if (!cable) {
@@ -695,10 +1251,20 @@ static int loopback_open(struct snd_pcm_substream *substream)
}
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);
@@ -724,18 +1290,37 @@ static int loopback_open(struct snd_pcm_substream *substream)
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;
+
+ scoped_guard(spinlock_irq, &cable->lock) {
+ cable->streams[substream->stream] = dpcm;
+ }
+
unlock:
if (err < 0) {
free_cable(substream);
kfree(dpcm);
}
- mutex_unlock(&loopback->cable_lock);
return err;
}
@@ -743,38 +1328,22 @@ static int loopback_close(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
+ int err = 0;
- loopback_timer_stop(dpcm);
- mutex_lock(&loopback->cable_lock);
+ if (dpcm->cable->ops->close_substream)
+ err = dpcm->cable->ops->close_substream(dpcm);
+ guard(mutex)(&loopback->cable_lock);
free_cable(substream);
- mutex_unlock(&loopback->cable_lock);
- return 0;
+ return err;
}
-static const struct snd_pcm_ops loopback_playback_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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
-};
-
-static const struct snd_pcm_ops loopback_capture_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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
static int loopback_pcm_new(struct loopback *loopback,
@@ -787,12 +1356,13 @@ static int 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");
+ strscpy(pcm->name, "Loopback PCM");
loopback->pcm[device] = pcm;
return 0;
@@ -814,6 +1384,7 @@ static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift;
@@ -832,14 +1403,13 @@ static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
val = 80000;
if (val > 120000)
val = 120000;
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift) {
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift = val;
change = 1;
}
- mutex_unlock(&loopback->cable_lock);
return change;
}
@@ -848,6 +1418,7 @@ static int loopback_notify_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify;
@@ -862,6 +1433,7 @@ static int loopback_notify_put(struct snd_kcontrol *kcontrol,
int change = 0;
val = ucontrol->value.integer.value[0] ? 1 : 0;
+ guard(mutex)(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify) {
loopback->setup[kcontrol->id.subdevice]
@@ -875,13 +1447,17 @@ 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;
+ guard(mutex)(&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;
+ }
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -892,7 +1468,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;
}
@@ -903,7 +1479,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;
}
@@ -924,6 +1500,7 @@ static int loopback_rate_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate;
@@ -946,13 +1523,36 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].channels;
return 0;
}
-static struct snd_kcontrol_new loopback_controls[] = {
+static int loopback_access_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const char * const texts[] = {"Interleaved", "Non-interleaved"};
+
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
+}
+
+static int loopback_access_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ snd_pcm_access_t access;
+
+ guard(mutex)(&loopback->cable_lock);
+ access = loopback->setup[kcontrol->id.subdevice][kcontrol->id.device].access;
+
+ ucontrol->value.enumerated.item[0] = !is_access_interleaved(access);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new loopback_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Rate Shift 100000",
@@ -998,7 +1598,15 @@ static struct snd_kcontrol_new loopback_controls[] = {
.name = "PCM Slave Channels",
.info = loopback_channels_info,
.get = loopback_channels_get
-}
+},
+#define ACCESS_IDX 6
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Access Mode",
+ .info = loopback_access_info,
+ .get = loopback_access_get,
+},
};
static int loopback_mixer_new(struct loopback *loopback, int notify)
@@ -1009,7 +1617,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify)
struct loopback_setup *setup;
int err, dev, substr, substr_count, idx;
- strcpy(card->mixername, "Loopback Mixer");
+ strscpy(card->mixername, "Loopback Mixer");
for (dev = 0; dev < 2; dev++) {
pcm = loopback->pcm[dev];
substr_count =
@@ -1019,6 +1627,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify)
setup->notify = notify;
setup->rate_shift = NO_PITCH;
setup->format = SNDRV_PCM_FORMAT_S16_LE;
+ setup->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
setup->rate = 48000;
setup->channels = 2;
for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
@@ -1029,6 +1638,14 @@ static int 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;
@@ -1042,12 +1659,12 @@ static int loopback_mixer_new(struct loopback *loopback, int notify)
case CHANNELS_IDX:
setup->channels_id = kctl->id;
break;
+ case ACCESS_IDX:
+ setup->access_id = kctl->id;
+ break;
default:
break;
}
- err = snd_ctl_add(card, kctl);
- if (err < 0)
- return err;
}
}
}
@@ -1070,13 +1687,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,
@@ -1104,27 +1716,60 @@ static void print_cable_info(struct snd_info_entry *entry,
struct loopback *loopback = entry->private_data;
int sub, num;
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
num = entry->name[strlen(entry->name)-1];
num = num == '0' ? 0 : 1;
for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++)
print_substream_info(buffer, loopback, sub, num);
- mutex_unlock(&loopback->cable_lock);
}
-static int 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);
+}
+
+static void print_timer_source_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
+
+ guard(mutex)(&loopback->cable_lock);
+ snd_iprintf(buffer, "%s\n",
+ loopback->timer_source ? loopback->timer_source : "");
+}
+
+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];
+
+ guard(mutex)(&loopback->cable_lock);
+ if (!snd_info_get_line(buffer, line, sizeof(line)))
+ loopback_set_timer_source(loopback, strim(line));
+}
+
+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 loopback_probe(struct platform_device *devptr)
@@ -1134,8 +1779,8 @@ static int loopback_probe(struct platform_device *devptr)
int dev = devptr->id;
int err;
- err = snd_card_new(&devptr->dev, 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;
@@ -1146,48 +1791,37 @@ static int 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);
- strcpy(card->driver, "Loopback");
- strcpy(card->shortname, "Loopback");
+ return err;
+ loopback_cable_proc_new(loopback, 0);
+ loopback_cable_proc_new(loopback, 1);
+ loopback_timer_source_proc_new(loopback);
+ strscpy(card->driver, "Loopback");
+ strscpy(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 loopback_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int loopback_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
- struct loopback *loopback = card->private_data;
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;
}
@@ -1199,20 +1833,15 @@ static int loopback_resume(struct device *pdev)
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
+static DEFINE_SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
#define SND_LOOPBACK_DRIVER "snd_aloop"
static struct platform_driver loopback_driver = {
.probe = loopback_probe,
- .remove = loopback_remove,
.driver = {
.name = SND_LOOPBACK_DRIVER,
- .pm = LOOPBACK_PM_OPS,
+ .pm = &loopback_pm,
},
};
@@ -1252,7 +1881,7 @@ static int __init alsa_card_loopback_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "aloop: No loopback enabled\n");
+ pr_err("aloop: No loopback enabled\n");
#endif
loopback_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 8fb9a54fe8ba..1860ff75fe15 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>
@@ -23,6 +9,7 @@
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/hrtimer.h>
@@ -39,7 +26,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,6 +43,8 @@ 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 */
@@ -65,6 +53,8 @@ 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 bool hrtimer = 1;
#endif
@@ -84,6 +74,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
@@ -131,7 +125,7 @@ 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;
@@ -158,13 +152,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
return 0;
}
-static struct dummy_model model_emu10k1 = {
+static const struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
-static 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,
@@ -174,7 +168,7 @@ static struct dummy_model model_rme9652 = {
.periods_max = 2,
};
-static 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,
@@ -184,7 +178,7 @@ static struct dummy_model model_ice1712 = {
.periods_max = 1024,
};
-static struct dummy_model model_uda1341 = {
+static const struct dummy_model model_uda1341 = {
.name = "uda1341",
.buffer_bytes_max = 16380,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -194,7 +188,7 @@ static struct dummy_model model_uda1341 = {
.periods_max = 255,
};
-static struct dummy_model model_ac97 = {
+static const struct dummy_model model_ac97 = {
.name = "ac97",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
@@ -204,7 +198,7 @@ static struct dummy_model model_ac97 = {
.rate_max = 48000,
};
-static 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),
@@ -218,7 +212,7 @@ static struct dummy_model model_ca0106 = {
.rate_max = 192000,
};
-static struct dummy_model *dummy_models[] = {
+static const struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
@@ -250,7 +244,7 @@ struct dummy_systimer_pcm {
static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
{
mod_timer(&dpcm->timer, jiffies +
- (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate);
+ DIV_ROUND_UP(dpcm->frac_period_rest, dpcm->rate));
}
static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
@@ -275,19 +269,19 @@ static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
static int dummy_systimer_start(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- spin_lock(&dpcm->lock);
+
+ guard(spinlock)(&dpcm->lock);
dpcm->base_time = jiffies;
dummy_systimer_rearm(dpcm);
- spin_unlock(&dpcm->lock);
return 0;
}
static int dummy_systimer_stop(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- spin_lock(&dpcm->lock);
- del_timer(&dpcm->timer);
- spin_unlock(&dpcm->lock);
+
+ guard(spinlock)(&dpcm->lock);
+ timer_delete(&dpcm->timer);
return 0;
}
@@ -308,16 +302,15 @@ static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
static void dummy_systimer_callback(struct timer_list *t)
{
- struct dummy_systimer_pcm *dpcm = from_timer(dpcm, t, timer);
- unsigned long flags;
+ struct dummy_systimer_pcm *dpcm = timer_container_of(dpcm, t, timer);
int elapsed = 0;
-
- spin_lock_irqsave(&dpcm->lock, flags);
- dummy_systimer_update(dpcm);
- dummy_systimer_rearm(dpcm);
- elapsed = dpcm->elapsed;
- dpcm->elapsed = 0;
- spin_unlock_irqrestore(&dpcm->lock, flags);
+
+ scoped_guard(spinlock_irqsave, &dpcm->lock) {
+ dummy_systimer_update(dpcm);
+ dummy_systimer_rearm(dpcm);
+ elapsed = dpcm->elapsed;
+ dpcm->elapsed = 0;
+ }
if (elapsed)
snd_pcm_period_elapsed(dpcm->substream);
}
@@ -326,13 +319,10 @@ static snd_pcm_uframes_t
dummy_systimer_pointer(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- snd_pcm_uframes_t pos;
- spin_lock(&dpcm->lock);
+ guard(spinlock)(&dpcm->lock);
dummy_systimer_update(dpcm);
- pos = dpcm->frac_pos / HZ;
- spin_unlock(&dpcm->lock);
- return pos;
+ return dpcm->frac_pos / HZ;
}
static int dummy_systimer_create(struct snd_pcm_substream *substream)
@@ -464,8 +454,7 @@ 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_SOFT);
- dpcm->timer.function = dummy_hrtimer_callback;
+ hrtimer_setup(&dpcm->timer, dummy_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dpcm->substream = substream;
atomic_set(&dpcm->running, 0);
return 0;
@@ -543,21 +532,13 @@ 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;
@@ -641,14 +622,7 @@ static int alloc_fake_buffer(void)
static int dummy_pcm_copy(struct snd_pcm_substream *substream,
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)
+ struct iov_iter *iter, unsigned long bytes)
{
return 0; /* do nothing */
}
@@ -666,28 +640,23 @@ 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_user = dummy_pcm_copy,
- .copy_kernel = dummy_pcm_copy_kernel,
+ .copy = dummy_pcm_copy,
.fill_silence = dummy_pcm_silence,
.page = dummy_pcm_page,
};
@@ -696,7 +665,7 @@ 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,
@@ -712,11 +681,11 @@ static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
pcm->private_data = dummy;
pcm->info_flags = 0;
- strcpy(pcm->name, "Dummy PCM");
+ strscpy(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;
@@ -740,21 +709,20 @@ 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)
{
struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
- spin_unlock_irq(&dummy->mixer_lock);
return 0;
}
@@ -766,21 +734,20 @@ 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;
- spin_lock_irq(&dummy->mixer_lock);
+ if (right < mixer_volume_level_min)
+ right = mixer_volume_level_min;
+ if (right > mixer_volume_level_max)
+ right = mixer_volume_level_max;
+ guard(spinlock_irq)(&dummy->mixer_lock);
change = dummy->mixer_volume[addr][0] != left ||
dummy->mixer_volume[addr][1] != right;
dummy->mixer_volume[addr][0] = left;
dummy->mixer_volume[addr][1] = right;
- spin_unlock_irq(&dummy->mixer_lock);
return change;
}
@@ -793,17 +760,16 @@ 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)
{
struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
- spin_unlock_irq(&dummy->mixer_lock);
return 0;
}
@@ -815,12 +781,11 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
left = ucontrol->value.integer.value[0] & 1;
right = ucontrol->value.integer.value[1] & 1;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
change = dummy->capture_source[addr][0] != left &&
dummy->capture_source[addr][1] != right;
dummy->capture_source[addr][0] = left;
dummy->capture_source[addr][1] = right;
- spin_unlock_irq(&dummy->mixer_lock);
return change;
}
@@ -875,7 +840,7 @@ static int snd_dummy_iobox_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_dummy_controls[] = {
+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),
@@ -903,7 +868,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
int err;
spin_lock_init(&dummy->mixer_lock);
- strcpy(card->mixername, "Dummy Mixer");
+ strscpy(card->mixername, "Dummy Mixer");
dummy->iobox = 1;
for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
@@ -927,10 +892,10 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
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));
}
}
@@ -938,7 +903,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,
};
@@ -970,7 +935,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"),
@@ -1037,14 +1002,8 @@ static void dummy_proc_write(struct snd_info_entry *entry,
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)
@@ -1054,20 +1013,19 @@ 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_new(&devptr->dev, 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;
dummy->card = card;
for (mdl = dummy_models; *mdl && model[dev]; mdl++) {
if (strcmp(model[dev], (*mdl)->name) == 0) {
- printk(KERN_INFO
- "snd-dummy: Using model '%s' for card %i\n",
+ pr_info("snd-dummy: Using model '%s' for card %i\n",
(*mdl)->name, card->number);
m = dummy->model = *mdl;
break;
@@ -1080,7 +1038,7 @@ static int 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;
@@ -1109,42 +1067,36 @@ static int 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;
- strcpy(card->driver, "Dummy");
- strcpy(card->shortname, "Dummy");
+ return err;
+ strscpy(card->driver, "Dummy");
+ strscpy(card->shortname, "Dummy");
sprintf(card->longname, "Dummy %i", dev + 1);
dummy_proc_init(dummy);
err = snd_card_register(card);
- if (err == 0) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
-
-static int snd_dummy_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int snd_dummy_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_dummy *dummy = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(dummy->pcm);
return 0;
}
-
+
static int snd_dummy_resume(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
@@ -1153,20 +1105,15 @@ static int snd_dummy_resume(struct device *pdev)
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
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
#define SND_DUMMY_DRIVER "snd_dummy"
static struct platform_driver snd_dummy_driver = {
.probe = snd_dummy_probe,
- .remove = snd_dummy_remove,
.driver = {
.name = SND_DUMMY_DRIVER,
- .pm = SND_DUMMY_PM_OPS,
+ .pm = &snd_dummy_pm,
},
};
@@ -1212,7 +1159,7 @@ static int __init alsa_card_dummy_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "Dummy soundcard not found or device busy\n");
+ pr_err("Dummy soundcard not found or device busy\n");
#endif
snd_dummy_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
deleted file mode 100644
index 18fd12996cf7..000000000000
--- a/sound/drivers/ml403-ac97cr.c
+++ /dev/null
@@ -1,1335 +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/module.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 bool 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 const 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 const 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 const 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 const 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
-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" */
- 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
-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 (%pR)\n",
- resource);
- 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, 0,
- 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, 0,
- 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
-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
-snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device)
-{
- struct snd_pcm *pcm;
- int err;
-
- 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);
- return 0;
-}
-
-static int 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_new(&pfdev->dev, 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);
- 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);
-
- 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));
- 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,
- },
-};
-
-module_platform_driver(snd_ml403_ac97cr_driver);
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
index 918f83f34c11..0a96e238ee92 100644
--- a/sound/drivers/mpu401/Makefile
+++ b/sound/drivers/mpu401/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-mpu401-objs := mpu401.o
-snd-mpu401-uart-objs := mpu401_uart.o
+snd-mpu401-y := mpu401.o
+snd-mpu401-uart-y := mpu401_uart.o
obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index b6715764cd1c..d3f9424088d4 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -1,23 +1,8 @@
+// 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>
@@ -71,15 +56,15 @@ static int snd_mpu401_create(struct device *devptr, int dev,
int err;
if (!uart_enter[dev])
- snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n");
+ dev_err(devptr, "the uart_enter option is obsolete; remove it\n");
*rcard = NULL;
- err = snd_card_new(devptr, 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");
- strcpy(card->shortname, card->driver);
+ strscpy(card->driver, "MPU-401 UART");
+ strscpy(card->shortname, card->driver);
sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]);
if (irq[dev] >= 0) {
sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]);
@@ -90,16 +75,12 @@ static int snd_mpu401_create(struct device *devptr, int dev,
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0,
irq[dev], NULL);
if (err < 0) {
- printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
- goto _err;
+ dev_err(devptr, "MPU401 not detected at 0x%lx\n", port[dev]);
+ return err;
}
*rcard = card;
return 0;
-
- _err:
- snd_card_free(card);
- return err;
}
static int snd_mpu401_probe(struct platform_device *devptr)
@@ -109,35 +90,27 @@ static int snd_mpu401_probe(struct platform_device *devptr)
struct snd_card *card;
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify port\n");
+ dev_err(&devptr->dev, "specify port\n");
return -EINVAL;
}
if (irq[dev] == SNDRV_AUTO_IRQ) {
- snd_printk(KERN_ERR "specify or disable IRQ\n");
+ dev_err(&devptr->dev, "specify or disable IRQ\n");
return -EINVAL;
}
err = snd_mpu401_create(&devptr->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;
- }
platform_set_drvdata(devptr, card);
return 0;
}
-static int snd_mpu401_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- return 0;
-}
-
#define SND_MPU401_DRIVER "snd_mpu401"
static struct platform_driver snd_mpu401_driver = {
.probe = snd_mpu401_probe,
- .remove = snd_mpu401_remove,
.driver = {
.name = SND_MPU401_DRIVER,
},
@@ -160,11 +133,11 @@ static int snd_mpu401_pnp(int dev, struct pnp_dev *device,
{
if (!pnp_port_valid(device, 0) ||
pnp_port_flags(device, 0) & IORESOURCE_DISABLED) {
- snd_printk(KERN_ERR "no PnP port\n");
+ dev_err(&device->dev, "no PnP port\n");
return -ENODEV;
}
if (pnp_port_len(device, 0) < IO_EXTENT) {
- snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n",
+ dev_err(&device->dev, "PnP port length is %llu, expected %d\n",
(unsigned long long)pnp_port_len(device, 0),
IO_EXTENT);
return -ENODEV;
@@ -173,7 +146,7 @@ static int snd_mpu401_pnp(int dev, struct pnp_dev *device,
if (!pnp_irq_valid(device, 0) ||
pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) {
- snd_printk(KERN_WARNING "no PnP irq, using polling\n");
+ dev_warn(&device->dev, "no PnP irq, using polling\n");
irq[dev] = -1;
} else {
irq[dev] = pnp_irq(device, 0);
@@ -197,10 +170,9 @@ static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
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;
- }
pnp_set_drvdata(pnp_dev, card);
snd_mpu401_devices++;
++dev;
@@ -209,19 +181,10 @@ static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
return -ENODEV;
}
-static void 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 = snd_mpu401_pnp_remove,
};
#else
static struct pnp_driver snd_mpu401_pnp_driver;
@@ -242,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++) {
@@ -270,7 +234,7 @@ static int __init alsa_card_mpu401_init(void)
if (!snd_mpu401_devices) {
#ifdef MODULE
- printk(KERN_ERR "MPU-401 device not found or device busy\n");
+ pr_err("MPU-401 device not found or device busy\n");
#endif
snd_mpu401_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 3e745f47dd2f..4af89822bf32 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.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 MPU-401 in UART mode
@@ -6,26 +7,10 @@
* 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 <linux/io.h>
@@ -88,34 +73,29 @@ static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)
mpu->read(mpu, MPU401D(mpu));
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n",
- mpu->read(mpu, MPU401C(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
static void uart_interrupt_tx(struct snd_mpu401 *mpu)
{
- unsigned long flags;
-
if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
- spin_lock_irqsave(&mpu->output_lock, flags);
+ guard(spinlock_irqsave)(&mpu->output_lock);
snd_mpu401_uart_output_write(mpu);
- spin_unlock_irqrestore(&mpu->output_lock, flags);
}
}
static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
{
- unsigned long flags;
-
if (mpu->info_flags & MPU401_INFO_INPUT) {
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
snd_mpu401_uart_input_read(mpu);
else
snd_mpu401_uart_clear_rx(mpu);
- spin_unlock_irqrestore(&mpu->input_lock, flags);
}
if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
/* ok. for better Tx performance try do some output
@@ -171,13 +151,12 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
*/
static void snd_mpu401_uart_timer(struct timer_list *t)
{
- struct snd_mpu401 *mpu = from_timer(mpu, t, timer);
- unsigned long flags;
+ struct snd_mpu401 *mpu = timer_container_of(mpu, t, timer);
- spin_lock_irqsave(&mpu->timer_lock, flags);
- /*mpu->mode |= MPU401_MODE_TIMER;*/
- mod_timer(&mpu->timer, 1 + jiffies);
- spin_unlock_irqrestore(&mpu->timer_lock, flags);
+ scoped_guard(spinlock_irqsave, &mpu->timer_lock) {
+ /*mpu->mode |= MPU401_MODE_TIMER;*/
+ mod_timer(&mpu->timer, 1 + jiffies);
+ }
if (mpu->rmidi)
_snd_mpu401_uart_interrupt(mpu);
}
@@ -187,16 +166,13 @@ static void snd_mpu401_uart_timer(struct timer_list *t)
*/
static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
{
- unsigned long flags;
-
- spin_lock_irqsave (&mpu->timer_lock, flags);
+ guard(spinlock_irqsave)(&mpu->timer_lock);
if (mpu->timer_invoked == 0) {
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;
- spin_unlock_irqrestore (&mpu->timer_lock, flags);
}
/*
@@ -204,16 +180,13 @@ static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
*/
static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
{
- unsigned long flags;
-
- spin_lock_irqsave (&mpu->timer_lock, flags);
+ guard(spinlock_irqsave)(&mpu->timer_lock);
if (mpu->timer_invoked) {
mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :
~MPU401_MODE_OUTPUT_TIMER;
if (! mpu->timer_invoked)
- del_timer(&mpu->timer);
+ timer_delete(&mpu->timer);
}
- spin_unlock_irqrestore (&mpu->timer_lock, flags);
}
/*
@@ -224,10 +197,9 @@ static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
mpu->write(mpu, 0x00, MPU401D(mpu));
/*snd_mpu401_uart_clear_rx(mpu);*/
@@ -239,8 +211,9 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
udelay(10);
#ifdef CONFIG_SND_DEBUG
if (!timeout)
- snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n",
- mpu->read(mpu, MPU401C(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: tx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
mpu->write(mpu, cmd, MPU401C(mpu));
@@ -257,12 +230,12 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
ok = 1;
} else
ok = 1;
- spin_unlock_irqrestore(&mpu->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx "
- "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port,
- mpu->read(mpu, MPU401C(mpu)),
- mpu->read(mpu, MPU401D(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n",
+ cmd, mpu->port,
+ mpu->read(mpu, MPU401C(mpu)),
+ mpu->read(mpu, MPU401D(mpu)));
return 1;
}
return 0;
@@ -286,8 +259,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;
@@ -308,8 +284,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;
@@ -364,7 +343,6 @@ static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)
static void
snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_mpu401 *mpu;
int max = 64;
@@ -380,9 +358,8 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
}
/* read data in advance */
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
snd_mpu401_uart_input_read(mpu);
- spin_unlock_irqrestore(&mpu->input_lock, flags);
} else {
if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_remove_timer(mpu, 1);
@@ -451,7 +428,6 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
static void
snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_mpu401 *mpu;
mpu = substream->rmidi->private_data;
@@ -466,9 +442,8 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
snd_mpu401_uart_add_timer(mpu, 0);
/* output pending data */
- spin_lock_irqsave(&mpu->output_lock, flags);
+ guard(spinlock_irqsave)(&mpu->output_lock);
snd_mpu401_uart_output_write(mpu);
- spin_unlock_irqrestore(&mpu->output_lock, flags);
} else {
if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
snd_mpu401_uart_remove_timer(mpu, 0);
@@ -539,8 +514,9 @@ 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) {
@@ -554,13 +530,14 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
spin_lock_init(&mpu->timer_lock);
mpu->hardware = hardware;
mpu->irq = -1;
+ mpu->rmidi = rmidi;
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) {
- snd_printk(KERN_ERR "mpu401_uart: "
- "unable to grab port 0x%lx size %d\n",
- port, res_size);
+ dev_err(rmidi->dev,
+ "mpu401_uart: unable to grab port 0x%lx size %d\n",
+ port, res_size);
err = -EBUSY;
goto free_device;
}
@@ -580,8 +557,8 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
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);
+ dev_err(rmidi->dev,
+ "mpu401_uart: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto free_device;
}
@@ -607,7 +584,6 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
if (out_enable)
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
}
- mpu->rmidi = rmidi;
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
@@ -617,19 +593,3 @@ free_device:
}
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 547662e02fcc..d31eadf4be5f 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,7 +32,6 @@
* - 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>
@@ -62,6 +46,7 @@
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include <linux/delay.h>
+#include <linux/string.h>
/*
* globals
@@ -69,7 +54,6 @@
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
@@ -302,10 +286,6 @@ static void snd_mtpav_output_port_write(struct mtpav *mtp_card,
snd_mtpav_send_byte(mtp_card, 0xf5);
snd_mtpav_send_byte(mtp_card, portp->hwport);
- /*
- snd_printk(KERN_DEBUG "new outport: 0x%x\n",
- (unsigned int) portp->hwport);
- */
if (!(outbyte & 0x80) && portp->running_status)
snd_mtpav_send_byte(mtp_card, portp->running_status);
}
@@ -324,11 +304,9 @@ static void snd_mtpav_output_write(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
snd_mtpav_output_port_write(mtp_card, portp, substream);
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
}
@@ -354,14 +332,12 @@ static int snd_mtpav_input_open(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode |= MTPAV_MODE_INPUT_OPENED;
portp->input = substream;
if (mtp_card->share_irq++ == 0)
snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
}
@@ -372,14 +348,12 @@ static int snd_mtpav_input_close(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode &= ~MTPAV_MODE_INPUT_OPENED;
portp->input = NULL;
if (--mtp_card->share_irq == 0)
snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
}
@@ -390,15 +364,12 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
if (up)
portp->mode |= MTPAV_MODE_INPUT_TRIGGERED;
else
portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
-
}
@@ -408,11 +379,10 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int
static void snd_mtpav_output_timer(struct timer_list *t)
{
- unsigned long flags;
- struct mtpav *chip = from_timer(chip, t, timer);
+ struct mtpav *chip = timer_container_of(chip, t, timer);
int p;
- spin_lock_irqsave(&chip->spinlock, flags);
+ guard(spinlock_irqsave)(&chip->spinlock);
/* reprogram timer */
mod_timer(&chip->timer, 1 + jiffies);
/* process each port */
@@ -421,7 +391,6 @@ static void snd_mtpav_output_timer(struct timer_list *t)
if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output)
snd_mtpav_output_port_write(chip, portp, portp->output);
}
- spin_unlock_irqrestore(&chip->spinlock, flags);
}
/* spinlock held! */
@@ -433,7 +402,7 @@ static void snd_mtpav_add_output_timer(struct mtpav *chip)
/* spinlock held! */
static void snd_mtpav_remove_output_timer(struct mtpav *chip)
{
- del_timer(&chip->timer);
+ timer_delete(&chip->timer);
}
/*
@@ -443,12 +412,10 @@ static int snd_mtpav_output_open(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode |= MTPAV_MODE_OUTPUT_OPENED;
portp->output = substream;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
};
@@ -459,12 +426,10 @@ static int snd_mtpav_output_close(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED;
portp->output = NULL;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
};
@@ -475,21 +440,20 @@ static void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, in
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
-
- spin_lock_irqsave(&mtp_card->spinlock, flags);
- if (up) {
- if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
- if (mtp_card->istimer++ == 0)
- snd_mtpav_add_output_timer(mtp_card);
- portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+
+ scoped_guard(spinlock_irqsave, &mtp_card->spinlock) {
+ if (up) {
+ if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
+ if (mtp_card->istimer++ == 0)
+ snd_mtpav_add_output_timer(mtp_card);
+ portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+ }
+ } else {
+ portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
+ if (--mtp_card->istimer == 0)
+ snd_mtpav_remove_output_timer(mtp_card);
}
- } else {
- portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
- if (--mtp_card->istimer == 0)
- snd_mtpav_remove_output_timer(mtp_card);
}
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
if (up)
snd_mtpav_output_write(substream);
@@ -539,8 +503,6 @@ static void snd_mtpav_read_bytes(struct mtpav *mcrd)
u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
- /* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); */
-
if (!(sbyt & SIGS_BYTE))
return;
@@ -572,9 +534,8 @@ static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
{
struct mtpav *mcard = dev_id;
- spin_lock(&mcard->spinlock);
+ guard(spinlock)(&mcard->spinlock);
snd_mtpav_read_bytes(mcard);
- spin_unlock(&mcard->spinlock);
return IRQ_HANDLED;
}
@@ -583,13 +544,16 @@ static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
*/
static int snd_mtpav_get_ISA(struct mtpav *mcard)
{
- if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
- snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
+ mcard->res_port = devm_request_region(mcard->card->dev, port, 3,
+ "MotuMTPAV MIDI");
+ if (!mcard->res_port) {
+ dev_err(mcard->card->dev, "MTVAP port 0x%lx is busy\n", port);
return -EBUSY;
}
mcard->port = port;
- if (request_irq(irq, snd_mtpav_irqh, 0, "MOTU MTPAV", mcard)) {
- snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
+ if (devm_request_irq(mcard->card->dev, irq, snd_mtpav_irqh, 0,
+ "MOTU MTPAV", mcard)) {
+ dev_err(mcard->card->dev, "MTVAP IRQ %d busy\n", irq);
return -EBUSY;
}
mcard->irq = irq;
@@ -625,11 +589,11 @@ static void snd_mtpav_set_name(struct mtpav *chip,
else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
else if (substream->number == chip->num_ports * 2)
- strcpy(substream->name, "MTP computer");
+ strscpy(substream->name, "MTP computer");
else if (substream->number == chip->num_ports * 2 + 1)
- strcpy(substream->name, "MTP ADAT");
+ strscpy(substream->name, "MTP ADAT");
else
- strcpy(substream->name, "MTP broadcast");
+ strscpy(substream->name, "MTP broadcast");
}
static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
@@ -645,10 +609,11 @@ static int 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;
@@ -676,15 +641,10 @@ static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
static void snd_mtpav_free(struct snd_card *card)
{
struct mtpav *crd = card->private_data;
- unsigned long flags;
- spin_lock_irqsave(&crd->spinlock, flags);
+ guard(spinlock_irqsave)(&crd->spinlock);
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);
}
/*
@@ -695,8 +655,8 @@ static int snd_mtpav_probe(struct platform_device *dev)
int err;
struct mtpav *mtp_card;
- err = snd_card_new(&dev->dev, 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;
@@ -709,20 +669,18 @@ static int snd_mtpav_probe(struct platform_device *dev)
mtp_card->outmidihwport = 0xffffffff;
timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0);
- card->private_free = snd_mtpav_free;
-
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");
+ strscpy(card->driver, "MTPAV");
+ strscpy(card->shortname, "MTPAV on parallel port");
snprintf(card->longname, sizeof(card->longname),
"MTPAV on parallel port at 0x%lx", port);
@@ -730,20 +688,14 @@ static int snd_mtpav_probe(struct platform_device *dev)
err = snd_card_register(mtp_card->card);
if (err < 0)
- goto __error;
-
- platform_set_drvdata(dev, card);
- printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port);
- return 0;
+ return err;
- __error:
- snd_card_free(card);
- return err;
-}
+ card->private_free = snd_mtpav_free;
-static int snd_mtpav_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(dev, card);
+ dev_info(card->dev,
+ "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n",
+ irq, port);
return 0;
}
@@ -751,7 +703,6 @@ static int snd_mtpav_remove(struct platform_device *devptr)
static struct platform_driver snd_mtpav_driver = {
.probe = snd_mtpav_probe,
- .remove = snd_mtpav_remove,
.driver = {
.name = SND_MTPAV_DRIVER,
},
@@ -761,7 +712,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 f32e81342247..fe50b48c10e7 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -1,21 +1,7 @@
+// 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>
@@ -25,6 +11,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
@@ -41,17 +28,16 @@ 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
@@ -275,7 +261,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];
}
@@ -367,7 +353,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,
@@ -446,9 +432,8 @@ static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl,
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.integer.value[0] = mts->smpte_switch;
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -459,14 +444,12 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
struct snd_ctl_elem_value *uctl)
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- int changed = 0;
int val = !!uctl->value.integer.value[0];
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->smpte_switch == val)
- goto __out;
+ return 0;
- changed = 1;
mts->smpte_switch = val;
if (mts->smpte_switch) {
mts64_smpte_start(mts->pardev->port,
@@ -476,12 +459,10 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
} else {
mts64_smpte_stop(mts->pardev->port);
}
-__out:
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 1;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_switch = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Playback Switch",
.index = 0,
@@ -529,9 +510,8 @@ static int snd_mts64_ctl_smpte_time_get(struct snd_kcontrol *kctl,
struct mts64 *mts = snd_kcontrol_chip(kctl);
int idx = kctl->private_value;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.integer.value[0] = mts->time[idx];
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -542,19 +522,17 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
struct mts64 *mts = snd_kcontrol_chip(kctl);
int idx = kctl->private_value;
unsigned int time = uctl->value.integer.value[0] % 60;
- int changed = 0;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->time[idx] != time) {
- changed = 1;
mts->time[idx] = time;
+ return 1;
}
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 0;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_time_hours = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_hours = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Hours",
.index = 0,
@@ -565,7 +543,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_hours = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_minutes = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Minutes",
.index = 0,
@@ -576,7 +554,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_seconds = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Seconds",
.index = 0,
@@ -587,7 +565,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_frames = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_frames = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Frames",
.index = 0,
@@ -614,9 +592,8 @@ static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.enumerated.item[0] = mts->fps;
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -625,21 +602,19 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uctl)
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- int changed = 0;
if (uctl->value.enumerated.item[0] >= 5)
return -EINVAL;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->fps != uctl->value.enumerated.item[0]) {
- changed = 1;
mts->fps = uctl->value.enumerated.item[0];
+ return 1;
}
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 0;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_fps = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_fps = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Fps",
.index = 0,
@@ -655,7 +630,7 @@ static int snd_mts64_ctl_create(struct snd_card *card,
struct mts64 *mts)
{
int err, i;
- static struct snd_kcontrol_new *control[] = {
+ static const struct snd_kcontrol_new *control[] = {
&mts64_ctl_smpte_switch,
&mts64_ctl_smpte_time_hours,
&mts64_ctl_smpte_time_minutes,
@@ -667,8 +642,8 @@ static int snd_mts64_ctl_create(struct snd_card *card,
for (i = 0; control[i]; ++i) {
err = snd_ctl_add(card, snd_ctl_new1(control[i], mts));
if (err < 0) {
- snd_printd("Cannot create control: %s\n",
- control[i]->name);
+ dev_dbg(card->dev, "Cannot create control: %s\n",
+ control[i]->name);
return err;
}
}
@@ -701,15 +676,14 @@ static int snd_mts64_rawmidi_open(struct snd_rawmidi_substream *substream)
static int snd_mts64_rawmidi_close(struct snd_rawmidi_substream *substream)
{
struct mts64 *mts = substream->rmidi->private_data;
- unsigned long flags;
--(mts->open_count);
if (mts->open_count == 0) {
/* We need the spinlock_irqsave here because we can still
have IRQs at this point */
- spin_lock_irqsave(&mts->lock, flags);
- mts64_device_close(mts);
- spin_unlock_irqrestore(&mts->lock, flags);
+ scoped_guard(spinlock_irqsave, &mts->lock) {
+ mts64_device_close(mts);
+ }
msleep(500);
@@ -724,29 +698,24 @@ static void snd_mts64_rawmidi_output_trigger(struct snd_rawmidi_substream *subst
{
struct mts64 *mts = substream->rmidi->private_data;
u8 data;
- unsigned long flags;
- spin_lock_irqsave(&mts->lock, flags);
+ guard(spinlock_irqsave)(&mts->lock);
while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
mts64_write_midi(mts, data, substream->number+1);
snd_rawmidi_transmit_ack(substream, 1);
}
- spin_unlock_irqrestore(&mts->lock, flags);
}
static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct mts64 *mts = substream->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&mts->lock, flags);
+ guard(spinlock_irqsave)(&mts->lock);
if (up)
mts->mode[substream->number] |= MTS64_MODE_INPUT_TRIGGERED;
else
mts->mode[substream->number] &= ~MTS64_MODE_INPUT_TRIGGERED;
-
- spin_unlock_irqrestore(&mts->lock, flags);
}
static const struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
@@ -778,7 +747,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card)
return err;
rmidi->private_data = mts;
- strcpy(rmidi->name, CARD_NAME);
+ strscpy(rmidi->name, CARD_NAME);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -806,7 +775,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card)
mts->midi_input_substream[substream->number] = substream;
switch(substream->number) {
case MTS64_SMPTE_SUBSTREAM:
- strcpy(substream->name, "Miditerminal SMPTE");
+ strscpy(substream->name, "Miditerminal SMPTE");
break;
default:
sprintf(substream->name,
@@ -830,7 +799,10 @@ static void snd_mts64_interrupt(void *private)
u8 status, data;
struct snd_rawmidi_substream *substream;
- spin_lock(&mts->lock);
+ if (!mts)
+ return;
+
+ guard(spinlock)(&mts->lock);
ret = mts64_read(mts->pardev->port);
data = ret & 0x00ff;
status = ret >> 8;
@@ -839,13 +811,11 @@ static void snd_mts64_interrupt(void *private)
mts->current_midi_input_port = mts64_map_midi_input(data);
} else {
if (mts->current_midi_input_port == -1)
- goto __out;
+ return;
substream = mts->midi_input_substream[mts->current_midi_input_port];
if (mts->mode[substream->number] & MTS64_MODE_INPUT_TRIGGERED)
snd_rawmidi_receive(substream, &data, 1);
}
-__out:
- spin_unlock(&mts->lock);
}
static void snd_mts64_attach(struct parport *p)
@@ -894,7 +864,6 @@ static struct parport_driver mts64_parport_driver = {
.probe = snd_mts64_dev_probe,
.match_port = snd_mts64_attach,
.detach = snd_mts64_detach,
- .devmodel = true,
};
/*********************************************************************
@@ -939,11 +908,11 @@ static int snd_mts64_probe(struct platform_device *pdev)
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card);
if (err < 0) {
- snd_printd("Cannot create card\n");
+ dev_dbg(&pdev->dev, "Cannot create card\n");
return err;
}
- strcpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "ESI " CARD_NAME);
+ strscpy(card->driver, DRIVER_NAME);
+ strscpy(card->shortname, "ESI " CARD_NAME);
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
@@ -953,20 +922,21 @@ static int snd_mts64_probe(struct platform_device *pdev)
&mts64_cb, /* callbacks */
pdev->id); /* device number */
if (!pardev) {
- snd_printd("Cannot register pardevice\n");
+ dev_dbg(card->dev, "Cannot register pardevice\n");
err = -EIO;
goto __err;
}
/* claim parport */
if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ dev_dbg(card->dev, "Cannot claim parport 0x%lx\n", pardev->port->base);
err = -EIO;
goto free_pardev;
}
- if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
- snd_printd("Cannot create main component\n");
+ err = snd_mts64_create(card, pardev, &mts);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot create main component\n");
goto release_pardev;
}
card->private_data = mts;
@@ -978,24 +948,27 @@ static int snd_mts64_probe(struct platform_device *pdev)
goto __err;
}
- if ((err = snd_mts64_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
+ err = snd_mts64_rawmidi_create(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "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);
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
- snd_printd("Cannot register card\n");
+ err = snd_card_register(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot register card\n");
goto __err;
}
- snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
+ dev_info(card->dev, "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0;
release_pardev:
@@ -1007,14 +980,12 @@ __err:
return err;
}
-static int 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 = {
@@ -1046,7 +1017,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 83bca9f1fbdf..cf4826308365 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
+snd-opl3-lib-y := opl3_lib.o opl3_synth.o
snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
ifneq ($(CONFIG_SND_SEQUENCER_OSS),)
snd-opl3-synth-y += opl3_oss.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 588963d6be28..fa8a2ccbbd51 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,21 +7,6 @@
* 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>
@@ -31,16 +17,14 @@
#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;
unsigned long port;
/*
@@ -50,20 +34,17 @@ static void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
outb((unsigned char) cmd, port);
udelay(10);
outb((unsigned char) val, port + 1);
udelay(30);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
{
- unsigned long flags;
unsigned long port;
/*
@@ -73,7 +54,7 @@ static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
outb((unsigned char) cmd, port);
inb(opl3->l_port);
@@ -82,8 +63,6 @@ static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
outb((unsigned char) val, port + 1);
inb(opl3->l_port);
inb(opl3->l_port);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
static int snd_opl3_detect(struct snd_opl3 * opl3)
@@ -107,7 +86,7 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
signature = stat1 = inb(opl3->l_port); /* Status register */
if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */
- snd_printd("OPL3: stat1 = 0x%x\n", stat1);
+ dev_dbg(opl3->card->dev, "OPL3: stat1 = 0x%x\n", stat1);
return -ENODEV;
}
/* Set timer1 to 0xff */
@@ -123,7 +102,7 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
/* Reset the IRQ of the FM chip */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */
- snd_printd("OPL3: stat2 = 0x%x\n", stat2);
+ dev_dbg(opl3->card->dev, "OPL3: stat2 = 0x%x\n", stat2);
return -ENODEV;
}
@@ -157,34 +136,30 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
static int snd_opl3_timer1_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
ticks = timer->sticks;
tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
static int snd_opl3_timer1_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
@@ -194,34 +169,30 @@ static int snd_opl3_timer1_stop(struct snd_timer * timer)
static int snd_opl3_timer2_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
ticks = timer->sticks;
tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
static int snd_opl3_timer2_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
@@ -229,7 +200,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,
@@ -238,7 +209,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,
@@ -258,8 +229,9 @@ 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) {
- strcpy(timer->name, "AdLib timer #1");
+ err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "AdLib timer #1");
timer->private_data = opl3;
timer->hw = snd_opl3_timer1;
}
@@ -278,8 +250,9 @@ 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) {
- strcpy(timer->name, "AdLib timer #2");
+ err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "AdLib timer #2");
timer->private_data = opl3;
timer->hw = snd_opl3_timer2;
}
@@ -302,9 +275,6 @@ void snd_opl3_interrupt(struct snd_hwdep * hw)
opl3 = hw->private_data;
status = inb(opl3->l_port);
-#if 0
- snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status);
-#endif
if (!(status & 0x80))
return;
@@ -347,7 +317,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;
@@ -363,7 +333,8 @@ int snd_opl3_new(struct snd_card *card,
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;
}
@@ -377,7 +348,8 @@ EXPORT_SYMBOL(snd_opl3_new);
int snd_opl3_init(struct snd_opl3 *opl3)
{
if (! opl3->command) {
- printk(KERN_ERR "snd_opl3_init: command not defined!\n");
+ dev_err(opl3->card->dev,
+ "snd_opl3_init: command not defined!\n");
return -EINVAL;
}
@@ -411,19 +383,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) {
- snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);
+ opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)");
+ if (!opl3->res_l_port) {
+ dev_err(card->dev, "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) {
+ dev_err(card->dev, "opl3: can't grab right port 0x%lx\n", r_port);
+ snd_device_free(card, opl3);
+ return -EBUSY;
+ }
}
}
opl3->l_port = l_port;
@@ -438,9 +414,10 @@ int snd_opl3_create(struct snd_card *card,
break;
default:
opl3->command = &snd_opl2_command;
- if ((err = snd_opl3_detect(opl3)) < 0) {
- snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
- opl3->l_port, opl3->r_port);
+ err = snd_opl3_detect(opl3);
+ if (err < 0) {
+ dev_dbg(card->dev, "OPL2/3 chip not detected at 0x%lx/0x%lx\n",
+ opl3->l_port, opl3->r_port);
snd_device_free(card, opl3);
return err;
}
@@ -464,11 +441,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;
@@ -492,7 +472,8 @@ 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;
}
@@ -502,18 +483,18 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
if (device == 0)
hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
#endif
- strcpy(hw->name, hw->id);
+ strscpy(hw->name, hw->id);
switch (opl3->hardware & OPL3_HW_MASK) {
case OPL3_HW_OPL2:
- strcpy(hw->name, "OPL2 FM");
+ strscpy(hw->name, "OPL2 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL2;
break;
case OPL3_HW_OPL3:
- strcpy(hw->name, "OPL3 FM");
+ strscpy(hw->name, "OPL3 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL3;
break;
case OPL3_HW_OPL4:
- strcpy(hw->name, "OPL4 FM");
+ strscpy(hw->name, "OPL4 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL4;
break;
}
@@ -529,7 +510,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
#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);
+ strscpy(opl3->seq_dev->name, hw->name);
*(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3;
}
#endif
@@ -539,19 +520,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 bb3f3a5a6951..6d3c5b5a35ff 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,9 +11,12 @@
#include "opl3_voice.h"
#include <sound/asoundef.h>
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
-extern bool use_internal_drums;
+#ifdef DEBUG_MIDI
+#define opl3_dbg(opl3, fmt, ...) \
+ dev_dbg(((struct snd_opl3 *)(opl3))->card->dev, fmt, ##__VA_ARGS__)
+#else
+#define opl3_dbg(opl3, fmt, ...) do {} while (0)
+#endif
static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
struct snd_midi_channel *chan);
@@ -41,7 +30,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 +76,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,
@@ -125,14 +114,17 @@ static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
#ifdef DEBUG_ALLOC
-static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
+static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice)
+{
int i;
- char *str = "x.24";
+ const char *str = "x.24";
+ char buf[MAX_OPL3_VOICES + 1];
- printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
for (i = 0; i < opl3->max_voices; i++)
- printk(KERN_CONT "%c", *(str + opl3->voices[i].state + 1));
- printk(KERN_CONT "\n");
+ buf[i] = str[opl3->voices[i].state + 1];
+ buf[i] = 0;
+ dev_dbg(opl3->card->dev, "time %.5i: %s [%.2i]: %s\n",
+ opl3->use_time, s, voice, buf);
}
#endif
@@ -198,8 +190,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 */
@@ -222,9 +213,10 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
for (i = 0; i < END; i++) {
if (best[i].voice >= 0) {
#ifdef DEBUG_ALLOC
- printk(KERN_DEBUG "%s %iop allocation on voice %i\n",
- alloc_type[i], instr_4op ? 4 : 2,
- best[i].voice);
+ dev_dbg(opl3->card->dev,
+ "%s %iop allocation on voice %i\n",
+ alloc_type[i], instr_4op ? 4 : 2,
+ best[i].voice);
#endif
return best[i].voice;
}
@@ -241,30 +233,28 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
void snd_opl3_timer_func(struct timer_list *t)
{
- struct snd_opl3 *opl3 = from_timer(opl3, t, tlist);
- unsigned long flags;
+ struct snd_opl3 *opl3 = timer_container_of(opl3, t, tlist);
int again = 0;
int i;
- spin_lock_irqsave(&opl3->voice_lock, flags);
- for (i = 0; i < opl3->max_voices; i++) {
- struct snd_opl3_voice *vp = &opl3->voices[i];
- if (vp->state > 0 && vp->note_off_check) {
- if (vp->note_off == jiffies)
- snd_opl3_note_off_unsafe(opl3, vp->note, 0,
- vp->chan);
- else
- again++;
+ scoped_guard(spinlock_irqsave, &opl3->voice_lock) {
+ for (i = 0; i < opl3->max_voices; i++) {
+ struct snd_opl3_voice *vp = &opl3->voices[i];
+ if (vp->state > 0 && vp->note_off_check) {
+ if (vp->note_off == jiffies)
+ snd_opl3_note_off_unsafe(opl3, vp->note, 0,
+ vp->chan);
+ else
+ again++;
+ }
}
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->sys_timer_lock);
if (again)
mod_timer(&opl3->tlist, jiffies + 1); /* invoke again */
else
opl3->sys_timer_status = 0;
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
}
/*
@@ -272,19 +262,17 @@ void snd_opl3_timer_func(struct timer_list *t)
*/
static void snd_opl3_start_timer(struct snd_opl3 *opl3)
{
- unsigned long flags;
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->sys_timer_lock);
if (! opl3->sys_timer_status) {
mod_timer(&opl3->tlist, jiffies + 1);
opl3->sys_timer_status = 1;
}
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
}
/* ------------------------------ */
-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
};
@@ -317,14 +305,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
struct fm_patch *patch;
struct fm_instrument *fm;
- unsigned long flags;
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n",
- chan->number, chan->midi_program, note, vel);
-#endif
+ opl3_dbg(opl3, "Note on, ch %i, inst %i, note %i, vel %i\n",
+ chan->number, chan->midi_program, note, vel);
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, drum voice numbers are notes on drum channel */
@@ -347,20 +332,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
prg = chan->midi_program;
}
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
if (use_internal_drums) {
snd_opl3_drum_switch(opl3, note, vel, 1, chan);
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
__extra_prg:
patch = snd_opl3_find_patch(opl3, prg, bank, 0);
- if (!patch) {
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ if (!patch)
return;
- }
fm = &patch->inst;
switch (patch->type) {
@@ -372,14 +354,12 @@ 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;
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> OPL%i instrument: %s\n",
- instr_4op ? 3 : 2, patch->name);
-#endif
+ opl3_dbg(opl3, " --> OPL%i instrument: %s\n",
+ instr_4op ? 3 : 2, patch->name);
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, allocate voice on free OPL3 channel */
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
@@ -389,10 +369,8 @@ 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);
+ if (voice < 0)
return;
- }
if (voice < MAX_OPL2_VOICES) {
/* Left register block for voices 0 .. 8 */
@@ -415,7 +393,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;
@@ -440,10 +418,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> setting OPL3 connection: 0x%x\n",
- opl3->connection_reg);
-#endif
+ opl3_dbg(opl3, " --> setting OPL3 connection: 0x%x\n",
+ opl3->connection_reg);
/*
* calculate volume depending on connection
* between FM operators (see include/opl3.h)
@@ -460,7 +436,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;
@@ -475,9 +451,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* Program the FM voice characteristics */
for (i = 0; i < (instr_4op ? 4 : 2); i++) {
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> programming operator %i\n", i);
-#endif
+ opl3_dbg(opl3, " --> programming operator %i\n", i);
op_offset = snd_opl3_regmap[voice_offset][i];
/* Set OPL3 AM_VIB register of requested voice/operator */
@@ -555,9 +529,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* Set output sound flag */
blocknum |= OPL3_KEYON_BIT;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> trigger voice %i\n", voice);
-#endif
+ opl3_dbg(opl3, " --> trigger voice %i\n", voice);
/* Set OPL3 KEYON_BLOCK register of requested voice */
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
opl3->command(opl3, opl3_reg, blocknum);
@@ -611,12 +583,9 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
bank = 0;
prg = extra_prg - 1;
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " *** allocating extra program\n");
-#endif
+ opl3_dbg(opl3, " *** allocating extra program\n");
goto __extra_prg;
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
@@ -642,9 +611,7 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
}
/* kill voice */
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> kill voice %i\n", voice);
-#endif
+ opl3_dbg(opl3, " --> kill voice %i\n", voice);
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
/* clear Key ON bit */
opl3->command(opl3, opl3_reg, vp->keyon_reg);
@@ -678,10 +645,8 @@ static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n",
- chan->number, chan->midi_program, note);
-#endif
+ opl3_dbg(opl3, "Note off, ch %i, inst %i, note %i\n",
+ chan->number, chan->midi_program, note);
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
if (chan->drum_channel && use_internal_drums) {
@@ -709,11 +674,9 @@ void snd_opl3_note_off(void *p, int note, int vel,
struct snd_midi_channel *chan)
{
struct snd_opl3 *opl3 = p;
- unsigned long flags;
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
snd_opl3_note_off_unsafe(p, note, vel, chan);
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
/*
@@ -721,13 +684,8 @@ 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);
-#endif
+ opl3_dbg(p, "Key pressure, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
/*
@@ -735,13 +693,8 @@ 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);
-#endif
+ opl3_dbg(p, "Terminate note, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
@@ -797,9 +750,7 @@ static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *
int voice;
struct snd_opl3_voice *vp;
- unsigned long flags;
-
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
for (voice = 0; voice < opl3->max_voices; voice++) {
@@ -815,7 +766,6 @@ static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *
snd_opl3_update_pitch(opl3, voice);
}
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
/*
@@ -827,10 +777,8 @@ void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
struct snd_opl3 *opl3;
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
- type, chan->number, chan->midi_program);
-#endif
+ opl3_dbg(opl3, "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+ type, chan->number, chan->midi_program);
switch (type) {
case MIDI_CTL_MSB_MODWHEEL:
@@ -861,13 +809,8 @@ 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);
-#endif
+ opl3_dbg(p, "NRPN, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
/*
@@ -876,10 +819,5 @@ 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
+ opl3_dbg(p, "SYSEX\n");
}
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 22c3e4bca220..6d39b2b77b80 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -1,21 +1,8 @@
+// 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>
@@ -29,9 +16,7 @@ static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
/* 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,
@@ -112,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) {
@@ -151,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 */
@@ -159,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;
@@ -206,14 +193,14 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
return -EINVAL;
if (count < (int)sizeof(sbi)) {
- snd_printk(KERN_ERR "FM Error: Patch record too short\n");
+ dev_err(opl3->card->dev, "FM Error: Patch record too short\n");
return -EINVAL;
}
if (copy_from_user(&sbi, buf, sizeof(sbi)))
return -EFAULT;
if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
- snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
+ dev_err(opl3->card->dev, "FM Error: Invalid instrument number %d\n",
sbi.channel);
return -EINVAL;
}
@@ -240,9 +227,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
- snd_printk(KERN_ERR "OPL3: "
- "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
- "Fix the program.\n");
+ dev_err(opl3->card->dev,
+ "OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
return -EINVAL;
case SNDCTL_SYNTH_MEMAVL:
@@ -261,11 +247,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 5f881c4cdf1f..d3278428d360 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,21 +6,6 @@
*
* 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"
@@ -54,13 +40,11 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
int idx;
struct snd_hwdep *hwdep = opl3->hwdep;
- mutex_lock(&hwdep->open_mutex);
- if (hwdep->used) {
- mutex_unlock(&hwdep->open_mutex);
- return -EBUSY;
+ scoped_guard(mutex, &hwdep->open_mutex) {
+ if (hwdep->used)
+ return -EBUSY;
+ hwdep->used++;
}
- hwdep->used++;
- mutex_unlock(&hwdep->open_mutex);
snd_opl3_reset(opl3);
@@ -82,22 +66,21 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
{
- unsigned long flags;
struct snd_hwdep *hwdep;
/* Stop system timer */
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
- if (opl3->sys_timer_status) {
- del_timer(&opl3->tlist);
- opl3->sys_timer_status = 0;
+ scoped_guard(spinlock_irq, &opl3->sys_timer_lock) {
+ if (opl3->sys_timer_status) {
+ timer_delete(&opl3->tlist);
+ opl3->sys_timer_status = 0;
+ }
}
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
snd_opl3_reset(opl3);
hwdep = opl3->hwdep;
- mutex_lock(&hwdep->open_mutex);
- hwdep->used--;
- mutex_unlock(&hwdep->open_mutex);
+ scoped_guard(mutex, &hwdep->open_mutex) {
+ hwdep->used--;
+ }
wake_up(&hwdep->open_wait);
}
@@ -106,7 +89,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) {
@@ -121,7 +105,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;
@@ -142,7 +127,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,
@@ -241,7 +226,8 @@ static int snd_opl3_seq_probe(struct 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;
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index ddcc1a325a61..10f622b439a0 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -1,28 +1,16 @@
+// 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 IS_ENABLED(CONFIG_SND_SEQUENCER)
#define OPL3_SUPPORT_SYNTH
@@ -103,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)))
@@ -168,10 +158,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
return 0;
#endif
-#ifdef CONFIG_SND_DEBUG
default:
- snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd);
-#endif
+ dev_dbg(opl3->card->dev, "unknown IOCTL: 0x%x\n", cmd);
}
return -ENOTTY;
}
@@ -300,7 +288,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;
}
@@ -448,7 +436,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;
@@ -473,7 +461,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 a2445163008e..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>
@@ -41,7 +28,7 @@ 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 */
#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
@@ -52,4 +39,8 @@ void snd_opl3_free_seq_oss(struct snd_opl3 *opl3);
#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 6e86a4092b4c..a841630b45c2 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o
+snd-opl4-lib-y := 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
+snd-opl4-synth-y := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c
index db76a5bf2bd2..44fbc6bf0654 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"
@@ -60,10 +47,9 @@ EXPORT_SYMBOL(snd_opl4_read);
void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size)
{
- unsigned long flags;
u8 memcfg;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
@@ -78,18 +64,15 @@ void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size
insb(opl4->pcm_port + 1, buf, size);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
-
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
EXPORT_SYMBOL(snd_opl4_read_memory);
void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size)
{
- unsigned long flags;
u8 memcfg;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
@@ -104,8 +87,6 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
outsb(opl4->pcm_port + 1, buf, size);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
-
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
EXPORT_SYMBOL(snd_opl4_write_memory);
@@ -127,7 +108,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
snd_opl4_enable_opl4(opl4);
id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
- snd_printdd("OPL4[02]=%02x\n", id1);
+ dev_dbg(opl4->card->dev, "OPL4[02]=%02x\n", id1);
switch (id1 & OPL4_DEVICE_ID_MASK) {
case 0x20:
opl4->hardware = OPL3_HW_OPL4;
@@ -143,7 +124,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff);
id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM);
id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM);
- snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2);
+ dev_dbg(opl4->card->dev, "OPL4 id1=%02x id2=%02x\n", id1, id2);
if (id1 != 0x00 || id2 != 0xff)
return -ENODEV;
@@ -165,7 +146,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
opl4->seq_dev_num = seq_device;
if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4,
sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) {
- strcpy(opl4->seq_dev->name, "OPL4 Wavetable");
+ strscpy(opl4->seq_dev->name, "OPL4 Wavetable");
*(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4;
opl4->seq_dev->private_data = opl4;
opl4->seq_dev->private_free = snd_opl4_seq_dev_free;
@@ -197,7 +178,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
};
@@ -213,7 +194,7 @@ int snd_opl4_create(struct snd_card *card,
opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM");
opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX");
if (!opl4->res_fm_port || !opl4->res_pcm_port) {
- snd_printk(KERN_ERR "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port);
+ dev_err(card->dev, "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port);
snd_opl4_free(opl4);
return -EBUSY;
}
@@ -227,7 +208,7 @@ int snd_opl4_create(struct snd_card *card,
err = snd_opl4_detect(opl4);
if (err < 0) {
snd_opl4_free(opl4);
- snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
+ dev_dbg(card->dev, "OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
return err;
}
@@ -263,15 +244,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_mixer.c b/sound/drivers/opl4/opl4_mixer.c
index 04079de4c35f..deebb8636437 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"
@@ -32,13 +19,11 @@ static int snd_opl4_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static int snd_opl4_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl4 *opl4 = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
u8 reg = kcontrol->private_value;
u8 value;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
value = snd_opl4_read(opl4, reg);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
ucontrol->value.integer.value[0] = 7 - (value & 7);
ucontrol->value.integer.value[1] = 7 - ((value >> 3) & 7);
return 0;
@@ -47,20 +32,18 @@ static int snd_opl4_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static int snd_opl4_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl4 *opl4 = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
u8 reg = kcontrol->private_value;
u8 value, old_value;
value = (7 - (ucontrol->value.integer.value[0] & 7)) |
((7 - (ucontrol->value.integer.value[1] & 7)) << 3);
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
old_value = snd_opl4_read(opl4, reg);
snd_opl4_write(opl4, reg, value);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
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 cd2c07fa2ef4..fd0ba4704d9f 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -1,20 +1,7 @@
+// 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"
@@ -27,13 +14,10 @@ static int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
{
struct snd_opl4 *opl4 = entry->private_data;
- mutex_lock(&opl4->access_mutex);
- if (opl4->memory_access) {
- mutex_unlock(&opl4->access_mutex);
+ guard(mutex)(&opl4->access_mutex);
+ if (opl4->memory_access)
return -EBUSY;
- }
opl4->memory_access++;
- mutex_unlock(&opl4->access_mutex);
return 0;
}
@@ -42,9 +26,8 @@ static int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
{
struct snd_opl4 *opl4 = entry->private_data;
- mutex_lock(&opl4->access_mutex);
+ guard(mutex)(&opl4->access_mutex);
opl4->memory_access--;
- mutex_unlock(&opl4->access_mutex);
return 0;
}
@@ -89,7 +72,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,
@@ -104,7 +87,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 */
@@ -114,10 +97,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;
diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c
index 03d6202f4829..7bb22089a093 100644
--- a/sound/drivers/opl4/opl4_seq.c
+++ b/sound/drivers/opl4/opl4_seq.c
@@ -63,24 +63,18 @@ static int snd_opl4_seq_use(void *private_data, struct snd_seq_port_subscribe *i
struct snd_opl4 *opl4 = private_data;
int err;
- mutex_lock(&opl4->access_mutex);
-
- if (opl4->used) {
- mutex_unlock(&opl4->access_mutex);
- return -EBUSY;
- }
- opl4->used++;
-
- if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
- err = snd_opl4_seq_use_inc(opl4);
- if (err < 0) {
- mutex_unlock(&opl4->access_mutex);
- return err;
+ scoped_guard(mutex, &opl4->access_mutex) {
+ if (opl4->used)
+ return -EBUSY;
+ opl4->used++;
+
+ if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
+ err = snd_opl4_seq_use_inc(opl4);
+ if (err < 0)
+ return err;
}
}
- mutex_unlock(&opl4->access_mutex);
-
snd_opl4_synth_reset(opl4);
return 0;
}
@@ -91,16 +85,16 @@ static int snd_opl4_seq_unuse(void *private_data, struct snd_seq_port_subscribe
snd_opl4_synth_shutdown(opl4);
- mutex_lock(&opl4->access_mutex);
- opl4->used--;
- mutex_unlock(&opl4->access_mutex);
+ scoped_guard(mutex, &opl4->access_mutex) {
+ opl4->used--;
+ }
if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
snd_opl4_seq_use_dec(opl4);
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,
diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c
index 7bc1e58c95aa..82dbb8519ab1 100644
--- a/sound/drivers/opl4/opl4_synth.c
+++ b/sound/drivers/opl4/opl4_synth.c
@@ -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,
@@ -272,13 +272,12 @@ static unsigned char snd_opl4_volume_table[128] = {
*/
void snd_opl4_synth_reset(struct snd_opl4 *opl4)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&opl4->reg_lock, flags);
- for (i = 0; i < OPL4_MAX_VOICES; i++)
- snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &opl4->reg_lock) {
+ for (i = 0; i < OPL4_MAX_VOICES; i++)
+ snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
+ }
INIT_LIST_HEAD(&opl4->off_voices);
INIT_LIST_HEAD(&opl4->on_voices);
@@ -296,14 +295,12 @@ void snd_opl4_synth_reset(struct snd_opl4 *opl4)
*/
void snd_opl4_synth_shutdown(struct snd_opl4 *opl4)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++)
snd_opl4_write(opl4, OPL4_REG_MISC + i,
opl4->voices[i].reg_misc & ~OPL4_KEY_ON_BIT);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -313,17 +310,15 @@ static void snd_opl4_do_for_note(struct snd_opl4 *opl4, int note, struct snd_mid
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan == chan && voice->note == note) {
func(opl4, voice);
}
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -334,17 +329,15 @@ static void snd_opl4_do_for_channel(struct snd_opl4 *opl4,
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan == chan) {
func(opl4, voice);
}
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -354,16 +347,14 @@ static void snd_opl4_do_for_all(struct snd_opl4 *opl4,
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan)
func(opl4, voice);
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
static void snd_opl4_update_volume(struct snd_opl4 *opl4, struct opl4_voice *voice)
@@ -486,7 +477,6 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
struct opl4_voice *voice[2];
const struct opl4_sound *sound[2];
int voices = 0, i;
- unsigned long flags;
/* determine the number of voices and voice parameters */
i = chan->drum_channel ? 0x80 : (chan->midi_program & 0x7f);
@@ -501,41 +491,41 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
}
/* allocate and initialize the needed voices */
- spin_lock_irqsave(&opl4->reg_lock, flags);
- for (i = 0; i < voices; i++) {
- voice[i] = snd_opl4_get_voice(opl4);
- list_move_tail(&voice[i]->list, &opl4->on_voices);
- voice[i]->chan = chan;
- voice[i]->note = note;
- voice[i]->velocity = vel & 0x7f;
- voice[i]->sound = sound[i];
- }
+ scoped_guard(spinlock_irqsave, &opl4->reg_lock) {
+ for (i = 0; i < voices; i++) {
+ voice[i] = snd_opl4_get_voice(opl4);
+ list_move_tail(&voice[i]->list, &opl4->on_voices);
+ voice[i]->chan = chan;
+ voice[i]->note = note;
+ voice[i]->velocity = vel & 0x7f;
+ voice[i]->sound = sound[i];
+ }
- /* set tone number (triggers header loading) */
- for (i = 0; i < voices; i++) {
- voice[i]->reg_f_number =
- (sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
- snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
- voice[i]->reg_f_number);
- snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
- sound[i]->tone & 0xff);
- }
+ /* set tone number (triggers header loading) */
+ for (i = 0; i < voices; i++) {
+ voice[i]->reg_f_number =
+ (sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
+ snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
+ voice[i]->reg_f_number);
+ snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
+ sound[i]->tone & 0xff);
+ }
- /* set parameters which can be set while loading */
- for (i = 0; i < voices; i++) {
- voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
- snd_opl4_update_pan(opl4, voice[i]);
- snd_opl4_update_pitch(opl4, voice[i]);
- voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
- snd_opl4_update_volume(opl4, voice[i]);
+ /* set parameters which can be set while loading */
+ for (i = 0; i < voices; i++) {
+ voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
+ snd_opl4_update_pan(opl4, voice[i]);
+ snd_opl4_update_pitch(opl4, voice[i]);
+ voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
+ snd_opl4_update_volume(opl4, voice[i]);
+ }
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
/* wait for completion of loading */
snd_opl4_wait_for_wave_headers(opl4);
/* set remaining parameters */
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < voices; i++) {
snd_opl4_update_tone_parameters(opl4, voice[i]);
voice[i]->reg_lfo_vibrato = voice[i]->sound->reg_lfo_vibrato;
@@ -549,7 +539,6 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number,
voice[i]->reg_misc);
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
static void snd_opl4_voice_off(struct snd_opl4 *opl4, struct opl4_voice *voice)
diff --git a/sound/drivers/opl4/yrw801.c b/sound/drivers/opl4/yrw801.c
index 6c335492d082..9e464b84b905 100644
--- a/sound/drivers/opl4/yrw801.c
+++ b/sound/drivers/opl4/yrw801.c
@@ -43,7 +43,7 @@ int snd_yrw801_detect(struct snd_opl4 *opl4)
snd_opl4_read_memory(opl4, buf, 0x1ffffe, 2);
if (buf[0] != 0x01)
return -ENODEV;
- snd_printdd("YRW801 ROM version %02x.%02x\n", buf[0], buf[1]);
+ dev_dbg(opl4->card->dev, "YRW801 ROM version %02x.%02x\n", buf[0], buf[1]);
return 0;
}
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
deleted file mode 100644
index d16bc14a0f0e..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_occurred: %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 transferred 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 transferred 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/pcmtest.c b/sound/drivers/pcmtest.c
new file mode 100644
index 000000000000..19b3f306c564
--- /dev/null
+++ b/sound/drivers/pcmtest.c
@@ -0,0 +1,780 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Virtual ALSA driver for PCM testing/fuzzing
+ *
+ * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com>
+ *
+ * This is a simple virtual ALSA driver, which can be used for audio applications/PCM middle layer
+ * testing or fuzzing.
+ * It can:
+ * - Simulate 'playback' and 'capture' actions
+ * - Generate random or pattern-based capture data
+ * - Check playback buffer for containing looped template, and notify about the results
+ * through the debugfs entry
+ * - Inject delays into the playback and capturing processes. See 'inject_delay' parameter.
+ * - Inject errors during the PCM callbacks.
+ * - Register custom RESET ioctl and notify when it is called through the debugfs entry
+ * - Work in interleaved and non-interleaved modes
+ * - Support up to 8 substreams
+ * - Support up to 4 channels
+ * - Support framerates from 8 kHz to 48 kHz
+ *
+ * When driver works in the capture mode with multiple channels, it duplicates the looped
+ * pattern to each separate channel. For example, if we have 2 channels, format = U8, interleaved
+ * access mode and pattern 'abacaba', the DMA buffer will look like aabbccaabbaaaa..., so buffer for
+ * each channel will contain abacabaabacaba... Same for the non-interleaved mode.
+ *
+ * However, it may break the capturing on the higher framerates with small period size, so it is
+ * better to choose larger period sizes.
+ *
+ * You can find the corresponding selftest in the 'alsa' selftests folder.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+
+#define TIMER_PER_SEC 5
+#define TIMER_INTERVAL (HZ / TIMER_PER_SEC)
+#define DELAY_JIFFIES HZ
+#define PLAYBACK_SUBSTREAM_CNT 8
+#define CAPTURE_SUBSTREAM_CNT 8
+#define MAX_CHANNELS_NUM 4
+
+#define DEFAULT_PATTERN "abacaba"
+#define DEFAULT_PATTERN_LEN 7
+
+#define FILL_MODE_RAND 0
+#define FILL_MODE_PAT 1
+
+#define MAX_PATTERN_LEN 4096
+
+static int index = -1;
+static char *id = "pcmtest";
+static bool enable = true;
+static int inject_delay;
+static bool inject_hwpars_err;
+static bool inject_prepare_err;
+static bool inject_trigger_err;
+static bool inject_open_err;
+
+static short fill_mode = FILL_MODE_PAT;
+
+static u8 playback_capture_test;
+static u8 ioctl_reset_test;
+static struct dentry *driver_debug_dir;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for pcmtest soundcard");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for pcmtest soundcard");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable pcmtest soundcard.");
+module_param(fill_mode, short, 0600);
+MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)");
+module_param(inject_delay, int, 0600);
+MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)");
+module_param(inject_hwpars_err, bool, 0600);
+MODULE_PARM_DESC(inject_hwpars_err, "Inject EBUSY error in the 'hw_params' callback");
+module_param(inject_prepare_err, bool, 0600);
+MODULE_PARM_DESC(inject_prepare_err, "Inject EINVAL error in the 'prepare' callback");
+module_param(inject_trigger_err, bool, 0600);
+MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback");
+module_param(inject_open_err, bool, 0600);
+MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback");
+
+struct pcmtst {
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ struct platform_device *pdev;
+};
+
+struct pcmtst_buf_iter {
+ size_t buf_pos; // position in the DMA buffer
+ size_t period_pos; // period-relative position
+ size_t b_rw; // Bytes to write on every timer tick
+ size_t s_rw_ch; // Samples to write to one channel on every tick
+ unsigned int sample_bytes; // sample_bits / 8
+ bool is_buf_corrupted; // playback test result indicator
+ size_t period_bytes; // bytes in a one period
+ bool interleaved; // Interleaved/Non-interleaved mode
+ size_t total_bytes; // Total bytes read/written
+ size_t chan_block; // Bytes in one channel buffer when non-interleaved
+ struct snd_pcm_substream *substream;
+ bool suspend; // We need to pause timer without shutting it down
+ struct timer_list timer_instance;
+};
+
+static struct snd_pcm_hardware snd_pcmtst_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = MAX_CHANNELS_NUM,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+struct pattern_buf {
+ char *buf;
+ u32 len;
+};
+
+static int buf_allocated;
+static struct pattern_buf patt_bufs[MAX_CHANNELS_NUM];
+
+static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t bytes)
+{
+ v_iter->total_bytes += by;
+ v_iter->buf_pos += by;
+ if (v_iter->buf_pos >= bytes)
+ v_iter->buf_pos %= bytes;
+}
+
+/*
+ * Position in the DMA buffer when we are in the non-interleaved mode. We increment buf_pos
+ * every time we write a byte to any channel, so the position in the current channel buffer is
+ * (position in the DMA buffer) / count_of_channels + size_of_channel_buf * current_channel
+ */
+static inline size_t buf_pos_n(struct pcmtst_buf_iter *v_iter, unsigned int channels,
+ unsigned int chan_num)
+{
+ return v_iter->buf_pos / channels + v_iter->chan_block * chan_num;
+}
+
+/*
+ * Get the count of bytes written for the current channel in the interleaved mode.
+ * This is (count of samples written for the current channel) * bytes_in_sample +
+ * (relative position in the current sample)
+ */
+static inline size_t ch_pos_i(size_t b_total, unsigned int channels, unsigned int b_sample)
+{
+ return b_total / channels / b_sample * b_sample + (b_total % b_sample);
+}
+
+static void check_buf_block_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t i;
+ short ch_num;
+ u8 current_byte;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ current_byte = runtime->dma_area[v_iter->buf_pos];
+ if (!current_byte)
+ break;
+ ch_num = (v_iter->total_bytes / v_iter->sample_bytes) % runtime->channels;
+ if (current_byte != patt_bufs[ch_num].buf[ch_pos_i(v_iter->total_bytes,
+ runtime->channels,
+ v_iter->sample_bytes)
+ % patt_bufs[ch_num].len]) {
+ v_iter->is_buf_corrupted = true;
+ break;
+ }
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ // If we broke during the loop, add remaining bytes to the buffer position.
+ inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes);
+}
+
+static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ unsigned int channels = runtime->channels;
+ size_t i;
+ short ch_num;
+ u8 current_byte;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ ch_num = i % channels;
+ current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)];
+ if (!current_byte)
+ break;
+ if (current_byte != patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
+ % patt_bufs[ch_num].len]) {
+ v_iter->is_buf_corrupted = true;
+ break;
+ }
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes);
+}
+
+/*
+ * Check one block of the buffer. Here we iterate the buffer until we find '0'. This condition is
+ * necessary because we need to detect when the reading/writing ends, so we assume that the pattern
+ * doesn't contain zeros.
+ */
+static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ check_buf_block_i(v_iter, runtime);
+ else
+ check_buf_block_ni(v_iter, runtime);
+}
+
+/*
+ * Fill buffer in the non-interleaved mode. The order of samples is C0, ..., C0, C1, ..., C1, C2...
+ * The channel buffers lay in the DMA buffer continuously (see default copy
+ * handlers in the pcm_lib.c file).
+ *
+ * Here we increment the DMA buffer position every time we write a byte to any channel 'buffer'.
+ * We need this to simulate the correct hardware pointer moving.
+ */
+static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t i;
+ unsigned int channels = runtime->channels;
+ short ch_num;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ ch_num = i % channels;
+ runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)] =
+ patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
+ % patt_bufs[ch_num].len];
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+}
+
+// Fill buffer in the interleaved mode. The order of samples is C0, C1, C2, C0, C1, C2, ...
+static void fill_block_pattern_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t sample;
+ size_t pos_in_ch, pos_pattern;
+ short ch, pos_sample;
+
+ pos_in_ch = ch_pos_i(v_iter->total_bytes, runtime->channels, v_iter->sample_bytes);
+
+ for (sample = 0; sample < v_iter->s_rw_ch; sample++) {
+ for (ch = 0; ch < runtime->channels; ch++) {
+ for (pos_sample = 0; pos_sample < v_iter->sample_bytes; pos_sample++) {
+ pos_pattern = (pos_in_ch + sample * v_iter->sample_bytes
+ + pos_sample) % patt_bufs[ch].len;
+ runtime->dma_area[v_iter->buf_pos] = patt_bufs[ch].buf[pos_pattern];
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ }
+ }
+}
+
+static void fill_block_pattern(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ fill_block_pattern_i(v_iter, runtime);
+ else
+ fill_block_pattern_n(v_iter, runtime);
+}
+
+static void fill_block_rand_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ unsigned int channels = runtime->channels;
+ // Remaining space in all channel buffers
+ size_t bytes_remain = runtime->dma_bytes - v_iter->buf_pos;
+ unsigned int i;
+
+ for (i = 0; i < channels; i++) {
+ if (v_iter->b_rw <= bytes_remain) {
+ //b_rw - count of bytes must be written for all channels at each timer tick
+ get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i),
+ v_iter->b_rw / channels);
+ } else {
+ // Write to the end of buffer and start from the beginning of it
+ get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i),
+ bytes_remain / channels);
+ get_random_bytes(runtime->dma_area + v_iter->chan_block * i,
+ (v_iter->b_rw - bytes_remain) / channels);
+ }
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
+}
+
+static void fill_block_rand_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t in_cur_block = runtime->dma_bytes - v_iter->buf_pos;
+
+ if (v_iter->b_rw <= in_cur_block) {
+ get_random_bytes(&runtime->dma_area[v_iter->buf_pos], v_iter->b_rw);
+ } else {
+ get_random_bytes(&runtime->dma_area[v_iter->buf_pos], in_cur_block);
+ get_random_bytes(runtime->dma_area, v_iter->b_rw - in_cur_block);
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
+}
+
+static void fill_block_random(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ fill_block_rand_i(v_iter, runtime);
+ else
+ fill_block_rand_n(v_iter, runtime);
+}
+
+static void fill_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ switch (fill_mode) {
+ case FILL_MODE_RAND:
+ fill_block_random(v_iter, runtime);
+ break;
+ case FILL_MODE_PAT:
+ fill_block_pattern(v_iter, runtime);
+ break;
+ }
+}
+
+/*
+ * Here we iterate through the buffer by (buffer_size / iterates_per_second) bytes.
+ * The driver uses timer to simulate the hardware pointer moving, and notify the PCM middle layer
+ * about period elapsed.
+ */
+static void timer_timeout(struct timer_list *data)
+{
+ struct pcmtst_buf_iter *v_iter;
+ struct snd_pcm_substream *substream;
+
+ v_iter = timer_container_of(v_iter, data, timer_instance);
+ substream = v_iter->substream;
+
+ if (v_iter->suspend)
+ return;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted)
+ check_buf_block(v_iter, substream->runtime);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ fill_block(v_iter, substream->runtime);
+ else
+ inc_buf_pos(v_iter, v_iter->b_rw, substream->runtime->dma_bytes);
+
+ v_iter->period_pos += v_iter->b_rw;
+ if (v_iter->period_pos >= v_iter->period_bytes) {
+ v_iter->period_pos %= v_iter->period_bytes;
+ snd_pcm_period_elapsed(substream);
+ }
+
+ if (!v_iter->suspend)
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
+}
+
+static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter;
+
+ if (inject_open_err)
+ return -EBUSY;
+
+ v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL);
+ if (!v_iter)
+ return -ENOMEM;
+
+ v_iter->substream = substream;
+ runtime->hw = snd_pcmtst_hw;
+ runtime->private_data = v_iter;
+
+ playback_capture_test = 0;
+ ioctl_reset_test = 0;
+
+ timer_setup(&v_iter->timer_instance, timer_timeout, 0);
+
+ return 0;
+}
+
+static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ timer_shutdown_sync(&v_iter->timer_instance);
+ playback_capture_test = !v_iter->is_buf_corrupted;
+ kfree(v_iter);
+ return 0;
+}
+
+static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->buf_pos = 0;
+ v_iter->is_buf_corrupted = false;
+ v_iter->period_pos = 0;
+ v_iter->total_bytes = 0;
+}
+
+static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->suspend = false;
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+}
+
+static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ if (inject_trigger_err)
+ return -EINVAL;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ reset_buf_iterator(v_iter);
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ // We can't call timer_shutdown_sync here, as it is forbidden to sleep here
+ v_iter->suspend = true;
+ timer_delete(&v_iter->timer_instance);
+ break;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_pcmtst_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, v_iter->buf_pos);
+}
+
+static int snd_pcmtst_free(struct pcmtst *pcmtst)
+{
+ if (!pcmtst)
+ return 0;
+ kfree(pcmtst);
+ return 0;
+}
+
+// These callbacks are required, but empty - all freeing occurs in pdev_remove
+static int snd_pcmtst_dev_free(struct snd_device *device)
+{
+ return 0;
+}
+
+static void pcmtst_pdev_release(struct device *dev)
+{
+}
+
+static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter = runtime->private_data;
+
+ if (inject_prepare_err)
+ return -EINVAL;
+
+ v_iter->sample_bytes = samples_to_bytes(runtime, 1);
+ v_iter->period_bytes = snd_pcm_lib_period_bytes(substream);
+ v_iter->interleaved = true;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) {
+ v_iter->chan_block = snd_pcm_lib_buffer_bytes(substream) / runtime->channels;
+ v_iter->interleaved = false;
+ }
+ // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes
+ v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC;
+ v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels;
+
+ return 0;
+}
+
+static int snd_pcmtst_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ if (inject_hwpars_err)
+ return -EBUSY;
+ return 0;
+}
+
+static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int snd_pcmtst_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ ioctl_reset_test = 1;
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_pcmtst_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ timer_delete_sync(&v_iter->timer_instance);
+
+ return 0;
+}
+
+static const struct snd_pcm_ops snd_pcmtst_playback_ops = {
+ .open = snd_pcmtst_pcm_open,
+ .close = snd_pcmtst_pcm_close,
+ .trigger = snd_pcmtst_pcm_trigger,
+ .hw_params = snd_pcmtst_pcm_hw_params,
+ .ioctl = snd_pcmtst_ioctl,
+ .sync_stop = snd_pcmtst_sync_stop,
+ .hw_free = snd_pcmtst_pcm_hw_free,
+ .prepare = snd_pcmtst_pcm_prepare,
+ .pointer = snd_pcmtst_pcm_pointer,
+};
+
+static const struct snd_pcm_ops snd_pcmtst_capture_ops = {
+ .open = snd_pcmtst_pcm_open,
+ .close = snd_pcmtst_pcm_close,
+ .trigger = snd_pcmtst_pcm_trigger,
+ .hw_params = snd_pcmtst_pcm_hw_params,
+ .hw_free = snd_pcmtst_pcm_hw_free,
+ .ioctl = snd_pcmtst_ioctl,
+ .sync_stop = snd_pcmtst_sync_stop,
+ .prepare = snd_pcmtst_pcm_prepare,
+ .pointer = snd_pcmtst_pcm_pointer,
+};
+
+static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(pcmtst->card, "PCMTest", 0, PLAYBACK_SUBSTREAM_CNT,
+ CAPTURE_SUBSTREAM_CNT, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = pcmtst;
+ strscpy(pcm->name, "PCMTest");
+ pcmtst->pcm = pcm;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcmtst_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcmtst_capture_ops);
+
+ err = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pcmtst->pdev->dev,
+ 0, 128 * 1024);
+ return err;
+}
+
+static int snd_pcmtst_create(struct snd_card *card, struct platform_device *pdev,
+ struct pcmtst **r_pcmtst)
+{
+ struct pcmtst *pcmtst;
+ int err;
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_pcmtst_dev_free,
+ };
+
+ pcmtst = kzalloc(sizeof(*pcmtst), GFP_KERNEL);
+ if (!pcmtst)
+ return -ENOMEM;
+ pcmtst->card = card;
+ pcmtst->pdev = pdev;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pcmtst, &ops);
+ if (err < 0)
+ goto _err_free_chip;
+
+ err = snd_pcmtst_new_pcm(pcmtst);
+ if (err < 0)
+ goto _err_free_chip;
+
+ *r_pcmtst = pcmtst;
+ return 0;
+
+_err_free_chip:
+ snd_pcmtst_free(pcmtst);
+ return err;
+}
+
+static int pcmtst_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct pcmtst *pcmtst;
+ int err;
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ err = snd_devm_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
+ err = snd_pcmtst_create(card, pdev, &pcmtst);
+ if (err < 0)
+ return err;
+
+ strscpy(card->driver, "PCM-TEST Driver");
+ strscpy(card->shortname, "PCM-Test");
+ strscpy(card->longname, "PCM-Test virtual driver");
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, pcmtst);
+
+ return 0;
+}
+
+static void pdev_remove(struct platform_device *pdev)
+{
+ struct pcmtst *pcmtst = platform_get_drvdata(pdev);
+
+ snd_pcmtst_free(pcmtst);
+}
+
+static struct platform_device pcmtst_pdev = {
+ .name = "pcmtest",
+ .dev.release = pcmtst_pdev_release,
+};
+
+static struct platform_driver pcmtst_pdrv = {
+ .probe = pcmtst_probe,
+ .remove = pdev_remove,
+ .driver = {
+ .name = "pcmtest",
+ },
+};
+
+static ssize_t pattern_write(struct file *file, const char __user *u_buff, size_t len, loff_t *off)
+{
+ struct pattern_buf *patt_buf = file->f_inode->i_private;
+ ssize_t to_write = len;
+
+ if (*off + to_write > MAX_PATTERN_LEN)
+ to_write = MAX_PATTERN_LEN - *off;
+
+ // Crop silently everything over the buffer
+ if (to_write <= 0)
+ return len;
+
+ if (copy_from_user(patt_buf->buf + *off, u_buff, to_write))
+ return -EFAULT;
+
+ patt_buf->len = *off + to_write;
+ *off += to_write;
+
+ return to_write;
+}
+
+static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len, loff_t *off)
+{
+ struct pattern_buf *patt_buf = file->f_inode->i_private;
+ ssize_t to_read = len;
+
+ if (*off + to_read >= MAX_PATTERN_LEN)
+ to_read = MAX_PATTERN_LEN - *off;
+ if (to_read <= 0)
+ return 0;
+
+ if (copy_to_user(u_buff, patt_buf->buf + *off, to_read))
+ to_read = 0;
+ else
+ *off += to_read;
+
+ return to_read;
+}
+
+static const struct file_operations fill_pattern_fops = {
+ .read = pattern_read,
+ .write = pattern_write,
+};
+
+static int setup_patt_bufs(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(patt_bufs); i++) {
+ patt_bufs[i].buf = kzalloc(MAX_PATTERN_LEN, GFP_KERNEL);
+ if (!patt_bufs[i].buf)
+ break;
+ strcpy(patt_bufs[i].buf, DEFAULT_PATTERN);
+ patt_bufs[i].len = DEFAULT_PATTERN_LEN;
+ }
+
+ return i;
+}
+
+static const char * const pattern_files[] = { "fill_pattern0", "fill_pattern1",
+ "fill_pattern2", "fill_pattern3"};
+static int init_debug_files(int buf_count)
+{
+ size_t i;
+ char len_file_name[32];
+
+ driver_debug_dir = debugfs_create_dir("pcmtest", NULL);
+ if (IS_ERR(driver_debug_dir))
+ return PTR_ERR(driver_debug_dir);
+ debugfs_create_u8("pc_test", 0444, driver_debug_dir, &playback_capture_test);
+ debugfs_create_u8("ioctl_test", 0444, driver_debug_dir, &ioctl_reset_test);
+
+ for (i = 0; i < buf_count; i++) {
+ debugfs_create_file(pattern_files[i], 0600, driver_debug_dir,
+ &patt_bufs[i], &fill_pattern_fops);
+ snprintf(len_file_name, sizeof(len_file_name), "%s_len", pattern_files[i]);
+ debugfs_create_u32(len_file_name, 0444, driver_debug_dir, &patt_bufs[i].len);
+ }
+
+ return 0;
+}
+
+static void free_pattern_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < buf_allocated; i++)
+ kfree(patt_bufs[i].buf);
+}
+
+static void clear_debug_files(void)
+{
+ debugfs_remove_recursive(driver_debug_dir);
+}
+
+static int __init mod_init(void)
+{
+ int err = 0;
+
+ buf_allocated = setup_patt_bufs();
+ if (!buf_allocated)
+ return -ENOMEM;
+
+ snd_pcmtst_hw.channels_max = buf_allocated;
+
+ err = init_debug_files(buf_allocated);
+ if (err)
+ return err;
+ err = platform_device_register(&pcmtst_pdev);
+ if (err)
+ return err;
+ err = platform_driver_register(&pcmtst_pdrv);
+ if (err)
+ platform_device_unregister(&pcmtst_pdev);
+ return err;
+}
+
+static void __exit mod_exit(void)
+{
+ clear_debug_files();
+ free_pattern_buffers();
+
+ platform_driver_unregister(&pcmtst_pdrv);
+ platform_device_unregister(&pcmtst_pdev);
+}
+
+MODULE_DESCRIPTION("Virtual ALSA driver for PCM testing/fuzzing");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Orlov");
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile
index b19555b440da..309c09497261 100644
--- a/sound/drivers/pcsp/Makefile
+++ b/sound/drivers/pcsp/Makefile
@@ -1,2 +1,3 @@
-snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-pcsp-y := 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 0dd3f46eb03e..ff6bb375c900 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
*
@@ -21,7 +22,6 @@
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 */
@@ -42,17 +42,17 @@ struct snd_pcsp pcsp_chip;
static int snd_pcsp_create(struct snd_card *card)
{
- static struct snd_device_ops ops = { };
unsigned int resolution = hrtimer_resolution;
- int err, div, min_div, order;
+ int div, min_div, order;
if (!nopcm) {
if (resolution > PCSP_MAX_PERIOD_NS) {
- printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%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");
+ dev_err(card->dev,
+ "PCSP: Timer resolution is not sufficient (%unS)\n",
+ resolution);
+ dev_err(card->dev,
+ "PCSP: Make sure you have HPET and ACPI enabled.\n");
+ dev_err(card->dev, "PCSP: Turned into nopcm mode.\n");
nopcm = 1;
}
}
@@ -62,8 +62,8 @@ static int snd_pcsp_create(struct snd_card *card)
else
min_div = MAX_DIV;
#if PCSP_DEBUG
- printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n",
- loops_per_jiffy, min_div, resolution);
+ dev_dbg(card->dev, "PCSP: lpj=%li, min_div=%i, res=%u\n",
+ loops_per_jiffy, min_div, resolution);
#endif
div = MAX_DIV / min_div;
@@ -83,15 +83,18 @@ static int 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 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;
@@ -100,40 +103,36 @@ static int snd_card_pcsp_probe(int devnum, struct device *dev)
if (devnum != 0)
return -EINVAL;
- hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- pcsp_chip.timer.function = pcsp_do_timer;
+ hrtimer_setup(&pcsp_chip.timer, pcsp_do_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- err = snd_card_new(dev, 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)
- goto free_card;
+ return err;
if (!nopcm) {
err = snd_pcsp_new_pcm(&pcsp_chip);
if (err < 0)
- goto free_card;
+ return err;
}
err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
if (err < 0)
- goto free_card;
+ return err;
- strcpy(card->driver, "PC-Speaker");
- strcpy(card->shortname, "pcsp");
+ strscpy(card->driver, "PC-Speaker");
+ strscpy(card->shortname, "pcsp");
sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
pcsp_chip.port);
err = snd_card_register(card);
if (err < 0)
- goto free_card;
+ return err;
+ card->private_free = alsa_card_pcsp_free;
return 0;
-
-free_card:
- snd_card_free(card);
- return err;
}
static int alsa_card_pcsp_init(struct device *dev)
@@ -142,24 +141,19 @@ static int alsa_card_pcsp_init(struct device *dev)
err = snd_card_pcsp_probe(0, dev);
if (err) {
- printk(KERN_ERR "PC-Speaker initialization failed.\n");
+ dev_err(dev, "PC-Speaker initialization failed.\n");
return err;
}
/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
if (debug_pagealloc_enabled()) {
- printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
- "which may make the sound noisy.\n");
+ dev_warn(dev,
+ "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, which may make the sound noisy.\n");
}
return 0;
}
-static void alsa_card_pcsp_exit(struct snd_pcsp *chip)
-{
- snd_card_free(chip->card);
-}
-
static int pcsp_probe(struct platform_device *dev)
{
int err;
@@ -169,43 +163,27 @@ static int 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 pcsp_remove(struct platform_device *dev)
-{
- struct snd_pcsp *chip = platform_get_drvdata(dev);
- pcspkr_input_remove(chip->input_dev);
- alsa_card_pcsp_exit(chip);
- return 0;
-}
-
static void pcsp_stop_beep(struct snd_pcsp *chip)
{
pcsp_sync_stop(chip);
pcspkr_stop_sound();
}
-#ifdef CONFIG_PM_SLEEP
static int pcsp_suspend(struct device *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_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
static void pcsp_shutdown(struct platform_device *dev)
{
@@ -216,10 +194,9 @@ static void pcsp_shutdown(struct platform_device *dev)
static struct platform_driver pcsp_platform_driver = {
.driver = {
.name = "pcspkr",
- .pm = PCSP_PM_OPS,
+ .pm = &pcsp_pm,
},
.probe = pcsp_probe,
- .remove = pcsp_remove,
.shutdown = pcsp_shutdown,
};
diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c
index bfc25811985f..5a799f7f00a2 100644
--- a/sound/drivers/pcsp/pcsp_input.c
+++ b/sound/drivers/pcsp/pcsp_input.c
@@ -1,16 +1,11 @@
+// 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>
@@ -59,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:
@@ -82,7 +78,7 @@ 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;
@@ -99,19 +95,9 @@ int 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 e80079b38a56..42bfc9eab6eb 100644
--- a/sound/drivers/pcsp/pcsp_input.h
+++ b/sound/drivers/pcsp/pcsp_input.h
@@ -9,7 +9,6 @@
#define __PCSP_INPUT_H__
int pcspkr_input_init(struct input_dev **rdev, struct device *dev);
-int pcspkr_input_remove(struct input_dev *dev);
void pcspkr_stop_sound(void);
#endif
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index 8f0f05bbc081..80b313f4fcd3 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -12,6 +12,7 @@
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <sound/core.h>
#include <sound/pcm.h>
#include "pcsp.h"
@@ -23,10 +24,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;
@@ -36,7 +37,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
@@ -105,8 +106,8 @@ static void pcsp_pointer_update(struct snd_pcsp *chip)
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? "
- "(%zi %zi %zi)\n",
+ dev_dbg(chip->card->dev,
+ "PCSP: buffer_bytes mod period_bytes != 0 ? (%zi %zi %zi)\n",
chip->playback_ptr, period_bytes, buffer_bytes);
#endif
periods_elapsed += buffer_bytes;
@@ -119,11 +120,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)
@@ -138,14 +137,14 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
pointer_update = !chip->thalf;
ns = pcsp_timer_update(chip);
if (!ns) {
- printk(KERN_WARNING "PCSP: unexpected stop\n");
+ dev_warn(chip->card->dev, "PCSP: unexpected stop\n");
return HRTIMER_NORESTART;
}
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;
}
@@ -153,10 +152,10 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
static int pcsp_start_playing(struct snd_pcsp *chip)
{
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: start_playing called\n");
+ dev_dbg(chip->card->dev, "PCSP: start_playing called\n");
#endif
if (atomic_read(&chip->timer_active)) {
- printk(KERN_ERR "PCSP: Timer already active\n");
+ dev_err(chip->card->dev, "PCSP: Timer already active\n");
return -EIO;
}
@@ -174,7 +173,7 @@ static int pcsp_start_playing(struct snd_pcsp *chip)
static void pcsp_stop_playing(struct snd_pcsp *chip)
{
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: stop_playing called\n");
+ dev_dbg(chip->card->dev, "PCSP: stop_playing called\n");
#endif
if (!atomic_read(&chip->timer_active))
return;
@@ -196,14 +195,14 @@ 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)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: close called\n");
+ dev_dbg(chip->card->dev, "PCSP: close called\n");
#endif
pcsp_sync_stop(chip);
chip->playback_substream = NULL;
@@ -214,12 +213,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;
}
@@ -227,10 +221,10 @@ static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: hw_free called\n");
+ dev_dbg(chip->card->dev, "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)
@@ -243,14 +237,13 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
snd_pcm_format_physical_width(substream->runtime->format) >> 3;
chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: prepare called, "
- "size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
- snd_pcm_lib_buffer_bytes(substream),
- snd_pcm_lib_period_bytes(substream),
- snd_pcm_lib_buffer_bytes(substream) /
- snd_pcm_lib_period_bytes(substream),
- substream->runtime->periods,
- chip->fmt_size);
+ dev_dbg(chip->card->dev, "PCSP: prepare called, size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ snd_pcm_lib_buffer_bytes(substream) /
+ snd_pcm_lib_period_bytes(substream),
+ substream->runtime->periods,
+ chip->fmt_size);
#endif
return 0;
}
@@ -259,7 +252,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: trigger called\n");
+ dev_dbg(chip->card->dev, "PCSP: trigger called\n");
#endif
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -313,10 +306,10 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: open called\n");
+ dev_dbg(chip->card->dev, "PCSP: open called\n");
#endif
if (atomic_read(&chip->timer_active)) {
- printk(KERN_ERR "PCSP: still active!!\n");
+ dev_err(chip->card->dev, "PCSP: still active!!\n");
return -EBUSY;
}
runtime->hw = snd_pcsp_playback;
@@ -327,7 +320,6 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
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,
@@ -348,13 +340,13 @@ int snd_pcsp_new_pcm(struct snd_pcsp *chip)
chip->pcm->private_data = chip;
chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- strcpy(chip->pcm->name, "pcsp");
+ strscpy(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 be2990451bcd..27d6150329a8 100644
--- a/sound/drivers/pcsp/pcsp_mixer.c
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -73,7 +73,7 @@ static int pcsp_treble_put(struct snd_kcontrol *kcontrol,
if (treble != chip->treble) {
chip->treble = treble;
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: rate set to %li\n", PCSP_RATE());
+ dev_dbg(chip->card->dev, "PCSP: rate set to %li\n", PCSP_RATE());
#endif
changed = 1;
}
@@ -120,17 +120,17 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
.put = pcsp_##ctl_type##_put, \
}
-static struct snd_kcontrol_new 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 snd_pcsp_controls_spkr[] = {
+static const struct snd_kcontrol_new snd_pcsp_controls_spkr[] = {
PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
};
static int snd_pcsp_ctls_add(struct snd_pcsp *chip,
- struct snd_kcontrol_new *ctls, int num)
+ const struct snd_kcontrol_new *ctls, int num)
{
int i, err;
struct snd_card *card = chip->card;
@@ -158,7 +158,7 @@ int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
if (err < 0)
return err;
- strcpy(card->mixername, "PC-Speaker");
+ strscpy(card->mixername, "PC-Speaker");
return 0;
}
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index ec8a94325ef6..b903a138fc2a 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
@@ -60,17 +47,16 @@ 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
@@ -196,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);
@@ -409,9 +385,8 @@ static void portman_flush_input(struct portman *pm, unsigned char port)
command = RXDATA1;
break;
default:
- snd_printk(KERN_WARNING
- "portman_flush_input() Won't flush port %i\n",
- port);
+ dev_warn(pm->card->dev, "%s Won't flush port %i\n",
+ __func__, port);
return;
}
@@ -470,7 +445,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)
@@ -480,7 +455,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. */
@@ -521,29 +496,25 @@ static void snd_portman_midi_input_trigger(struct snd_rawmidi_substream *substre
int up)
{
struct portman *pm = substream->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&pm->reg_lock, flags);
+ guard(spinlock_irqsave)(&pm->reg_lock);
if (up)
pm->mode[substream->number] |= PORTMAN2X4_MODE_INPUT_TRIGGERED;
else
pm->mode[substream->number] &= ~PORTMAN2X4_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&pm->reg_lock, flags);
}
static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct portman *pm = substream->rmidi->private_data;
- unsigned long flags;
unsigned char byte;
- spin_lock_irqsave(&pm->reg_lock, flags);
+ guard(spinlock_irqsave)(&pm->reg_lock);
if (up) {
while ((snd_rawmidi_transmit(substream, &byte, 1) == 1))
portman_write_midi(pm, substream->number, byte);
}
- spin_unlock_irqrestore(&pm->reg_lock, flags);
}
static const struct snd_rawmidi_ops snd_portman_midi_output = {
@@ -574,7 +545,7 @@ static int snd_portman_rawmidi_create(struct snd_card *card)
return err;
rmidi->private_data = pm;
- strcpy(rmidi->name, CARD_NAME);
+ strscpy(rmidi->name, CARD_NAME);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -615,7 +586,7 @@ static void snd_portman_interrupt(void *userdata)
unsigned char midivalue = 0;
struct portman *pm = ((struct snd_card*)userdata)->private_data;
- spin_lock(&pm->reg_lock);
+ guard(spinlock)(&pm->reg_lock);
/* While any input data is waiting */
while ((portman_read_status(pm) & INT_REQ) == INT_REQ) {
@@ -642,8 +613,6 @@ static void snd_portman_interrupt(void *userdata)
}
}
-
- spin_unlock(&pm->reg_lock);
}
static void snd_portman_attach(struct parport *p)
@@ -692,7 +661,6 @@ static struct parport_driver portman_parport_driver = {
.probe = snd_portman_dev_probe,
.match_port = snd_portman_attach,
.detach = snd_portman_detach,
- .devmodel = true,
};
/*********************************************************************
@@ -737,11 +705,11 @@ static int snd_portman_probe(struct platform_device *pdev)
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card);
if (err < 0) {
- snd_printd("Cannot create card\n");
+ dev_dbg(&pdev->dev, "Cannot create card\n");
return err;
}
- strcpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, CARD_NAME);
+ strscpy(card->driver, DRIVER_NAME);
+ strscpy(card->shortname, CARD_NAME);
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
@@ -751,20 +719,21 @@ static int snd_portman_probe(struct platform_device *pdev)
&portman_cb, /* callbacks */
pdev->id); /* device number */
if (pardev == NULL) {
- snd_printd("Cannot register pardevice\n");
+ dev_dbg(card->dev, "Cannot register pardevice\n");
err = -EIO;
goto __err;
}
/* claim parport */
if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ dev_dbg(card->dev, "Cannot claim parport 0x%lx\n", pardev->port->base);
err = -EIO;
goto free_pardev;
}
- if ((err = portman_create(card, pardev, &pm)) < 0) {
- snd_printd("Cannot create main component\n");
+ err = portman_create(card, pardev, &pm);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot create main component\n");
goto release_pardev;
}
card->private_data = pm;
@@ -776,24 +745,27 @@ static int snd_portman_probe(struct platform_device *pdev)
goto __err;
}
- if ((err = snd_portman_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
+ err = snd_portman_rawmidi_create(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "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);
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
- snd_printd("Cannot register card\n");
+ err = snd_card_register(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot register card\n");
goto __err;
}
- snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
+ dev_info(card->dev, "Portman 2x4 on 0x%lx\n", p->base);
return 0;
release_pardev:
@@ -805,14 +777,12 @@ __err:
return err;
}
-static int 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;
}
@@ -845,7 +815,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..766206c6ca75
--- /dev/null
+++ b/sound/drivers/serial-generic.c
@@ -0,0 +1,376 @@
+// 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.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
+
+#define INTERNAL_BUF_SIZE 256
+
+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;
+
+ char tx_buf[INTERNAL_BUF_SIZE];
+};
+
+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);
+}
+
+static void snd_serial_generic_tx_work(struct work_struct *work)
+{
+ 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, drvdata->tx_buf,
+ INTERNAL_BUF_SIZE);
+ num_bytes = serdev_device_write_buf(drvdata->serdev, drvdata->tx_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 size_t snd_serial_generic_receive_buf(struct serdev_device *serdev,
+ const u8 *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);
+ strscpy(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;
+
+ strscpy(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 = 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 0a67b8b9f176..3c28961091b1 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
@@ -47,7 +34,6 @@
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 */
@@ -55,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",
@@ -129,7 +115,6 @@ struct snd_uart16550 {
int irq;
unsigned long base;
- struct resource *res_base;
unsigned int speed;
unsigned int speed_base;
@@ -181,7 +166,7 @@ static inline void snd_uart16550_add_timer(struct snd_uart16550 *uart)
static inline void snd_uart16550_del_timer(struct snd_uart16550 *uart)
{
if (uart->timer_running) {
- del_timer(&uart->buffer_timer);
+ timer_delete(&uart->buffer_timer);
uart->timer_running = 0;
}
}
@@ -239,9 +224,9 @@ static void snd_uart16550_io_loop(struct snd_uart16550 * uart)
snd_rawmidi_receive(uart->midi_input[substream], &c, 1);
if (status & UART_LSR_OE)
- snd_printk(KERN_WARNING
- "%s: Overrun on device at 0x%lx\n",
- uart->rmidi->name, uart->base);
+ dev_warn(uart->card->dev,
+ "%s: Overrun on device at 0x%lx\n",
+ uart->rmidi->name, uart->base);
}
/* remember the last stream */
@@ -296,29 +281,24 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id)
struct snd_uart16550 *uart;
uart = dev_id;
- spin_lock(&uart->open_lock);
- if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
- spin_unlock(&uart->open_lock);
+ guard(spinlock)(&uart->open_lock);
+ if (uart->filemode == SERIAL_MODE_NOT_OPENED)
return IRQ_NONE;
- }
/* indicate to the UART that the interrupt has been serviced */
inb(uart->base + UART_IIR);
snd_uart16550_io_loop(uart);
- spin_unlock(&uart->open_lock);
return IRQ_HANDLED;
}
/* When the polling mode, this function calls snd_uart16550_io_loop. */
static void snd_uart16550_buffer_timer(struct timer_list *t)
{
- unsigned long flags;
struct snd_uart16550 *uart;
- uart = from_timer(uart, t, buffer_timer);
- spin_lock_irqsave(&uart->open_lock, flags);
+ uart = timer_container_of(uart, t, buffer_timer);
+ guard(spinlock_irqsave)(&uart->open_lock);
snd_uart16550_del_timer(uart);
snd_uart16550_io_loop(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
/*
@@ -337,9 +317,9 @@ static int 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) {
- snd_printk(KERN_ERR "u16550: can't grab port 0x%lx\n", io_base);
+ if (!devm_request_region(uart->card->dev, io_base, 8, "Serial MIDI")) {
+ dev_err(uart->card->dev,
+ "u16550: can't grab port 0x%lx\n", io_base);
return -EBUSY;
}
@@ -514,71 +494,61 @@ static void snd_uart16550_do_close(struct snd_uart16550 * uart)
static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_open(uart);
uart->filemode |= SERIAL_MODE_INPUT_OPEN;
uart->midi_input[substream->number] = substream;
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
}
static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
uart->midi_input[substream->number] = NULL;
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_close(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
}
static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream,
int up)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (up)
uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
else
uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_open(uart);
uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
uart->midi_output[substream->number] = substream;
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
};
static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
uart->midi_output[substream->number] = NULL;
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_close(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
};
@@ -635,9 +605,9 @@ static int snd_uart16550_output_byte(struct snd_uart16550 *uart,
}
} else {
if (!snd_uart16550_write_buffer(uart, midi_byte)) {
- snd_printk(KERN_WARNING
- "%s: Buffer overrun on device at 0x%lx\n",
- uart->rmidi->name, uart->base);
+ dev_warn(uart->card->dev,
+ "%s: Buffer overrun on device at 0x%lx\n",
+ uart->rmidi->name, uart->base);
return 0;
}
}
@@ -647,7 +617,6 @@ static int snd_uart16550_output_byte(struct snd_uart16550 *uart,
static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
unsigned char midi_byte, addr_byte;
struct snd_uart16550 *uart = substream->rmidi->private_data;
char first;
@@ -658,7 +627,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
* variables (ie buff_in & buff_out)
*/
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->irq < 0) /* polling */
snd_uart16550_io_loop(uart);
@@ -733,21 +702,19 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
}
lasttime = jiffies;
}
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream,
int up)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
- if (up)
- uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
- else
- uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
- spin_unlock_irqrestore(&uart->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &uart->open_lock) {
+ if (up)
+ uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
+ else
+ uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
+ }
if (up)
snd_uart16550_output_write(substream);
}
@@ -766,21 +733,6 @@ static const struct snd_rawmidi_ops snd_uart16550_input =
.trigger = snd_uart16550_input_trigger,
};
-static int snd_uart16550_free(struct snd_uart16550 *uart)
-{
- 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 snd_uart16550_create(struct snd_card *card,
unsigned long iobase,
int irq,
@@ -790,14 +742,12 @@ static int snd_uart16550_create(struct snd_card *card,
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;
@@ -806,17 +756,17 @@ static int snd_uart16550_create(struct snd_card *card,
uart->base = iobase;
uart->drop_on_full = droponfull;
- if ((err = snd_uart16550_detect(uart)) <= 0) {
- printk(KERN_ERR "no UART detected at 0x%lx\n", iobase);
- snd_uart16550_free(uart);
+ err = snd_uart16550_detect(uart);
+ if (err <= 0) {
+ dev_err(card->dev, "no UART detected at 0x%lx\n", iobase);
return -ENODEV;
}
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
- if (request_irq(irq, snd_uart16550_interrupt,
- 0, "Serial MIDI", uart)) {
- snd_printk(KERN_WARNING
- "irq %d busy. Using Polling.\n", irq);
+ if (devm_request_irq(card->dev, irq, snd_uart16550_interrupt,
+ 0, "Serial MIDI", uart)) {
+ dev_warn(card->dev,
+ "irq %d busy. Using Polling.\n", irq);
} else {
uart->irq = irq;
}
@@ -831,12 +781,6 @@ static int snd_uart16550_create(struct snd_card *card,
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:
@@ -883,7 +827,7 @@ static int snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
&snd_uart16550_input);
snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&snd_uart16550_output);
- strcpy(rrawmidi->name, "Serial MIDI");
+ strscpy(rrawmidi->name, "Serial MIDI");
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -918,47 +862,43 @@ static int snd_serial_probe(struct platform_device *devptr)
case SNDRV_SERIAL_GENERIC:
break;
default:
- snd_printk(KERN_ERR
- "Adaptor type is out of range 0-%d (%d)\n",
- SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]);
+ dev_err(&devptr->dev,
+ "Adaptor type is out of range 0-%d (%d)\n",
+ SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]);
return -ENODEV;
}
if (outs[dev] < 1 || outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
- snd_printk(KERN_ERR
- "Count of outputs is out of range 1-%d (%d)\n",
- SNDRV_SERIAL_MAX_OUTS, outs[dev]);
+ dev_err(&devptr->dev,
+ "Count of outputs is out of range 1-%d (%d)\n",
+ SNDRV_SERIAL_MAX_OUTS, outs[dev]);
return -ENODEV;
}
if (ins[dev] < 1 || ins[dev] > SNDRV_SERIAL_MAX_INS) {
- snd_printk(KERN_ERR
- "Count of inputs is out of range 1-%d (%d)\n",
- SNDRV_SERIAL_MAX_INS, ins[dev]);
+ dev_err(&devptr->dev,
+ "Count of inputs is out of range 1-%d (%d)\n",
+ SNDRV_SERIAL_MAX_INS, ins[dev]);
return -ENODEV;
}
- err = snd_card_new(&devptr->dev, 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)");
+ strscpy(card->driver, "Serial");
+ strscpy(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,
@@ -966,28 +906,18 @@ static int snd_serial_probe(struct platform_device *devptr)
uart->base,
uart->irq);
- 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 snd_serial_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- return 0;
}
#define SND_SERIAL_DRIVER "snd_serial_u16550"
static struct platform_driver snd_serial_driver = {
.probe = snd_serial_probe,
- .remove = snd_serial_remove,
.driver = {
.name = SND_SERIAL_DRIVER,
},
@@ -1006,7 +936,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;
@@ -1027,7 +958,7 @@ static int __init alsa_card_serial_init(void)
}
if (! cards) {
#ifdef MODULE
- printk(KERN_ERR "serial midi soundcard not found or device busy\n");
+ pr_err("serial midi soundcard not found or device busy\n");
#endif
snd_serial_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 33ef13a72e69..a204f42d1026 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
- *
*/
/*
@@ -57,7 +43,6 @@
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
@@ -90,49 +75,38 @@ static int snd_virmidi_probe(struct platform_device *devptr)
int idx, err;
int dev = devptr->id;
- err = snd_card_new(&devptr->dev, 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;
vmidi->card = card;
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);
+ dev_warn(&devptr->dev,
+ "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;
err = snd_virmidi_new(card, idx, &rmidi);
if (err < 0)
- goto __nodev;
- rdev = rmidi->private_data;
+ return err;
vmidi->midi[idx] = rmidi;
- strcpy(rmidi->name, "Virtual Raw MIDI");
- rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+ strscpy(rmidi->name, "Virtual Raw MIDI");
}
- strcpy(card->driver, "VirMIDI");
- strcpy(card->shortname, "VirMIDI");
+ strscpy(card->driver, "VirMIDI");
+ strscpy(card->shortname, "VirMIDI");
sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
err = snd_card_register(card);
- if (!err) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
-__nodev:
- snd_card_free(card);
- return err;
-}
+ if (err)
+ return err;
-static int snd_virmidi_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(devptr, card);
return 0;
}
@@ -140,7 +114,6 @@ static int snd_virmidi_remove(struct platform_device *devptr)
static struct platform_driver snd_virmidi_driver = {
.probe = snd_virmidi_probe,
- .remove = snd_virmidi_remove,
.driver = {
.name = SND_VIRMIDI_DRIVER,
},
@@ -182,7 +155,7 @@ static int __init alsa_card_virmidi_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n");
+ pr_err("Card-VirMIDI soundcard not found or device busy\n");
#endif
snd_virmidi_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile
index 9a168a3c1560..ae1b3e09283f 100644
--- a/sound/drivers/vx/Makefile
+++ b/sound/drivers/vx/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
+snd-vx-lib-y := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o
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 121357397a6d..52b93407bfe3 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>
@@ -52,7 +39,7 @@ 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;
- 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",
@@ -65,7 +52,9 @@ int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int t
return 0;
//msleep(10);
} while (time_after_eq(end_time, jiffies));
- snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg));
+ dev_dbg(chip->card->dev,
+ "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n",
+ reg_names[reg], mask, snd_vx_inb(chip, reg));
return -EIO;
}
@@ -123,27 +112,33 @@ 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) {
- snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n");
+ err = vx_inb(chip, ISR);
+ if (err & ISR_ERR) {
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "transfer_end: error in rx_full\n");
return err;
}
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
err |= vx_inb(chip, RXL);
- snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err);
+ dev_dbg(chip->card->dev, "transfer_end: error = 0x%x\n", err);
return -(VX_ERR_MASK | err);
}
return 0;
@@ -245,21 +240,12 @@ 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) {
- snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n");
+ err = vx_reset_chk(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev, "vx_send_msg: vx_reset_chk error\n");
return err;
}
-#if 0
- printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n",
- rmh->Cmd[0], rmh->LgCmd, rmh->DspStat);
- if (rmh->LgCmd > 1) {
- printk(KERN_DEBUG " ");
- for (i = 1; i < rmh->LgCmd; i++)
- printk(KERN_CONT "0x%06x ", rmh->Cmd[i]);
- printk(KERN_CONT "\n");
- }
-#endif
/* Check bit M is set according to length of the command */
if (rmh->LgCmd > 1)
rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND;
@@ -267,8 +253,9 @@ 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) {
- snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev, "vx_send_msg: wait tx empty error\n");
return err;
}
@@ -278,25 +265,31 @@ 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) {
- snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n");
+ err = vx_send_irq_dsp(chip, IRQ_MESSAGE);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "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) {
- snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n");
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: rx_full read error\n");
return err;
}
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
err |= vx_inb(chip, RXL);
- snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err);
+ dev_dbg(chip->card->dev,
+ "msg got error = 0x%x at cmd[0]\n", err);
err = -(VX_ERR_MASK | err);
return err;
}
@@ -305,8 +298,10 @@ 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) {
- snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: tx_ready error\n");
return err;
}
@@ -316,14 +311,18 @@ 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) {
- snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n");
+ err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "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) {
- snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: TX_READY error\n");
return err;
}
/* End of transfer */
@@ -345,12 +344,8 @@ 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)
{
- int err;
-
- mutex_lock(&chip->lock);
- err = vx_send_msg_nolock(chip, rmh);
- mutex_unlock(&chip->lock);
- return err;
+ guard(mutex)(&chip->lock);
+ return vx_send_msg_nolock(chip, rmh);
}
@@ -372,20 +367,21 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
if (chip->chip_status & VX_STAT_IS_STALE)
return -EBUSY;
-#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;
@@ -404,18 +400,14 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
*/
int vx_send_rih(struct vx_core *chip, int cmd)
{
- int err;
-
- mutex_lock(&chip->lock);
- err = vx_send_rih_nolock(chip, cmd);
- mutex_unlock(&chip->lock);
- return err;
+ guard(mutex)(&chip->lock);
+ return vx_send_rih_nolock(chip, cmd);
}
#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
*/
@@ -449,7 +441,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
if (no_fillup)
break;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
- snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+ dev_err(chip->card->dev, "dsp boot failed at %d\n", i);
return -EIO;
}
vx_outb(chip, TXH, 0);
@@ -458,7 +450,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
} else {
const unsigned char *image = boot->data + i;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
- snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+ dev_err(chip->card->dev, "dsp boot failed at %d\n", i);
return -EIO;
}
vx_outb(chip, TXH, image[0]);
@@ -481,13 +473,12 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret)
int err;
vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT);
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
err = vx_send_msg_nolock(chip, &chip->irq_rmh);
if (err < 0)
*ret = 0;
else
*ret = chip->irq_rmh.Stat[0];
- mutex_unlock(&chip->lock);
return err;
}
@@ -506,26 +497,21 @@ irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev)
if (vx_test_irq_src(chip, &events) < 0)
return IRQ_HANDLED;
-#if 0
- if (events & 0x000800)
- printk(KERN_ERR "DSP Stream underrun ! IRQ events = 0x%x\n", events);
-#endif
- // printk(KERN_DEBUG "IRQ events = 0x%x\n", events);
-
/* We must prevent any application using this DSP
* and block any further request until the application
* either unregisters or reloads the DSP
*/
if (events & FATAL_DSP_ERROR) {
- snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n");
+ dev_err(chip->card->dev, "vx_core: fatal DSP error!!\n");
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)
@@ -601,17 +587,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");
@@ -643,10 +629,7 @@ 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);
}
@@ -663,7 +646,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);
@@ -693,9 +677,10 @@ 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) {
- printk(KERN_ERR
- "dsp loading error at position %d\n", i);
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "dsp loading error at position %d\n", i);
return err;
}
cptr = image;
@@ -709,11 +694,11 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
csum = (csum >> 24) | (csum << 8);
vx_outb(chip, TXL, *cptr++);
}
- snd_printdd(KERN_DEBUG "checksum = 0x%08x\n", csum);
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);
@@ -732,12 +717,8 @@ EXPORT_SYMBOL(snd_vx_dsp_load);
*/
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;
}
@@ -758,7 +739,8 @@ int snd_vx_resume(struct vx_core *chip)
continue;
err = chip->ops->load_dsp(chip, i, chip->firmware[i]);
if (err < 0) {
- snd_printk(KERN_ERR "vx: firmware resume error at DSP %d\n", i);
+ dev_err(chip->card->dev,
+ "vx: firmware resume error at DSP %d\n", i);
return -EIO;
}
}
@@ -773,6 +755,11 @@ 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
@@ -783,10 +770,13 @@ EXPORT_SYMBOL(snd_vx_resume);
* 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;
@@ -794,8 +784,9 @@ 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)
+ chip = devres_alloc(snd_vx_release, sizeof(*chip) + extra_size,
+ GFP_KERNEL);
+ if (!chip)
return NULL;
mutex_init(&chip->lock);
chip->irq = -1;
@@ -806,7 +797,7 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
chip->card = card;
card->private_data = chip;
- strcpy(card->driver, hw->name);
+ strscpy(card->driver, hw->name);
sprintf(card->shortname, "Digigram %s", hw->name);
vx_proc_init(chip);
@@ -815,18 +806,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 3014b86362bd..a7f8ddf4df5a 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -1,23 +1,10 @@
+// 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>
@@ -45,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",
},
@@ -71,8 +58,8 @@ int snd_vx_setup_firmware(struct vx_core *chip)
if (! fw_files[chip->type][i])
continue;
sprintf(path, "vx/%s", fw_files[chip->type][i]);
- if (request_firmware(&fw, path, chip->dev)) {
- snd_printk(KERN_ERR "vx: can't load firmware %s\n", path);
+ if (request_firmware(&fw, path, chip->card->dev)) {
+ dev_err(chip->card->dev, "vx: can't load firmware %s\n", path);
return -ENOENT;
}
err = chip->ops->load_dsp(chip, i, fw);
@@ -91,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;
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index 98a41ac40b60..9dc5cecaa86a 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>
@@ -38,9 +25,8 @@ static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int dat
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
chip->ops->write_codec(chip, codec, data);
- mutex_unlock(&chip->lock);
}
/*
@@ -179,9 +165,8 @@ static void vx_change_audio_source(struct vx_core *chip, int src)
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
chip->ops->change_audio_source(chip, src);
- mutex_unlock(&chip->lock);
}
@@ -424,10 +409,10 @@ static int vx_output_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
{
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int codec = kcontrol->id.index;
- mutex_lock(&chip->mixer_mutex);
+
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->output_level[codec][0];
ucontrol->value.integer.value[1] = chip->output_level[codec][1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -442,16 +427,14 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
val[1] = ucontrol->value.integer.value[1];
if (val[0] > vmax || val[1] > vmax)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->output_level[codec][0] ||
val[1] != chip->output_level[codec][1]) {
vx_set_analog_output_level(chip, codec, val[0], val[1]);
chip->output_level[codec][0] = val[0];
chip->output_level[codec][1] = val[1];
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -503,14 +486,12 @@ static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if (ucontrol->value.enumerated.item[0] > 1)
return -EINVAL;
}
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
chip->audio_source_target = ucontrol->value.enumerated.item[0];
vx_sync_audio_source(chip);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -547,14 +528,12 @@ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
if (ucontrol->value.enumerated.item[0] > 2)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->clock_mode != ucontrol->value.enumerated.item[0]) {
chip->clock_mode = ucontrol->value.enumerated.item[0];
vx_set_clock(chip, chip->freq);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -584,10 +563,9 @@ static int vx_audio_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
int audio = kcontrol->private_value & 0xff;
int capture = (kcontrol->private_value >> 8) & 1;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio];
ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -602,15 +580,13 @@ static int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
val[1] = ucontrol->value.integer.value[1];
if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->audio_gain[capture][audio] ||
val[1] != chip->audio_gain[capture][audio+1]) {
vx_set_audio_gain(chip, audio, capture, val[0]);
vx_set_audio_gain(chip, audio+1, capture, val[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -619,10 +595,9 @@ static int vx_audio_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_monitor[audio];
ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -637,17 +612,15 @@ static int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->audio_monitor[audio] ||
val[1] != chip->audio_monitor[audio+1]) {
vx_set_monitor_level(chip, audio, val[0],
chip->audio_monitor_active[audio]);
vx_set_monitor_level(chip, audio+1, val[1],
chip->audio_monitor_active[audio+1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -658,10 +631,9 @@ static int vx_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_active[audio];
ucontrol->value.integer.value[1] = chip->audio_active[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -670,17 +642,15 @@ static int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
vx_set_audio_switch(chip, audio,
!!ucontrol->value.integer.value[0]);
vx_set_audio_switch(chip, audio+1,
!!ucontrol->value.integer.value[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -689,10 +659,9 @@ static int vx_monitor_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio];
ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -701,17 +670,15 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
!!ucontrol->value.integer.value[0]);
vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
!!ucontrol->value.integer.value[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -767,12 +734,11 @@ static int vx_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff;
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -794,14 +760,12 @@ static int vx_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->uer_bits != val) {
chip->uer_bits = val;
vx_set_iec958_status(chip, val);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -916,14 +880,15 @@ int snd_vx_mixer_new(struct vx_core *chip)
struct snd_card *card = chip->card;
char name[32];
- strcpy(card->mixername, card->driver);
+ strscpy(card->mixername, card->driver);
/* output level controls */
for (i = 0; i < chip->hw->num_outs; i++) {
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;
}
@@ -934,22 +899,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++) {
@@ -957,31 +926,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]);
@@ -989,14 +964,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 380a028469c4..7fd8f413d6cf 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;
}
@@ -206,8 +190,10 @@ static int vx_set_ibl(struct vx_core *chip, struct vx_ibl_info *info)
info->max_size = rmh.Stat[1];
info->min_size = rmh.Stat[2];
info->granularity = rmh.Stat[3];
- snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n",
- info->size, info->max_size, info->min_size, info->granularity);
+ dev_dbg(chip->card->dev,
+ "%s: size = %d, max = %d, min = %d, gran = %d\n",
+ __func__, info->size, info->max_size, info->min_size,
+ info->granularity);
return 0;
}
@@ -357,10 +343,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
@@ -545,7 +533,6 @@ 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++;
@@ -631,24 +618,23 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip,
if (space < 0) {
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
- snd_printd("error hbuffer\n");
+ dev_dbg(chip->card->dev, "error hbuffer\n");
return space;
}
if (space < size) {
vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
- snd_printd("no enough hbuffer space %d\n", space);
+ dev_dbg(chip->card->dev, "no enough hbuffer space %d\n", space);
return -EIO; /* XRUN */
}
/* we don't need irqsave here, because this function
* is called from either trigger callback or irq handler
*/
- mutex_lock(&chip->lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->lock);
return err;
}
@@ -697,8 +683,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;
}
}
@@ -715,7 +702,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;
@@ -764,11 +752,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:
@@ -788,24 +778,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)
@@ -824,28 +796,33 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
/* IEC958 status (raw-mode) was changed */
/* we reopen the pipe */
struct vx_rmh rmh;
- snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode);
+ dev_dbg(chip->card->dev,
+ "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;
}
if (chip->pcm_running && chip->freq != runtime->rate) {
- snd_printk(KERN_ERR "vx: cannot set different clock %d "
- "from the current %d\n", runtime->rate, chip->freq);
+ dev_err(chip->card->dev,
+ "vx: cannot set different clock %d from the current %d\n",
+ runtime->rate, chip->freq);
return -EINVAL;
}
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)) {
@@ -876,14 +853,9 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
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,
};
@@ -1098,14 +1070,9 @@ static snd_pcm_uframes_t vx_pcm_capture_pointer(struct snd_pcm_substream *subs)
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,
};
@@ -1127,7 +1094,7 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
chip->irq_rmh.Cmd[0] |= 0x00000002; /* SEL_END_OF_BUF_EVENTS */
if (vx_send_msg(chip, &chip->irq_rmh) < 0) {
- snd_printdd(KERN_ERR "msg send error!!\n");
+ dev_dbg(chip->card->dev, "msg send error!!\n");
return;
}
@@ -1177,7 +1144,8 @@ static int vx_init_audio_io(struct vx_core *chip)
vx_init_rmh(&rmh, CMD_SUPPORTED);
if (vx_send_msg(chip, &rmh) < 0) {
- snd_printk(KERN_ERR "vx: cannot get the supported audio data\n");
+ dev_err(chip->card->dev,
+ "vx: cannot get the supported audio data\n");
return -ENXIO;
}
@@ -1199,8 +1167,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
@@ -1233,7 +1200,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++) {
@@ -1250,12 +1218,14 @@ 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);
+ strscpy(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 ef0b40c0a594..1d90db3b0abd 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>
@@ -62,7 +49,7 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
{
int val;
- mutex_lock(&chip->lock);
+ guard(mutex)(&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,6 @@ 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;
}
- mutex_unlock(&chip->lock);
return val;
}
@@ -84,7 +70,7 @@ 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)
{
val = !!val; /* 0 or 1 */
- mutex_lock(&chip->lock);
+ guard(mutex)(&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));
@@ -92,7 +78,6 @@ 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));
}
- mutex_unlock(&chip->lock);
}
/*
@@ -191,10 +176,10 @@ static void vx_change_clock_source(struct vx_core *chip, int source)
{
/* we mute DAC to prevent clicks */
vx_toggle_dac_mute(chip, 1);
- mutex_lock(&chip->lock);
- chip->ops->set_clock_source(chip, source);
- chip->clock_source = source;
- mutex_unlock(&chip->lock);
+ scoped_guard(mutex, &chip->lock) {
+ chip->ops->set_clock_source(chip, source);
+ chip->clock_source = source;
+ }
/* unmute */
vx_toggle_dac_mute(chip, 0);
}
@@ -209,8 +194,9 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
/* 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);
- mutex_lock(&chip->lock);
+ dev_dbg(chip->card->dev,
+ "set internal clock to 0x%x from freq %d\n", clock, freq);
+ guard(mutex)(&chip->lock);
if (vx_is_pcmcia(chip)) {
vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
vx_outb(chip, LOFREQ, clock & 0xff);
@@ -218,7 +204,6 @@ 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);
}
- mutex_unlock(&chip->lock);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 529d9f405fa9..5973c25c2add 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_FIREWIRE
bool "FireWire sound devices"
depends on FIREWIRE
@@ -17,8 +18,25 @@ config SND_DICE
select SND_HWDEP
select SND_FIREWIRE_LIB
help
- Say Y here to include support for many DACs based on the DICE
- chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces.
+ 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.
@@ -37,10 +55,11 @@ config SND_OXFW
* Mackie(Loud) Onyx 1640i (former model)
* Mackie(Loud) Onyx Satellite
* Mackie(Loud) Tapco Link.Firewire
- * Mackie(Loud) d.2 pro/d.4 pro
+ * 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.
@@ -75,14 +94,14 @@ config SND_BEBOB
tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
select SND_FIREWIRE_LIB
select SND_HWDEP
- help
+ 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 (FireWire Option)
+ * Mackie d.2 (optional FireWire card with DM1000 ASIC)
* Stanton FinalScratch 2 (ScratchAmp)
* Tascam IF-FW/DM
* Behringer XENIX UFX 1204/1604
@@ -108,9 +127,10 @@ config SND_BEBOB
* 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.
+ 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"
@@ -146,8 +166,23 @@ config SND_FIREWIRE_MOTU
select SND_HWDEP
help
Say Y here to enable support for FireWire devices which MOTU produced:
+ * 828
+ * 896
* 828mk2
- * 828mk3
+ * 896hd
+ * Traveler
+ * Ultralite
+ * 8pre
+ * 828mk3 (FireWire only)
+ * 828mk3 (Hybrid)
+ * 896mk3 (FireWire only)
+ * 896mk3 (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.
@@ -159,5 +194,9 @@ config SND_FIREFACE
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
index 44a7b510b75b..45018a5c224f 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,9 +2,9 @@
# 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 \
+snd-firewire-lib-y := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
-snd-isight-objs := isight.o
+snd-isight-y := isight.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_DICE) += dice/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
index 4210e5c6262e..3660c312bf33 100644
--- a/sound/firewire/amdtp-am824.c
+++ b/sound/firewire/amdtp-am824.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/slab.h>
@@ -37,8 +36,6 @@ struct amdtp_am824 {
u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
u8 midi_position;
-
- unsigned int frame_multiplier;
};
/**
@@ -60,8 +57,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
{
struct amdtp_am824 *p = s->protocol;
unsigned int midi_channels;
- unsigned int i;
- int err;
+ unsigned int pcm_frame_multiplier;
+ int i, err;
if (amdtp_stream_running(s))
return -EINVAL;
@@ -78,25 +75,26 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
return -EINVAL;
- err = amdtp_stream_set_parameters(s, rate,
- pcm_channels + midi_channels);
- if (err < 0)
- return err;
-
- s->fdf = AMDTP_FDF_AM824 | s->sfc;
-
- p->pcm_channels = pcm_channels;
- p->midi_ports = midi_ports;
-
/*
* 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)
- p->frame_multiplier = 2;
+ pcm_frame_multiplier = 2;
else
- p->frame_multiplier = 1;
+ 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++)
@@ -147,19 +145,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
}
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)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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) {
@@ -173,19 +176,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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) {
@@ -285,7 +293,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
- unsigned int frames)
+ unsigned int frames, unsigned int data_block_counter)
{
struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
@@ -294,7 +302,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
for (f = 0; f < frames; f++) {
b = (u8 *)&buffer[p->midi_position];
- port = (s->data_block_counter + f) % 8;
+ port = (data_block_counter + f) % 8;
if (f < MAX_MIDI_RX_BLOCKS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL &&
@@ -312,16 +320,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
}
}
-static void read_midi_messages(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+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;
- unsigned int f, port;
int len;
u8 *b;
+ int f;
for (f = 0; f < frames; f++) {
- port = (s->data_block_counter + f) % 8;
+ 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;
@@ -332,45 +344,56 @@ static void read_midi_messages(struct amdtp_stream *s,
}
}
-static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
- unsigned int data_blocks, unsigned int *syt)
+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;
- struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
- unsigned int pcm_frames;
-
- if (pcm) {
- write_pcm_s32(s, pcm, buffer, data_blocks);
- pcm_frames = data_blocks * p->frame_multiplier;
- } else {
- write_pcm_silence(s, buffer, data_blocks);
- pcm_frames = 0;
- }
+ unsigned int pcm_frames = 0;
+ int i;
- if (p->midi_ports)
- write_midi_messages(s, buffer, data_blocks);
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
- return pcm_frames;
+ 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 unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
- unsigned int data_blocks, unsigned int *syt)
+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;
- struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
- unsigned int pcm_frames;
-
- if (pcm) {
- read_pcm_s32(s, pcm, buffer, data_blocks);
- pcm_frames = data_blocks * p->frame_multiplier;
- } else {
- pcm_frames = 0;
- }
+ 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 (p->midi_ports)
- read_midi_messages(s, buffer, 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);
+ }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
/**
@@ -379,20 +402,19 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe
* @s: the AMDTP stream to initialize
* @unit: the target of the stream
* @dir: the direction of stream
- * @flags: the packet transmission method to use
+ * @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, enum cip_flags flags)
+ enum amdtp_stream_direction dir, unsigned int flags)
{
- amdtp_stream_process_data_blocks_t process_data_blocks;
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
- process_data_blocks = process_tx_data_blocks;
+ process_ctx_payloads = process_ir_ctx_payloads;
else
- process_data_blocks = process_rx_data_blocks;
+ process_ctx_payloads = process_it_ctx_payloads;
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
- process_data_blocks,
- sizeof(struct amdtp_am824));
+ 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
index 06d280783581..2b092b1061ba 100644
--- a/sound/firewire/amdtp-am824.h
+++ b/sound/firewire/amdtp-am824.h
@@ -45,5 +45,5 @@ 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, enum cip_flags flags);
+ 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
index ea0d486652c8..208f97cf8de6 100644
--- a/sound/firewire/amdtp-stream-trace.h
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -1,8 +1,8 @@
+/* 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
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#undef TRACE_SYSTEM
@@ -13,147 +13,17 @@
#include <linux/tracepoint.h>
-TRACE_EVENT(in_packet,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_length, unsigned int index),
- TP_ARGS(s, cycles, cip_header, payload_length, index),
- TP_STRUCT__entry(
- __field(unsigned int, second)
- __field(unsigned int, cycle)
- __field(int, channel)
- __field(int, src)
- __field(int, dest)
- __field(u32, cip_header0)
- __field(u32, cip_header1)
- __field(unsigned int, payload_quadlets)
- __field(unsigned int, packet_index)
- __field(unsigned int, irq)
- __field(unsigned int, index)
- ),
- TP_fast_assign(
- __entry->second = cycles / CYCLES_PER_SECOND;
- __entry->cycle = cycles % CYCLES_PER_SECOND;
- __entry->channel = s->context->channel;
- __entry->src = fw_parent_device(s->unit)->node_id;
- __entry->dest = fw_parent_device(s->unit)->card->node_id;
- __entry->cip_header0 = cip_header[0];
- __entry->cip_header1 = cip_header[1];
- __entry->payload_quadlets = payload_length / 4;
- __entry->packet_index = s->packet_index;
- __entry->irq = !!in_interrupt();
- __entry->index = index;
- ),
- TP_printk(
- "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
- __entry->second,
- __entry->cycle,
- __entry->src,
- __entry->dest,
- __entry->channel,
- __entry->cip_header0,
- __entry->cip_header1,
- __entry->payload_quadlets,
- __entry->packet_index,
- __entry->irq,
- __entry->index)
-);
-
-TRACE_EVENT(out_packet,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
- TP_ARGS(s, cycles, cip_header, payload_length, index),
- TP_STRUCT__entry(
- __field(unsigned int, second)
- __field(unsigned int, cycle)
- __field(int, channel)
- __field(int, src)
- __field(int, dest)
- __field(u32, cip_header0)
- __field(u32, cip_header1)
- __field(unsigned int, payload_quadlets)
- __field(unsigned int, packet_index)
- __field(unsigned int, irq)
- __field(unsigned int, index)
- ),
- TP_fast_assign(
- __entry->second = cycles / CYCLES_PER_SECOND;
- __entry->cycle = cycles % CYCLES_PER_SECOND;
- __entry->channel = s->context->channel;
- __entry->src = fw_parent_device(s->unit)->card->node_id;
- __entry->dest = fw_parent_device(s->unit)->node_id;
- __entry->cip_header0 = be32_to_cpu(cip_header[0]);
- __entry->cip_header1 = be32_to_cpu(cip_header[1]);
- __entry->payload_quadlets = payload_length / 4;
- __entry->packet_index = s->packet_index;
- __entry->irq = !!in_interrupt();
- __entry->index = index;
- ),
- TP_printk(
- "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
- __entry->second,
- __entry->cycle,
- __entry->src,
- __entry->dest,
- __entry->channel,
- __entry->cip_header0,
- __entry->cip_header1,
- __entry->payload_quadlets,
- __entry->packet_index,
- __entry->irq,
- __entry->index)
-);
-
-TRACE_EVENT(in_packet_without_header,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index),
- TP_ARGS(s, cycles, payload_quadlets, data_blocks, index),
- TP_STRUCT__entry(
- __field(unsigned int, second)
- __field(unsigned int, cycle)
- __field(int, channel)
- __field(int, src)
- __field(int, dest)
- __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->second = cycles / CYCLES_PER_SECOND;
- __entry->cycle = cycles % CYCLES_PER_SECOND;
- __entry->channel = s->context->channel;
- __entry->src = fw_parent_device(s->unit)->node_id;
- __entry->dest = fw_parent_device(s->unit)->card->node_id;
- __entry->payload_quadlets = payload_quadlets;
- __entry->data_blocks = data_blocks,
- __entry->data_block_counter = s->data_block_counter,
- __entry->packet_index = s->packet_index;
- __entry->irq = !!in_interrupt();
- __entry->index = index;
- ),
- TP_printk(
- "%02u %04u %04x %04x %02d %03u %3u %3u %02u %01u %02u",
- __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)
-);
-
-TRACE_EVENT(out_packet_without_header,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
- TP_ARGS(s, cycles, payload_length, data_blocks, index),
+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)
@@ -162,20 +32,31 @@ TRACE_EVENT(out_packet_without_header,
__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;
- __entry->src = fw_parent_device(s->unit)->card->node_id;
- __entry->dest = fw_parent_device(s->unit)->node_id;
- __entry->payload_quadlets = payload_length / 4;
- __entry->data_blocks = data_blocks,
- __entry->data_blocks = s->data_block_counter,
- __entry->packet_index = s->packet_index;
- __entry->irq = !!in_interrupt();
+ 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(
- "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
+ "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
+ __entry->cycle_time,
__entry->second,
__entry->cycle,
__entry->src,
@@ -186,7 +67,9 @@ TRACE_EVENT(out_packet_without_header,
__entry->data_block_counter,
__entry->packet_index,
__entry->irq,
- __entry->index)
+ __entry->index,
+ __print_array(__get_dynamic_array(cip_header),
+ __get_dynamic_array_len(cip_header), 1))
);
#endif
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 4a1dc145327b..5cdc34877fc1 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -1,14 +1,15 @@
+// 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>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#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>
@@ -19,6 +20,8 @@
#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"
@@ -30,7 +33,8 @@
#define TAG_NO_CIP_HEADER 0
#define TAG_CIP 1
-/* common isochronous packet header parameters */
+// 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
@@ -45,39 +49,53 @@
#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
-/* TODO: make these configurable */
-#define INTERRUPT_INTERVAL 16
-#define QUEUE_LENGTH 48
+// 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 IN_PACKET_HEADER_SIZE 4
-#define OUT_PACKET_HEADER_SIZE 0
+#define IT_PKT_HEADER_SIZE_CIP CIP_HEADER_SIZE
+#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
-static void pcm_period_tasklet(unsigned long data);
+// 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
+
+static void pcm_period_work(struct work_struct *work);
/**
* 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 packet transmission method to use
+ * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
* @fmt: the value of fmt field in CIP header
- * @process_data_blocks: callback handler to process data blocks
+ * @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, enum cip_flags flags,
+ enum amdtp_stream_direction dir, unsigned int flags,
unsigned int fmt,
- amdtp_stream_process_data_blocks_t process_data_blocks,
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
unsigned int protocol_size)
{
- if (process_data_blocks == NULL)
+ if (process_ctx_payloads == NULL)
return -EINVAL;
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
@@ -89,14 +107,13 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->flags = flags;
s->context = ERR_PTR(-1);
mutex_init(&s->mutex);
- tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
+ INIT_WORK(&s->period_work, pcm_period_work);
s->packet_index = 0;
- init_waitqueue_head(&s->callback_wait);
- s->callbacked = false;
+ init_waitqueue_head(&s->ready_wait);
s->fmt = fmt;
- s->process_data_blocks = process_data_blocks;
+ s->process_ctx_payloads = process_ctx_payloads;
return 0;
}
@@ -140,6 +157,31 @@ const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
};
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]);
+ }
+
+ if (step == 0)
+ return -EINVAL;
+
+ 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.
@@ -149,16 +191,17 @@ 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_BATCH |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ 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_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
- /* SNDRV_PCM_INFO_BATCH */
hw->periods_min = 2;
hw->periods_max = UINT_MAX;
@@ -169,19 +212,36 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
- /*
- * Currently firewire-lib processes 16 packets in one software
- * interrupt callback. This equals to 2msec but actually the
- * interval of the interrupts has a jitter.
- * Additionally, even if adding a constraint to fit period size to
- * 2msec, actual calculated frames per period doesn't equal to 2msec,
- * depending on sampling rate.
- * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
- * Here let us use 5msec for safe period interrupt.
- */
+ // 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,
- 5000, UINT_MAX);
+ 250, maximum_usec_per_period);
if (err < 0)
goto end;
@@ -194,16 +254,19 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
* 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.
- *
- * TODO: These constraints can be improved with proper rules.
- * Currently apply LCM of SYT_INTERVALs.
*/
- err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+ 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;
- err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
end:
return err;
}
@@ -214,12 +277,14 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
* @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 data_block_quadlets, unsigned int pcm_frame_multiplier)
{
unsigned int sfc;
@@ -234,16 +299,32 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
s->data_block_quadlets = data_block_quadlets;
s->syt_interval = amdtp_syt_intervals[sfc];
- /* default buffering in the device */
+ // 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)
- /* additional buffering needed to adjust for no-data packets */
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
@@ -253,16 +334,14 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
*/
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
- unsigned int multiplier = 1;
- unsigned int header_size = 0;
+ unsigned int cip_header_size;
- if (s->flags & CIP_JUMBO_PAYLOAD)
- multiplier = 5;
if (!(s->flags & CIP_NO_HEADER))
- header_size = 8;
+ cip_header_size = CIP_HEADER_SIZE;
+ else
+ cip_header_size = 0;
- return header_size +
- s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+ return cip_header_size + amdtp_stream_get_max_ctx_payload_size(s);
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -274,31 +353,49 @@ EXPORT_SYMBOL(amdtp_stream_get_max_payload);
*/
void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
{
- tasklet_kill(&s->period_tasklet);
+ cancel_work_sync(&s->period_work);
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_stream *s,
- unsigned int syt)
+#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)
{
- unsigned int phase, data_blocks;
+ const unsigned int syt_interval = s->syt_interval;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
- /* Blocking mode. */
- if (s->flags & CIP_BLOCKING) {
- /* This module generate empty packet for 'no data'. */
- if (syt == CIP_SYT_NO_INFO)
- data_blocks = 0;
+ if (desc->syt_offset != CIP_SYT_NO_INFO)
+ desc->data_blocks = syt_interval;
else
- data_blocks = s->syt_interval;
- /* Non-blocking mode. */
- } else {
- if (!cip_sfc_is_base_44100(s->sfc)) {
- /* Sample_rate / 8000 is an integer, and precomputed. */
- data_blocks = s->data_block_state;
+ 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 {
- phase = s->data_block_state;
+ unsigned int phase = state;
/*
* This calculates the number of data blocks per packet so that
@@ -308,30 +405,31 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
* as possible in the sequence (to prevent underruns of the
* device's buffer).
*/
- if (s->sfc == CIP_SFC_44100)
+ if (sfc == CIP_SFC_44100)
/* 6 6 5 6 5 6 5 ... */
- data_blocks = 5 + ((phase & 1) ^
- (phase == 0 || phase >= 40));
+ desc->data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40));
else
/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
- data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
- if (++phase >= (80 >> (s->sfc >> 1)))
+ desc->data_blocks = 11 * (sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (sfc >> 1)))
phase = 0;
- s->data_block_state = phase;
+ state = phase;
}
+
+ pos = (pos + 1) % size;
}
- return data_blocks;
+ s->ctx_data.rx.data_block_state = state;
}
-static unsigned int calculate_syt(struct amdtp_stream *s,
- unsigned int cycle)
+static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
+ unsigned int *syt_offset_state, enum cip_sfc sfc)
{
- unsigned int syt_offset, phase, index, syt;
+ unsigned int syt_offset;
- if (s->last_syt_offset < TICKS_PER_CYCLE) {
- if (!cip_sfc_is_base_44100(s->sfc))
- syt_offset = s->last_syt_offset + s->syt_offset_state;
+ 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:
@@ -343,28 +441,163 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
* 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
* This code generates _exactly_ the same sequence.
*/
- phase = s->syt_offset_state;
- index = phase % 13;
- syt_offset = s->last_syt_offset;
+ 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;
- s->syt_offset_state = phase;
+ *syt_offset_state = phase;
}
} else
- syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
- s->last_syt_offset = syt_offset;
+ syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
+ *last_syt_offset = syt_offset;
- if (syt_offset < TICKS_PER_CYCLE) {
- syt_offset += s->transfer_delay;
- syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
- syt += syt_offset % TICKS_PER_CYCLE;
+ 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);
+ }
- return syt & CIP_SYT_MASK;
+ 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 {
- return CIP_SYT_NO_INFO;
+ 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,
@@ -381,155 +614,132 @@ static void update_pcm_pointers(struct amdtp_stream *s,
s->pcm_period_pointer += frames;
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size;
- tasklet_hi_schedule(&s->period_tasklet);
+
+ // 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.
+ //
+ // Use another work item for period elapsed event to prevent the following AB/BA
+ // deadlock:
+ //
+ // thread 1 thread 2
+ // ================================= =================================
+ // A.work item (process) pcm ioctl (process)
+ // v v
+ // process_rx_packets() B.PCM stream lock
+ // process_tx_packets() v
+ // v callbacks in snd_pcm_ops
+ // update_pcm_pointers() v
+ // snd_pcm_elapsed() fw_iso_context_flush_completions()
+ // snd_pcm_stream_lock_irqsave() disable_work_sync()
+ // v v
+ // wait until release of B wait until A exits
+ if (!pcm->runtime->no_period_wakeup)
+ queue_work(system_highpri_wq, &s->period_work);
}
}
-static void pcm_period_tasklet(unsigned long data)
+static void pcm_period_work(struct work_struct *work)
{
- struct amdtp_stream *s = (void *)data;
+ struct amdtp_stream *s = container_of(work, struct amdtp_stream,
+ period_work);
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
if (pcm)
snd_pcm_period_elapsed(pcm);
}
-static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
- unsigned int payload_length)
+static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
+ bool sched_irq)
{
- struct fw_iso_packet p = {0};
- int err = 0;
+ int err;
- if (IS_ERR(s->context))
- goto end;
+ params->interrupt = sched_irq;
+ params->tag = s->tag;
+ params->sy = 0;
- p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
- p.tag = s->tag;
- p.header_length = header_length;
- if (payload_length > 0)
- p.payload_length = payload_length;
- else
- p.skip = true;
- err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+ 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 >= QUEUE_LENGTH)
+ if (++s->packet_index >= s->queue_size)
s->packet_index = 0;
end:
return err;
}
static inline int queue_out_packet(struct amdtp_stream *s,
- unsigned int payload_length)
+ struct fw_iso_packet *params, bool sched_irq)
{
- return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
+ 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)
+static inline int queue_in_packet(struct amdtp_stream *s,
+ struct fw_iso_packet *params)
{
- return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
+ // 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 int handle_out_packet(struct amdtp_stream *s,
- unsigned int payload_length, unsigned int cycle,
- unsigned int index)
+static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
+ unsigned int data_block_counter, unsigned int syt)
{
- __be32 *buffer;
- unsigned int syt;
- unsigned int data_blocks;
- unsigned int pcm_frames;
- struct snd_pcm_substream *pcm;
-
- buffer = s->buffer.packets[s->packet_index].buffer;
- syt = calculate_syt(s, cycle);
- data_blocks = calculate_data_blocks(s, syt);
- pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
-
- if (s->flags & CIP_DBC_IS_END_EVENT)
- s->data_block_counter =
- (s->data_block_counter + data_blocks) & 0xff;
-
- buffer[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
+ 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) |
- s->data_block_counter);
- buffer[1] = cpu_to_be32(CIP_EOH |
- ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
- ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
- (syt & CIP_SYT_MASK));
-
- if (!(s->flags & CIP_DBC_IS_END_EVENT))
- s->data_block_counter =
- (s->data_block_counter + data_blocks) & 0xff;
- payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-
- trace_out_packet(s, cycle, buffer, payload_length, index);
-
- if (queue_out_packet(s, payload_length) < 0)
- return -EIO;
-
- pcm = READ_ONCE(s->pcm);
- if (pcm && pcm_frames > 0)
- update_pcm_pointers(s, pcm, pcm_frames);
-
- /* No need to return the number of handled data blocks. */
- return 0;
+ 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 int handle_out_packet_without_header(struct amdtp_stream *s,
- unsigned int payload_length, unsigned int cycle,
- unsigned int index)
+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)
{
- __be32 *buffer;
- unsigned int syt;
- unsigned int data_blocks;
- unsigned int pcm_frames;
- struct snd_pcm_substream *pcm;
+ unsigned int payload_length;
+ __be32 *cip_header;
- buffer = s->buffer.packets[s->packet_index].buffer;
- syt = calculate_syt(s, cycle);
- data_blocks = calculate_data_blocks(s, syt);
- pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+ payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
+ params->payload_length = payload_length;
- payload_length = data_blocks * 4 * s->data_block_quadlets;
-
- trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
- index);
-
- if (queue_out_packet(s, payload_length) < 0)
- return -EIO;
-
- pcm = READ_ONCE(s->pcm);
- if (pcm && pcm_frames > 0)
- update_pcm_pointers(s, pcm, pcm_frames);
+ 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;
+ }
- /* No need to return the number of handled data blocks. */
- return 0;
+ 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 handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_length, unsigned int cycle,
- unsigned int index)
+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)
{
- __be32 *buffer;
u32 cip_header[2];
- unsigned int sph, fmt, fdf, syt;
- unsigned int data_block_quadlets, data_block_counter, dbc_interval;
- unsigned int data_blocks;
- struct snd_pcm_substream *pcm;
- unsigned int pcm_frames;
+ unsigned int sph;
+ unsigned int fmt;
+ unsigned int fdf;
+ unsigned int dbc;
bool lost;
- buffer = s->buffer.packets[s->packet_index].buffer;
- cip_header[0] = be32_to_cpu(buffer[0]);
- cip_header[1] = be32_to_cpu(buffer[1]);
-
- trace_in_packet(s, cycle, cip_header, payload_length, index);
+ 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'.
@@ -541,9 +751,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
- data_blocks = 0;
- pcm_frames = 0;
- goto end;
+ return -EAGAIN;
}
/* Check valid protocol or not. */
@@ -553,19 +761,16 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
- data_blocks = 0;
- pcm_frames = 0;
- goto end;
+ return -EAGAIN;
}
/* Calculate data blocks */
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
- if (payload_length < 12 ||
- (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
- data_blocks = 0;
+ if (payload_length == 0 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+ *data_blocks = 0;
} else {
- data_block_quadlets =
- (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+ 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,
@@ -576,234 +781,888 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- data_blocks = (payload_length / 4 - 2) /
- data_block_quadlets;
+ *data_blocks = payload_length / sizeof(__be32) / data_block_quadlets;
}
/* Check data block counter continuity */
- data_block_counter = cip_header[0] & CIP_DBC_MASK;
- if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
- s->data_block_counter != UINT_MAX)
- data_block_counter = s->data_block_counter;
-
- if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
- data_block_counter == s->tx_first_dbc) ||
- s->data_block_counter == UINT_MAX) {
+ 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 = data_block_counter != s->data_block_counter;
+ lost = dbc != *data_block_counter;
} else {
- if (data_blocks > 0 && s->tx_dbc_interval > 0)
- dbc_interval = s->tx_dbc_interval;
- else
- dbc_interval = data_blocks;
+ unsigned int dbc_interval;
- lost = data_block_counter !=
- ((s->data_block_counter + dbc_interval) & 0xff);
+ if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) {
+ if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
+ dbc_interval = s->ctx_data.tx.dbc_interval;
+ else
+ dbc_interval = *data_blocks;
+ } else {
+ dbc_interval = payload_length / sizeof(__be32);
+ }
+
+ lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
dev_err(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n",
- s->data_block_counter, data_block_counter);
+ *data_block_counter, dbc);
return -EIO;
}
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+ *data_block_counter = dbc;
- if (s->flags & CIP_DBC_IS_END_EVENT)
- s->data_block_counter = data_block_counter;
+ 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
- s->data_block_counter =
- (data_block_counter + data_blocks) & 0xff;
-end:
- if (queue_in_packet(s) < 0)
+ 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;
+ }
- pcm = READ_ONCE(s->pcm);
- if (pcm && pcm_frames > 0)
- update_pcm_pointers(s, pcm, pcm_frames);
+ 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;
}
-static int handle_in_packet_without_header(struct amdtp_stream *s,
- unsigned int payload_quadlets, unsigned int cycle,
- unsigned int index)
+// 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)
{
- __be32 *buffer;
- unsigned int data_blocks;
- struct snd_pcm_substream *pcm;
- unsigned int pcm_frames;
+ return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff);
+}
- buffer = s->buffer.packets[s->packet_index].buffer;
- data_blocks = payload_quadlets / s->data_block_quadlets;
+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);
+}
- trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
- index);
+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;
+}
- pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend)
+{
+ if (minuend < subtrahend)
+ minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
- if (queue_in_packet(s) < 0)
- return -EIO;
+ return minuend - subtrahend;
+}
- pcm = READ_ONCE(s->pcm);
- if (pcm && pcm_frames > 0)
- update_pcm_pointers(s, pcm, pcm_frames);
+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;
}
-/*
- * 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_cycle_count(u32 tstamp)
+static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
+ unsigned int transfer_delay)
{
- return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
+ 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 inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
+static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count)
{
- cycle += addend;
- if (cycle >= 8 * CYCLES_PER_SECOND)
- cycle -= 8 * CYCLES_PER_SECOND;
- return cycle;
+ 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 u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
+static inline void cancel_stream(struct amdtp_stream *s)
{
- if (cycle < subtrahend)
- cycle += 8 * CYCLES_PER_SECOND;
- return cycle - subtrahend;
+ struct work_struct *work = current_work();
+
+ s->packet_index = -1;
+
+ // Detect work items for any isochronous context. The work item for pcm_period_work()
+ // should be avoided since the call of snd_pcm_period_elapsed() can reach via
+ // snd_pcm_ops.pointer() under acquiring PCM stream(group) lock and causes dead lock at
+ // snd_pcm_stop_xrun().
+ if (work && work != &s->period_work)
+ amdtp_stream_pcm_abort(s);
+ WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
}
-static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
- size_t header_length, void *header,
- void *private_data)
+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;
- unsigned int i, packets = header_length / 4;
- u32 cycle;
+ 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;
- cycle = compute_cycle_count(tstamp);
+ // Calculate the number of packets in buffer and check XRUN.
+ packets = header_length / sizeof(*ctx_header);
- /* Align to actual cycle count for the last packet. */
- cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
+ 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) {
- cycle = increment_cycle_count(cycle, 1);
- if (s->handle_packet(s, 0, cycle, i) < 0) {
- s->packet_index = -1;
- if (in_interrupt())
- amdtp_stream_pcm_abort(s);
- WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
+ DEFINE_RAW_FLEX(struct fw_iso_packet, template, header, CIP_HEADER_QUADLETS);
+ bool sched_irq = false;
+
+ build_it_pkt_header(s, desc->cycle, template, 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, sched_irq) < 0) {
+ cancel_stream(s);
return;
}
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
- fw_iso_context_queue_flush(s->context);
+ s->ctx_data.rx.event_count = event_count;
+ s->packet_descs_cursor = desc;
}
-static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
- size_t header_length, void *header,
- void *private_data)
+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;
- unsigned int i, packets;
- unsigned int payload_length, max_payload_length;
- __be32 *headers = header;
- u32 cycle;
+ 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;
- /* The number of packets in buffer */
- packets = header_length / IN_PACKET_HEADER_SIZE;
+ packets = header_length / sizeof(*ctx_header);
- cycle = compute_cycle_count(tstamp);
+ cycle = compute_ohci_it_cycle(ctx_header[packets - 1], s->queue_size);
+ s->next_cycle = increment_ohci_cycle_count(cycle, 1);
- /* Align to actual cycle count for the last packet. */
- cycle = decrement_cycle_count(cycle, packets);
+ 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;
+ }
+ }
+}
- /* For buffer-over-run prevention. */
- max_payload_length = s->max_payload_length;
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data);
- for (i = 0; i < packets; i++) {
- cycle = increment_cycle_count(cycle, 1);
+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;
- /* The number of bytes in this packet */
- payload_length =
- (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
- if (payload_length > max_payload_length) {
- dev_err(&s->unit->device,
- "Detect jumbo payload: %04x %04x\n",
- payload_length, max_payload_length);
+ 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;
}
+ }
+}
- if (s->handle_packet(s, payload_length, cycle, i) < 0)
+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;
}
- /* Queueing error or detecting invalid payload. */
- if (i < packets) {
- s->packet_index = -1;
- if (in_interrupt())
- amdtp_stream_pcm_abort(s);
- WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
+ 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);
}
- fw_iso_context_queue_flush(s->context);
+ 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;
+ }
+ }
}
-/* this is executed one time */
-static void amdtp_stream_first_callback(struct fw_iso_context *context,
- u32 tstamp, size_t header_length,
- void *header, void *private_data)
+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;
- u32 cycle;
- unsigned int packets;
+ struct amdtp_domain *d = s->domain;
- s->max_payload_length = amdtp_stream_get_max_payload(s);
+ process_rx_packets(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+}
- /*
- * For in-stream, first packet has come.
- * For out-stream, prepared to transmit first packet
- */
- s->callbacked = true;
- wake_up(&s->callback_wait);
+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;
- cycle = compute_cycle_count(tstamp);
+ 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) {
- packets = header_length / IN_PACKET_HEADER_SIZE;
- cycle = decrement_cycle_count(cycle, packets);
- context->callback.sc = in_stream_callback;
- if (s->flags & CIP_NO_HEADER)
- s->handle_packet = handle_in_packet_without_header;
- else
- s->handle_packet = handle_in_packet;
+ context->callback.sc = drop_tx_packets_initially;
} else {
- packets = header_length / 4;
- cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
- context->callback.sc = out_stream_callback;
- if (s->flags & CIP_NO_HEADER)
- s->handle_packet = handle_out_packet_without_header;
+ if (s == d->irq_target)
+ context->callback.sc = irq_target_callback_skip;
else
- s->handle_packet = handle_out_packet;
+ context->callback.sc = skip_rx_packets;
}
- s->start_cycle = cycle;
-
context->callback.sc(context, tstamp, header_length, header, s);
}
@@ -812,63 +1671,62 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
* @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.
*/
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
+static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
+ unsigned int queue_size, unsigned int idle_irq_interval)
{
- static const struct {
- unsigned int data_block;
- unsigned int syt_offset;
- } 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 },
- };
- unsigned int header_size;
+ 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;
- int type, tag, err;
+ struct pkt_desc *descs;
+ int i, type, tag, err;
- mutex_lock(&s->mutex);
+ guard(mutex)(&s->mutex);
if (WARN_ON(amdtp_stream_running(s) ||
- (s->data_block_quadlets < 1))) {
- err = -EBADFD;
- goto err_unlock;
- }
+ (s->data_block_quadlets < 1)))
+ return -EBADFD;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: IT context should be used for constant IRQ.
+ if (is_irq_target)
+ return -EINVAL;
- if (s->direction == AMDTP_IN_STREAM)
s->data_block_counter = UINT_MAX;
- else
+ } else {
s->data_block_counter = 0;
- s->data_block_state = initial_state[s->sfc].data_block;
- s->syt_offset_state = initial_state[s->sfc].syt_offset;
- s->last_syt_offset = TICKS_PER_CYCLE;
+ }
- /* initialize packet buffer */
+ // initialize packet buffer.
if (s->direction == AMDTP_IN_STREAM) {
dir = DMA_FROM_DEVICE;
type = FW_ISO_CONTEXT_RECEIVE;
- header_size = IN_PACKET_HEADER_SIZE;
+ 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;
- header_size = OUT_PACKET_HEADER_SIZE;
+ ctx_header_size = 0; // No effect for IT context.
}
- err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
- amdtp_stream_get_max_payload(s), dir);
+ 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;
+ return err;
+ s->queue_size = queue_size;
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
- type, channel, speed, header_size,
- amdtp_stream_first_callback, s);
+ type, channel, speed, ctx_header_size,
+ amdtp_stream_first_callback, s);
if (IS_ERR(s->context)) {
err = PTR_ERR(s->context);
if (err == -EBUSY)
@@ -879,19 +1737,99 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
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 {
- if (s->direction == AMDTP_IN_STREAM)
- err = queue_in_packet(s);
- else
- err = queue_out_packet(s, 0);
+ 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_context;
+ goto err_pkt_descs;
} while (s->packet_index > 0);
/* NOTE: TAG1 matches CIP. This just affects in stream. */
@@ -899,76 +1837,73 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
- s->callbacked = false;
+ s->ready_processing = false;
err = fw_iso_context_start(s->context, -1, 0, tag);
if (err < 0)
- goto err_context;
-
- mutex_unlock(&s->mutex);
+ goto err_pkt_descs;
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;
}
-EXPORT_SYMBOL(amdtp_stream_start);
/**
- * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * 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_stream_pcm_pointer(struct amdtp_stream *s)
+unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
+ struct amdtp_stream *s)
{
- /*
- * This function is called in software IRQ context of period_tasklet or
- * process context.
- *
- * When the software IRQ context was scheduled by software IRQ context
- * of IR/IT contexts, queued packets were already handled. Therefore,
- * no need to flush the queue in buffer anymore.
- *
- * When the process context reach here, some packets will be already
- * queued in the buffer. These packets should be handled immediately
- * to keep better granularity of PCM pointer.
- *
- * Later, the process context will sometimes schedules software IRQ
- * context of the period_tasklet. Then, no need to flush the queue by
- * the same reason as described for IR/IT contexts.
- */
- if (!in_interrupt() && amdtp_stream_running(s))
- fw_iso_context_flush_completions(s->context);
+ struct amdtp_stream *irq_target = d->irq_target;
+
+ if (irq_target && amdtp_stream_running(irq_target)) {
+ // The work item to call snd_pcm_period_elapsed() can reach here by the call of
+ // snd_pcm_ops.pointer(), however less packets would be available then. Therefore
+ // the following call is just for user process contexts.
+ if (current_work() != &s->period_work)
+ fw_iso_context_flush_completions(irq_target->context);
+ }
return READ_ONCE(s->pcm_buffer_pointer);
}
-EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer);
/**
- * amdtp_stream_pcm_ack - acknowledge queued PCM frames
+ * 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_stream_pcm_ack(struct amdtp_stream *s)
+int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s)
{
- /*
- * Process isochronous packets for recent isochronous cycle to handle
- * queued PCM frames.
- */
- if (amdtp_stream_running(s))
- fw_iso_context_flush_completions(s->context);
+ 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(amdtp_stream_pcm_ack);
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack);
/**
* amdtp_stream_update - update the stream after a bus reset
@@ -989,26 +1924,28 @@ EXPORT_SYMBOL(amdtp_stream_update);
* All PCM and MIDI devices of the stream must be stopped before the stream
* itself can be stopped.
*/
-void amdtp_stream_stop(struct amdtp_stream *s)
+static void amdtp_stream_stop(struct amdtp_stream *s)
{
- mutex_lock(&s->mutex);
+ guard(mutex)(&s->mutex);
- if (!amdtp_stream_running(s)) {
- mutex_unlock(&s->mutex);
+ if (!amdtp_stream_running(s))
return;
- }
- tasklet_kill(&s->period_tasklet);
+ cancel_work_sync(&s->period_work);
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;
- s->callbacked = false;
-
- mutex_unlock(&s->mutex);
+ 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);
+ }
}
-EXPORT_SYMBOL(amdtp_stream_stop);
/**
* amdtp_stream_pcm_abort - abort the running PCM device
@@ -1026,3 +1963,198 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
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
index e45de3eecfe3..775db3fc4959 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -33,6 +33,13 @@
* @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.
+ * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with
+ * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets
+ * in CIP payload, instead of the number of accumulated data blocks.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -45,6 +52,9 @@ enum cip_flags {
CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100,
+ CIP_UNALIGHED_DBC = 0x200,
+ CIP_UNAWARE_SYT = 0x400,
+ CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800,
};
/**
@@ -91,27 +101,81 @@ enum amdtp_stream_direction {
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 unsigned int (*amdtp_stream_process_data_blocks_t)(
- struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt);
+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;
- enum cip_flags flags;
+ // 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;
- int (*handle_packet)(struct amdtp_stream *s,
- unsigned int payload_quadlets, unsigned int cycle,
- unsigned int index);
- unsigned int max_payload_length;
+ 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;
@@ -119,57 +183,53 @@ struct amdtp_stream {
unsigned int data_block_counter;
unsigned int sph;
unsigned int fmt;
- unsigned int fdf;
- /* quirk: fixed interval of dbc between previos/current packets. */
- unsigned int tx_dbc_interval;
- /* quirk: indicate the value of dbc field in a first packet. */
- unsigned int tx_first_dbc;
- /* Internal flags. */
+ // Internal flags.
+ unsigned int transfer_delay;
enum cip_sfc sfc;
unsigned int syt_interval;
- unsigned int transfer_delay;
- unsigned int data_block_state;
- unsigned int last_syt_offset;
- unsigned int syt_offset_state;
/* For a PCM substream processing. */
struct snd_pcm_substream *pcm;
- struct tasklet_struct period_tasklet;
+ struct work_struct period_work;
snd_pcm_uframes_t pcm_buffer_pointer;
unsigned int pcm_period_pointer;
+ unsigned int pcm_frame_multiplier;
- /* To wait for first packet. */
- bool callbacked;
- wait_queue_head_t callback_wait;
- u32 start_cycle;
+ // 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_data_blocks_t process_data_blocks;
+ 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, enum cip_flags flags,
+ enum amdtp_stream_direction dir, unsigned int flags,
unsigned int fmt,
- amdtp_stream_process_data_blocks_t process_data_blocks,
+ 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 data_block_quadlets, unsigned int pcm_frame_multiplier);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
void amdtp_stream_update(struct amdtp_stream *s);
-void amdtp_stream_stop(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);
-unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
-int amdtp_stream_pcm_ack(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
@@ -224,24 +284,89 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
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_stream_wait_callback - sleep till callbacked or timeout
- * @s: the AMDTP stream
- * @timeout: msec till timeout
+ * 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 stream should be stopped.
+ * If this function return false, the AMDTP domain should be stopped.
*/
-static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
- unsigned int timeout)
+static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms)
{
- return wait_event_timeout(s->callback_wait,
- s->callbacked == true,
- msecs_to_jiffies(timeout)) > 0;
+ 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
index 14bc84c51ef5..b913e805bd7a 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+snd-bebob-y := 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
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 730ea91d9be8..01e2c4cc03d4 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -16,7 +15,7 @@
MODULE_DESCRIPTION("BridgeCo BeBoB driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -41,14 +40,12 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_EDIROL 0x000040ab
#define VEN_PRESONUS 0x00000a92
#define VEN_BRIDGECO 0x000007f5
-#define VEN_MACKIE1 0x0000000f
-#define VEN_MACKIE2 0x00000ff2
+#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_ACOUSTIC 0x00000002
#define VEN_CME 0x0000000a
#define VEN_PHONIC 0x00001496
#define VEN_LYNX 0x000019e5
@@ -57,14 +54,15 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_TERRATEC 0x00000aac
#define VEN_YAMAHA 0x0000a0de
#define VEN_FOCUSRITE 0x0000130e
-#define VEN_MAUDIO1 0x00000d6c
-#define VEN_MAUDIO2 0x000007f5
+#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)
@@ -75,7 +73,6 @@ name_device(struct snd_bebob *bebob)
u32 hw_id;
u32 data[2] = {0};
u32 revision;
- u32 version;
int err;
/* get vendor name from root directory */
@@ -108,15 +105,9 @@ name_device(struct snd_bebob *bebob)
if (err < 0)
goto end;
- err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION,
- &version);
- if (err < 0)
- goto end;
- bebob->version = version;
-
- strcpy(bebob->card->driver, "BeBoB");
- strcpy(bebob->card->shortname, model);
- strcpy(bebob->card->mixername, model);
+ strscpy(bebob->card->driver, "BeBoB");
+ strscpy(bebob->card->shortname, model);
+ strscpy(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,
@@ -126,33 +117,19 @@ end:
return err;
}
-static void bebob_free(struct snd_bebob *bebob)
-{
- snd_bebob_stream_destroy_duplex(bebob);
- fw_unit_put(bebob->unit);
-
- kfree(bebob->maudio_special_quirk);
-
- mutex_destroy(&bebob->mutex);
- kfree(bebob);
-}
-
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
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);
+ scoped_guard(mutex, &devices_mutex) {
+ clear_bit(bebob->card_index, devices_used);
+ }
+
+ snd_bebob_stream_destroy_duplex(bebob);
- bebob_free(card->private_data);
+ mutex_destroy(&bebob->mutex);
+ fw_unit_put(bebob->unit);
}
static const struct snd_bebob_spec *
@@ -180,41 +157,93 @@ check_audiophile_booted(struct fw_unit *unit)
return strncmp(name, "FW Audiophile Bootloader", 24) != 0;
}
-static void
-do_registration(struct work_struct *work)
+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)
{
- struct snd_bebob *bebob =
- container_of(work, struct snd_bebob, dwork.work);
unsigned int card_index;
+ struct snd_card *card;
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
int err;
- if (bebob->registered)
- return;
-
- mutex_lock(&devices_mutex);
+ 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;
- 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;
+ 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;
}
- err = snd_card_new(&bebob->unit->device, index[card_index],
- id[card_index], THIS_MODULE, 0, &bebob->card);
- if (err < 0) {
- mutex_unlock(&devices_mutex);
- return;
+ scoped_guard(mutex, &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)
+ return -ENOENT;
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*bebob), &card);
+ if (err < 0)
+ return err;
+ card->private_free = bebob_card_free;
+ set_bit(card_index, devices_used);
}
+ 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 (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
+ if (entry->model_id == MODEL_MAUDIO_FW1814)
err = snd_bebob_maudio_special_discover(bebob, true);
else
err = snd_bebob_maudio_special_discover(bebob, false);
@@ -244,91 +273,26 @@ do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(bebob->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- set_bit(card_index, devices_used);
- mutex_unlock(&devices_mutex);
-
- /*
- * After registered, bebob instance can be released corresponding to
- * releasing the sound card instance.
- */
- bebob->card->private_free = bebob_card_free;
- bebob->card->private_data = bebob;
- bebob->registered = true;
-
- return;
-error:
- mutex_unlock(&devices_mutex);
- snd_bebob_stream_destroy_duplex(bebob);
- snd_card_free(bebob->card);
- dev_info(&bebob->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int
-bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
-{
- struct snd_bebob *bebob;
- const struct snd_bebob_spec *spec;
-
- if (entry->vendor_id == VEN_FOCUSRITE &&
- entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
- spec = get_saffire_spec(unit);
- else if (entry->vendor_id == VEN_MAUDIO1 &&
- 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) {
- if (entry->vendor_id == VEN_MAUDIO1 ||
- entry->vendor_id == VEN_MAUDIO2)
- return snd_bebob_maudio_load_firmware(unit);
- else
- return -ENODEV;
- }
-
- /* Allocate this independent of sound card instance. */
- bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
- if (bebob == NULL)
- return -ENOMEM;
-
- bebob->unit = fw_unit_get(unit);
- bebob->entry = entry;
- bebob->spec = spec;
- dev_set_drvdata(&unit->device, bebob);
-
- mutex_init(&bebob->mutex);
- spin_lock_init(&bebob->lock);
- init_waitqueue_head(&bebob->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
-
- if (entry->vendor_id != VEN_MAUDIO1 ||
- (entry->model_id != MODEL_MAUDIO_FW1814 &&
- entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
- snd_fw_schedule_registration(unit, &bebob->dwork);
- } else {
- /*
- * 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);
+ 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;
}
/*
@@ -355,11 +319,7 @@ bebob_update(struct fw_unit *unit)
if (bebob == NULL)
return;
- /* Postpone a workqueue for deferred registration. */
- if (!bebob->registered)
- snd_fw_schedule_registration(unit, &bebob->dwork);
- else
- fcp_bus_reset(bebob->unit);
+ fcp_bus_reset(bebob->unit);
}
static void bebob_remove(struct fw_unit *unit)
@@ -369,20 +329,8 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL)
return;
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&bebob->dwork);
-
- if (bebob->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(bebob->card);
- } else {
- /* Don't forget this case. */
- bebob_free(bebob);
- }
+ // Block till all of ALSA character devices are released.
+ snd_card_free(bebob->card);
}
static const struct snd_bebob_rate_spec normal_rate_spec = {
@@ -395,6 +343,22 @@ static const struct snd_bebob_spec spec_normal = {
.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),
@@ -411,9 +375,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* 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_MACKIE2, 0x00010065, &spec_normal),
- /* Mackie, d.2 (Firewire Option) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
+ 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 */
@@ -432,20 +396,21 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* 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, 0x00001eee, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal),
/* ESI, Quatafire610 */
SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
- /* AcousticReality, eARMasterOne */
- SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
/* CME, MatrixKFW */
SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
- /* Phonic, Helix Board 12 MkII */
+ // Phonic Helix Board 12 FireWire MkII.
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
- /* Phonic, Helix Board 18 MkII */
+ // Phonic Helix Board 18 FireWire MkII.
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
- /* Phonic, Helix Board 24 MkII */
+ // Phonic Helix Board 24 FireWire MkII.
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
- /* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
+ // 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),
@@ -463,7 +428,8 @@ static const struct ieee1394_device_id bebob_id_table[] = {
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 */
+ // 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),
@@ -472,33 +438,35 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* 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, 0x00000006, &saffirepro_10_spec),
+ 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 */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL), /* bootloader */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_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_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_AUDIOPHILE_BOTH,
&maudio_audiophile_spec),
/* M-Audio, Firewire Solo */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010062, &maudio_solo_spec),
/* M-Audio, Ozonic */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x0000000a, &maudio_ozonic_spec),
/* M-Audio NRV10 */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010081, &maudio_nrv10_spec),
/* M-Audio, ProFireLightbridge */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROFIRELIGHTBRIDGE, &spec_normal),
/* Firewire 1814 */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL), /* bootloader */
- SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
+ 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_MAUDIO1, MODEL_MAUDIO_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 */
@@ -509,11 +477,6 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* Infrasonic, Windy6 */
/* Mackie, Digital X Bus x.200 */
/* Mackie, Digital X Bus x.400 */
- /* Phonic, HB 12 */
- /* Phonic, HB 24 */
- /* Phonic, HB 18 */
- /* Phonic, FireFly 202 */
- /* Phonic, FireFly 302 */
/* Rolf Spuler, Firewire Guitar */
{}
};
@@ -522,7 +485,7 @@ MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
static struct fw_driver bebob_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "snd-bebob",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = bebob_probe,
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index df1b1e94c43c..4d73ecb30d79 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* bebob.h - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_BEBOB_H_INCLUDED
@@ -76,6 +75,11 @@ struct snd_bebob_spec {
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;
@@ -84,17 +88,12 @@ struct snd_bebob {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
- const struct ieee1394_device_id *entry;
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;
- bool connected;
-
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
@@ -116,8 +115,7 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
- /* For BeBoB version quirk. */
- unsigned int version;
+ struct amdtp_domain domain;
};
static inline int
@@ -201,6 +199,8 @@ int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
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);
@@ -218,7 +218,10 @@ 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_start_duplex(struct snd_bebob *bebob, unsigned int rate);
+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);
@@ -250,13 +253,4 @@ 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);
-#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
-{ \
- .match_flags = IEEE1394_MATCH_VENDOR_ID | \
- IEEE1394_MATCH_MODEL_ID, \
- .vendor_id = vendor, \
- .model_id = model, \
- .driver_data = (kernel_ulong_t)data \
-}
-
#endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
index f9b4225dd86f..022df09c68ff 100644
--- a/sound/firewire/bebob/bebob_command.c
+++ b/sound/firewire/bebob/bebob_command.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_command.c - driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
@@ -144,6 +143,42 @@ end:
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)
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index 52b8b61ecddd..06d6a37cd853 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_focusrite.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
@@ -28,6 +27,8 @@
#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
@@ -190,6 +191,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
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;
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
index 83e791810c52..216d1fceb6e7 100644
--- a/sound/firewire/bebob/bebob_hwdep.c
+++ b/sound/firewire/bebob/bebob_hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_hwdep.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -37,13 +36,10 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
}
memset(&event, 0, sizeof(event));
- if (bebob->dev_lock_changed) {
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = (bebob->dev_lock_count > 0);
- bebob->dev_lock_changed = false;
-
- count = min_t(long, count, sizeof(event.lock_status));
- }
+ 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);
@@ -57,18 +53,14 @@ 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);
+ guard(spinlock_irq)(&bebob->lock);
if (bebob->dev_lock_changed)
- events = POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&bebob->lock);
-
- return events;
+ return 0;
}
static int
@@ -82,7 +74,7 @@ hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -94,39 +86,27 @@ hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
static int
hwdep_lock(struct snd_bebob *bebob)
{
- int err;
-
- spin_lock_irq(&bebob->lock);
+ guard(spinlock_irq)(&bebob->lock);
if (bebob->dev_lock_count == 0) {
bebob->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&bebob->lock);
-
- return err;
}
static int
hwdep_unlock(struct snd_bebob *bebob)
{
- int err;
-
- spin_lock_irq(&bebob->lock);
+ guard(spinlock_irq)(&bebob->lock);
if (bebob->dev_lock_count == -1) {
bebob->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -EBADFD;
}
-
- spin_unlock_irq(&bebob->lock);
-
- return err;
}
static int
@@ -134,10 +114,9 @@ hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_bebob *bebob = hwdep->private_data;
- spin_lock_irq(&bebob->lock);
+ guard(spinlock_irq)(&bebob->lock);
if (bebob->dev_lock_count == -1)
bebob->dev_lock_count = 0;
- spin_unlock_irq(&bebob->lock);
return 0;
}
@@ -187,7 +166,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
if (err < 0)
goto end;
- strcpy(hwdep->name, "BeBoB");
+ strscpy(hwdep->name, "BeBoB");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
hwdep->ops = ops;
hwdep->private_data = bebob;
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index bd55620c6a47..376a9a175479 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_maudio.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
@@ -96,17 +95,13 @@ 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[3] = {
- cpu_to_le32(MAUDIO_BOOTLOADER_CUE1),
- cpu_to_le32(MAUDIO_BOOTLOADER_CUE2),
- cpu_to_le32(MAUDIO_BOOTLOADER_CUE3)
- };
+ __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)
- goto end;
+ return err;
/*
* firmware version 5058 or later has date later than "20070401", but
* 'date' is not null-terminated.
@@ -114,20 +109,28 @@ int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
if (date < 0x3230303730343031LL) {
dev_err(&unit->device,
"Use firmware version 5058 or later\n");
- err = -ENOSYS;
- goto end;
+ 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, sizeof(cues));
+ 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;
}
-end:
+
return err;
}
@@ -257,11 +260,12 @@ snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
struct special_params *params;
int err;
- params = kzalloc(sizeof(struct special_params), GFP_KERNEL);
- if (params == NULL)
+ params = devm_kzalloc(&bebob->card->card_dev,
+ sizeof(struct special_params), GFP_KERNEL);
+ if (!params)
return -ENOMEM;
- mutex_lock(&bebob->mutex);
+ guard(mutex)(&bebob->mutex);
bebob->maudio_special_quirk = (void *)params;
params->is1814 = is1814;
@@ -273,12 +277,12 @@ snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to initialize clock params: %d\n", err);
- goto end;
+ return err;
}
err = add_special_controls(bebob);
if (err < 0)
- goto end;
+ return err;
special_stream_formation_set(bebob);
@@ -289,12 +293,6 @@ snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
bebob->midi_input_ports = 2;
bebob->midi_output_ports = 2;
}
-end:
- if (err < 0) {
- kfree(params);
- bebob->maudio_special_quirk = NULL;
- }
- mutex_unlock(&bebob->mutex);
return err;
}
@@ -383,14 +381,12 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl,
if (id >= ARRAY_SIZE(special_clk_types))
return -EINVAL;
- mutex_lock(&bebob->mutex);
+ guard(mutex)(&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;
@@ -456,14 +452,14 @@ static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
unsigned int dig_in_iface;
int err, val;
- mutex_lock(&bebob->mutex);
+ guard(mutex)(&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;
+ return err;
}
/* encoded id for user value */
@@ -474,9 +470,7 @@ static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
val = 2;
uval->value.enumerated.item[0] = val;
-end:
- mutex_unlock(&bebob->mutex);
- return err;
+ return 0;
}
static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
@@ -494,7 +488,7 @@ static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
dig_in_fmt = (id >> 1) & 0x01;
dig_in_iface = id & 0x01;
- mutex_lock(&bebob->mutex);
+ guard(mutex)(&bebob->mutex);
err = avc_maudio_set_special_clk(bebob,
params->clk_src,
@@ -502,24 +496,19 @@ static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
params->dig_out_fmt,
params->clk_lock);
if (err < 0)
- goto end;
+ return err;
/* For ADAT, optical interface is only available. */
- if (params->dig_in_fmt > 0) {
- err = 1;
- goto end;
- }
+ if (params->dig_in_fmt > 0)
+ return 1;
/* 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;
+ return 1;
}
static const struct snd_kcontrol_new special_dig_in_iface_ctl = {
.name = "Digital Input Interface",
@@ -546,9 +535,9 @@ static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
- mutex_lock(&bebob->mutex);
+
+ guard(mutex)(&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,
@@ -563,7 +552,7 @@ static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
if (id >= ARRAY_SIZE(special_dig_out_iface_labels))
return -EINVAL;
- mutex_lock(&bebob->mutex);
+ guard(mutex)(&bebob->mutex);
err = avc_maudio_set_special_clk(bebob,
params->clk_src,
@@ -574,7 +563,6 @@ static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
err = 1;
}
- mutex_unlock(&bebob->mutex);
return err;
}
static const struct snd_kcontrol_new special_dig_out_iface_ctl = {
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 3befa3eca6ef..678631f31d3c 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -1,72 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_midi.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "bebob.h"
-static int midi_capture_open(struct snd_rawmidi_substream *substream)
+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)
- goto end;
+ return err;
- mutex_lock(&bebob->mutex);
- bebob->substreams_counter++;
- err = snd_bebob_stream_start_duplex(bebob, 0);
- mutex_unlock(&bebob->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_bebob_stream_lock_release(bebob);
-end:
- return err;
-}
-static int midi_playback_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)
- goto end;
-
- mutex_lock(&bebob->mutex);
- bebob->substreams_counter++;
- err = snd_bebob_stream_start_duplex(bebob, 0);
- mutex_unlock(&bebob->mutex);
- if (err < 0)
- snd_bebob_stream_lock_release(bebob);
-end:
return err;
}
-static int midi_capture_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 int midi_playback_close(struct snd_rawmidi_substream *substream)
+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);
+ scoped_guard(mutex, &bebob->mutex) {
+ bebob->substreams_counter--;
+ snd_bebob_stream_stop_duplex(bebob);
+ }
snd_bebob_stream_lock_release(bebob);
return 0;
@@ -75,9 +47,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&bebob->lock);
if (up)
amdtp_am824_midi_trigger(&bebob->tx_stream,
@@ -85,16 +56,13 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
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);
+ guard(spinlock_irqsave)(&bebob->lock);
if (up)
amdtp_am824_midi_trigger(&bebob->rx_stream,
@@ -102,8 +70,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
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,
@@ -112,22 +78,22 @@ static void set_midi_substream_names(struct snd_bebob *bebob,
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);
+ scnprintf(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_capture_open,
- .close = midi_capture_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_capture_trigger,
};
static const struct snd_rawmidi_ops playback_ops = {
- .open = midi_playback_open,
- .close = midi_playback_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index e6adab3ef42e..692d33bac2d2 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_pcm.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
@@ -130,18 +129,17 @@ end:
return err;
}
-static int
-pcm_open(struct snd_pcm_substream *substream)
+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;
- unsigned int sampling_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)
- goto end;
+ return err;
err = pcm_init_hw_params(bebob, substream);
if (err < 0)
@@ -151,27 +149,45 @@ pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- /*
- * When source of clock is internal or any PCM stream are running,
- * the available sampling rate is limited at current sampling rate.
- */
- if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
- amdtp_stream_pcm_running(&bebob->tx_stream) ||
- amdtp_stream_pcm_running(&bebob->rx_stream)) {
- err = spec->get(bebob, &sampling_rate);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get sampling rate: %d\n", err);
- goto err_locked;
+ scoped_guard(mutex, &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) {
+ 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)
+ 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;
+ }
}
-
- substream->runtime->hw.rate_min = sampling_rate;
- substream->runtime->hw.rate_max = sampling_rate;
}
snd_pcm_set_sync(substream);
-end:
- return err;
+
+ return 0;
err_locked:
snd_bebob_stream_lock_release(bebob);
return err;
@@ -185,86 +201,48 @@ pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int
-pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&bebob->mutex);
- bebob->substreams_counter++;
- mutex_unlock(&bebob->mutex);
+ 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);
+
+ guard(mutex)(&bebob->mutex);
+ err = snd_bebob_stream_reserve_duplex(bebob, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++bebob->substreams_counter;
}
- return 0;
-}
-static int
-pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_bebob *bebob = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&bebob->mutex);
- bebob->substreams_counter++;
- mutex_unlock(&bebob->mutex);
- }
-
- return 0;
+ return err;
}
-static int
-pcm_capture_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&bebob->mutex);
- bebob->substreams_counter--;
- mutex_unlock(&bebob->mutex);
- }
-
- snd_bebob_stream_stop_duplex(bebob);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-static int
-pcm_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_bebob *bebob = substream->private_data;
+ guard(mutex)(&bebob->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&bebob->mutex);
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
bebob->substreams_counter--;
- mutex_unlock(&bebob->mutex);
- }
snd_bebob_stream_stop_duplex(bebob);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int
pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+ err = snd_bebob_stream_start_duplex(bebob);
if (err >= 0)
amdtp_stream_pcm_prepare(&bebob->tx_stream);
@@ -274,10 +252,9 @@ static int
pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+ err = snd_bebob_stream_start_duplex(bebob);
if (err >= 0)
amdtp_stream_pcm_prepare(&bebob->rx_stream);
@@ -321,31 +298,33 @@ pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t
-pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&bebob->tx_stream);
+
+ return amdtp_domain_stream_pcm_pointer(&bebob->domain,
+ &bebob->tx_stream);
}
-static snd_pcm_uframes_t
-pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&bebob->rx_stream);
+
+ 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_stream_pcm_ack(&bebob->tx_stream);
+ 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_stream_pcm_ack(&bebob->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
}
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
@@ -353,27 +332,22 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_capture_hw_params,
- .hw_free = pcm_capture_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_playback_hw_params,
- .hw_free = pcm_playback_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -383,10 +357,12 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
goto end;
pcm->private_data = bebob;
+ pcm->nonatomic = true;
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
index ec24f96794f5..f659b888a6d1 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_proc.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
@@ -163,12 +162,8 @@ add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(bebob->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, bebob, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, bebob, op);
}
void snd_bebob_proc_init(struct snd_bebob *bebob)
@@ -183,11 +178,7 @@ void snd_bebob_proc_init(struct snd_bebob *bebob)
bebob->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- return;
- }
+ root->mode = S_IFDIR | 0555;
add_node(bebob, root, "clock", proc_read_clock);
add_node(bebob, root, "firmware", proc_read_hw_info);
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 4d3034a68bdf..449cb17717f0 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -1,15 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_stream.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
-#define CALLBACK_TIMEOUT 2000
-#define FW_ISO_RESOURCE_DELAY 1000
+#define READY_TIMEOUT_MS 4000
/*
* NOTE;
@@ -253,8 +251,7 @@ end:
return err;
}
-static unsigned int
-map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+static int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
{
unsigned int sec, sections, ch, channels;
unsigned int pcm, midi, location;
@@ -377,24 +374,6 @@ end:
}
static int
-init_both_connections(struct snd_bebob *bebob)
-{
- int err;
-
- err = cmp_connection_init(&bebob->in_conn,
- bebob->unit, CMP_INPUT, 0);
- if (err < 0)
- goto end;
-
- err = cmp_connection_init(&bebob->out_conn,
- bebob->unit, CMP_OUTPUT, 0);
- if (err < 0)
- cmp_connection_destroy(&bebob->in_conn);
-end:
- return err;
-}
-
-static int
check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
{
struct cmp_connection *conn;
@@ -418,74 +397,13 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
return err;
}
-static int
-make_both_connections(struct snd_bebob *bebob, unsigned int rate)
-{
- int index, pcm_channels, midi_channels, err = 0;
-
- if (bebob->connected)
- goto end;
-
- /* confirm params for both streams */
- err = get_formation_index(rate, &index);
- if (err < 0)
- goto end;
- pcm_channels = bebob->tx_stream_formations[index].pcm;
- midi_channels = bebob->tx_stream_formations[index].midi;
- err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
- pcm_channels, midi_channels * 8,
- false);
- if (err < 0)
- goto end;
-
- pcm_channels = bebob->rx_stream_formations[index].pcm;
- midi_channels = bebob->rx_stream_formations[index].midi;
- err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
- pcm_channels, midi_channels * 8,
- false);
- if (err < 0)
- goto end;
-
- /* establish connections for both streams */
- err = cmp_connection_establish(&bebob->out_conn,
- amdtp_stream_get_max_payload(&bebob->tx_stream));
- if (err < 0)
- goto end;
- err = cmp_connection_establish(&bebob->in_conn,
- amdtp_stream_get_max_payload(&bebob->rx_stream));
- if (err < 0) {
- cmp_connection_break(&bebob->out_conn);
- goto end;
- }
-
- bebob->connected = true;
-end:
- return err;
-}
-
-static void
-break_both_connections(struct snd_bebob *bebob)
+static void break_both_connections(struct snd_bebob *bebob)
{
cmp_connection_break(&bebob->in_conn);
cmp_connection_break(&bebob->out_conn);
-
- bebob->connected = false;
-
- /* These models seems to be in transition state for a longer time. */
- if (bebob->maudio_special_quirk != NULL)
- msleep(200);
}
-static void
-destroy_both_connections(struct snd_bebob *bebob)
-{
- cmp_connection_destroy(&bebob->in_conn);
- cmp_connection_destroy(&bebob->out_conn);
-}
-
-static int
-start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
- unsigned int rate)
+static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
int err = 0;
@@ -495,205 +413,274 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
else
conn = &bebob->out_conn;
- /* channel mapping */
+ // channel mapping.
if (bebob->maudio_special_quirk == NULL) {
err = map_data_channels(bebob, stream);
if (err < 0)
- goto end;
+ return err;
}
- /* start amdtp stream */
- err = amdtp_stream_start(stream,
- conn->resources.channel,
- conn->speed);
-end:
- 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);
}
-int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+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;
- err = init_both_connections(bebob);
+ 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)
- goto end;
+ return err;
- err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
- AMDTP_IN_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, bebob->unit, dir_stream, flags);
if (err < 0) {
- amdtp_stream_destroy(&bebob->tx_stream);
- destroy_both_connections(bebob);
- goto end;
+ cmp_connection_destroy(conn);
+ return err;
}
- /*
- * BeBoB v3 transfers packets with these qurks:
- * - In the beginning of streaming, the value of dbc is incremented
- * even if no data blocks are transferred.
- * - The value of dbc is reset suddenly.
- */
- if (bebob->version > 2)
- bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
- CIP_SKIP_DBC_ZERO_CHECK;
+ return 0;
+}
- /*
- * At high sampling rate, M-Audio special firmware transmits empty
- * packet with the value of dbc incremented by 8 but the others are
- * valid to IEC 61883-1.
- */
- if (bebob->maudio_special_quirk)
- bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
+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 = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
- AMDTP_OUT_STREAM, CIP_BLOCKING);
+ err = init_stream(bebob, &bebob->rx_stream);
if (err < 0) {
- amdtp_stream_destroy(&bebob->tx_stream);
- amdtp_stream_destroy(&bebob->rx_stream);
- destroy_both_connections(bebob);
+ destroy_stream(bebob, &bebob->tx_stream);
+ return err;
}
-end:
+
+ err = amdtp_domain_init(&bebob->domain);
+ if (err < 0) {
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
+ }
+
return err;
}
-int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
+static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int index)
{
- const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
- unsigned int curr_rate;
- int err = 0;
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
- /* Need no substreams */
- if (bebob->substreams_counter == 0)
- goto end;
+ 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;
+ }
- /*
- * Considering JACK/FFADO streaming:
- * TODO: This can be removed hwdep functionality becomes popular.
- */
- err = check_connection_used_by_others(bebob, &bebob->rx_stream);
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, false);
if (err < 0)
- goto end;
+ return err;
- /*
- * packet queueing error or detecting discontinuity
- *
- * At bus reset, connections should not be broken here. So streams need
- * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
- */
- if (amdtp_streaming_error(&bebob->rx_stream))
- amdtp_stream_stop(&bebob->rx_stream);
- if (amdtp_streaming_error(&bebob->tx_stream))
- amdtp_stream_stop(&bebob->tx_stream);
- if (!amdtp_stream_running(&bebob->rx_stream) &&
- !amdtp_stream_running(&bebob->tx_stream))
- break_both_connections(bebob);
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
- /* stop streams if rate is different */
- err = rate_spec->get(bebob, &curr_rate);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get sampling rate: %d\n", err);
- goto end;
- }
+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 (rate != curr_rate) {
- amdtp_stream_stop(&bebob->rx_stream);
- amdtp_stream_stop(&bebob->tx_stream);
+ 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);
}
- /* master should be always running */
- if (!amdtp_stream_running(&bebob->rx_stream)) {
- /*
- * 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.
- */
- if (bebob->maudio_special_quirk == NULL) {
- err = rate_spec->set(bebob, rate);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to set sampling rate: %d\n",
- err);
- goto end;
- }
+ 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 = make_both_connections(bebob, rate);
+ err = get_formation_index(rate, &index);
if (err < 0)
- goto end;
+ return err;
- err = start_stream(bebob, &bebob->rx_stream, rate);
+ 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) {
- dev_err(&bebob->unit->device,
- "fail to run AMDTP master stream:%d\n", err);
- break_both_connections(bebob);
- goto end;
+ cmp_connection_release(&bebob->out_conn);
+ return err;
}
- /*
- * NOTE:
- * The firmware customized by M-Audio uses these commands to
- * start transmitting stream. This is not usual way.
- */
- if (bebob->maudio_special_quirk != NULL) {
- err = rate_spec->set(bebob, rate);
+ 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);
- amdtp_stream_stop(&bebob->rx_stream);
- break_both_connections(bebob);
- goto end;
+ goto error;
}
}
- /* wait first callback */
- if (!amdtp_stream_wait_callback(&bebob->rx_stream,
- CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(&bebob->rx_stream);
- break_both_connections(bebob);
+ // 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 end;
+ goto error;
}
}
- /* start slave if needed */
- if (!amdtp_stream_running(&bebob->tx_stream)) {
- err = start_stream(bebob, &bebob->tx_stream, rate);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to run AMDTP slave stream:%d\n", err);
- amdtp_stream_stop(&bebob->rx_stream);
- break_both_connections(bebob);
- goto end;
- }
-
- /* wait first callback */
- if (!amdtp_stream_wait_callback(&bebob->tx_stream,
- CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(&bebob->tx_stream);
- amdtp_stream_stop(&bebob->rx_stream);
- break_both_connections(bebob);
- err = -ETIMEDOUT;
- }
- }
-end:
+ 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_stream_pcm_abort(&bebob->rx_stream);
- amdtp_stream_stop(&bebob->rx_stream);
-
- amdtp_stream_pcm_abort(&bebob->tx_stream);
- amdtp_stream_stop(&bebob->tx_stream);
-
+ amdtp_domain_stop(&bebob->domain);
break_both_connections(bebob);
+
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
}
}
@@ -703,10 +690,10 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
*/
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
{
- amdtp_stream_destroy(&bebob->rx_stream);
- amdtp_stream_destroy(&bebob->tx_stream);
+ amdtp_domain_destroy(&bebob->domain);
- destroy_both_connections(bebob);
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
}
/*
@@ -784,42 +771,42 @@ parse_stream_formation(u8 *buf, unsigned int len,
return 0;
}
-static int
-fill_stream_formations(struct snd_bebob *bebob, enum avc_bridgeco_plug_dir dir,
- unsigned short pid)
+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;
- struct snd_bebob_stream_formation *formations;
unsigned int len, eid;
- u8 addr[AVC_BRIDGECO_ADDR_BYTES];
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;
- if (dir == AVC_BRIDGECO_PLUG_DIR_IN)
- formations = bebob->rx_stream_formations;
- else
- formations = bebob->tx_stream_formations;
+ 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);
- for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; eid++) {
len = FORMAT_MAXIMUM_LENGTH;
- avc_bridgeco_fill_unit_addr(addr, dir,
- AVC_BRIDGECO_PLUG_UNIT_ISOC, pid);
- err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf,
- &len, eid);
- /* No entries remained. */
+ 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 %s plug %d:%d\n",
- eid,
- (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
- "out",
- pid, err);
+ "fail to get stream format %d for isoc %d plug %d:%d\n",
+ eid, plug_dir, plug_id, err);
break;
}
@@ -832,6 +819,54 @@ fill_stream_formations(struct snd_bebob *bebob, enum avc_bridgeco_plug_dir dir,
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)
{
@@ -874,8 +909,6 @@ 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];
- enum avc_bridgeco_plug_type type;
- unsigned int i;
int err;
/* the number of plugs for isoc in/out, ext in/out */
@@ -896,67 +929,25 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
goto end;
}
- avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
- AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
- err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get type for isoc in plug 0: %d\n", err);
- goto end;
- } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
- err = -ENOSYS;
- goto end;
- }
- err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_IN, 0);
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_IN, 0,
+ bebob->rx_stream_formations);
if (err < 0)
goto end;
- avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
- AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
- err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get type for isoc out plug 0: %d\n", err);
- goto end;
- } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
- err = -ENOSYS;
- goto end;
- }
- err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_OUT, 0);
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_OUT, 0,
+ bebob->tx_stream_formations);
if (err < 0)
goto end;
- /* count external input plugs for MIDI */
- bebob->midi_input_ports = 0;
- for (i = 0; i < plugs[2]; i++) {
- avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
- AVC_BRIDGECO_PLUG_UNIT_EXT, i);
- err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get type for external in plug %d: %d\n",
- i, err);
- goto end;
- } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
- bebob->midi_input_ports++;
- }
- }
+ 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;
- /* count external output plugs for MIDI */
- bebob->midi_output_ports = 0;
- for (i = 0; i < plugs[3]; i++) {
- avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
- AVC_BRIDGECO_PLUG_UNIT_EXT, i);
- err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
- if (err < 0) {
- dev_err(&bebob->unit->device,
- "fail to get type for external out plug %d: %d\n",
- i, err);
- goto end;
- } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
- bebob->midi_output_ports++;
- }
- }
+ 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)
@@ -973,33 +964,24 @@ void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
{
- int err;
-
- spin_lock_irq(&bebob->lock);
+ guard(spinlock_irq)(&bebob->lock);
/* user land lock this */
- if (bebob->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (bebob->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
{
- spin_lock_irq(&bebob->lock);
+ guard(spinlock_irq)(&bebob->lock);
if (WARN_ON(bebob->dev_lock_count <= 0))
- goto end;
+ return;
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
index 9770c2127a7a..c2dd074eca32 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_terratec.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
diff --git a/sound/firewire/bebob/bebob_yamaha_terratec.c b/sound/firewire/bebob/bebob_yamaha_terratec.c
index 8bd78fef3516..ce1975e6ab86 100644
--- a/sound/firewire/bebob/bebob_yamaha_terratec.c
+++ b/sound/firewire/bebob/bebob_yamaha_terratec.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bebob_yamaha.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index ae3bc1940efa..b2b76c7c71b3 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Connection Management Procedures (IEC 61883-1) helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/device.h>
@@ -185,6 +185,28 @@ void cmp_connection_destroy(struct cmp_connection *c)
}
EXPORT_SYMBOL(cmp_connection_destroy);
+int cmp_connection_reserve(struct cmp_connection *c,
+ unsigned int max_payload_bytes)
+{
+ guard(mutex)(&c->mutex);
+
+ if (WARN_ON(c->resources.allocated))
+ return -EBUSY;
+
+ c->speed = min(c->max_speed,
+ fw_parent_device(c->resources.unit)->max_speed);
+
+ return fw_iso_resources_allocate(&c->resources, max_payload_bytes,
+ c->speed);
+}
+EXPORT_SYMBOL(cmp_connection_reserve);
+
+void cmp_connection_release(struct cmp_connection *c)
+{
+ guard(mutex)(&c->mutex);
+ fw_iso_resources_free(&c->resources);
+}
+EXPORT_SYMBOL(cmp_connection_release);
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
{
@@ -262,7 +284,6 @@ static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
/**
* cmp_connection_establish - establish a connection to the target
* @c: the connection manager
- * @max_payload_bytes: the amount of data (including CIP headers) per packet
*
* This function establishes a point-to-point connection from the local
* computer to the target by allocating isochronous resources (channel and
@@ -270,25 +291,16 @@ static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
* When this function succeeds, the caller is responsible for starting
* transmitting packets.
*/
-int cmp_connection_establish(struct cmp_connection *c,
- unsigned int max_payload_bytes)
+int cmp_connection_establish(struct cmp_connection *c)
{
int err;
+ guard(mutex)(&c->mutex);
+
if (WARN_ON(c->connected))
return -EISCONN;
- c->speed = min(c->max_speed,
- fw_parent_device(c->resources.unit)->max_speed);
-
- mutex_lock(&c->mutex);
-
retry_after_bus_reset:
- err = fw_iso_resources_allocate(&c->resources,
- max_payload_bytes, c->speed);
- if (err < 0)
- goto err_mutex;
-
if (c->direction == CMP_OUTPUT)
err = pcr_modify(c, opcr_set_modify, pcr_set_check,
ABORT_ON_BUS_RESET);
@@ -297,76 +309,17 @@ retry_after_bus_reset:
ABORT_ON_BUS_RESET);
if (err == -EAGAIN) {
- fw_iso_resources_free(&c->resources);
- goto retry_after_bus_reset;
+ err = fw_iso_resources_update(&c->resources);
+ if (err >= 0)
+ goto retry_after_bus_reset;
}
- if (err < 0)
- goto err_resources;
-
- c->connected = true;
-
- mutex_unlock(&c->mutex);
-
- return 0;
-
-err_resources:
- fw_iso_resources_free(&c->resources);
-err_mutex:
- mutex_unlock(&c->mutex);
+ if (err >= 0)
+ c->connected = true;
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_resources;
-
- mutex_unlock(&c->mutex);
-
- return 0;
-
-err_resources:
- fw_iso_resources_free(&c->resources);
-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);
@@ -384,21 +337,15 @@ void cmp_connection_break(struct cmp_connection *c)
{
int err;
- mutex_lock(&c->mutex);
+ guard(mutex)(&c->mutex);
- if (!c->connected) {
- mutex_unlock(&c->mutex);
+ if (!c->connected)
return;
- }
err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
if (err < 0)
cmp_error(c, "plug is still connected\n");
- fw_iso_resources_free(&c->resources);
-
c->connected = false;
-
- mutex_unlock(&c->mutex);
}
EXPORT_SYMBOL(cmp_connection_break);
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
index b60b415caa8f..66fc08b742d2 100644
--- a/sound/firewire/cmp.h
+++ b/sound/firewire/cmp.h
@@ -42,9 +42,11 @@ int cmp_connection_init(struct cmp_connection *connection,
int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
void cmp_connection_destroy(struct cmp_connection *connection);
-int cmp_connection_establish(struct cmp_connection *connection,
- unsigned int max_payload);
-int cmp_connection_update(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);
void cmp_connection_break(struct cmp_connection *connection);
#endif
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 55b4be9b0034..36e25a3cf3c6 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,3 +1,6 @@
-snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
- dice-pcm.o dice-hwdep.o dice.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-dice-y := 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 dice-weiss.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
index 7a8af0f91c96..747ff0952483 100644
--- a/sound/firewire/dice/dice-hwdep.c
+++ b/sound/firewire/dice/dice-hwdep.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
@@ -56,18 +55,14 @@ 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);
+ guard(spinlock_irq)(&dice->lock);
if (dice->dev_lock_changed || dice->notification_bits != 0)
- events = POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&dice->lock);
-
- return events;
+ return 0;
}
static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
@@ -80,7 +75,7 @@ static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -91,48 +86,35 @@ static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
static int hwdep_lock(struct snd_dice *dice)
{
- int err;
-
- spin_lock_irq(&dice->lock);
+ guard(spinlock_irq)(&dice->lock);
if (dice->dev_lock_count == 0) {
dice->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&dice->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_dice *dice)
{
- int err;
-
- spin_lock_irq(&dice->lock);
+ guard(spinlock_irq)(&dice->lock);
if (dice->dev_lock_count == -1) {
dice->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -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);
+ guard(spinlock_irq)(&dice->lock);
if (dice->dev_lock_count == -1)
dice->dev_lock_count = 0;
- spin_unlock_irq(&dice->lock);
return 0;
}
@@ -180,7 +162,7 @@ int snd_dice_create_hwdep(struct snd_dice *dice)
err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
if (err < 0)
return err;
- strcpy(hwdep->name, "DICE");
+ strscpy(hwdep->name, "DICE");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
hwdep->ops = ops;
hwdep->private_data = dice;
diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h
index 15a484b05298..9cad3d608229 100644
--- a/sound/firewire/dice/dice-interface.h
+++ b/sound/firewire/dice/dice-interface.h
@@ -175,13 +175,18 @@
#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
-/* Some old firmware versions do not have the following global registers: */
-
/*
* Supported sample rates and clock sources; read-only.
*/
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index 8ff6da3c51f7..722bce379345 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* dice_midi.c - a part of driver for Dice based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
@@ -16,12 +15,15 @@ static int midi_open(struct snd_rawmidi_substream *substream)
if (err < 0)
return err;
- mutex_lock(&dice->mutex);
-
- dice->substreams_counter++;
- err = snd_dice_stream_start_duplex(dice, 0);
-
- mutex_unlock(&dice->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_dice_stream_lock_release(dice);
@@ -33,12 +35,10 @@ 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);
+ scoped_guard(mutex, &dice->mutex) {
+ --dice->substreams_counter;
+ snd_dice_stream_stop_duplex(dice);
+ }
snd_dice_stream_lock_release(dice);
return 0;
@@ -47,9 +47,8 @@ static int midi_close(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&dice->lock);
if (up)
amdtp_am824_midi_trigger(&dice->tx_stream[0],
@@ -57,16 +56,13 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
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);
+ guard(spinlock_irqsave)(&dice->lock);
if (up)
amdtp_am824_midi_trigger(&dice->rx_stream[0],
@@ -74,8 +70,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
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,
@@ -84,8 +78,8 @@ static void set_midi_substream_names(struct snd_dice *dice,
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);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", dice->card->shortname, subs->number + 1);
}
}
@@ -101,27 +95,18 @@ int snd_dice_create_midi(struct snd_dice *dice)
.close = midi_close,
.trigger = midi_playback_trigger,
};
- __be32 reg;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
unsigned int midi_in_ports, midi_out_ports;
+ int i;
int err;
- /*
- * Use the number of MIDI conformant data channel at current sampling
- * transfer frequency.
- */
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
- &reg, sizeof(reg));
- if (err < 0)
- return err;
- midi_in_ports = be32_to_cpu(reg);
-
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
- &reg, sizeof(reg));
- if (err < 0)
- return err;
- midi_out_ports = be32_to_cpu(reg);
+ 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;
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
index 7cb9e9713ac3..d5319cd2cc6f 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -1,51 +1,122 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#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, unsigned int size)
+ unsigned int index)
{
struct snd_pcm_hardware *hw = &runtime->hw;
- struct amdtp_stream *stream;
- unsigned int rate;
- __be32 reg;
- int err;
-
- /*
- * Retrieve current Multi Bit Linear Audio data channel and limit to
- * it.
- */
- if (dir == AMDTP_IN_STREAM) {
- stream = &dice->tx_stream[index];
- err = snd_dice_transaction_read_tx(dice,
- size * index + TX_NUMBER_AUDIO,
- &reg, sizeof(reg));
- } else {
- stream = &dice->rx_stream[index];
- err = snd_dice_transaction_read_rx(dice,
- size * index + RX_NUMBER_AUDIO,
- &reg, sizeof(reg));
+ 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);
}
- if (err < 0)
- return err;
-
- hw->channels_min = hw->channels_max = be32_to_cpu(reg);
-
- /* Retrieve current sampling transfer frequency and limit to it. */
- err = snd_dice_transaction_get_rate(dice, &rate);
- if (err < 0)
- return err;
- hw->rates = snd_pcm_rate_to_rate_bit(rate);
snd_pcm_limit_hw_rates(runtime);
return 0;
@@ -56,36 +127,34 @@ static int init_hw_info(struct snd_dice *dice,
{
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;
- __be32 reg[2];
- unsigned int count, size;
int err;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = AM824_IN_PCM_FORMAT_BITS;
dir = AMDTP_IN_STREAM;
- stream = &dice->tx_stream[substream->pcm->device];
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
- sizeof(reg));
+ stream = &dice->tx_stream[index];
} else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
dir = AMDTP_OUT_STREAM;
- stream = &dice->rx_stream[substream->pcm->device];
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
- sizeof(reg));
+ stream = &dice->rx_stream[index];
}
+ err = limit_channels_and_rates(dice, substream->runtime, dir,
+ index);
if (err < 0)
return err;
- count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
- if (substream->pcm->device >= count)
- return -ENXIO;
-
- size = be32_to_cpu(reg[1]) * 4;
- err = limit_channels_and_rates(dice, substream->runtime, dir,
- substream->pcm->device, size);
+ 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;
@@ -95,19 +164,80 @@ static int init_hw_info(struct snd_dice *dice,
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)
- goto end;
+ 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;
+ }
+
+ scoped_guard(mutex, &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)
+ 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)
+ 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);
-end:
- return err;
+
+ return 0;
err_locked:
snd_dice_stream_lock_release(dice);
return err;
@@ -122,75 +252,44 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_dice *dice = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&dice->mutex);
- dice->substreams_counter++;
- mutex_unlock(&dice->mutex);
- }
-
- return 0;
-}
-static int playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&dice->mutex);
- dice->substreams_counter++;
- mutex_unlock(&dice->mutex);
+ 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);
+
+ guard(mutex)(&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;
}
- return 0;
-}
-
-static int capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_dice *dice = substream->private_data;
-
- mutex_lock(&dice->mutex);
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- dice->substreams_counter--;
-
- snd_dice_stream_stop_duplex(dice);
-
- mutex_unlock(&dice->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return err;
}
-static int playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
- mutex_lock(&dice->mutex);
+ guard(mutex)(&dice->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- dice->substreams_counter--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dice->substreams_counter;
snd_dice_stream_stop_duplex(dice);
- mutex_unlock(&dice->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int capture_prepare(struct snd_pcm_substream *substream)
@@ -199,9 +298,9 @@ static int capture_prepare(struct snd_pcm_substream *substream)
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
- mutex_lock(&dice->mutex);
- err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
- mutex_unlock(&dice->mutex);
+ scoped_guard(mutex, &dice->mutex) {
+ err = snd_dice_stream_start_duplex(dice);
+ }
if (err >= 0)
amdtp_stream_pcm_prepare(stream);
@@ -213,9 +312,9 @@ static int playback_prepare(struct snd_pcm_substream *substream)
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
- mutex_lock(&dice->mutex);
- err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
- mutex_unlock(&dice->mutex);
+ scoped_guard(mutex, &dice->mutex) {
+ err = snd_dice_stream_start_duplex(dice);
+ }
if (err >= 0)
amdtp_stream_pcm_prepare(stream);
@@ -264,14 +363,14 @@ 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_stream_pcm_pointer(stream);
+ 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_stream_pcm_pointer(stream);
+ return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
}
static int capture_ack(struct snd_pcm_substream *substream)
@@ -279,7 +378,7 @@ 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_stream_pcm_ack(stream);
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
}
static int playback_ack(struct snd_pcm_substream *substream)
@@ -287,7 +386,7 @@ 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_stream_pcm_ack(stream);
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
}
int snd_dice_create_pcm(struct snd_dice *dice)
@@ -295,67 +394,44 @@ int snd_dice_create_pcm(struct snd_dice *dice)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = capture_hw_params,
- .hw_free = capture_hw_free,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
.prepare = capture_prepare,
.trigger = capture_trigger,
.pointer = capture_pointer,
.ack = capture_ack,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = playback_hw_params,
- .hw_free = playback_hw_free,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
.prepare = playback_prepare,
.trigger = playback_trigger,
.pointer = playback_pointer,
.ack = playback_ack,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
- __be32 reg;
struct snd_pcm *pcm;
- unsigned int i, max_capture, max_playback, capture, playback;
+ unsigned int capture, playback;
+ int i, j;
int err;
- /* Check whether PCM substreams are required. */
- if (dice->force_two_pcms) {
- max_capture = max_playback = 2;
- } else {
- max_capture = max_playback = 0;
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
- max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
-
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
- max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
- }
-
for (i = 0; i < MAX_STREAMS; i++) {
capture = playback = 0;
- if (i < max_capture)
- capture = 1;
- if (i < max_playback)
- playback = 1;
- if (capture == 0 && playback == 0)
- break;
+ 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);
+ pcm->nonatomic = true;
+ strscpy(pcm->name, dice->card->shortname);
if (capture > 0)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
@@ -364,6 +440,9 @@ int snd_dice_create_pcm(struct snd_dice *dice)
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
index f5c1d1bced59..db0a03123c4f 100644
--- a/sound/firewire/dice/dice-proc.c
+++ b/sound/firewire/dice/dice-proc.c
@@ -1,10 +1,9 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
@@ -148,12 +147,12 @@ static void dice_proc_read(struct snd_info_entry *entry,
>> 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);
- 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);
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))
@@ -243,10 +242,66 @@ static void dice_proc_read(struct snd_info_entry *entry,
}
}
-void snd_dice_create_proc(struct snd_dice *dice)
+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;
- if (!snd_card_proc_new(dice->card, "dice", &entry))
- snd_info_set_text_ops(entry, dice, dice_proc_read);
+ 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
index 8573289c381e..d5ffe7c82993 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -1,16 +1,15 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
-#define CALLBACK_TIMEOUT 200
-#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
+#define READY_TIMEOUT_MS 200
+#define NOTIFICATION_TIMEOUT_MS 100
struct reg_params {
unsigned int count;
@@ -30,13 +29,39 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
[6] = 192000,
};
-/*
- * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
- * to GLOBAL_STATUS. Especially, just after powering on, these are different.
- */
-static int ensure_phase_lock(struct snd_dice *dice)
+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, nominal;
+ __be32 reg, new;
+ u32 data;
+ int i;
int err;
err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
@@ -44,26 +69,29 @@ static int ensure_phase_lock(struct snd_dice *dice)
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,
- &reg, sizeof(reg));
+ &new, sizeof(new));
if (err < 0)
return err;
if (wait_for_completion_timeout(&dice->clock_accepted,
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
- /*
- * Old versions of Dice firmware transfer no notification when
- * the same clock status as current one is set. In this case,
- * just check current clock status.
- */
- err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
- &nominal, sizeof(nominal));
- if (err < 0)
- return err;
- if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
+ if (reg != new)
return -ETIMEDOUT;
}
@@ -96,18 +124,9 @@ static int get_register_params(struct snd_dice *dice,
static void release_resources(struct snd_dice *dice)
{
- unsigned int i;
-
- for (i = 0; i < MAX_STREAMS; i++) {
- if (amdtp_stream_running(&dice->tx_stream[i])) {
- amdtp_stream_pcm_abort(&dice->tx_stream[i]);
- amdtp_stream_stop(&dice->tx_stream[i]);
- }
- if (amdtp_stream_running(&dice->rx_stream[i])) {
- amdtp_stream_pcm_abort(&dice->rx_stream[i]);
- amdtp_stream_stop(&dice->rx_stream[i]);
- }
+ 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]);
}
@@ -133,36 +152,23 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
}
}
-static int keep_resources(struct snd_dice *dice,
- enum amdtp_stream_direction dir, unsigned int index,
- unsigned int rate, unsigned int pcm_chs,
- unsigned int midi_ports)
+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)
{
- struct amdtp_stream *stream;
- struct fw_iso_resources *resources;
bool double_pcm_frames;
unsigned int i;
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];
- }
-
- /*
- * 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;
+ // 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;
@@ -188,30 +194,39 @@ static int keep_resources(struct snd_dice *dice,
fw_parent_device(dice->unit)->max_speed);
}
-static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
- unsigned int rate, struct reg_params *params)
+static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
+ enum amdtp_stream_direction dir,
+ struct reg_params *params)
{
- __be32 reg[2];
- unsigned int i, pcm_chs, midi_ports;
- struct amdtp_stream *streams;
- struct fw_iso_resources *resources;
- struct fw_device *fw_dev = fw_parent_device(dice->unit);
- int err = 0;
+ enum snd_dice_rate_mode mode;
+ int i;
+ int err;
- if (dir == AMDTP_IN_STREAM) {
- streams = dice->tx_stream;
- resources = dice->tx_resources;
- } else {
- streams = dice->rx_stream;
- resources = dice->rx_resources;
- }
+ 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;
- for (i = 0; i < params->count; i++) {
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));
@@ -221,39 +236,142 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
pcm_chs = be32_to_cpu(reg[0]);
midi_ports = be32_to_cpu(reg[1]);
- err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
+ // 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;
- reg[0] = cpu_to_be32(resources[i].channel);
+ 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[0]));
+ &reg, sizeof(reg));
} else {
err = snd_dice_transaction_write_rx(dice,
params->size * i + RX_ISOCHRONOUS,
- reg, sizeof(reg[0]));
+ &reg, sizeof(reg));
}
if (err < 0)
return err;
if (dir == AMDTP_IN_STREAM) {
- reg[0] = cpu_to_be32(fw_dev->max_speed);
+ reg = cpu_to_be32(max_speed);
err = snd_dice_transaction_write_tx(dice,
params->size * i + TX_SPEED,
- reg, sizeof(reg[0]));
+ &reg, sizeof(reg));
if (err < 0)
return err;
}
- err = amdtp_stream_start(&streams[i], resources[i].channel,
- fw_dev->max_speed);
+ err = amdtp_domain_add_stream(&dice->domain, stream,
+ resources->channel, max_speed);
if (err < 0)
return err;
}
- return err;
+ return 0;
}
/*
@@ -261,12 +379,13 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
* - None streams are running.
* - All streams are running.
*/
-int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
+int snd_dice_stream_start_duplex(struct snd_dice *dice)
{
- unsigned int curr_rate;
- unsigned int i;
+ unsigned int generation = dice->rx_resources[0].generation;
struct reg_params tx_params, rx_params;
- bool need_to_start;
+ unsigned int i;
+ unsigned int rate;
+ enum snd_dice_rate_mode mode;
int err;
if (dice->substreams_counter == 0)
@@ -276,50 +395,46 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
if (err < 0)
return err;
- err = snd_dice_transaction_get_rate(dice, &curr_rate);
- if (err < 0) {
- dev_err(&dice->unit->device,
- "fail to get sampling rate\n");
- return err;
- }
- if (rate == 0)
- rate = curr_rate;
- if (rate != curr_rate)
- return -EINVAL;
-
- /* Judge to need to restart streams. */
- for (i = 0; i < MAX_STREAMS; i++) {
- if (i < tx_params.count) {
- if (amdtp_streaming_error(&dice->tx_stream[i]) ||
- !amdtp_stream_running(&dice->tx_stream[i]))
- break;
- }
- if (i < rx_params.count) {
- if (amdtp_streaming_error(&dice->rx_stream[i]) ||
- !amdtp_stream_running(&dice->rx_stream[i]))
- break;
+ // 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;
}
}
- need_to_start = (i < MAX_STREAMS);
-
- if (need_to_start) {
- /* Stop transmission. */
- snd_dice_transaction_clear_enable(dice);
- stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
- stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
- release_resources(dice);
- err = ensure_phase_lock(dice);
- if (err < 0) {
- dev_err(&dice->unit->device,
- "fail to ensure phase lock\n");
- return err;
+ 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);
}
+ }
- /* Start both streams. */
+ // 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;
@@ -331,25 +446,24 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
goto error;
}
- for (i = 0; i < MAX_STREAMS; i++) {
- if ((i < tx_params.count &&
- !amdtp_stream_wait_callback(&dice->tx_stream[i],
- CALLBACK_TIMEOUT)) ||
- (i < rx_params.count &&
- !amdtp_stream_wait_callback(&dice->rx_stream[i],
- CALLBACK_TIMEOUT))) {
- err = -ETIMEDOUT;
- 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 err;
+ return 0;
error:
- snd_dice_transaction_clear_enable(dice);
- stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
- stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
- release_resources(dice);
+ amdtp_domain_stop(&dice->domain);
+ finish_session(dice, &tx_params, &rx_params);
return err;
}
@@ -362,17 +476,13 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
{
struct reg_params tx_params, rx_params;
- if (dice->substreams_counter > 0)
- return;
+ if (dice->substreams_counter == 0) {
+ if (get_register_params(dice, &tx_params, &rx_params) >= 0)
+ finish_session(dice, &tx_params, &rx_params);
- snd_dice_transaction_clear_enable(dice);
-
- if (get_register_params(dice, &tx_params, &rx_params) == 0) {
- stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
- stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ amdtp_domain_stop(&dice->domain);
+ release_resources(dice);
}
-
- release_resources(dice);
}
static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
@@ -435,7 +545,7 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
err = init_stream(dice, AMDTP_IN_STREAM, i);
if (err < 0) {
for (; i >= 0; i--)
- destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
goto end;
}
}
@@ -447,7 +557,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
destroy_stream(dice, AMDTP_OUT_STREAM, i);
for (i = 0; i < MAX_STREAMS; i++)
destroy_stream(dice, AMDTP_IN_STREAM, i);
- break;
+ 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:
@@ -462,6 +580,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
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)
@@ -479,11 +599,76 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
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;
@@ -492,32 +677,23 @@ static void dice_lock_changed(struct snd_dice *dice)
int snd_dice_stream_lock_try(struct snd_dice *dice)
{
- int err;
-
- spin_lock_irq(&dice->lock);
+ guard(spinlock_irq)(&dice->lock);
- if (dice->dev_lock_count < 0) {
- err = -EBUSY;
- goto out;
- }
+ if (dice->dev_lock_count < 0)
+ return -EBUSY;
if (dice->dev_lock_count++ == 0)
dice_lock_changed(dice);
- err = 0;
-out:
- spin_unlock_irq(&dice->lock);
- return err;
+ return 0;
}
void snd_dice_stream_lock_release(struct snd_dice *dice)
{
- spin_lock_irq(&dice->lock);
+ guard(spinlock_irq)(&dice->lock);
if (WARN_ON(dice->dev_lock_count <= 0))
- goto out;
+ return;
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
index 0f0350320ae8..a3f7dfa990a4 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -1,10 +1,9 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
@@ -137,7 +136,6 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
{
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);
@@ -150,13 +148,13 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
bits = be32_to_cpup(data);
- spin_lock_irqsave(&dice->lock, flags);
- dice->notification_bits |= bits;
- spin_unlock_irqrestore(&dice->lock, flags);
+ scoped_guard(spinlock_irqsave, &dice->lock) {
+ dice->notification_bits |= bits;
+ }
fw_send_response(card, request, RCODE_COMPLETE);
- if (bits & NOTIFY_LOCK_CHG)
+ if (bits & NOTIFY_CLOCK_ACCEPTED)
complete(&dice->clock_accepted);
wake_up(&dice->hwdep_wait);
}
@@ -265,7 +263,7 @@ int snd_dice_transaction_reinit(struct snd_dice *dice)
static int get_subaddrs(struct snd_dice *dice)
{
static const int min_values[10] = {
- 10, 0x64 / 4,
+ 10, 0x60 / 4,
10, 0x18 / 4,
10, 0x18 / 4,
0, 0,
@@ -301,33 +299,40 @@ static int get_subaddrs(struct snd_dice *dice)
}
}
- /*
- * 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 (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;
+ 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;
- dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
- dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
- /* Set up later. */
- if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
- dice->clock_caps = 1;
+ /* 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;
diff --git a/sound/firewire/dice/dice-weiss.c b/sound/firewire/dice/dice-weiss.c
new file mode 100644
index 000000000000..129d43408956
--- /dev/null
+++ b/sound/firewire/dice/dice-weiss.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-weiss.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2023 Rolf Anderegg and Michele Perrone
+
+#include "dice.h"
+
+struct dice_weiss_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];
+};
+
+// Weiss DAC202: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss MAN301: 192kHz 2-channel music archive network player
+static const struct dice_weiss_spec man301 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT202: 192kHz unidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT203: 192kHz bidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int203 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss ADC2: 192kHz A/D converter with microphone preamps and line nputs
+static const struct dice_weiss_spec adc2 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss DAC2/Minerva: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac2_minerva = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+static const struct dice_weiss_spec vesta = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU interface
+static const struct dice_weiss_spec afi1 = {
+ .tx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+ .rx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+};
+
+int snd_dice_detect_weiss_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_weiss_spec *spec;
+ } *entry, entries[] = {
+ {0x000007, &dac202},
+ {0x000008, &dac202}, // Maya edition: same audio I/O as DAC202.
+ {0x000006, &int202},
+ {0x00000a, &int203},
+ {0x00000b, &man301},
+ {0x000001, &adc2},
+ {0x000003, &dac2_minerva},
+ {0x000002, &vesta},
+ {0x000004, &afi1},
+ };
+ 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));
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 4ddb4cdd054b..bcbe80344328 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -1,54 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TC Applied Technologies Digital Interface Communications Engine driver
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
MODULE_DESCRIPTION("DICE driver");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
#define OUI_WEISS 0x001c6a
#define OUI_LOUD 0x000ff2
#define OUI_FOCUSRITE 0x00130e
-#define OUI_TCELECTRONIC 0x001486
+#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
-/*
- * Some models support several isochronous channels, while these streams are not
- * always available. In this case, add the model name to this list.
- */
-static bool force_two_pcm_support(struct fw_unit *unit)
-{
- static const char *const models[] = {
- /* TC Electronic models. */
- "StudioKonnekt48",
- /* Focusrite models. */
- "SAFFIRE_PRO_40",
- "LIQUID_SAFFIRE_56",
- "SAFFIRE_PRO_40_1",
- };
- char model[32];
- unsigned int i;
- int err;
-
- err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
- if (err < 0)
- return false;
-
- for (i = 0; i < ARRAY_SIZE(models); i++) {
- if (strcmp(models[i], model) == 0)
- break;
- }
-
- return i < ARRAY_SIZE(models);
-}
+#define MODEL_ALESIS_IO_BOTH 0x000001
static int check_dice_category(struct fw_unit *unit)
{
@@ -75,15 +55,12 @@ static int check_dice_category(struct fw_unit *unit)
}
}
- if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
- if (force_two_pcm_support(unit))
- return 0;
- }
-
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) ||
@@ -125,9 +102,9 @@ static void dice_card_strings(struct snd_dice *dice)
unsigned int i;
int err;
- strcpy(card->driver, "DICE");
+ strscpy(card->driver, "DICE");
- strcpy(card->shortname, "DICE");
+ strscpy(card->shortname, "DICE");
BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME,
card->shortname,
@@ -140,54 +117,69 @@ static void dice_card_strings(struct snd_dice *dice)
card->shortname[sizeof(card->shortname) - 1] = '\0';
}
- strcpy(vendor, "?");
+ strscpy(vendor, "?");
fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
- strcpy(model, "?");
+ strscpy(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");
+ strscpy(card->mixername, "DICE");
}
-static void dice_free(struct snd_dice *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);
- fw_unit_put(dice->unit);
mutex_destroy(&dice->mutex);
- kfree(dice);
-}
-
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void dice_card_free(struct snd_card *card)
-{
- dice_free(card->private_data);
+ fw_unit_put(dice->unit);
}
-static void do_registration(struct work_struct *work)
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_dice *dice = container_of(work, struct snd_dice, dwork.work);
+ struct snd_card *card;
+ struct snd_dice *dice;
+ snd_dice_detect_formats_t detect_formats;
int err;
- if (dice->registered)
- return;
+ if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
+ err = check_dice_category(unit);
+ if (err < 0)
+ return -ENODEV;
+ }
- err = snd_card_new(&dice->unit->device, -1, NULL, THIS_MODULE, 0,
- &dice->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card);
if (err < 0)
- return;
+ 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;
- if (force_two_pcm_support(dice->unit))
- dice->force_two_pcms = 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)
@@ -199,6 +191,10 @@ static void do_registration(struct work_struct *work)
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;
@@ -217,112 +213,250 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(dice->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- /*
- * After registered, dice instance can be released corresponding to
- * releasing the sound card instance.
- */
- dice->card->private_free = dice_card_free;
- dice->card->private_data = dice;
- dice->registered = true;
-
- return;
-error:
- snd_dice_stream_destroy_duplex(dice);
- snd_dice_transaction_destroy(dice);
- snd_dice_stream_destroy_duplex(dice);
- snd_card_free(dice->card);
- dev_info(&dice->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
-{
- struct snd_dice *dice;
- int err;
-
- err = check_dice_category(unit);
- if (err < 0)
- return -ENODEV;
-
- /* Allocate this independent of sound card instance. */
- dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL);
- if (dice == NULL)
- return -ENOMEM;
-
- dice->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, dice);
-
- spin_lock_init(&dice->lock);
- mutex_init(&dice->mutex);
- init_completion(&dice->clock_accepted);
- init_waitqueue_head(&dice->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
- snd_fw_schedule_registration(unit, &dice->dwork);
-
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);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&dice->dwork);
-
- if (dice->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(dice->card);
- } else {
- /* Don't forget this case. */
- dice_free(dice);
- }
+ // 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);
- /* Postpone a workqueue for deferred registration. */
- if (!dice->registered)
- snd_fw_schedule_registration(unit, &dice->dwork);
-
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice);
- /*
- * After registration, userspace can start packet streaming, then this
- * code block works fine.
- */
- if (dice->registered) {
- mutex_lock(&dice->mutex);
- snd_dice_stream_update_duplex(dice);
- mutex_unlock(&dice->mutex);
- }
+ guard(mutex)(&dice->mutex);
+ snd_dice_stream_update_duplex(dice);
}
#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_VERSION,
- .version = DICE_INTERFACE,
+ .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/2626 has a different value in version field. */
+ /* M-Audio Profire 610 has a different value in version field. */
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_SPECIFIER_ID,
- .vendor_id = 0x000d6c,
- .specifier_id = 0x000d6c,
+ 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,
+ },
+ // Weiss DAC202: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000007,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC202: 192kHz 2-channel DAC (Maya edition)
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000008,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss MAN301: 192kHz 2-channel music archive network player
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000b,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT202: 192kHz unidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000006,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT203: 192kHz bidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000a,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss ADC2: 192kHz A/D converter with microphone preamps and inputs
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000001,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC2/Minerva: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000003,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000004,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VERSION,
+ .version = DICE_INTERFACE,
},
{ }
};
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index da00e75e09d4..4c0ad7335998 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -1,10 +1,9 @@
+/* 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_DICE_H_INCLUDED
@@ -63,15 +62,22 @@
*/
#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;
- bool registered;
- struct delayed_work dwork;
-
/* Offsets for sub-addresses */
unsigned int global_offset;
unsigned int rx_offset;
@@ -80,6 +86,10 @@ struct snd_dice {
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;
@@ -95,11 +105,12 @@ struct snd_dice {
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;
+ bool global_enabled:1;
+ bool disable_double_pcm_frames:1;
struct completion clock_accepted;
unsigned int substreams_counter;
- bool force_two_pcms;
+ struct amdtp_domain domain;
};
enum snd_dice_addr_type {
@@ -190,11 +201,17 @@ 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_start_duplex(struct snd_dice *dice, unsigned int rate);
+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);
@@ -207,4 +224,14 @@ 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);
+int snd_dice_detect_weiss_formats(struct snd_dice *dice);
+
#endif
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
index 1123e68c8b28..6dc18bd2e186 100644
--- a/sound/firewire/digi00x/Makefile
+++ b/sound/firewire/digi00x/Makefile
@@ -1,4 +1,5 @@
-snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-digi00x-y := 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
index 4a884a335248..7db0024495b7 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -1,11 +1,10 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm.h>
@@ -124,11 +123,11 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
* 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);
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1);
if (err < 0)
return err;
- s->fdf = AMDTP_FDF_AM824 | s->sfc;
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
p->pcm_channels = pcm_channels;
@@ -144,17 +143,23 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
}
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+ __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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++;
for (i = 0; i < frames; ++i) {
@@ -170,17 +175,23 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
}
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+ __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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++;
for (i = 0; i < frames; ++i) {
@@ -235,7 +246,7 @@ static inline void midi_use_bytes(struct amdtp_stream *s,
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
- unsigned int data_blocks)
+ unsigned int data_blocks, unsigned int data_block_counter)
{
struct amdtp_dot *p = s->protocol;
unsigned int f, port;
@@ -243,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
u8 *b;
for (f = 0; f < data_blocks; f++) {
- port = (s->data_block_counter + f) % 8;
+ port = (data_block_counter + f) % 8;
b = (u8 *)&buffer[0];
len = 0;
@@ -330,66 +341,65 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
WRITE_ONCE(p->midi[port], midi);
}
-static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+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_pcm_substream *pcm;
- unsigned int pcm_frames;
-
- pcm = READ_ONCE(s->pcm);
- if (pcm) {
- read_pcm_s32(s, pcm, buffer, data_blocks);
- pcm_frames = data_blocks;
- } else {
- pcm_frames = 0;
- }
+ 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, buffer, data_blocks);
+ read_midi_messages(s, buf, data_blocks);
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
- struct snd_pcm_substream *pcm;
- unsigned int pcm_frames;
-
- pcm = READ_ONCE(s->pcm);
- if (pcm) {
- write_pcm_s32(s, pcm, buffer, data_blocks);
- pcm_frames = data_blocks;
- } else {
- write_pcm_silence(s, buffer, data_blocks);
- pcm_frames = 0;
- }
+ 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;
- write_midi_messages(s, buffer, 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);
+ }
- return pcm_frames;
+ 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_data_blocks_t process_data_blocks;
- enum cip_flags flags;
-
- /* Use different mode between incoming/outgoing. */
- if (dir == AMDTP_IN_STREAM) {
- flags = CIP_NONBLOCKING;
- process_data_blocks = process_tx_data_blocks;
- } else {
- flags = CIP_BLOCKING;
- process_data_blocks = process_rx_data_blocks;
- }
+ 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_data_blocks, sizeof(struct amdtp_dot));
+ process_ctx_payloads, sizeof(struct amdtp_dot));
}
void amdtp_dot_reset(struct amdtp_stream *s)
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
index a084c2a834db..435d18417cf0 100644
--- a/sound/firewire/digi00x/digi00x-hwdep.c
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -1,9 +1,8 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -64,18 +63,14 @@ 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);
+ guard(spinlock_irq)(&dg00x->lock);
if (dg00x->dev_lock_changed || dg00x->msg)
- events = POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&dg00x->lock);
-
- return events;
+ return 0;
}
static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
@@ -88,7 +83,7 @@ static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -99,48 +94,35 @@ static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
static int hwdep_lock(struct snd_dg00x *dg00x)
{
- int err;
-
- spin_lock_irq(&dg00x->lock);
+ guard(spinlock_irq)(&dg00x->lock);
if (dg00x->dev_lock_count == 0) {
dg00x->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&dg00x->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_dg00x *dg00x)
{
- int err;
-
- spin_lock_irq(&dg00x->lock);
+ guard(spinlock_irq)(&dg00x->lock);
if (dg00x->dev_lock_count == -1) {
dg00x->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -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);
+ guard(spinlock_irq)(&dg00x->lock);
if (dg00x->dev_lock_count == -1)
dg00x->dev_lock_count = 0;
- spin_unlock_irq(&dg00x->lock);
return 0;
}
@@ -189,7 +171,7 @@ int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
if (err < 0)
return err;
- strcpy(hwdep->name, "Digi00x");
+ strscpy(hwdep->name, "Digi00x");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
hwdep->ops = ops;
hwdep->private_data = dg00x;
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
index 7ab3d0810f6b..bcdaf003514b 100644
--- a/sound/firewire/digi00x/digi00x-midi.c
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -1,9 +1,8 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "digi00x.h"
@@ -17,10 +16,15 @@ static int midi_open(struct snd_rawmidi_substream *substream)
if (err < 0)
return err;
- mutex_lock(&dg00x->mutex);
- dg00x->substreams_counter++;
- err = snd_dg00x_stream_start_duplex(dg00x, 0);
- mutex_unlock(&dg00x->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_dg00x_stream_lock_release(dg00x);
@@ -31,10 +35,10 @@ 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);
+ scoped_guard(mutex, &dg00x->mutex) {
+ --dg00x->substreams_counter;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ }
snd_dg00x_stream_lock_release(dg00x);
return 0;
@@ -45,21 +49,18 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
{
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);
+ guard(spinlock_irqsave)(&dg00x->lock);
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,
@@ -67,21 +68,18 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
{
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);
+ guard(spinlock_irqsave)(&dg00x->lock);
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,
@@ -96,14 +94,14 @@ static void set_substream_names(struct snd_dg00x *dg00x,
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);
+ scnprintf(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);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
}
}
}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
index 796f4b4645f5..75f81545d50c 100644
--- a/sound/firewire/digi00x/digi00x-pcm.c
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -1,9 +1,8 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "digi00x.h"
@@ -101,14 +100,14 @@ static int pcm_init_hw_params(struct snd_dg00x *dg00x,
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;
- unsigned int rate;
int err;
err = snd_dg00x_stream_lock_try(dg00x);
if (err < 0)
- goto end;
+ return err;
err = pcm_init_hw_params(dg00x, substream);
if (err < 0)
@@ -128,19 +127,41 @@ static int pcm_open(struct snd_pcm_substream *substream)
}
}
- if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
- amdtp_stream_pcm_running(&dg00x->rx_stream) ||
- amdtp_stream_pcm_running(&dg00x->tx_stream)) {
- err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
- if (err < 0)
- goto err_locked;
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
+ scoped_guard(mutex, &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)
+ 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);
-end:
- return err;
+
+ return 0;
err_locked:
snd_dg00x_stream_lock_release(dg00x);
return err;
@@ -155,111 +176,68 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&dg00x->mutex);
- dg00x->substreams_counter++;
- mutex_unlock(&dg00x->mutex);
+ 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);
+
+ guard(mutex)(&dg00x->mutex);
+ err = snd_dg00x_stream_reserve_duplex(dg00x, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++dg00x->substreams_counter;
}
- return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_dg00x *dg00x = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&dg00x->mutex);
- dg00x->substreams_counter++;
- mutex_unlock(&dg00x->mutex);
- }
-
- return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_dg00x *dg00x = substream->private_data;
-
- mutex_lock(&dg00x->mutex);
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- dg00x->substreams_counter--;
-
- snd_dg00x_stream_stop_duplex(dg00x);
-
- mutex_unlock(&dg00x->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return err;
}
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_dg00x *dg00x = substream->private_data;
- mutex_lock(&dg00x->mutex);
+ guard(mutex)(&dg00x->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- dg00x->substreams_counter--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dg00x->substreams_counter;
snd_dg00x_stream_stop_duplex(dg00x);
- mutex_unlock(&dg00x->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_dg00x *dg00x = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&dg00x->mutex);
+ guard(mutex)(&dg00x->mutex);
- err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ 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;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&dg00x->mutex);
+ guard(mutex)(&dg00x->mutex);
- err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ 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;
}
@@ -303,28 +281,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_dg00x *dg00x = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+ 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_stream_pcm_pointer(&dg00x->rx_stream);
+ 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_stream_pcm_ack(&dg00x->tx_stream);
+ 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_stream_pcm_ack(&dg00x->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream);
}
int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
@@ -332,27 +310,22 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_capture_hw_params,
- .hw_free = pcm_capture_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_playback_hw_params,
- .hw_free = pcm_playback_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -362,10 +335,12 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
return err;
pcm->private_data = dg00x;
+ pcm->nonatomic = true;
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
index a1d601f31165..00b047fefb8d 100644
--- a/sound/firewire/digi00x/digi00x-proc.c
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -1,9 +1,8 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "digi00x.h"
@@ -79,21 +78,9 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- return;
- }
+ root->mode = S_IFDIR | 0555;
entry = snd_info_create_card_entry(dg00x->card, "clock", root);
- if (entry == NULL) {
- snd_info_free_entry(root);
- return;
- }
-
- snd_info_set_text_ops(entry, dg00x, proc_read_clock);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- snd_info_free_entry(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
index 4d3b4ebbdd49..250ffdb26ebd 100644
--- a/sound/firewire/digi00x/digi00x-stream.c
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -1,14 +1,13 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "digi00x.h"
-#define CALLBACK_TIMEOUT 500
+#define READY_TIMEOUT_MS 200
const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
[SND_DG00X_RATE_44100] = 44100,
@@ -125,11 +124,22 @@ int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
static void finish_session(struct snd_dg00x *dg00x)
{
- __be32 data = cpu_to_be32(0x00000003);
+ __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)
@@ -138,11 +148,20 @@ static int begin_session(struct snd_dg00x *dg00x)
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)
- goto error;
+ return err;
curr = be32_to_cpu(data);
if (curr == 0)
@@ -157,39 +176,23 @@ static int begin_session(struct snd_dg00x *dg00x)
DG00X_OFFSET_STREAMING_SET,
&data, sizeof(data), 0);
if (err < 0)
- goto error;
+ break;
msleep(20);
curr--;
}
- return 0;
-error:
- finish_session(dg00x);
return err;
}
-static void release_resources(struct snd_dg00x *dg00x)
+static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
+ unsigned int rate)
{
- __be32 data = 0;
-
- /* Unregister isochronous channels for both direction. */
- snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
- &data, sizeof(data), 0);
-
- /* Release isochronous resources. */
- fw_iso_resources_free(&dg00x->tx_resources);
- fw_iso_resources_free(&dg00x->rx_resources);
-}
-
-static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
-{
- unsigned int i;
- __be32 data;
+ struct fw_iso_resources *resources;
+ int i;
int err;
- /* Check sampling rate. */
+ // Check sampling rate.
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
if (snd_dg00x_stream_rates[i] == rate)
break;
@@ -197,66 +200,76 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
if (i == SND_DG00X_RATE_COUNT)
return -EINVAL;
- /* Keep resources for out-stream. */
- err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+ 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;
- err = fw_iso_resources_allocate(&dg00x->rx_resources,
- amdtp_stream_get_max_payload(&dg00x->rx_stream),
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- return err;
+}
- /* Keep resources for in-stream. */
- err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
- snd_dg00x_stream_pcm_channels[i]);
+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 = fw_iso_resources_allocate(&dg00x->tx_resources,
- amdtp_stream_get_max_payload(&dg00x->tx_stream),
- fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- goto error;
- /* 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);
+ err = amdtp_dot_init(s, dg00x->unit, dir);
if (err < 0)
- goto error;
+ fw_iso_resources_destroy(resources);
- return 0;
-error:
- release_resources(dg00x);
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;
- /* For out-stream. */
- err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+ err = init_stream(dg00x, &dg00x->rx_stream);
if (err < 0)
- goto error;
- err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
- if (err < 0)
- goto error;
+ return err;
- /* For in-stream. */
- err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
- if (err < 0)
- goto error;
- err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
- if (err < 0)
- goto error;
+ err = init_stream(dg00x, &dg00x->tx_stream);
+ if (err < 0) {
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&dg00x->domain);
+ if (err < 0) {
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
+ }
- return 0;
-error:
- snd_dg00x_stream_destroy_duplex(dg00x);
return err;
}
@@ -266,35 +279,81 @@ error:
*/
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
{
- amdtp_stream_destroy(&dg00x->rx_stream);
- fw_iso_resources_destroy(&dg00x->rx_resources);
+ amdtp_domain_destroy(&dg00x->domain);
- amdtp_stream_destroy(&dg00x->tx_stream);
- fw_iso_resources_destroy(&dg00x->tx_resources);
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
}
-int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+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 = 0;
-
- if (dg00x->substreams_counter == 0)
- goto end;
+ int err;
- /* Check current sampling rate. */
err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
if (err < 0)
- goto error;
+ return err;
if (rate == 0)
rate = curr_rate;
- if (curr_rate != rate ||
- amdtp_streaming_error(&dg00x->tx_stream) ||
+
+ 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;
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- release_resources(dg00x);
+ err = fw_iso_resources_update(&dg00x->rx_resources);
+ if (err < 0)
+ goto error;
}
/*
@@ -302,75 +361,53 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
* which source of clock is used.
*/
if (!amdtp_stream_running(&dg00x->rx_stream)) {
- err = snd_dg00x_stream_set_local_rate(dg00x, rate);
- if (err < 0)
- goto error;
+ int spd = fw_parent_device(dg00x->unit)->max_speed;
- err = keep_resources(dg00x, rate);
+ err = begin_session(dg00x);
if (err < 0)
goto error;
- err = begin_session(dg00x);
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
+ dg00x->rx_resources.channel, spd);
if (err < 0)
goto error;
- err = amdtp_stream_start(&dg00x->rx_stream,
- dg00x->rx_resources.channel,
- fw_parent_device(dg00x->unit)->max_speed);
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
+ dg00x->tx_resources.channel, spd);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
- CALLBACK_TIMEOUT)) {
- err = -ETIMEDOUT;
- goto error;
- }
- }
-
- /*
- * The value of SYT field in transmitted packets is always 0x0000. Thus,
- * duplex streams with timestamp synchronization cannot be built.
- */
- if (!amdtp_stream_running(&dg00x->tx_stream)) {
- err = amdtp_stream_start(&dg00x->tx_stream,
- dg00x->tx_resources.channel,
- fw_parent_device(dg00x->unit)->max_speed);
+ // 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_stream_wait_callback(&dg00x->tx_stream,
- CALLBACK_TIMEOUT)) {
+ if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
}
-end:
- return err;
+
+ return 0;
error:
+ amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- release_resources(dg00x);
-
return err;
}
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
{
- if (dg00x->substreams_counter > 0)
- return;
-
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- finish_session(dg00x);
- release_resources(dg00x);
+ if (dg00x->substreams_counter == 0) {
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
- /*
- * Just after finishing the session, the device may lost transmitting
- * functionality for a short time.
- */
- msleep(50);
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+ }
}
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
@@ -390,33 +427,24 @@ void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
{
- int err;
-
- spin_lock_irq(&dg00x->lock);
+ guard(spinlock_irq)(&dg00x->lock);
/* user land lock this */
- if (dg00x->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (dg00x->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
{
- spin_lock_irq(&dg00x->lock);
+ guard(spinlock_irq)(&dg00x->lock);
if (WARN_ON(dg00x->dev_lock_count <= 0))
- goto end;
+ return;
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
index af9bc8504781..8a1667159930 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -1,9 +1,8 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/asound.h>
@@ -12,11 +11,9 @@
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);
+ scoped_guard(spinlock_irqsave, &dg00x->lock) {
+ dg00x->msg = be32_to_cpu(*buf);
+ }
wake_up(&dg00x->hwdep_wait);
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 1f5e1d23f31a..f73a9fc8adb1 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -1,20 +1,20 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "digi00x.h"
MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
#define VENDOR_DIGIDESIGN 0x00a07e
#define MODEL_CONSOLE 0x000001
#define MODEL_RACK 0x000002
+#define SPEC_VERSION 0x000001
static int name_card(struct snd_dg00x *dg00x)
{
@@ -30,9 +30,9 @@ static int name_card(struct snd_dg00x *dg00x)
model = skip_spaces(name);
- strcpy(dg00x->card->driver, "Digi00x");
- strcpy(dg00x->card->shortname, model);
- strcpy(dg00x->card->mixername, model);
+ strscpy(dg00x->card->driver, "Digi00x");
+ strscpy(dg00x->card->shortname, model);
+ strscpy(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],
@@ -41,34 +41,38 @@ static int name_card(struct snd_dg00x *dg00x)
return 0;
}
-static void dg00x_free(struct snd_dg00x *dg00x)
+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);
- fw_unit_put(dg00x->unit);
-
mutex_destroy(&dg00x->mutex);
+ fw_unit_put(dg00x->unit);
}
-static void dg00x_card_free(struct snd_card *card)
-{
- dg00x_free(card->private_data);
-}
-
-static void do_registration(struct work_struct *work)
+static int snd_dg00x_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_dg00x *dg00x =
- container_of(work, struct snd_dg00x, dwork.work);
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
int err;
- if (dg00x->registered)
- return;
-
- err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
- &dg00x->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dg00x), &card);
if (err < 0)
- return;
+ 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)
@@ -96,102 +100,50 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(dg00x->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- dg00x->card->private_free = dg00x_card_free;
- dg00x->card->private_data = dg00x;
- dg00x->registered = true;
-
- return;
-error:
- snd_dg00x_transaction_unregister(dg00x);
- snd_dg00x_stream_destroy_duplex(dg00x);
- snd_card_free(dg00x->card);
- dev_info(&dg00x->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int snd_dg00x_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_dg00x *dg00x;
-
- /* Allocate this independent of sound card instance. */
- dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
- if (dg00x == NULL)
- return -ENOMEM;
-
- dg00x->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, dg00x);
-
- mutex_init(&dg00x->mutex);
- spin_lock_init(&dg00x->lock);
- init_waitqueue_head(&dg00x->hwdep_wait);
-
- dg00x->is_console = entry->model_id == MODEL_CONSOLE;
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
- snd_fw_schedule_registration(unit, &dg00x->dwork);
-
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);
- /* Postpone a workqueue for deferred registration. */
- if (!dg00x->registered)
- snd_fw_schedule_registration(unit, &dg00x->dwork);
-
snd_dg00x_transaction_reregister(dg00x);
- /*
- * After registration, userspace can start packet streaming, then this
- * code block works fine.
- */
- if (dg00x->registered) {
- mutex_lock(&dg00x->mutex);
- snd_dg00x_stream_update_duplex(dg00x);
- mutex_unlock(&dg00x->mutex);
- }
+ guard(mutex)(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
}
static void snd_dg00x_remove(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&dg00x->dwork);
-
- if (dg00x->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(dg00x->card);
- } else {
- /* Don't forget this case. */
- dg00x_free(dg00x);
- }
+ // 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,
},
{}
@@ -201,7 +153,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
static struct fw_driver dg00x_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "snd-firewire-digi00x",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = snd_dg00x_probe,
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 4dd1bbf2ed3c..82b647d383c5 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -1,9 +1,8 @@
+/* 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_DIGI00X_H_INCLUDED
@@ -38,9 +37,6 @@ struct snd_dg00x {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
struct amdtp_stream tx_stream;
struct fw_iso_resources tx_resources;
@@ -60,6 +56,8 @@ struct snd_dg00x {
/* Console models have additional MIDI ports for control surface. */
bool is_console;
+
+ struct amdtp_domain domain;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
@@ -140,7 +138,10 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
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_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+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);
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index 61dda828f767..e60bfd0ee4ac 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Function Control Protocol (IEC 61883-1) helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/device.h>
@@ -240,13 +240,11 @@ int fcp_avc_transaction(struct fw_unit *unit,
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);
- if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
- t.deferrable = true;
-
- spin_lock_irq(&transactions_lock);
- list_add_tail(&t.list, &transactions);
- spin_unlock_irq(&transactions_lock);
+ scoped_guard(spinlock_irq, &transactions_lock) {
+ list_add_tail(&t.list, &transactions);
+ }
for (;;) {
tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST
@@ -282,9 +280,9 @@ deferred:
}
}
- spin_lock_irq(&transactions_lock);
- list_del(&t.list);
- spin_unlock_irq(&transactions_lock);
+ scoped_guard(spinlock_irq, &transactions_lock) {
+ list_del(&t.list);
+ }
return ret;
}
@@ -302,7 +300,7 @@ void fcp_bus_reset(struct fw_unit *unit)
{
struct fcp_transaction *t;
- spin_lock_irq(&transactions_lock);
+ guard(spinlock_irq)(&transactions_lock);
list_for_each_entry(t, &transactions, list) {
if (t->unit == unit &&
(t->state == STATE_PENDING ||
@@ -311,7 +309,6 @@ void fcp_bus_reset(struct fw_unit *unit)
wake_up(&t->wait);
}
}
- spin_unlock_irq(&transactions_lock);
}
EXPORT_SYMBOL(fcp_bus_reset);
@@ -343,12 +340,11 @@ static void fcp_response(struct fw_card *card, struct fw_request *request,
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);
+ guard(spinlock_irqsave)(&transactions_lock);
list_for_each_entry(t, &transactions, list) {
struct fw_device *device = fw_parent_device(t->unit);
if (device->card != card ||
@@ -372,7 +368,6 @@ static void fcp_response(struct fw_card *card, struct fw_request *request,
wake_up(&t->wait);
}
}
- spin_unlock_irqrestore(&transactions_lock, flags);
}
static struct fw_address_handler response_register_handler = {
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile
index 8f807284ba54..b397d95877a0 100644
--- a/sound/firewire/fireface/Makefile
+++ b/sound/firewire/fireface/Makefile
@@ -1,3 +1,5 @@
-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-ff400.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireface-y := 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
index 77c7598b61ab..76c9d33ed572 100644
--- a/sound/firewire/fireface/amdtp-ff.c
+++ b/sound/firewire/fireface/amdtp-ff.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* amdtp-ff.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm.h>
@@ -25,22 +24,27 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_channels = pcm_channels;
data_channels = pcm_channels;
- return amdtp_stream_set_parameters(s, rate, data_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)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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) {
@@ -53,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __le32 *buffer, unsigned int frames)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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) {
@@ -103,53 +112,56 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
-static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
- struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
- unsigned int pcm_frames;
-
- if (pcm) {
- write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
- pcm_frames = data_blocks;
- } else {
- write_pcm_silence(s, (__le32 *)buffer, data_blocks);
- pcm_frames = 0;
- }
+ 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);
+ }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+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_pcm_substream *pcm = READ_ONCE(s->pcm);
- unsigned int pcm_frames;
-
- if (pcm) {
- read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
- pcm_frames = data_blocks;
- } else {
- pcm_frames = 0;
- }
+ 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;
- return pcm_frames;
+ 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_data_blocks_t process_data_blocks;
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
- process_data_blocks = process_tx_data_blocks;
+ process_ctx_payloads = process_ir_ctx_payloads;
else
- process_data_blocks = process_rx_data_blocks;
+ process_ctx_payloads = process_it_ctx_payloads;
- return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
- process_data_blocks, sizeof(struct amdtp_ff));
+ 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
index 68e273fa5d23..5976abf2e1ab 100644
--- a/sound/firewire/fireface/ff-hwdep.c
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-hwdep.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -16,16 +15,23 @@
#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);
- union snd_firewire_event event;
spin_lock_irq(&ff->lock);
- while (!ff->dev_lock_changed) {
+ while (!ff->dev_lock_changed && !has_msg(ff)) {
prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&ff->lock);
schedule();
@@ -35,19 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&ff->lock);
}
- memset(&event, 0, sizeof(event));
- if (ff->dev_lock_changed) {
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = (ff->dev_lock_count > 0);
+ 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;
- count = min_t(long, count, sizeof(event.lock_status));
- }
+ spin_unlock_irq(&ff->lock);
- 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);
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ count = 0;
+ }
return count;
}
@@ -56,18 +72,14 @@ 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)
- events = POLLIN | POLLRDNORM;
+ guard(spinlock_irq)(&ff->lock);
+ if (ff->dev_lock_changed || has_msg(ff))
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&ff->lock);
-
- return events;
+ return 0;
}
static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
@@ -80,7 +92,7 @@ static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -91,48 +103,35 @@ static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
static int hwdep_lock(struct snd_ff *ff)
{
- int err;
-
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
if (ff->dev_lock_count == 0) {
ff->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&ff->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_ff *ff)
{
- int err;
-
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
if (ff->dev_lock_count == -1) {
ff->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -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);
+ guard(spinlock_irq)(&ff->lock);
if (ff->dev_lock_count == -1)
ff->dev_lock_count = 0;
- spin_unlock_irq(&ff->lock);
return 0;
}
@@ -181,7 +180,7 @@ int snd_ff_create_hwdep_devices(struct snd_ff *ff)
if (err < 0)
return err;
- strcpy(hwdep->name, ff->card->driver);
+ strscpy(hwdep->name, ff->card->driver);
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
hwdep->ops = hwdep_ops;
hwdep->private_data = ff;
diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c
index 6a49611ee462..9f6aa490e5bf 100644
--- a/sound/firewire/fireface/ff-midi.c
+++ b/sound/firewire/fireface/ff-midi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-midi.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
@@ -19,7 +18,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
struct snd_ff *ff = substream->rmidi->private_data;
/* Initialize internal status. */
- ff->running_status[substream->number] = 0;
+ ff->on_sysex[substream->number] = 0;
ff->rx_midi_error[substream->number] = false;
WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);
@@ -47,31 +46,25 @@ 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);
+ guard(spinlock_irqsave)(&ff->lock);
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);
+ guard(spinlock_irqsave)(&ff->lock);
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,
@@ -80,8 +73,8 @@ static void set_midi_substream_names(struct snd_rawmidi_str *stream,
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);
+ scnprintf(substream->name, sizeof(substream->name),
+ "%s MIDI %d", name, substream->number + 1);
}
}
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c
index e3c16308363d..7ad8204fbfe8 100644
--- a/sound/firewire/fireface/ff-pcm.c
+++ b/sound/firewire/fireface/ff-pcm.c
@@ -1,18 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-pcm.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
-static inline unsigned int get_multiplier_mode_with_index(unsigned int index)
-{
- return ((int)index - 1) / 2;
-}
-
static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
@@ -24,10 +18,16 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(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;
@@ -49,10 +49,16 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(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;
@@ -66,7 +72,6 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
const unsigned int *pcm_channels)
{
- unsigned int mode;
unsigned int rate, channels;
int i;
@@ -76,7 +81,12 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
hw->rate_max = 0;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(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)
@@ -129,6 +139,7 @@ static int pcm_init_hw_params(struct snd_ff *ff,
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;
@@ -145,28 +156,46 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto release_lock;
- 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) {
- err = -EIO;
- goto release_lock;
- }
+ scoped_guard(mutex, &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) {
+ err = -EIO;
+ goto release_lock;
+ }
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
- } else {
- if (amdtp_stream_pcm_running(&ff->rx_stream) ||
- amdtp_stream_pcm_running(&ff->tx_stream)) {
- rate = amdtp_rate_table[ff->rx_stream.sfc];
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)
+ 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)
+ goto release_lock;
+ }
}
}
@@ -188,76 +217,39 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_ff *ff = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&ff->mutex);
- ff->substreams_counter++;
- mutex_unlock(&ff->mutex);
- }
-
- return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&ff->mutex);
- ff->substreams_counter++;
- mutex_unlock(&ff->mutex);
+ 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);
+
+ guard(mutex)(&ff->mutex);
+ err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++ff->substreams_counter;
}
- return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_ff *ff = substream->private_data;
-
- mutex_lock(&ff->mutex);
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- ff->substreams_counter--;
-
- snd_ff_stream_stop_duplex(ff);
-
- mutex_unlock(&ff->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return err;
}
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
- mutex_lock(&ff->mutex);
+ guard(mutex)(&ff->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- ff->substreams_counter--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --ff->substreams_counter;
snd_ff_stream_stop_duplex(ff);
- mutex_unlock(&ff->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -266,14 +258,12 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&ff->mutex);
+ guard(mutex)(&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;
}
@@ -283,14 +273,12 @@ static int pcm_playback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&ff->mutex);
+ guard(mutex)(&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;
}
@@ -334,28 +322,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_ff *ff = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&ff->tx_stream);
+ 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_stream_pcm_pointer(&ff->rx_stream);
+ 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_stream_pcm_ack(&ff->tx_stream);
+ 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_stream_pcm_ack(&ff->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
}
int snd_ff_create_pcm_devices(struct snd_ff *ff)
@@ -363,27 +351,22 @@ 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,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_capture_hw_params,
- .hw_free = pcm_capture_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_playback_hw_params,
- .hw_free = pcm_playback_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -393,10 +376,12 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
return err;
pcm->private_data = ff;
+ pcm->nonatomic = true;
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
index 69441d121f71..4aecc8dcbb99 100644
--- a/sound/firewire/fireface/ff-proc.c
+++ b/sound/firewire/fireface/ff-proc.c
@@ -1,27 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-proc.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./ff.h"
-static void proc_dump_clock_config(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src)
{
- struct snd_ff *ff = entry->private_data;
-
- ff->spec->protocol->dump_clock_config(ff, buffer);
+ 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_sync_status(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+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_sync_status(ff, buffer);
+ ff->spec->protocol->dump_status(ff, buffer);
}
static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
@@ -32,12 +40,8 @@ static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(ff->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, ff, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, ff, op);
}
void snd_ff_proc_init(struct snd_ff *ff)
@@ -52,12 +56,7 @@ void snd_ff_proc_init(struct snd_ff *ff)
ff->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- return;
- }
+ root->mode = S_IFDIR | 0555;
- add_node(ff, root, "clock-config", proc_dump_clock_config);
- add_node(ff, root, "sync-status", proc_dump_sync_status);
+ add_node(ff, root, "status", proc_dump_status);
}
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c
deleted file mode 100644
index 12aa15df435d..000000000000
--- a/sound/firewire/fireface/ff-protocol-ff400.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * ff-protocol-ff400.c - a part of driver for RME Fireface series
- *
- * Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/delay.h>
-#include "ff.h"
-
-#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
-#define FF400_SYNC_STATUS 0x0000801c0000ull
-#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */
-#define FF400_CLOCK_CONFIG 0x0000801c0004ull
-
-#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull
-#define FF400_MIDI_RX_PORT_0 0x000080180000ull
-#define FF400_MIDI_RX_PORT_1 0x000080190000ull
-
-static int ff400_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,
- FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
- if (err < 0)
- return err;
- data = le32_to_cpu(reg);
-
- /* Calculate sampling rate. */
- switch ((data >> 1) & 0x03) {
- case 0x01:
- *rate = 32000;
- break;
- case 0x00:
- *rate = 44100;
- break;
- case 0x03:
- *rate = 48000;
- break;
- case 0x02:
- default:
- return -EIO;
- }
-
- if (data & 0x08)
- *rate *= 2;
- else if (data & 0x10)
- *rate *= 4;
-
- /* Calculate source of clock. */
- if (data & 0x01) {
- *src = SND_FF_CLOCK_SRC_INTERNAL;
- } else {
- /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */
- switch ((data >> 10) & 0x07) {
- case 0x03:
- *src = SND_FF_CLOCK_SRC_SPDIF;
- break;
- case 0x04:
- *src = SND_FF_CLOCK_SRC_WORD;
- break;
- case 0x05:
- *src = SND_FF_CLOCK_SRC_LTC;
- break;
- case 0x00:
- default:
- *src = SND_FF_CLOCK_SRC_ADAT;
- break;
- }
- }
-
- return 0;
-}
-
-static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
-{
- __le32 reg;
- int i, 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);
-
- /*
- * 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 int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable)
-{
- __le32 *reg;
- int i;
-
- reg = kzalloc(sizeof(__le32) * 18, GFP_KERNEL);
- if (reg == NULL)
- 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 < 18; ++i)
- reg[i] = cpu_to_le32(0x00000001);
- }
-
- return snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
- FF400_FETCH_PCM_FRAMES, reg,
- sizeof(__le32) * 18, 0);
-}
-
-static void ff400_dump_sync_status(struct snd_ff *ff,
- struct snd_info_buffer *buffer)
-{
- __le32 reg;
- u32 data;
- int err;
-
- err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
- FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
- if (err < 0)
- return;
-
- data = le32_to_cpu(reg);
-
- snd_iprintf(buffer, "External source detection:\n");
-
- snd_iprintf(buffer, "Word Clock:");
- if ((data >> 24) & 0x20) {
- if ((data >> 24) & 0x40)
- snd_iprintf(buffer, "sync\n");
- else
- snd_iprintf(buffer, "lock\n");
- } else {
- snd_iprintf(buffer, "none\n");
- }
-
- snd_iprintf(buffer, "S/PDIF:");
- if ((data >> 16) & 0x10) {
- if ((data >> 16) & 0x04)
- snd_iprintf(buffer, "sync\n");
- else
- snd_iprintf(buffer, "lock\n");
- } else {
- snd_iprintf(buffer, "none\n");
- }
-
- snd_iprintf(buffer, "ADAT:");
- if ((data >> 8) & 0x04) {
- if ((data >> 8) & 0x10)
- snd_iprintf(buffer, "sync\n");
- else
- snd_iprintf(buffer, "lock\n");
- } else {
- snd_iprintf(buffer, "none\n");
- }
-
- snd_iprintf(buffer, "\nUsed external source:\n");
-
- if (((data >> 22) & 0x07) == 0x07) {
- snd_iprintf(buffer, "None\n");
- } else {
- switch ((data >> 22) & 0x07) {
- case 0x00:
- snd_iprintf(buffer, "ADAT:");
- break;
- case 0x03:
- snd_iprintf(buffer, "S/PDIF:");
- break;
- case 0x04:
- snd_iprintf(buffer, "Word:");
- break;
- case 0x07:
- snd_iprintf(buffer, "Nothing:");
- break;
- case 0x01:
- case 0x02:
- case 0x05:
- case 0x06:
- default:
- snd_iprintf(buffer, "unknown:");
- break;
- }
-
- if ((data >> 25) & 0x07) {
- switch ((data >> 25) & 0x07) {
- case 0x01:
- snd_iprintf(buffer, "32000\n");
- break;
- case 0x02:
- snd_iprintf(buffer, "44100\n");
- break;
- case 0x03:
- snd_iprintf(buffer, "48000\n");
- break;
- case 0x04:
- snd_iprintf(buffer, "64000\n");
- break;
- case 0x05:
- snd_iprintf(buffer, "88200\n");
- break;
- case 0x06:
- snd_iprintf(buffer, "96000\n");
- break;
- case 0x07:
- snd_iprintf(buffer, "128000\n");
- break;
- case 0x08:
- snd_iprintf(buffer, "176400\n");
- break;
- case 0x09:
- snd_iprintf(buffer, "192000\n");
- break;
- case 0x00:
- snd_iprintf(buffer, "unknown\n");
- break;
- }
- }
- }
-
- snd_iprintf(buffer, "Multiplied:");
- snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250);
-}
-
-static void ff400_dump_clock_config(struct snd_ff *ff,
- struct snd_info_buffer *buffer)
-{
- __le32 reg;
- u32 data;
- unsigned int rate;
- const char *src;
- int err;
-
- err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
- FF400_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 & 0x20) ? "Professional" : "Consumer",
- (data & 0x40) ? "on" : "off");
-
- snd_iprintf(buffer, "Optical output interface format: %s\n",
- ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT");
-
- snd_iprintf(buffer, "Word output single speed: %s\n",
- ((data >> 8) & 0x20) ? "on" : "off");
-
- snd_iprintf(buffer, "S/PDIF input interface: %s\n",
- ((data >> 8) & 0x02) ? "Optical" : "Coaxial");
-
- switch ((data >> 1) & 0x03) {
- case 0x01:
- rate = 32000;
- break;
- case 0x00:
- rate = 44100;
- break;
- case 0x03:
- rate = 48000;
- break;
- case 0x02:
- default:
- return;
- }
-
- if (data & 0x08)
- rate *= 2;
- else if (data & 0x10)
- rate *= 4;
-
- snd_iprintf(buffer, "Sampling rate: %d\n", rate);
-
- if (data & 0x01) {
- src = "Internal";
- } else {
- switch ((data >> 10) & 0x07) {
- case 0x00:
- src = "ADAT";
- break;
- case 0x03:
- src = "S/PDIF";
- break;
- case 0x04:
- src = "Word";
- break;
- case 0x05:
- src = "LTC";
- break;
- default:
- return;
- }
- }
-
- snd_iprintf(buffer, "Sync to clock source: %s\n", src);
-}
-
-const struct snd_ff_protocol snd_ff_protocol_ff400 = {
- .get_clock = ff400_get_clock,
- .begin_session = ff400_begin_session,
- .finish_session = ff400_finish_session,
- .switch_fetching_mode = ff400_switch_fetching_mode,
-
- .dump_sync_status = ff400_dump_sync_status,
- .dump_clock_config = ff400_dump_clock_config,
-
- .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR,
- .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0,
- .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1,
-};
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
new file mode 100644
index 000000000000..0907d0a2296f
--- /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",
+ str_on_off(data & 0x00000040));
+
+ snd_iprintf(buffer, "Optical output interface format: %s\n",
+ (data & 0x00000100) ? "S/PDIF" : "ADAT");
+
+ snd_iprintf(buffer, "Word output single speed: %s\n",
+ str_on_off(data & 0x00002000));
+
+ 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
index 78880922120e..ba42490f2b0e 100644
--- a/sound/firewire/fireface/ff-stream.c
+++ b/sound/firewire/fireface/ff-stream.c
@@ -1,132 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-stream.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
-#define CALLBACK_TIMEOUT_MS 200
+#define READY_TIMEOUT_MS 200
-static int get_rate_mode(unsigned int rate, unsigned int *mode)
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode)
{
- int i;
-
- for (i = 0; i < CIP_SFC_COUNT; i++) {
- if (amdtp_rate_table[i] == rate)
- break;
- }
-
- if (i == CIP_SFC_COUNT)
+ 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 = ((int)i - 1) / 2;
+ *mode = modes[sfc];
return 0;
}
-/*
- * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
- * we can allocate between 0 and 7 channel.
- */
-static int keep_resources(struct snd_ff *ff, unsigned int rate)
-{
- int mode;
- int err;
-
- err = get_rate_mode(rate, &mode);
- if (err < 0)
- return err;
-
- /* Keep resources for in-stream. */
- err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
- ff->spec->pcm_capture_channels[mode]);
- if (err < 0)
- return err;
- 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. */
- err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
- ff->spec->pcm_playback_channels[mode]);
- if (err < 0)
- return err;
- 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 void release_resources(struct snd_ff *ff)
-{
- fw_iso_resources_free(&ff->tx_resources);
- fw_iso_resources_free(&ff->rx_resources);
-}
-
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, enum amdtp_stream_direction dir)
+static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
- int err;
struct fw_iso_resources *resources;
- struct amdtp_stream *stream;
+ enum amdtp_stream_direction dir;
+ int err;
- if (dir == AMDTP_IN_STREAM) {
+ if (s == &ff->tx_stream) {
resources = &ff->tx_resources;
- stream = &ff->tx_stream;
+ dir = AMDTP_IN_STREAM;
} else {
resources = &ff->rx_resources;
- stream = &ff->rx_stream;
+ dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, ff->unit);
if (err < 0)
return err;
- err = amdtp_ff_init(stream, ff->unit, dir);
+ 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, enum amdtp_stream_direction dir)
+static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
- if (dir == AMDTP_IN_STREAM) {
- amdtp_stream_destroy(&ff->tx_stream);
+ amdtp_stream_destroy(s);
+
+ if (s == &ff->tx_stream)
fw_iso_resources_destroy(&ff->tx_resources);
- } else {
- amdtp_stream_destroy(&ff->rx_stream);
+ else
fw_iso_resources_destroy(&ff->rx_resources);
- }
}
int snd_ff_stream_init_duplex(struct snd_ff *ff)
{
int err;
- err = init_stream(ff, AMDTP_OUT_STREAM);
+ err = init_stream(ff, &ff->rx_stream);
if (err < 0)
- goto end;
+ 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);
+ }
- err = init_stream(ff, AMDTP_IN_STREAM);
- if (err < 0)
- destroy_stream(ff, AMDTP_OUT_STREAM);
-end:
return err;
}
@@ -136,31 +100,82 @@ end:
*/
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
{
- destroy_stream(ff, AMDTP_IN_STREAM);
- destroy_stream(ff, AMDTP_OUT_STREAM);
+ amdtp_domain_destroy(&ff->domain);
+
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
}
-int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+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;
- if (ff->substreams_counter == 0)
- return 0;
-
err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
if (err < 0)
return err;
- if (curr_rate != rate ||
- amdtp_streaming_error(&ff->tx_stream) ||
- amdtp_streaming_error(&ff->rx_stream)) {
+
+ if (ff->substreams_counter == 0 || curr_rate != rate) {
+ enum snd_ff_stream_mode mode;
+ int i;
+
+ amdtp_domain_stop(&ff->domain);
finish_session(ff);
- amdtp_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
+ 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;
- release_resources(ff);
+ 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);
}
/*
@@ -168,78 +183,66 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
* packets. Then, the device transfers packets.
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
- err = keep_resources(ff, rate);
- if (err < 0)
- goto error;
+ int spd = fw_parent_device(ff->unit)->max_speed;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
goto error;
- err = amdtp_stream_start(&ff->rx_stream,
- ff->rx_resources.channel,
- fw_parent_device(ff->unit)->max_speed);
+ err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
+ ff->rx_resources.channel, spd);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&ff->rx_stream,
- CALLBACK_TIMEOUT_MS)) {
- err = -ETIMEDOUT;
- goto error;
- }
-
- err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
+ ff->tx_resources.channel, spd);
if (err < 0)
goto error;
- }
- if (!amdtp_stream_running(&ff->tx_stream)) {
- err = amdtp_stream_start(&ff->tx_stream,
- ff->tx_resources.channel,
- fw_parent_device(ff->unit)->max_speed);
+ // 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_stream_wait_callback(&ff->tx_stream,
- CALLBACK_TIMEOUT_MS)) {
+ 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_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
-
+ amdtp_domain_stop(&ff->domain);
finish_session(ff);
- release_resources(ff);
return err;
}
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
{
- if (ff->substreams_counter > 0)
- return;
+ if (ff->substreams_counter == 0) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
- amdtp_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
- finish_session(ff);
- release_resources(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)
{
- /* The device discontinue to transfer packets. */
- amdtp_stream_pcm_abort(&ff->tx_stream);
- amdtp_stream_stop(&ff->tx_stream);
+ 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);
- amdtp_stream_stop(&ff->rx_stream);
-
- fw_iso_resources_update(&ff->tx_resources);
- fw_iso_resources_update(&ff->rx_resources);
}
void snd_ff_stream_lock_changed(struct snd_ff *ff)
@@ -250,33 +253,24 @@ void snd_ff_stream_lock_changed(struct snd_ff *ff)
int snd_ff_stream_lock_try(struct snd_ff *ff)
{
- int err;
-
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
/* user land lock this */
- if (ff->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (ff->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_ff_stream_lock_release(struct snd_ff *ff)
{
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
if (WARN_ON(ff->dev_lock_count <= 0))
- goto end;
+ return;
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
index 332b29f8ed75..436da0a3bdcc 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-transaction.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
@@ -51,23 +50,17 @@ static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
finish_transmit_midi_msg(ff, 1, rcode);
}
-static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port,
- unsigned int index, u8 byte)
-{
- ff->msg_buf[port][index] = cpu_to_le32(byte);
-}
-
static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
{
struct snd_rawmidi_substream *substream =
READ_ONCE(ff->rx_midi_substreams[port]);
- u8 *buf = (u8 *)ff->msg_buf[port];
- int i, len;
+ 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;
@@ -81,26 +74,26 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
return;
}
- len = snd_rawmidi_transmit_peek(substream, buf,
- SND_FF_MAXIMIM_MIDI_QUADS);
- if (len <= 0)
+ quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);
+ if (quad_count <= 0)
return;
- for (i = len - 1; i >= 0; i--)
- fill_midi_buf(ff, port, i, buf[i]);
-
if (port == 0) {
- addr = ff->spec->protocol->midi_rx_port_0_reg;
+ addr = ff->spec->midi_rx_addrs[0];
callback = finish_transmit_midi0_msg;
} else {
- addr = ff->spec->protocol->midi_rx_port_1_reg;
+ 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(),
- len * 8 * NSEC_PER_SEC / 31250);
- ff->rx_bytes[port] = len;
+ 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
@@ -112,10 +105,9 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
*/
generation = fw_dev->generation;
smp_rmb();
- fw_send_request(fw_dev->card, &ff->transactions[port],
- TCODE_WRITE_BLOCK_REQUEST,
+ fw_send_request(fw_dev->card, &ff->transactions[port], tcode,
fw_dev->node_id, generation, fw_dev->max_speed,
- addr, &ff->msg_buf[port], len * 4,
+ addr, &ff->msg_buf[port], quad_count * 4,
callback, &ff->transactions[port]);
}
@@ -133,49 +125,20 @@ static void transmit_midi1_msg(struct work_struct *work)
transmit_midi_msg(ff, 1);
}
-static void handle_midi_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)
+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 quad;
- u8 byte;
- unsigned int index;
- struct snd_rawmidi_substream *substream;
- int i;
+ u32 tstamp = fw_request_get_timestamp(request);
fw_send_response(card, request, RCODE_COMPLETE);
- for (i = 0; i < length / 4; i++) {
- quad = le32_to_cpu(buf[i]);
-
- /* Message in first port. */
- /*
- * This value may represent the index of this unit when the same
- * units are on the same IEEE 1394 bus. This driver doesn't use
- * it.
- */
- index = (quad >> 8) & 0xff;
- if (index > 0) {
- substream = READ_ONCE(ff->tx_midi_substreams[0]);
- if (substream != NULL) {
- byte = quad & 0xff;
- snd_rawmidi_receive(substream, &byte, 1);
- }
- }
+ offset -= ff->async_handler.offset;
- /* Message in second port. */
- index = (quad >> 24) & 0xff;
- if (index > 0) {
- substream = READ_ONCE(ff->tx_midi_substreams[1]);
- if (substream != NULL) {
- byte = (quad >> 16) & 0xff;
- snd_rawmidi_receive(substream, &byte, 1);
- }
- }
- }
+ guard(spinlock_irqsave)(&ff->lock);
+ ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
}
static int allocate_own_address(struct snd_ff *ff, int i)
@@ -183,8 +146,8 @@ static int allocate_own_address(struct snd_ff *ff, int i)
struct fw_address_region midi_msg_region;
int err;
- ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4;
- ff->async_handler.address_callback = handle_midi_msg;
+ 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;
@@ -202,38 +165,13 @@ static int allocate_own_address(struct snd_ff *ff, int i)
return err;
}
-/*
- * The configuration to start asynchronous transactions for MIDI messages is in
- * 0x'0000'8010'051c. This register includes the other options, thus this driver
- * doesn't touch it and leaves the decision to userspace. The userspace MUST add
- * 0x04000000 to write transactions to the register to receive any MIDI
- * messages.
- *
- * Here, I just describe MIDI-related offsets of the register, in little-endian
- * order.
- *
- * Controllers are allowed to register higher 4 bytes of address to receive
- * the transactions. The register is 0x'0000'8010'03f4. On the other hand, the
- * controllers are not allowed to register lower 4 bytes of the address. They
- * are forced to select from 4 options by writing corresponding bits to
- * 0x'0000'8010'051c.
- *
- * The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes
- * of address to which the device transferrs the transactions.
- * - 6th: 0x'....'....'0000'0180
- * - 5th: 0x'....'....'0000'0100
- * - 4th: 0x'....'....'0000'0080
- * - 3rd: 0x'....'....'0000'0000
- *
- * This driver configure 0x'....'....'0000'0000 for units to receive MIDI
- * messages. 3rd bit of the register should be configured, however this driver
- * deligates this task to user space applications due to a restriction that
- * this register is write-only and the other bits have own effects.
- *
- * The 1st and 2nd bits in LSB of this register are used to cancel transferring
- * asynchronous transactions. These two bits have the same effect.
- * - 1st/2nd: cancel transferring
- */
+// 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;
@@ -247,7 +185,7 @@ int snd_ff_transaction_reregister(struct snd_ff *ff)
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->protocol->midi_high_addr_reg,
+ ff->spec->midi_high_addr,
&reg, sizeof(reg), 0);
}
@@ -288,7 +226,7 @@ void snd_ff_transaction_unregister(struct snd_ff *ff)
/* Release higher 4 bytes of address. */
reg = cpu_to_le32(0x00000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- ff->spec->protocol->midi_high_addr_reg,
+ 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
index 4974bc7980e9..5d2c4fbf4434 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
@@ -12,49 +11,66 @@
MODULE_DESCRIPTION("RME Fireface series Driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
static void name_card(struct snd_ff *ff)
{
struct fw_device *fw_dev = fw_parent_device(ff->unit);
-
- strcpy(ff->card->driver, "Fireface");
- strcpy(ff->card->shortname, ff->spec->name);
- strcpy(ff->card->mixername, ff->spec->name);
+ static 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];
+
+ strscpy(ff->card->driver, "Fireface");
+ strscpy(ff->card->shortname, name);
+ strscpy(ff->card->mixername, name);
snprintf(ff->card->longname, sizeof(ff->card->longname),
- "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
+ "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_free(struct snd_ff *ff)
+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);
- fw_unit_put(ff->unit);
+ kfree(ff->msg_parser);
mutex_destroy(&ff->mutex);
- kfree(ff);
-}
-
-static void ff_card_free(struct snd_card *card)
-{
- ff_free(card->private_data);
+ fw_unit_put(ff->unit);
}
-static void do_registration(struct work_struct *work)
+static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
+ struct snd_card *card;
+ struct snd_ff *ff;
int err;
- if (ff->registered)
- return;
-
- err = snd_card_new(&ff->unit->device, -1, NULL, THIS_MODULE, 0,
- &ff->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*ff), &card);
if (err < 0)
- return;
+ 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)
@@ -80,93 +96,98 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(ff->card);
+ 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;
- ff->card->private_free = ff_card_free;
- ff->card->private_data = ff;
- ff->registered = true;
-
- return;
-error:
- snd_ff_transaction_unregister(ff);
- snd_ff_stream_destroy_duplex(ff);
- snd_card_free(ff->card);
- dev_info(&ff->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int snd_ff_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_ff *ff;
-
- ff = kzalloc(sizeof(struct snd_ff), GFP_KERNEL);
- if (ff == NULL)
- return -ENOMEM;
-
- /* initialize myself */
- ff->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, ff);
-
- mutex_init(&ff->mutex);
- spin_lock_init(&ff->lock);
- init_waitqueue_head(&ff->hwdep_wait);
-
- ff->spec = (const struct snd_ff_spec *)entry->driver_data;
-
- /* Register this sound card later. */
- INIT_DEFERRABLE_WORK(&ff->dwork, do_registration);
- snd_fw_schedule_registration(unit, &ff->dwork);
-
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);
- /* Postpone a workqueue for deferred registration. */
- if (!ff->registered)
- snd_fw_schedule_registration(unit, &ff->dwork);
-
snd_ff_transaction_reregister(ff);
- if (ff->registered)
- snd_ff_stream_update_duplex(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);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_work_sync(&ff->dwork.work);
-
- if (ff->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(ff->card);
- } else {
- /* Don't forget this case. */
- ff_free(ff);
- }
+ // 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 = {
- .name = "Fireface400",
.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 |
@@ -174,11 +195,47 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
IEEE1394_MATCH_VERSION |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
- .specifier_id = 0x000a35,
- .version = 0x000002,
+ .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);
@@ -186,7 +243,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
static struct fw_driver ff_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "snd-fireface",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = snd_ff_probe,
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index 64df44beb950..7e42f5778a8a 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ff.h - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREFACE_H_INCLUDED
@@ -31,23 +30,37 @@
#include "../amdtp-stream.h"
#include "../iso-resources.h"
-#define SND_FF_STREAM_MODES 3
-
#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 char *const name;
-
- const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES];
- const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES];
+ 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 {
@@ -56,9 +69,7 @@ struct snd_ff {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
+ enum snd_ff_unit_version unit_version;
const struct snd_ff_spec *spec;
/* To handle MIDI tx. */
@@ -67,7 +78,7 @@ struct snd_ff {
/* TO handle MIDI rx. */
struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
- u8 running_status[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];
@@ -84,35 +95,43 @@ struct snd_ff {
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_ADAT,
+ SND_FF_CLOCK_SRC_ADAT1,
+ SND_FF_CLOCK_SRC_ADAT2,
SND_FF_CLOCK_SRC_WORD,
SND_FF_CLOCK_SRC_LTC,
- /* TODO: perhaps ADAT2 and TCO exists. */
+ /* 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);
- int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
-
- void (*dump_sync_status)(struct snd_ff *ff,
- struct snd_info_buffer *buffer);
- void (*dump_clock_config)(struct snd_ff *ff,
- struct snd_info_buffer *buffer);
-
- u64 midi_high_addr_reg;
- u64 midi_rx_port_0_reg;
- u64 midi_rx_port_1_reg;
+ 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);
@@ -125,8 +144,13 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
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);
@@ -136,6 +160,7 @@ 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);
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 15ef7f75a8ef..baaf3066c9b1 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,5 @@
-snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireworks-y := 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
index 71a0613d3da0..3378c7dce88a 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -1,10 +1,9 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -19,7 +18,7 @@
MODULE_DESCRIPTION("Echo Fireworks driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -91,14 +90,14 @@ get_hardware_info(struct snd_efw *efw)
(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);
+ strscpy(efw->card->driver, "Fireworks");
+ strscpy(efw->card->shortname, hwinfo->model_name);
+ strscpy(efw->card->mixername, hwinfo->model_name);
+ scnprintf(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;
@@ -184,72 +183,60 @@ end:
return err;
}
-static void efw_free(struct snd_efw *efw)
-{
- snd_efw_stream_destroy_duplex(efw);
- snd_efw_transaction_remove_instance(efw);
- fw_unit_put(efw->unit);
-
- kfree(efw->resp_buf);
-
- mutex_destroy(&efw->mutex);
- kfree(efw);
-}
-
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
static void
efw_card_free(struct snd_card *card)
{
struct snd_efw *efw = card->private_data;
- if (efw->card_index >= 0) {
- mutex_lock(&devices_mutex);
+ scoped_guard(mutex, &devices_mutex) {
clear_bit(efw->card_index, devices_used);
- mutex_unlock(&devices_mutex);
}
- efw_free(card->private_data);
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+
+ mutex_destroy(&efw->mutex);
+ fw_unit_put(efw->unit);
}
-static void
-do_registration(struct work_struct *work)
+static int efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
unsigned int card_index;
+ struct snd_card *card;
+ struct snd_efw *efw;
int err;
- if (efw->registered)
- return;
-
- mutex_lock(&devices_mutex);
-
- /* check registered cards */
- 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;
+ // check registered cards.
+ scoped_guard(mutex, &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)
+ return -ENOENT;
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*efw), &card);
+ if (err < 0)
+ return err;
+ card->private_free = efw_card_free;
+ set_bit(card_index, devices_used);
}
- err = snd_card_new(&efw->unit->device, index[card_index],
- id[card_index], THIS_MODULE, 0, &efw->card);
- if (err < 0) {
- mutex_unlock(&devices_mutex);
- return;
- }
+ 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 = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL);
- if (efw->resp_buf == NULL) {
+ // 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;
}
@@ -280,93 +267,47 @@ do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(efw->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- set_bit(card_index, devices_used);
- mutex_unlock(&devices_mutex);
-
- /*
- * After registered, efw instance can be released corresponding to
- * releasing the sound card instance.
- */
- efw->card->private_free = efw_card_free;
- efw->card->private_data = efw;
- efw->registered = true;
-
- return;
-error:
- mutex_unlock(&devices_mutex);
- snd_efw_transaction_remove_instance(efw);
- snd_efw_stream_destroy_duplex(efw);
- snd_card_free(efw->card);
- dev_info(&efw->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int
-efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
-{
- struct snd_efw *efw;
-
- efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
- if (efw == NULL)
- return -ENOMEM;
-
- efw->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, efw);
-
- mutex_init(&efw->mutex);
- spin_lock_init(&efw->lock);
- init_waitqueue_head(&efw->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
- snd_fw_schedule_registration(unit, &efw->dwork);
-
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);
- /* Postpone a workqueue for deferred registration. */
- if (!efw->registered)
- snd_fw_schedule_registration(unit, &efw->dwork);
-
snd_efw_transaction_bus_reset(efw->unit);
- /*
- * After registration, userspace can start packet streaming, then this
- * code block works fine.
- */
- if (efw->registered) {
- mutex_lock(&efw->mutex);
- snd_efw_stream_update_duplex(efw);
- mutex_unlock(&efw->mutex);
- }
+ guard(mutex)(&efw->mutex);
+ snd_efw_stream_update_duplex(efw);
}
static void efw_remove(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&efw->dwork);
-
- if (efw->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(efw->card);
- } else {
- /* Don't forget this case. */
- efw_free(efw);
- }
+ // 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[] = {
@@ -390,7 +331,7 @@ MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
static struct fw_driver efw_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "snd-fireworks",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = efw_probe,
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 9b19c7f05d57..c8d5879efe28 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -1,10 +1,9 @@
+/* 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREWORKS_H_INCLUDED
#define SOUND_FIREWORKS_H_INCLUDED
@@ -66,9 +65,6 @@ struct snd_efw {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
/* for transaction */
u32 seqnum;
bool resp_addr_changable;
@@ -89,8 +85,7 @@ struct snd_efw {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
- unsigned int capture_substreams;
- unsigned int playback_substreams;
+ unsigned int substreams_counter;
/* hardware metering parameters */
unsigned int phys_out;
@@ -109,6 +104,8 @@ struct snd_efw {
u8 *resp_buf;
u8 *pull_ptr;
u8 *push_ptr;
+
+ struct amdtp_domain domain;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,
@@ -177,11 +174,11 @@ struct snd_efw_phys_meters {
u32 in_meters;
u32 reserved4;
u32 reserved5;
- u32 values[0];
+ u32 values[];
} __packed;
enum snd_efw_clock_source {
SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
- SND_EFW_CLOCK_SOURCE_SYTMATCH = 1,
+ // Unused.
SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
SND_EFW_CLOCK_SOURCE_SPDIF = 3,
SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
@@ -207,7 +204,10 @@ 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_start_duplex(struct snd_efw *efw, unsigned int rate);
+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);
@@ -224,12 +224,4 @@ int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
int snd_efw_create_hwdep_device(struct snd_efw *efw);
-#define SND_EFW_DEV_ENTRY(vendor, model) \
-{ \
- .match_flags = IEEE1394_MATCH_VENDOR_ID | \
- IEEE1394_MATCH_MODEL_ID, \
- .vendor_id = vendor,\
- .model_id = model \
-}
-
#endif
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
index 94bab0476a65..2b595ee0bc35 100644
--- a/sound/firewire/fireworks/fireworks_command.c
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fireworks_command.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
@@ -120,14 +119,14 @@ efw_transaction(struct snd_efw *efw, unsigned int category,
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);
+ scoped_guard(spinlock, &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;
+ }
/* fill transaction header fields */
cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
index e0eff9328ee1..7d6bd8ceeab3 100644
--- a/sound/firewire/fireworks/fireworks_hwdep.c
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fireworks_hwdep.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -35,6 +34,7 @@ hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
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);
@@ -103,12 +103,10 @@ hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
.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);
+ scoped_guard(spinlock_irq, &efw->lock) {
+ event.lock_status.status = (efw->dev_lock_count > 0);
+ efw->dev_lock_changed = false;
+ }
count = min_t(long, count, sizeof(event.lock_status));
@@ -192,14 +190,12 @@ hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
poll_wait(file, &efw->hwdep_wait, wait);
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
- events = POLLIN | POLLRDNORM;
+ events = EPOLLIN | EPOLLRDNORM;
else
events = 0;
- spin_unlock_irq(&efw->lock);
-
- return events | POLLOUT;
+ return events | EPOLLOUT;
}
static int
@@ -213,7 +209,7 @@ hwdep_get_info(struct snd_efw *efw, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -225,39 +221,27 @@ hwdep_get_info(struct snd_efw *efw, void __user *arg)
static int
hwdep_lock(struct snd_efw *efw)
{
- int err;
-
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
if (efw->dev_lock_count == 0) {
efw->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&efw->lock);
-
- return err;
}
static int
hwdep_unlock(struct snd_efw *efw)
{
- int err;
-
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
if (efw->dev_lock_count == -1) {
efw->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -EBADFD;
}
-
- spin_unlock_irq(&efw->lock);
-
- return err;
}
static int
@@ -265,10 +249,9 @@ hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_efw *efw = hwdep->private_data;
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
if (efw->dev_lock_count == -1)
efw->dev_lock_count = 0;
- spin_unlock_irq(&efw->lock);
return 0;
}
@@ -319,7 +302,7 @@ int snd_efw_create_hwdep_device(struct snd_efw *efw)
err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
if (err < 0)
goto end;
- strcpy(hwdep->name, "Fireworks");
+ strscpy(hwdep->name, "Fireworks");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
hwdep->ops = ops;
hwdep->private_data = efw;
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index f5da2cd4ce42..405106a6aef9 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -1,73 +1,43 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "fireworks.h"
-static int midi_capture_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);
- efw->capture_substreams++;
- err = snd_efw_stream_start_duplex(efw, 0);
- mutex_unlock(&efw->mutex);
- if (err < 0)
- snd_efw_stream_lock_release(efw);
-
-end:
- return err;
-}
-
-static int midi_playback_open(struct snd_rawmidi_substream *substream)
+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;
+ return err;
- mutex_lock(&efw->mutex);
- efw->playback_substreams++;
- err = snd_efw_stream_start_duplex(efw, 0);
- mutex_unlock(&efw->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_efw_stream_lock_release(efw);
-end:
return err;
}
-static int midi_capture_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
- mutex_lock(&efw->mutex);
- efw->capture_substreams--;
- snd_efw_stream_stop_duplex(efw);
- mutex_unlock(&efw->mutex);
-
- snd_efw_stream_lock_release(efw);
- return 0;
-}
-
-static int midi_playback_close(struct snd_rawmidi_substream *substream)
-{
- struct snd_efw *efw = substream->rmidi->private_data;
-
- mutex_lock(&efw->mutex);
- efw->playback_substreams--;
- snd_efw_stream_stop_duplex(efw);
- mutex_unlock(&efw->mutex);
+ scoped_guard(mutex, &efw->mutex) {
+ --efw->substreams_counter;
+ snd_efw_stream_stop_duplex(efw);
+ }
snd_efw_stream_lock_release(efw);
return 0;
@@ -76,9 +46,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&efw->lock);
if (up)
amdtp_am824_midi_trigger(&efw->tx_stream,
@@ -86,16 +55,13 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
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);
+ guard(spinlock_irqsave)(&efw->lock);
if (up)
amdtp_am824_midi_trigger(&efw->rx_stream,
@@ -103,8 +69,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
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,
@@ -113,21 +77,21 @@ static void set_midi_substream_names(struct snd_efw *efw,
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);
+ scnprintf(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_capture_open,
- .close = midi_capture_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_capture_trigger,
};
static const struct snd_rawmidi_ops playback_ops = {
- .open = midi_playback_open,
- .close = midi_playback_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index 40faed5e6968..9399293a9fe9 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -1,10 +1,9 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
@@ -149,7 +148,7 @@ pcm_init_hw_params(struct snd_efw *efw,
}
/* limit rates */
- runtime->hw.rates = efw->supported_sampling_rate,
+ runtime->hw.rates = efw->supported_sampling_rate;
snd_pcm_limit_hw_rates(runtime);
limit_channels(&runtime->hw, pcm_channels);
@@ -174,13 +173,13 @@ end:
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- unsigned int sampling_rate;
+ 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)
- goto end;
+ return err;
err = pcm_init_hw_params(efw, substream);
if (err < 0)
@@ -190,23 +189,41 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- /*
- * When source of clock is not internal or any PCM streams are running,
- * available sampling rate is limited at current sampling rate.
- */
- if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
- amdtp_stream_pcm_running(&efw->tx_stream) ||
- amdtp_stream_pcm_running(&efw->rx_stream)) {
- err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
- if (err < 0)
- goto err_locked;
- substream->runtime->hw.rate_min = sampling_rate;
- substream->runtime->hw.rate_max = sampling_rate;
+ scoped_guard(mutex, &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)
+ 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)
+ 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);
-end:
- return err;
+
+ return 0;
err_locked:
snd_efw_stream_lock_release(efw);
return err;
@@ -219,81 +236,47 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&efw->mutex);
- efw->capture_substreams++;
- mutex_unlock(&efw->mutex);
+ 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);
+
+ guard(mutex)(&efw->mutex);
+ err = snd_efw_stream_reserve_duplex(efw, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++efw->substreams_counter;
}
- return 0;
-}
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_efw *efw = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&efw->mutex);
- efw->playback_substreams++;
- mutex_unlock(&efw->mutex);
- }
-
- return 0;
+ return err;
}
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&efw->mutex);
- efw->capture_substreams--;
- mutex_unlock(&efw->mutex);
- }
+ guard(mutex)(&efw->mutex);
- snd_efw_stream_stop_duplex(efw);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_efw *efw = substream->private_data;
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&efw->mutex);
- efw->playback_substreams--;
- mutex_unlock(&efw->mutex);
- }
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --efw->substreams_counter;
snd_efw_stream_stop_duplex(efw);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- err = snd_efw_stream_start_duplex(efw, runtime->rate);
+ err = snd_efw_stream_start_duplex(efw);
if (err >= 0)
amdtp_stream_pcm_prepare(&efw->tx_stream);
@@ -302,10 +285,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- err = snd_efw_stream_start_duplex(efw, runtime->rate);
+ err = snd_efw_stream_start_duplex(efw);
if (err >= 0)
amdtp_stream_pcm_prepare(&efw->rx_stream);
@@ -350,26 +332,28 @@ static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_efw *efw = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&efw->tx_stream);
+
+ 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_stream_pcm_pointer(&efw->rx_stream);
+
+ 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_stream_pcm_ack(&efw->tx_stream);
+ 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_stream_pcm_ack(&efw->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream);
}
int snd_efw_create_pcm_devices(struct snd_efw *efw)
@@ -377,27 +361,22 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_capture_hw_params,
- .hw_free = pcm_capture_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_playback_hw_params,
- .hw_free = pcm_playback_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -407,9 +386,11 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
goto end;
pcm->private_data = efw;
+ pcm->nonatomic = true;
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
index 9c21f31b8b21..12288567b0cd 100644
--- a/sound/firewire/fireworks/fireworks_proc.c
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -1,10 +1,9 @@
+// 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
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
@@ -199,12 +198,8 @@ add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(efw->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, efw, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, efw, op);
}
void snd_efw_proc_init(struct snd_efw *efw)
@@ -219,11 +214,7 @@ void snd_efw_proc_init(struct snd_efw *efw)
efw->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- return;
- }
+ root->mode = S_IFDIR | 0555;
add_node(efw, root, "clock", proc_read_clock);
add_node(efw, root, "firmware", proc_read_hwinfo);
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 827161bc269c..974084e1c083 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -1,16 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fireworks_stream.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
-#define CALLBACK_TIMEOUT 100
+#define READY_TIMEOUT_MS 1000
-static int
-init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
enum cmp_direction c_dir;
@@ -29,95 +27,78 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
if (err < 0)
- goto end;
+ return err;
- err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+ 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;
}
-end:
- return err;
-}
-static void
-stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
-{
- amdtp_stream_pcm_abort(stream);
- amdtp_stream_stop(stream);
+ 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;
+ }
- if (stream == &efw->tx_stream)
- cmp_connection_break(&efw->out_conn);
- else
- cmp_connection_break(&efw->in_conn);
+ return err;
}
-static int
-start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
- unsigned int sampling_rate)
+static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate)
{
struct cmp_connection *conn;
- unsigned int mode, pcm_channels, midi_ports;
int err;
- err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
- if (err < 0)
- goto end;
- if (stream == &efw->tx_stream) {
+ if (stream == &efw->tx_stream)
conn = &efw->out_conn;
- pcm_channels = efw->pcm_capture_channels[mode];
- midi_ports = efw->midi_out_ports;
- } else {
+ else
conn = &efw->in_conn;
- pcm_channels = efw->pcm_playback_channels[mode];
- midi_ports = efw->midi_in_ports;
- }
-
- err = amdtp_am824_set_parameters(stream, sampling_rate,
- pcm_channels, midi_ports, false);
- if (err < 0)
- goto end;
- /* establish connection via CMP */
- err = cmp_connection_establish(conn,
- amdtp_stream_get_max_payload(stream));
+ // Establish connection via CMP.
+ err = cmp_connection_establish(conn);
if (err < 0)
- goto end;
+ return err;
- /* start amdtp stream */
- err = amdtp_stream_start(stream,
- conn->resources.channel,
- conn->speed);
+ // Start amdtp stream.
+ err = amdtp_domain_add_stream(&efw->domain, stream,
+ conn->resources.channel, conn->speed);
if (err < 0) {
- stop_stream(efw, stream);
- goto end;
+ cmp_connection_break(conn);
+ return err;
}
- /* wait first callback */
- if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
- stop_stream(efw, stream);
- err = -ETIMEDOUT;
- }
-end:
- 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)
+// 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)
{
- struct cmp_connection *conn;
+ amdtp_stream_destroy(stream);
if (stream == &efw->tx_stream)
- conn = &efw->out_conn;
+ cmp_connection_destroy(&efw->out_conn);
else
- conn = &efw->in_conn;
-
- amdtp_stream_destroy(stream);
- cmp_connection_destroy(conn);
+ cmp_connection_destroy(&efw->in_conn);
}
static int
@@ -150,131 +131,208 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
err = init_stream(efw, &efw->tx_stream);
if (err < 0)
- goto end;
- /* 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.tx_first_dbc = 0x02;
- /* AudioFire9 always reports wrong dbs. */
- if (efw->is_af9)
- efw->tx_stream.flags |= CIP_WRONG_DBS;
- /* Firmware version 5.5 reports fixed interval for dbc. */
- if (efw->firmware_version == 0x5050000)
- efw->tx_stream.tx_dbc_interval = 8;
+ return err;
err = init_stream(efw, &efw->rx_stream);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
- goto end;
+ return err;
}
- /* set IEC61883 compliant mode (actually not fully compliant...) */
+ 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);
}
-end:
+
return err;
}
-int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
+static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int mode)
{
- unsigned int curr_rate;
- int err = 0;
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
- /* Need no substreams */
- if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
- goto end;
+ 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;
+ }
- /*
- * Considering JACK/FFADO streaming:
- * TODO: This can be removed hwdep functionality becomes popular.
- */
- err = check_connection_used_by_others(efw, &efw->rx_stream);
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
+ midi_ports, false);
if (err < 0)
- goto end;
+ 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;
- /* packet queueing error */
- if (amdtp_streaming_error(&efw->tx_stream))
- stop_stream(efw, &efw->tx_stream);
- if (amdtp_streaming_error(&efw->rx_stream))
- stop_stream(efw, &efw->rx_stream);
+ // 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 */
+ // stop streams if rate is different.
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
if (err < 0)
- goto end;
+ return err;
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
- stop_stream(efw, &efw->tx_stream);
- stop_stream(efw, &efw->rx_stream);
+ 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);
}
- /* master should be always running */
- if (!amdtp_stream_running(&efw->rx_stream)) {
+ if (efw->substreams_counter == 0 || rate != curr_rate) {
+ unsigned int mode;
+
err = snd_efw_command_set_sampling_rate(efw, rate);
if (err < 0)
- goto end;
+ return err;
- err = start_stream(efw, &efw->rx_stream, rate);
+ 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) {
- dev_err(&efw->unit->device,
- "fail to start AMDTP master stream:%d\n", err);
- goto end;
+ cmp_connection_release(&efw->in_conn);
+ cmp_connection_release(&efw->out_conn);
+ return err;
}
}
- /* start slave if needed */
- if (efw->capture_substreams > 0 &&
- !amdtp_stream_running(&efw->tx_stream)) {
+ 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) {
- dev_err(&efw->unit->device,
- "fail to start AMDTP slave stream:%d\n", err);
- stop_stream(efw, &efw->rx_stream);
+ 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;
}
}
-end:
+
+ 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->capture_substreams == 0) {
- stop_stream(efw, &efw->tx_stream);
+ if (efw->substreams_counter == 0) {
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
- if (efw->playback_substreams == 0)
- stop_stream(efw, &efw->rx_stream);
+ cmp_connection_release(&efw->out_conn);
+ cmp_connection_release(&efw->in_conn);
}
}
void snd_efw_stream_update_duplex(struct snd_efw *efw)
{
- if (cmp_connection_update(&efw->out_conn) < 0 ||
- cmp_connection_update(&efw->in_conn) < 0) {
- stop_stream(efw, &efw->rx_stream);
- stop_stream(efw, &efw->tx_stream);
- } else {
- amdtp_stream_update(&efw->rx_stream);
- amdtp_stream_update(&efw->tx_stream);
- }
+ 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);
}
@@ -287,33 +345,24 @@ void snd_efw_stream_lock_changed(struct snd_efw *efw)
int snd_efw_stream_lock_try(struct snd_efw *efw)
{
- int err;
-
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
/* user land lock this */
- if (efw->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (efw->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_efw_stream_lock_release(struct snd_efw *efw)
{
- spin_lock_irq(&efw->lock);
+ guard(spinlock_irq)(&efw->lock);
if (WARN_ON(efw->dev_lock_count <= 0))
- goto end;
+ return;
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
index 36a08ba51ec7..5c859773fe06 100644
--- a/sound/firewire/fireworks/fireworks_transaction.c
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fireworks_transaction.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -83,9 +82,9 @@ int snd_efw_transaction_run(struct fw_unit *unit,
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);
+ scoped_guard(spinlock_irq, &transaction_queues_lock) {
+ list_add_tail(&t.list, &transaction_queues);
+ }
tries = 0;
do {
@@ -108,9 +107,9 @@ int snd_efw_transaction_run(struct fw_unit *unit,
}
} while (1);
- spin_lock_irq(&transaction_queues_lock);
- list_del(&t.list);
- spin_unlock_irq(&transaction_queues_lock);
+ scoped_guard(spinlock_irq, &transaction_queues_lock) {
+ list_del(&t.list);
+ }
return ret;
}
@@ -124,7 +123,7 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
t = (struct snd_efw_transaction *)data;
length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
- spin_lock_irq(&efw->lock);
+ guard(spinlock)(&efw->lock);
if (efw->push_ptr < efw->pull_ptr)
capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
@@ -135,7 +134,7 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
/* confirm enough space for this response */
if (capacity < length) {
*rcode = RCODE_CONFLICT_ERROR;
- goto end;
+ return;
}
/* copy to ring buffer */
@@ -158,8 +157,6 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
wake_up(&efw->hwdep_wait);
*rcode = RCODE_COMPLETE;
-end:
- spin_unlock_irq(&efw->lock);
}
static void
@@ -170,7 +167,7 @@ handle_resp_for_user(struct fw_card *card, int generation, int source,
struct snd_efw *efw;
unsigned int i;
- spin_lock_irq(&instances_lock);
+ guard(spinlock_irq)(&instances_lock);
for (i = 0; i < SNDRV_CARDS; i++) {
efw = instances[i];
@@ -187,11 +184,9 @@ handle_resp_for_user(struct fw_card *card, int generation, int source,
break;
}
if (i == SNDRV_CARDS)
- goto end;
+ return;
copy_resp_to_buf(efw, data, length, rcode);
-end:
- spin_unlock_irq(&instances_lock);
}
static void
@@ -200,9 +195,8 @@ handle_resp_for_kernel(struct fw_card *card, int generation, int source,
{
struct fw_device *device;
struct transaction_queue *t;
- unsigned long flags;
- spin_lock_irqsave(&transaction_queues_lock, flags);
+ guard(spinlock_irqsave)(&transaction_queues_lock);
list_for_each_entry(t, &transaction_queues, list) {
device = fw_parent_device(t->unit);
if ((device->card != card) ||
@@ -220,7 +214,6 @@ handle_resp_for_kernel(struct fw_card *card, int generation, int source,
*rcode = RCODE_COMPLETE;
}
}
- spin_unlock_irqrestore(&transaction_queues_lock, flags);
}
static void
@@ -260,7 +253,7 @@ void snd_efw_transaction_add_instance(struct snd_efw *efw)
{
unsigned int i;
- spin_lock_irq(&instances_lock);
+ guard(spinlock_irq)(&instances_lock);
for (i = 0; i < SNDRV_CARDS; i++) {
if (instances[i] != NULL)
@@ -268,30 +261,26 @@ void snd_efw_transaction_add_instance(struct snd_efw *efw)
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);
+ guard(spinlock_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);
+ guard(spinlock_irq)(&transaction_queues_lock);
list_for_each_entry(t, &transaction_queues, list) {
if ((t->unit == unit) &&
(t->state == STATE_PENDING)) {
@@ -299,7 +288,6 @@ void snd_efw_transaction_bus_reset(struct fw_unit *unit)
wake_up(&t->wait);
}
}
- spin_unlock_irq(&transaction_queues_lock);
}
static struct fw_address_handler resp_register_handler = {
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 46092fa3ff9b..2b7f071d593b 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple iSight audio driver
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <asm/byteorder.h>
@@ -77,7 +77,7 @@ struct audio_payload {
MODULE_DESCRIPTION("iSight audio driver");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
static struct fw_iso_packet audio_packet = {
.payload_length = sizeof(struct audio_payload),
@@ -286,12 +286,6 @@ static int isight_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct isight *isight = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
WRITE_ONCE(isight->pcm_active, true);
@@ -333,11 +327,10 @@ static int isight_hw_free(struct snd_pcm_substream *substream)
WRITE_ONCE(isight->pcm_active, false);
- mutex_lock(&isight->mutex);
+ guard(mutex)(&isight->mutex);
isight_stop_streaming(isight);
- mutex_unlock(&isight->mutex);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int isight_start_streaming(struct isight *isight)
@@ -406,16 +399,12 @@ error:
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;
+ guard(mutex)(&isight->mutex);
+ return isight_start_streaming(isight);
}
static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -447,14 +436,11 @@ static int isight_create_pcm(struct isight *isight)
static const struct snd_pcm_ops ops = {
.open = isight_open,
.close = isight_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = isight_hw_params,
.hw_free = isight_hw_free,
.prepare = isight_prepare,
.trigger = isight_trigger,
.pointer = isight_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -463,9 +449,11 @@ static int isight_create_pcm(struct isight *isight)
if (err < 0)
return err;
pcm->private_data = isight;
- strcpy(pcm->name, "iSight");
+ pcm->nonatomic = true;
+ strscpy(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;
}
@@ -569,18 +557,20 @@ static int isight_create_mixer(struct isight *isight)
return err;
isight->gain_max = be32_to_cpu(value);
- isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
- isight->gain_tlv[1] = 2 * sizeof(unsigned int);
+ 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[2] = (s32)be32_to_cpu(value) * 100;
+ 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[3] = (s32)be32_to_cpu(value) * 100;
+ isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =
+ (s32)be32_to_cpu(value) * 100;
ctl = snd_ctl_new1(&gain_control, isight);
if (ctl)
@@ -601,8 +591,6 @@ static void isight_card_free(struct snd_card *card)
struct isight *isight = card->private_data;
fw_iso_resources_destroy(&isight->resources);
- fw_unit_put(isight->unit);
- mutex_destroy(&isight->mutex);
}
static u64 get_unit_base(struct fw_unit *unit)
@@ -639,19 +627,19 @@ static int isight_probe(struct fw_unit *unit,
if (!isight->audio_base) {
dev_err(&unit->device, "audio unit base not found\n");
err = -ENXIO;
- goto err_unit;
+ goto error;
}
fw_iso_resources_init(&isight->resources, unit);
card->private_free = isight_card_free;
- strcpy(card->driver, "iSight");
- strcpy(card->shortname, "Apple iSight");
+ strscpy(card->driver, "iSight");
+ strscpy(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");
+ strscpy(card->mixername, "iSight");
err = isight_create_pcm(isight);
if (err < 0)
@@ -668,12 +656,12 @@ static int isight_probe(struct fw_unit *unit,
dev_set_drvdata(&unit->device, isight);
return 0;
-
-err_unit:
- fw_unit_put(isight->unit);
- mutex_destroy(&isight->mutex);
error:
snd_card_free(card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+
return err;
}
@@ -684,9 +672,8 @@ static void isight_bus_reset(struct fw_unit *unit)
if (fw_iso_resources_update(&isight->resources) < 0) {
isight_pcm_abort(isight);
- mutex_lock(&isight->mutex);
+ guard(mutex)(&isight->mutex);
isight_stop_streaming(isight);
- mutex_unlock(&isight->mutex);
}
}
@@ -698,11 +685,15 @@ static void isight_remove(struct fw_unit *unit)
snd_card_disconnect(isight->card);
- mutex_lock(&isight->mutex);
- isight_stop_streaming(isight);
- mutex_unlock(&isight->mutex);
+ scoped_guard(mutex, &isight->mutex) {
+ isight_stop_streaming(isight);
+ }
- snd_card_free_when_closed(isight->card);
+ // 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[] = {
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
index 066b5df666f4..4f63279225c5 100644
--- a/sound/firewire/iso-resources.c
+++ b/sound/firewire/iso-resources.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* isochronous resources helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/device.h>
@@ -114,38 +114,34 @@ int fw_iso_resources_allocate(struct fw_iso_resources *r,
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);
+ scoped_guard(spinlock_irq, &card->lock) {
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ }
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");
+ scoped_guard(mutex, &r->mutex) {
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+ fw_iso_resource_manage(card, r->generation, r->channels_mask,
+ &channel, &bandwidth, true);
+ if (channel == -EAGAIN)
+ 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);
@@ -166,17 +162,15 @@ 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);
+ guard(mutex)(&r->mutex);
- if (!r->allocated) {
- mutex_unlock(&r->mutex);
+ if (!r->allocated)
return 0;
- }
- spin_lock_irq(&card->lock);
- r->generation = card->generation;
- r->bandwidth_overhead = current_bandwidth_overhead(card);
- spin_unlock_irq(&card->lock);
+ scoped_guard(spinlock_irq, &card->lock) {
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ }
bandwidth = r->bandwidth + r->bandwidth_overhead;
@@ -196,8 +190,6 @@ int fw_iso_resources_update(struct fw_iso_resources *r)
"isochronous resource allocation failed\n");
}
- mutex_unlock(&r->mutex);
-
return channel;
}
EXPORT_SYMBOL(fw_iso_resources_update);
@@ -218,7 +210,7 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
return;
card = fw_parent_device(r->unit)->card;
- mutex_lock(&r->mutex);
+ guard(mutex)(&r->mutex);
if (r->allocated) {
bandwidth = r->bandwidth + r->bandwidth_overhead;
@@ -230,7 +222,5 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
r->allocated = false;
}
-
- mutex_unlock(&r->mutex);
}
EXPORT_SYMBOL(fw_iso_resources_free);
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 39dfa74906ef..654e1a6050a9 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* miscellaneous helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
@@ -67,38 +67,6 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
-#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
-
-/**
- * snd_fw_schedule_registration - schedule work for sound card registration
- * @unit: an instance for unit on IEEE 1394 bus
- * @dwork: delayed work with callback function
- *
- * This function is not designed for general purposes. When new unit is
- * connected to IEEE 1394 bus, the bus is under bus-reset state because of
- * topological change. In this state, units tend to fail both of asynchronous
- * and isochronous communication. To avoid this problem, this function is used
- * to postpone sound card registration after the state. The callers must
- * set up instance of delayed work in advance.
- */
-void snd_fw_schedule_registration(struct fw_unit *unit,
- struct delayed_work *dwork)
-{
- u64 now, delay;
-
- now = get_jiffies_64();
- delay = fw_parent_device(unit)->card->reset_jiffies
- + msecs_to_jiffies(PROBE_DELAY_MS);
-
- if (time_after64(delay, now))
- delay -= now;
- else
- delay = 0;
-
- mod_delayed_work(system_wq, dwork, delay);
-}
-EXPORT_SYMBOL(snd_fw_schedule_registration);
-
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index dc815dc3933e..664dfdb9e58d 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -23,7 +23,4 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
-void snd_fw_schedule_registration(struct fw_unit *unit,
- struct delayed_work *dwork);
-
#endif
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 7c502d35103c..df0fe886dbc0 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,7 +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 \
+snd-firewire-motu-y := 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-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
index cd0cbfa9f96f..3d36f125cf6a 100644
--- a/sound/firewire/motu/amdtp-motu-trace.h
+++ b/sound/firewire/motu/amdtp-motu-trace.h
@@ -1,8 +1,8 @@
+/* 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
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#undef TRACE_SYSTEM
@@ -18,7 +18,7 @@ static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
unsigned int data_block_quadlets);
-TRACE_EVENT(in_data_block_sph,
+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(
@@ -28,8 +28,13 @@ TRACE_EVENT(in_data_block_sph,
__dynamic_array(u32, tstamps, data_blocks)
),
TP_fast_assign(
- __entry->src = fw_parent_device(s->unit)->node_id;
- __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ 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);
),
@@ -42,55 +47,7 @@ TRACE_EVENT(in_data_block_sph,
)
);
-TRACE_EVENT(out_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(
- __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(in_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(
- __entry->src = fw_parent_device(s->unit)->node_id;
- __entry->dst = fw_parent_device(s->unit)->card->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)
- )
-);
-
-TRACE_EVENT(out_data_block_message,
+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(
@@ -100,8 +57,13 @@ TRACE_EVENT(out_data_block_message,
__dynamic_array(u64, messages, data_blocks)
),
TP_fast_assign(
- __entry->src = fw_parent_device(s->unit)->card->node_id;
- __entry->dst = fw_parent_device(s->unit)->node_id;
+ 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);
),
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index f0555a24d90e..39ed57d2c5a0 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/slab.h>
@@ -17,6 +16,14 @@
#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.
@@ -24,14 +31,6 @@
#define MIDI_BYTES_PER_SECOND 3093
struct amdtp_motu {
- /* For timestamp processing. */
- unsigned int quotient_ticks_per_event;
- unsigned int remainder_ticks_per_event;
- unsigned int next_ticks;
- unsigned int next_accumulated;
- unsigned int next_cycles;
- unsigned int next_seconds;
-
unsigned int pcm_chunks;
unsigned int pcm_byte_offset;
@@ -42,26 +41,16 @@ struct amdtp_motu {
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)
{
- static const struct {
- unsigned int quotient_ticks_per_event;
- unsigned int remainder_ticks_per_event;
- } params[] = {
- [CIP_SFC_44100] = { 557, 123 },
- [CIP_SFC_48000] = { 512, 0 },
- [CIP_SFC_88200] = { 278, 282 },
- [CIP_SFC_96000] = { 256, 0 },
- [CIP_SFC_176400] = { 139, 141 },
- [CIP_SFC_192000] = { 128, 0 },
- };
struct amdtp_motu *p = s->protocol;
unsigned int pcm_chunks, data_chunks, data_block_quadlets;
- unsigned int delay;
unsigned int mode;
int i, err;
@@ -77,18 +66,14 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
if (i == ARRAY_SIZE(snd_motu_clock_rates))
return -EINVAL;
- pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ // 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;
-
- /*
- * Each data block includes SPH in its head. Data chunks follow with
- * 3 byte alignment. Padding follows with zero to conform to quadlet
- * alignment.
- */
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
- err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1);
if (err < 0)
return err;
@@ -102,41 +87,36 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->midi_db_count = 0;
p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
- /* IEEE 1394 bus requires. */
- delay = 0x2e00;
-
- /* For no-data or empty packets to adjust PCM sampling frequency. */
- delay += 8000 * 3072 * s->syt_interval / rate;
-
- p->next_seconds = 0;
- p->next_cycles = delay / 3072;
- p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
- p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
- p->next_ticks = delay % 3072;
- p->next_accumulated = 0;
-
return 0;
}
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_runtime *runtime,
- __be32 *buffer, unsigned int data_blocks)
+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, remaining_frames, i, c;
+ 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;
- channels = p->pcm_chunks;
dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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];
+ *dst = (byte[0] << 24) |
+ (byte[1] << 16) |
+ (byte[2] << 8);
byte += 3;
dst++;
}
@@ -146,19 +126,25 @@ static void read_pcm_s32(struct amdtp_stream *s,
}
}
-static void write_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_runtime *runtime,
- __be32 *buffer, unsigned int data_blocks)
+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, remaining_frames, i, c;
+ 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;
- channels = p->pcm_chunks;
src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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;
@@ -290,138 +276,210 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
/* This is just for v2/v3 protocol. */
for (i = 0; i < data_blocks; ++i) {
- *frames = (be32_to_cpu(buffer[1]) << 16) |
- (be32_to_cpu(buffer[2]) >> 16);
+ *frames = be32_to_cpu(buffer[1]);
+ *frames <<= 16;
+ *frames |= be32_to_cpu(buffer[2]) >> 16;
+ ++frames;
buffer += data_block_quadlets;
- frames++;
}
}
-static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer, unsigned int data_blocks,
- unsigned int *syt)
+static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count)
{
- struct amdtp_motu *p = s->protocol;
- struct snd_pcm_substream *pcm;
-
- trace_in_data_block_sph(s, data_blocks, buffer);
- trace_in_data_block_message(s, data_blocks, buffer);
+ int i;
- if (p->midi_ports)
- read_midi_messages(s, buffer, data_blocks);
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
- pcm = READ_ONCE(s->pcm);
- if (data_blocks > 0 && pcm)
- read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
+ trace_data_block_sph(s, data_blocks, buf);
+ trace_data_block_message(s, data_blocks, buf);
- return data_blocks;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
+static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *buf,
+ unsigned int data_blocks, unsigned int data_block_quadlets)
{
- p->next_accumulated += p->remainder_ticks_per_event;
- if (p->next_accumulated >= 441) {
- p->next_accumulated -= 441;
- p->next_ticks++;
- }
+ 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;
- p->next_ticks += p->quotient_ticks_per_event;
- if (p->next_ticks >= 3072) {
- p->next_ticks -= 3072;
- p->next_cycles++;
- }
+ 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 (p->next_cycles >= 8000) {
- p->next_cycles -= 8000;
- p->next_seconds++;
+ 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;
}
- if (p->next_seconds >= 128)
- p->next_seconds -= 128;
+ cache->tail = cache_tail;
+ cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
}
-static void write_sph(struct amdtp_stream *s, __be32 *buffer,
- unsigned int data_blocks)
+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;
- unsigned int next_cycles;
- unsigned int i;
- u32 sph;
+ 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++) {
- next_cycles = (s->start_cycle + p->next_cycles) % 8000;
- sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
+ 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);
- compute_next_elapse_from_start(p);
-
- buffer += s->data_block_quadlets;
+ 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 unsigned int process_rx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer, unsigned int data_blocks,
- unsigned int *syt)
+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 = (struct amdtp_motu *)s->protocol;
- struct snd_pcm_substream *pcm;
+ struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
+ unsigned int pcm_frames = 0;
+ int i;
- /* Not used. */
- *syt = 0xffff;
+ if (p->cache->rx_cycle_count == UINT_MAX)
+ p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
- /* TODO: how to interact control messages between userspace? */
+ // 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, buffer, data_blocks);
+ if (p->midi_ports)
+ write_midi_messages(s, buf, data_blocks);
- pcm = READ_ONCE(s->pcm);
- if (pcm)
- write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
- else
- write_pcm_silence(s, buffer, data_blocks);
+ write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
- write_sph(s, buffer, data_blocks);
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
- trace_out_data_block_sph(s, data_blocks, buffer);
- trace_out_data_block_message(s, data_blocks, buffer);
+ desc = cursor;
- return data_blocks;
+ // 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_protocol *const protocol)
+ const struct snd_motu_spec *spec, struct amdtp_motu_cache *cache)
{
- amdtp_stream_process_data_blocks_t process_data_blocks;
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
int fmt = CIP_FMT_MOTU;
- int flags = CIP_BLOCKING;
+ unsigned int flags = CIP_BLOCKING | CIP_UNAWARE_SYT;
+ struct amdtp_motu *p;
int err;
if (dir == AMDTP_IN_STREAM) {
- process_data_blocks = process_tx_data_blocks;
+ process_ctx_payloads = process_ir_ctx_payloads;
/*
* Units of version 3 transmits packets with invalid CIP header
* against IEC 61883-1.
*/
- if (protocol == &snd_motu_protocol_v3) {
+ 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_data_blocks = process_rx_data_blocks;
+ process_ctx_payloads = process_it_ctx_payloads;
flags |= CIP_DBC_IS_END_EVENT;
}
- err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
+ err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
sizeof(struct amdtp_motu));
if (err < 0)
return err;
s->sph = 1;
- s->fdf = MOTU_FDF_AM824;
+
+ 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..c6440e6e360b
--- /dev/null
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -0,0 +1,179 @@
+// 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;
+ int i;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ 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;
+ }
+ }
+ }
+ }
+}
+
+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;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
index 7b6a086866e7..981c19430cb0 100644
--- a/sound/firewire/motu/motu-hwdep.c
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -17,6 +16,14 @@
#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)
{
@@ -26,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
- while (!motu->dev_lock_changed && motu->msg == 0) {
+ 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();
@@ -41,20 +48,50 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
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.lock_status));
- } else {
+ 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.motu_notification));
- }
+ 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);
- spin_unlock_irq(&motu->lock);
+ // Header is filled later.
+ consumed += sizeof(event.motu_register_dsp_change);
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ 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;
}
@@ -63,18 +100,14 @@ 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)
- events = POLLIN | POLLRDNORM;
+ guard(spinlock_irq)(&motu->lock);
+ if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&motu->lock);
-
- return events | POLLOUT;
+ return 0;
}
static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
@@ -87,7 +120,7 @@ static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -98,48 +131,35 @@ static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
static int hwdep_lock(struct snd_motu *motu)
{
- int err;
-
- spin_lock_irq(&motu->lock);
+ guard(spinlock_irq)(&motu->lock);
if (motu->dev_lock_count == 0) {
motu->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&motu->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_motu *motu)
{
- int err;
-
- spin_lock_irq(&motu->lock);
+ guard(spinlock_irq)(&motu->lock);
if (motu->dev_lock_count == -1) {
motu->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -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);
+ guard(spinlock_irq)(&motu->lock);
if (motu->dev_lock_count == -1)
motu->dev_lock_count = 0;
- spin_unlock_irq(&motu->lock);
return 0;
}
@@ -156,6 +176,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
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;
}
@@ -188,11 +273,13 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu)
if (err < 0)
return err;
- strcpy(hwdep->name, "MOTU");
+ strscpy(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
index e55cab6d79c7..85e3260f9349 100644
--- a/sound/firewire/motu/motu-midi.c
+++ b/sound/firewire/motu/motu-midi.c
@@ -1,35 +1,12 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
-static int midi_capture_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);
-
- motu->capture_substreams++;
- err = snd_motu_stream_start_duplex(motu, 0);
-
- mutex_unlock(&motu->mutex);
-
- if (err < 0)
- snd_motu_stream_lock_release(motu);
-
- return err;
-}
-
-static int midi_playback_open(struct snd_rawmidi_substream *substream)
+static int midi_open(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
int err;
@@ -38,12 +15,15 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
return err;
- mutex_lock(&motu->mutex);
-
- motu->playback_substreams++;
- err = snd_motu_stream_start_duplex(motu, 0);
-
- mutex_unlock(&motu->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_motu_stream_lock_release(motu);
@@ -51,31 +31,14 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
return err;
}
-static int midi_capture_close(struct snd_rawmidi_substream *substream)
-{
- struct snd_motu *motu = substream->rmidi->private_data;
-
- mutex_lock(&motu->mutex);
-
- motu->capture_substreams--;
- snd_motu_stream_stop_duplex(motu);
-
- mutex_unlock(&motu->mutex);
-
- snd_motu_stream_lock_release(motu);
- return 0;
-}
-
-static int midi_playback_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
- mutex_lock(&motu->mutex);
-
- motu->playback_substreams--;
- snd_motu_stream_stop_duplex(motu);
-
- mutex_unlock(&motu->mutex);
+ scoped_guard(mutex, &motu->mutex) {
+ --motu->substreams_counter;
+ snd_motu_stream_stop_duplex(motu);
+ }
snd_motu_stream_lock_release(motu);
return 0;
@@ -84,9 +47,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&motu->lock);
if (up)
amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
@@ -94,16 +56,13 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
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);
+ guard(spinlock_irqsave)(&motu->lock);
if (up)
amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
@@ -111,8 +70,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
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,
@@ -121,21 +78,21 @@ static void set_midi_substream_names(struct snd_motu *motu,
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);
+ scnprintf(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_capture_open,
- .close = midi_capture_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_capture_trigger,
};
static const struct snd_rawmidi_ops playback_ops = {
- .open = midi_playback_open,
- .close = midi_playback_close,
+ .open = midi_open,
+ .close = midi_close,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
index 4330220890e8..600c571edf02 100644
--- a/sound/firewire/motu/motu-pcm.c
+++ b/sound/firewire/motu/motu-pcm.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm_params.h>
@@ -27,8 +26,7 @@ static int motu_rate_constraint(struct snd_pcm_hw_params *params,
rate = snd_motu_clock_rates[i];
mode = i / 2;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
if (!snd_interval_test(c, pcm_channels))
continue;
@@ -60,8 +58,7 @@ static int motu_channels_constraint(struct snd_pcm_hw_params *params,
if (!snd_interval_test(r, rate))
continue;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
channels.min = min(channels.min, pcm_channels);
channels.max = max(channels.max, pcm_channels);
}
@@ -83,8 +80,7 @@ static void limit_channels_and_rates(struct snd_motu *motu,
rate = snd_motu_clock_rates[i];
mode = i / 2;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
if (pcm_channels == 0)
continue;
@@ -134,49 +130,64 @@ static int init_hw_info(struct snd_motu *motu,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+ struct amdtp_domain *d = &motu->domain;
enum snd_motu_clock_source src;
- unsigned int rate;
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;
+ scoped_guard(mutex, &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 = init_hw_info(motu, substream);
+ if (err < 0)
+ goto err_locked;
- /*
- * When source of clock is not internal or any PCM streams are running,
- * available sampling rate is limited at current sampling rate.
- */
- err = protocol->get_clock_source(motu, &src);
- if (err < 0)
- goto err_locked;
- if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
- amdtp_stream_pcm_running(&motu->tx_stream) ||
- amdtp_stream_pcm_running(&motu->rx_stream)) {
- err = protocol->get_clock_rate(motu, &rate);
+ err = snd_motu_protocol_get_clock_source(motu, &src);
if (err < 0)
goto err_locked;
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
+
+ // 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 err;
+ return 0;
err_locked:
- mutex_unlock(&motu->mutex);
snd_motu_stream_lock_release(motu);
return err;
}
@@ -190,75 +201,39 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&motu->mutex);
- motu->capture_substreams++;
- mutex_unlock(&motu->mutex);
+ 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);
+
+ guard(mutex)(&motu->mutex);
+ err = snd_motu_stream_reserve_duplex(motu, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++motu->substreams_counter;
}
- return 0;
-}
-static int playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_motu *motu = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&motu->mutex);
- motu->playback_substreams++;
- mutex_unlock(&motu->mutex);
- }
-
- return 0;
-}
-
-static int capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_motu *motu = substream->private_data;
-
- mutex_lock(&motu->mutex);
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- motu->capture_substreams--;
-
- snd_motu_stream_stop_duplex(motu);
-
- mutex_unlock(&motu->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return err;
}
-static int playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
- mutex_lock(&motu->mutex);
+ guard(mutex)(&motu->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- motu->playback_substreams--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --motu->substreams_counter;
snd_motu_stream_stop_duplex(motu);
- mutex_unlock(&motu->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int capture_prepare(struct snd_pcm_substream *substream)
@@ -266,9 +241,9 @@ 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, substream->runtime->rate);
- mutex_unlock(&motu->mutex);
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_start_duplex(motu);
+ }
if (err >= 0)
amdtp_stream_pcm_prepare(&motu->tx_stream);
@@ -279,9 +254,9 @@ 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, substream->runtime->rate);
- mutex_unlock(&motu->mutex);
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_start_duplex(motu);
+ }
if (err >= 0)
amdtp_stream_pcm_prepare(&motu->rx_stream);
@@ -327,27 +302,27 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
- return amdtp_stream_pcm_pointer(&motu->tx_stream);
+ 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_stream_pcm_pointer(&motu->rx_stream);
+ 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_stream_pcm_ack(&motu->tx_stream);
+ 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_stream_pcm_ack(&motu->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);
}
int snd_motu_create_pcm_devices(struct snd_motu *motu)
@@ -355,28 +330,22 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = capture_hw_params,
- .hw_free = capture_hw_free,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
.prepare = capture_prepare,
.trigger = capture_trigger,
.pointer = capture_pointer,
.ack = capture_ack,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = playback_hw_params,
- .hw_free = playback_hw_free,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
.prepare = playback_prepare,
.trigger = playback_trigger,
.pointer = playback_pointer,
.ack = playback_ack,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -385,10 +354,12 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
if (err < 0)
return err;
pcm->private_data = motu;
- strcpy(pcm->name, motu->card->shortname);
+ pcm->nonatomic = true;
+ strscpy(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
index 4edc064999ed..f009cf7aa074 100644
--- a/sound/firewire/motu/motu-proc.c
+++ b/sound/firewire/motu/motu-proc.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./motu.h"
@@ -17,9 +16,11 @@ static const char *const clock_names[] = {
[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/PCIF on coaxial interface",
+ [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,
@@ -27,13 +28,12 @@ static void proc_read_clock(struct snd_info_entry *entry,
{
struct snd_motu *motu = entry->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int rate;
enum snd_motu_clock_source source;
- if (protocol->get_clock_rate(motu, &rate) < 0)
+ if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0)
return;
- if (protocol->get_clock_source(motu, &source) < 0)
+ if (snd_motu_protocol_get_clock_source(motu, &source) < 0)
return;
snd_iprintf(buffer, "Rate:\t%d\n", rate);
@@ -44,15 +44,14 @@ static void proc_read_format(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_motu *motu = entry->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int mode;
struct snd_motu_packet_format *formats;
int i;
- if (protocol->cache_packet_formats(motu) < 0)
+ if (snd_motu_protocol_cache_packet_formats(motu) < 0)
return;
- snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
+ snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
@@ -61,11 +60,11 @@ static void proc_read_format(struct snd_info_entry *entry,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
- formats->fixed_part_pcm_chunks[mode],
- formats->differed_part_pcm_chunks[mode]);
+ motu->spec->tx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
}
- snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
+ snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
@@ -74,8 +73,8 @@ static void proc_read_format(struct snd_info_entry *entry,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
- formats->fixed_part_pcm_chunks[mode],
- formats->differed_part_pcm_chunks[mode]);
+ motu->spec->rx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
}
}
@@ -87,12 +86,8 @@ static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(motu->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, motu, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, motu, op);
}
void snd_motu_proc_init(struct snd_motu *motu)
@@ -107,11 +102,7 @@ void snd_motu_proc_init(struct snd_motu *motu)
motu->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- 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
index 525b746330be..a5f70efa2e88 100644
--- a/sound/firewire/motu/motu-protocol-v2.c
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
@@ -13,6 +12,15 @@
#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
@@ -23,10 +31,21 @@
#define V2_OPT_IFACE_MODE_ADAT 1
#define V2_OPT_IFACE_MODE_SPDIF 2
-static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+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;
- unsigned int index;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
@@ -34,16 +53,11 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
if (err < 0)
return err;
- index = (be32_to_cpu(reg) & 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;
+ return get_clock_rate(be32_to_cpu(reg), rate);
}
-static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
{
__be32 reg;
u32 data;
@@ -71,162 +85,237 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
sizeof(reg));
}
-static int v2_get_clock_source(struct snd_motu *motu,
- enum snd_motu_clock_source *src)
+static int get_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
{
- __be32 reg;
- unsigned int index;
- int err;
-
- err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
-
- index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
- if (index > 5)
- return -EIO;
-
- /* To check the configuration of optical interface. */
- err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
-
- switch (index) {
- case 0:
+ switch (data & V2_CLOCK_SRC_MASK) {
+ case V2_CLOCK_SRC_INTERNAL:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
- case 1:
- if (be32_to_cpu(reg) & 0x00000200)
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
- else
- *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ case V2_CLOCK_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
break;
- case 2:
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ 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 4:
+ case V2_CLOCK_SRC_WORD_ON_BNC:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
break;
- case 5:
+ 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:
- return -EIO;
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
}
return 0;
}
-static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
+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)
{
- /* V2 protocol doesn't have this feature. */
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
return 0;
}
-static void calculate_fixed_part(struct snd_motu_packet_format *formats,
- enum amdtp_stream_direction dir,
- enum snd_motu_spec_flags flags,
- unsigned char analog_ports)
+// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
+static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
+ bool enable)
{
- unsigned char pcm_chunks[3] = {0, 0, 0};
+ unsigned int rate;
+ enum snd_motu_clock_source src;
+ int err;
- formats->msg_chunks = 2;
+ err = get_clock_source(motu, *data, &src);
+ if (err < 0)
+ return err;
- pcm_chunks[0] = analog_ports;
- pcm_chunks[1] = analog_ports;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] = analog_ports;
+ err = get_clock_rate(*data, &rate);
+ if (err < 0)
+ return err;
- if (dir == AMDTP_IN_STREAM) {
- if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
- if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
- } else {
- /*
- * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
- */
- pcm_chunks[0] += 4;
- pcm_chunks[1] += 4;
- }
+ if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
- /*
- * All of v2 models have a pair of coaxial interfaces for digital in/out
- * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
- * ports.
- */
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
-
- /* This part should be multiples of 4. */
- formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
- formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- formats->fixed_part_pcm_chunks[2] =
- round_up(2 + pcm_chunks[2], 4) - 2;
+ return 0;
}
-static void calculate_differed_part(struct snd_motu_packet_format *formats,
- enum snd_motu_spec_flags flags,
- u32 data, u32 mask, u32 shift)
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
{
- unsigned char pcm_chunks[3] = {0, 0};
-
- /*
- * When optical interfaces are configured for S/PDIF (TOSLINK),
- * the above PCM frames come from them, instead of coaxial
- * interfaces.
- */
- data = (data & mask) >> shift;
- if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
- data == V2_OPT_IFACE_MODE_ADAT) {
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
- }
+ 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;
- /* At mode x4, no data chunks are supported in this part. */
- formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
- formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ }
}
-static int v2_cache_packet_formats(struct snd_motu *motu)
+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);
- calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
- motu->spec->flags, motu->spec->analog_in_ports);
- calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
- data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
+ 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));
- calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
- motu->spec->flags, motu->spec->analog_out_ports);
- calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
- data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
+ 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;
- motu->tx_packet_formats.pcm_byte_offset = 10;
- motu->rx_packet_formats.pcm_byte_offset = 10;
+ 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_protocol snd_motu_protocol_v2 = {
- .get_clock_rate = v2_get_clock_rate,
- .set_clock_rate = v2_set_clock_rate,
- .get_clock_source = v2_get_clock_source,
- .switch_fetching_mode = v2_switch_fetching_mode,
- .cache_packet_formats = v2_cache_packet_formats,
+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
index c7cd9864dc4d..7254fdfe046a 100644
--- a/sound/firewire/motu/motu-protocol-v3.c
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
@@ -14,6 +13,13 @@
#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
@@ -25,7 +31,11 @@
#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
-static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+#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;
@@ -46,7 +56,8 @@ static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
return 0;
}
-static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
{
__be32 reg;
u32 data;
@@ -78,61 +89,85 @@ static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
return err;
if (need_to_wait) {
- /* Cost expensive. */
- if (msleep_interruptible(4000) > 0)
- return -EINTR;
+ 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;
}
-static int v3_get_clock_source(struct snd_motu *motu,
- enum snd_motu_clock_source *src)
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
{
__be32 reg;
u32 data;
- unsigned int val;
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 = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
- val = data & V3_CLOCK_SOURCE_MASK;
- if (val == 0x00) {
+ switch (data) {
+ case V3_CLOCK_SRC_INTERNAL:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
- } else if (val == 0x01) {
+ break;
+ case V3_CLOCK_SRC_WORD_ON_BNC:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
- } else if (val == 0x10) {
+ 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;
- } else if (val == 0x18 || val == 0x19) {
- err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
- &reg, sizeof(reg));
+ 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;
- data = be32_to_cpu(reg);
+ options = be32_to_cpu(reg);
- if (val == 0x18) {
- if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
+ 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 (data & V3_NO_ADAT_OPT_IN_IFACE_B)
+ 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;
}
- } else {
+ break;
+ }
+ default:
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
}
return 0;
}
-static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
{
__be32 reg;
u32 data;
@@ -154,153 +189,158 @@ static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
sizeof(reg));
}
-static void calculate_fixed_part(struct snd_motu_packet_format *formats,
- enum amdtp_stream_direction dir,
- enum snd_motu_spec_flags flags,
- unsigned char analog_ports)
+static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
{
- unsigned char pcm_chunks[3] = {0, 0, 0};
-
- formats->msg_chunks = 2;
-
- pcm_chunks[0] = analog_ports;
- pcm_chunks[1] = analog_ports;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] = analog_ports;
-
- if (dir == AMDTP_IN_STREAM) {
- if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] += 2;
- }
-
- if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] += 2;
+ 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 (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
+ 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;
}
- } else {
- /*
- * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
- */
- pcm_chunks[0] += 4;
- pcm_chunks[1] += 4;
}
- /*
- * At least, packets have two data chunks for S/PDIF on coaxial
- * interface.
- */
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
-
- /*
- * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
- * a result, this part can includes empty data chunks.
- */
- formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
- formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- formats->fixed_part_pcm_chunks[2] =
- round_up(2 + pcm_chunks[2], 4) - 2;
-}
-
-static void calculate_differed_part(struct snd_motu_packet_format *formats,
- enum snd_motu_spec_flags flags, u32 data,
- u32 a_enable_mask, u32 a_no_adat_mask,
- u32 b_enable_mask, u32 b_no_adat_mask)
-{
- unsigned char pcm_chunks[3] = {0, 0, 0};
- int i;
-
- if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
- if (data & a_no_adat_mask) {
- /*
- * Additional two data chunks for S/PDIF on optical
- * interface A. This includes empty data chunks.
- */
- pcm_chunks[0] += 4;
- 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 {
- /*
- * Additional data chunks for ADAT on optical interface
- * A.
- */
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
}
}
- if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
- if (data & b_no_adat_mask) {
- /*
- * Additional two data chunks for S/PDIF on optical
- * interface B. This includes empty data chunks.
- */
- pcm_chunks[0] += 4;
- 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 {
- /*
- * Additional data chunks for ADAT on optical interface
- * B.
- */
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
}
}
- for (i = 0; i < 3; ++i) {
- if (pcm_chunks[i] > 0)
- pcm_chunks[i] = round_up(pcm_chunks[i], 4);
-
- formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
- }
+ return 0;
}
-static int v3_cache_packet_formats(struct snd_motu *motu)
+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);
- calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
- motu->spec->flags, motu->spec->analog_in_ports);
- calculate_differed_part(&motu->tx_packet_formats,
- motu->spec->flags, data,
- V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
- V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
+ 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_896mk3 ||
+ 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;
+}
- calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
- motu->spec->flags, motu->spec->analog_out_ports);
- calculate_differed_part(&motu->rx_packet_formats,
- motu->spec->flags, data,
- V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
- V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
+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},
+};
- motu->tx_packet_formats.pcm_byte_offset = 10;
- motu->rx_packet_formats.pcm_byte_offset = 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.
+};
- return 0;
-}
+const struct snd_motu_spec snd_motu_spec_896mk3 = {
+ .name = "896mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {18, 14, 10},
+};
+
+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_protocol snd_motu_protocol_v3 = {
- .get_clock_rate = v3_get_clock_rate,
- .set_clock_rate = v3_set_clock_rate,
- .get_clock_source = v3_get_clock_source,
- .switch_fetching_mode = v3_switch_fetching_mode,
- .cache_packet_formats = v3_cache_packet_formats,
+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..a8053e3ef065
--- /dev/null
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -0,0 +1,413 @@
+// 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;
+ int i;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ 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);
+}
+
+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;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+}
+
+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;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(param, &parser->param, sizeof(*param));
+}
+
+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;
+
+ if (pos == parser->push_pos)
+ return false;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ *event = parser->event_queue[pos];
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->pull_pos = pos;
+
+ return true;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 73e7a5e527fc..e5f21360cfb7 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -1,14 +1,13 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
-#define CALLBACK_TIMEOUT 200
+#define READY_TIMEOUT_MS 200
#define ISOC_COMM_CONTROL_OFFSET 0x0b00
#define ISOC_COMM_CONTROL_MASK 0xffff0000
@@ -26,48 +25,47 @@
#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
-static int start_both_streams(struct snd_motu *motu, unsigned int rate)
+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;
- __be32 reg;
- u32 data;
int err;
- if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
- (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
- midi_ports = 1;
+ if (stream == &motu->rx_stream) {
+ resources = &motu->rx_resources;
+ packet_format = &motu->rx_packet_formats;
- /* Set packet formation to our packet streaming engine. */
- err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
- &motu->rx_packet_formats);
- if (err < 0)
- return err;
+ 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;
- else
- midi_ports = 0;
+ 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(&motu->tx_stream, rate, midi_ports,
- &motu->tx_packet_formats);
+ err = amdtp_motu_set_parameters(stream, rate, midi_ports,
+ packet_format);
if (err < 0)
return err;
- /* Get isochronous resources on the bus. */
- err = fw_iso_resources_allocate(&motu->rx_resources,
- amdtp_stream_get_max_payload(&motu->rx_stream),
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
+}
- err = fw_iso_resources_allocate(&motu->tx_resources,
- amdtp_stream_get_max_payload(&motu->tx_stream),
- fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
+static int begin_session(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
- /* Configure the unit to start isochronous communication. */
+ // Configure the unit to start isochronous communication.
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
if (err < 0)
@@ -84,13 +82,13 @@ static int start_both_streams(struct snd_motu *motu, unsigned int rate)
sizeof(reg));
}
-static void stop_both_streams(struct snd_motu *motu)
+static void finish_session(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
- err = motu->spec->protocol->switch_fetching_mode(motu, false);
+ err = snd_motu_protocol_switch_fetching_mode(motu, false);
if (err < 0)
return;
@@ -106,53 +104,13 @@ static void stop_both_streams(struct snd_motu *motu)
reg = cpu_to_be32(data);
snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
-
- fw_iso_resources_free(&motu->tx_resources);
- fw_iso_resources_free(&motu->rx_resources);
-}
-
-static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
-{
- struct fw_iso_resources *resources;
- int err;
-
- if (stream == &motu->rx_stream)
- resources = &motu->rx_resources;
- else
- resources = &motu->tx_resources;
-
- err = amdtp_stream_start(stream, resources->channel,
- fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
-
- if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(stream);
- fw_iso_resources_free(resources);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
-{
- struct fw_iso_resources *resources;
-
- if (stream == &motu->rx_stream)
- resources = &motu->rx_resources;
- else
- resources = &motu->tx_resources;
-
- amdtp_stream_stop(stream);
- fw_iso_resources_free(resources);
}
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
{
int err;
- err = motu->spec->protocol->cache_packet_formats(motu);
+ err = snd_motu_protocol_cache_packet_formats(motu);
if (err < 0)
return err;
@@ -175,6 +133,71 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
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;
@@ -190,9 +213,9 @@ static int ensure_packet_formats(struct snd_motu *motu)
data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
TX_PACKET_TRANSMISSION_SPEED_MASK);
- if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])
data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
- if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ 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;
@@ -201,83 +224,85 @@ static int ensure_packet_formats(struct snd_motu *motu)
sizeof(reg));
}
-int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
+int snd_motu_stream_start_duplex(struct snd_motu *motu)
{
- const struct snd_motu_protocol *protocol = motu->spec->protocol;
- unsigned int curr_rate;
+ unsigned int generation = motu->rx_resources.generation;
int err = 0;
- if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
+ if (motu->substreams_counter == 0)
return 0;
- /* Some packet queueing errors. */
if (amdtp_streaming_error(&motu->rx_stream) ||
amdtp_streaming_error(&motu->tx_stream)) {
- amdtp_stream_stop(&motu->rx_stream);
- amdtp_stream_stop(&motu->tx_stream);
- stop_both_streams(motu);
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
}
- err = snd_motu_stream_cache_packet_formats(motu);
- if (err < 0)
- return err;
+ if (generation != fw_parent_device(motu->unit)->card->generation) {
+ err = fw_iso_resources_update(&motu->rx_resources);
+ if (err < 0)
+ return err;
- /* Stop stream if rate is different. */
- err = protocol->get_clock_rate(motu, &curr_rate);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to get sampling rate: %d\n", err);
- return err;
- }
- if (rate == 0)
- rate = curr_rate;
- if (rate != curr_rate) {
- amdtp_stream_stop(&motu->rx_stream);
- amdtp_stream_stop(&motu->tx_stream);
- stop_both_streams(motu);
+ err = fw_iso_resources_update(&motu->tx_resources);
+ if (err < 0)
+ return err;
}
if (!amdtp_stream_running(&motu->rx_stream)) {
- err = protocol->set_clock_rate(motu, rate);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to set sampling rate: %d\n", err);
- return err;
- }
+ int spd = fw_parent_device(motu->unit)->max_speed;
err = ensure_packet_formats(motu);
if (err < 0)
return err;
- err = start_both_streams(motu, rate);
+ 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 = start_isoc_ctx(motu, &motu->rx_stream);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to start IT context: %d\n", err);
+ err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
+ motu->tx_resources.channel, spd);
+ if (err < 0)
goto stop_streams;
- }
- err = protocol->switch_fetching_mode(motu, true);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to enable frame fetching: %d\n", err);
+ 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;
}
- }
- if (!amdtp_stream_running(&motu->tx_stream) &&
- motu->capture_substreams > 0) {
- err = start_isoc_ctx(motu, &motu->tx_stream);
+ err = snd_motu_protocol_switch_fetching_mode(motu, true);
if (err < 0) {
dev_err(&motu->unit->device,
- "fail to start IR context: %d", err);
- amdtp_stream_stop(&motu->rx_stream);
+ "fail to enable frame fetching: %d\n", err);
goto stop_streams;
}
}
@@ -285,95 +310,93 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
return 0;
stop_streams:
- stop_both_streams(motu);
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
return err;
}
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
{
- if (motu->capture_substreams == 0) {
- if (amdtp_stream_running(&motu->tx_stream))
- stop_isoc_ctx(motu, &motu->tx_stream);
-
- if (motu->playback_substreams == 0) {
- if (amdtp_stream_running(&motu->rx_stream))
- stop_isoc_ctx(motu, &motu->rx_stream);
- stop_both_streams(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, enum amdtp_stream_direction dir)
+static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
{
- int err;
- struct amdtp_stream *stream;
struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
- if (dir == AMDTP_IN_STREAM) {
- stream = &motu->tx_stream;
+ if (s == &motu->tx_stream) {
resources = &motu->tx_resources;
+ dir = AMDTP_IN_STREAM;
} else {
- stream = &motu->rx_stream;
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(stream, motu->unit, dir, motu->spec->protocol);
- if (err < 0) {
- amdtp_stream_destroy(stream);
+ 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,
- enum amdtp_stream_direction dir)
+static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
{
- struct amdtp_stream *stream;
- struct fw_iso_resources *resources;
+ amdtp_stream_destroy(s);
- if (dir == AMDTP_IN_STREAM) {
- stream = &motu->tx_stream;
- resources = &motu->tx_resources;
- } else {
- stream = &motu->rx_stream;
- resources = &motu->rx_resources;
- }
-
- amdtp_stream_destroy(stream);
- fw_iso_resources_free(resources);
+ 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, AMDTP_IN_STREAM);
+ err = init_stream(motu, &motu->tx_stream);
if (err < 0)
return err;
- err = init_stream(motu, AMDTP_OUT_STREAM);
- if (err < 0)
- destroy_stream(motu, AMDTP_IN_STREAM);
+ 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.
- */
+// This function should be called before starting streams or after stopping
+// streams.
void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
{
- destroy_stream(motu, AMDTP_IN_STREAM);
- destroy_stream(motu, AMDTP_OUT_STREAM);
+ amdtp_domain_destroy(&motu->domain);
+
+ destroy_stream(motu, &motu->rx_stream);
+ destroy_stream(motu, &motu->tx_stream);
- motu->playback_substreams = 0;
- motu->capture_substreams = 0;
+ motu->substreams_counter = 0;
}
static void motu_lock_changed(struct snd_motu *motu)
@@ -384,32 +407,23 @@ static void motu_lock_changed(struct snd_motu *motu)
int snd_motu_stream_lock_try(struct snd_motu *motu)
{
- int err;
+ guard(spinlock_irq)(&motu->lock);
- spin_lock_irq(&motu->lock);
-
- if (motu->dev_lock_count < 0) {
- err = -EBUSY;
- goto out;
- }
+ if (motu->dev_lock_count < 0)
+ return -EBUSY;
if (motu->dev_lock_count++ == 0)
motu_lock_changed(motu);
- err = 0;
-out:
- spin_unlock_irq(&motu->lock);
- return err;
+ return 0;
}
void snd_motu_stream_lock_release(struct snd_motu *motu)
{
- spin_lock_irq(&motu->lock);
+ guard(spinlock_irq)(&motu->lock);
if (WARN_ON(motu->dev_lock_count <= 0))
- goto out;
+ return;
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
index 7fc30091e0de..804f4208cf81 100644
--- a/sound/firewire/motu/motu-transaction.c
+++ b/sound/firewire/motu/motu-transaction.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
@@ -52,7 +51,6 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
{
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);
@@ -64,9 +62,9 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
return;
}
- spin_lock_irqsave(&motu->lock, flags);
- motu->msg = be32_to_cpu(*buf);
- spin_unlock_irqrestore(&motu->lock, flags);
+ scoped_guard(spinlock_irqsave, &motu->lock) {
+ motu->msg = be32_to_cpu(*buf);
+ }
fw_send_response(card, request, RCODE_COMPLETE);
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 0d6b526105ab..fd2a9dddbfa6 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -1,9 +1,8 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
@@ -12,7 +11,7 @@
MODULE_DESCRIPTION("MOTU FireWire driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
/* mode 0 */
@@ -36,56 +35,53 @@ static void name_card(struct snd_motu *motu)
fw_csr_iterator_init(&it, motu->unit->directory);
while (fw_csr_iterator_next(&it, &key, &val)) {
switch (key) {
- case CSR_VERSION:
+ 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);
+ strscpy(motu->card->driver, "FW-MOTU");
+ strscpy(motu->card->shortname, motu->spec->name);
+ strscpy(motu->card->mixername, motu->spec->name);
snprintf(motu->card->longname, sizeof(motu->card->longname),
- "MOTU %s (version:%d), GUID %08x%08x at %s, S%d",
+ "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_free(struct snd_motu *motu)
+static void motu_card_free(struct snd_card *card)
{
- snd_motu_transaction_unregister(motu);
+ struct snd_motu *motu = card->private_data;
+ snd_motu_transaction_unregister(motu);
snd_motu_stream_destroy_duplex(motu);
- fw_unit_put(motu->unit);
mutex_destroy(&motu->mutex);
- kfree(motu);
-}
-
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void motu_card_free(struct snd_card *card)
-{
- motu_free(card->private_data);
+ fw_unit_put(motu->unit);
}
-static void do_registration(struct work_struct *work)
+static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
+ struct snd_card *card;
+ struct snd_motu *motu;
int err;
- if (motu->registered)
- return;
-
- err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
- &motu->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
if (err < 0)
- return;
+ 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);
@@ -116,143 +112,71 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(motu->card);
+ 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;
- /*
- * After registered, motu instance can be released corresponding to
- * releasing the sound card instance.
- */
- motu->card->private_free = motu_card_free;
- motu->card->private_data = motu;
- motu->registered = true;
-
- return;
-error:
- snd_motu_transaction_unregister(motu);
- snd_motu_stream_destroy_duplex(motu);
- snd_card_free(motu->card);
- dev_info(&motu->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int motu_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_motu *motu;
-
- /* Allocate this independently of sound card instance. */
- motu = kzalloc(sizeof(struct snd_motu), GFP_KERNEL);
- if (motu == NULL)
- return -ENOMEM;
-
- motu->spec = (const struct snd_motu_spec *)entry->driver_data;
- motu->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, motu);
-
- mutex_init(&motu->mutex);
- spin_lock_init(&motu->lock);
- init_waitqueue_head(&motu->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
- snd_fw_schedule_registration(unit, &motu->dwork);
-
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);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&motu->dwork);
-
- if (motu->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(motu->card);
- } else {
- /* Don't forget this case. */
- motu_free(motu);
- }
+ // 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);
- /* Postpone a workqueue for deferred registration. */
- if (!motu->registered)
- snd_fw_schedule_registration(unit, &motu->dwork);
-
/* The handler address register becomes initialized. */
snd_motu_transaction_reregister(motu);
}
-static const struct snd_motu_spec motu_828mk2 = {
- .name = "828mk2",
- .protocol = &snd_motu_protocol_v2,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
-
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_828mk3 = {
- .name = "828mk3",
- .protocol = &snd_motu_protocol_v3,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_TX_REVERB_CHUNK |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_HAS_OPT_IFACE_B |
- SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
-
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_audio_express = {
- .name = "AudioExpress",
- .protocol = &snd_motu_protocol_v3,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
- .analog_in_ports = 2,
- .analog_out_ports = 4,
-};
-
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
- IEEE1394_MATCH_MODEL_ID | \
- IEEE1394_MATCH_SPECIFIER_ID, \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
.vendor_id = OUI_MOTU, \
- .model_id = model, \
.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(0x101800, &motu_828mk2),
- SND_MOTU_DEV_ENTRY(0x106800, &motu_828mk3), /* FireWire only. */
- SND_MOTU_DEV_ENTRY(0x100800, &motu_828mk3), /* Hybrid. */
- SND_MOTU_DEV_ENTRY(0x104800, &motu_audio_express),
+ 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(0x000017, &snd_motu_spec_896mk3), // 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(0x000037, &snd_motu_spec_896mk3), // 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);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 4b23cf337c4b..c66be0a89ccf 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -1,9 +1,8 @@
+/* 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
@@ -37,8 +36,16 @@ struct snd_motu_packet_format {
unsigned char pcm_byte_offset;
unsigned char msg_chunks;
- unsigned char fixed_part_pcm_chunks[3];
- unsigned char differed_part_pcm_chunks[3];
+ 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 {
@@ -47,9 +54,6 @@ struct snd_motu {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
/* Model dependent information. */
const struct snd_motu_spec *spec;
@@ -60,8 +64,7 @@ struct snd_motu {
struct amdtp_stream rx_stream;
struct fw_iso_resources tx_resources;
struct fw_iso_resources rx_resources;
- unsigned int capture_substreams;
- unsigned int playback_substreams;
+ unsigned int substreams_counter;
/* For notification. */
struct fw_address_handler async_handler;
@@ -71,21 +74,22 @@ struct snd_motu {
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_SUPPORT_CLOCK_X2 = 0x0001,
- SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002,
- SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004,
- SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008,
- SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010,
- SND_MOTU_SPEC_TX_AESEBU_CHUNK = 0x0020,
- SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040,
- SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080,
- SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0100,
- SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200,
- SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400,
- SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800,
+ 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
@@ -103,34 +107,48 @@ enum snd_motu_clock_source {
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,
};
-struct snd_motu_protocol {
- int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
- int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
- int (*get_clock_source)(struct snd_motu *motu,
- enum snd_motu_clock_source *source);
- int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
- int (*cache_packet_formats)(struct snd_motu *motu);
+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_spec_flags flags;
-
- unsigned char analog_in_ports;
- unsigned char analog_out_ports;
+ enum snd_motu_protocol_version protocol_version;
+ // The combination of snd_motu_spec_flags enumeration-constants.
+ unsigned int flags;
- const struct snd_motu_protocol *const protocol;
+ unsigned char tx_fixed_pcm_chunks[3];
+ unsigned char rx_fixed_pcm_chunks[3];
};
-extern const struct snd_motu_protocol snd_motu_protocol_v2;
-extern const struct snd_motu_protocol snd_motu_protocol_v3;
+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_896mk3;
+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_protocol *const protocol);
+ 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);
@@ -150,7 +168,10 @@ 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_start_duplex(struct snd_motu *motu, unsigned int rate);
+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);
@@ -162,4 +183,117 @@ 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
index b474da7c6a1f..9ac8893a926f 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,4 @@
-snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-oxfw-y := 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
index ac3e2e301666..d2e57c76070d 100644
--- a/sound/firewire/oxfw/oxfw-command.c
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* oxfw_command.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -39,7 +38,7 @@ int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
else if (err < len + 10)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
- err = -ENOSYS;
+ err = -ENXIO;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else
@@ -84,7 +83,7 @@ int avc_stream_get_format(struct fw_unit *unit,
else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
- err = -ENOSYS;
+ err = -ENXIO;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
@@ -148,7 +147,7 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
- err = -ENOSYS;
+ err = -ENXIO;
if (err < 0)
goto end;
diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c
index 6c1828aff672..f8ac362fc73a 100644
--- a/sound/firewire/oxfw/oxfw-hwdep.c
+++ b/sound/firewire/oxfw/oxfw-hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -36,13 +35,11 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
}
memset(&event, 0, sizeof(event));
- if (oxfw->dev_lock_changed) {
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = (oxfw->dev_lock_count > 0);
- oxfw->dev_lock_changed = false;
+ 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));
- }
+ count = min_t(long, count, sizeof(event.lock_status));
spin_unlock_irq(&oxfw->lock);
@@ -56,18 +53,14 @@ 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);
+ guard(spinlock_irq)(&oxfw->lock);
if (oxfw->dev_lock_changed)
- events = POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&oxfw->lock);
-
- return events;
+ return 0;
}
static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
@@ -80,7 +73,7 @@ static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -91,48 +84,35 @@ static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
static int hwdep_lock(struct snd_oxfw *oxfw)
{
- int err;
-
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
if (oxfw->dev_lock_count == 0) {
oxfw->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&oxfw->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_oxfw *oxfw)
{
- int err;
-
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
if (oxfw->dev_lock_count == -1) {
oxfw->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -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);
+ guard(spinlock_irq)(&oxfw->lock);
if (oxfw->dev_lock_count == -1)
oxfw->dev_lock_count = 0;
- spin_unlock_irq(&oxfw->lock);
return 0;
}
@@ -180,7 +160,7 @@ int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
if (err < 0)
goto end;
- strcpy(hwdep->name, oxfw->card->driver);
+ strscpy(hwdep->name, oxfw->card->driver);
hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
hwdep->ops = hwdep_ops;
hwdep->private_data = oxfw;
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
index b7bbd77dfff1..a16bf885f918 100644
--- a/sound/firewire/oxfw/oxfw-midi.c
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* oxfw_midi.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -17,12 +16,15 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
return err;
- mutex_lock(&oxfw->mutex);
-
- oxfw->capture_substreams++;
- err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
-
- mutex_unlock(&oxfw->mutex);
+ scoped_guard(mutex, &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;
+ }
+ }
if (err < 0)
snd_oxfw_stream_lock_release(oxfw);
@@ -39,12 +41,13 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
return err;
- mutex_lock(&oxfw->mutex);
-
- oxfw->playback_substreams++;
- err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
-
- mutex_unlock(&oxfw->mutex);
+ scoped_guard(mutex, &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);
+ }
+ }
if (err < 0)
snd_oxfw_stream_lock_release(oxfw);
@@ -56,12 +59,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_oxfw *oxfw = substream->rmidi->private_data;
- mutex_lock(&oxfw->mutex);
-
- oxfw->capture_substreams--;
- snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
-
- mutex_unlock(&oxfw->mutex);
+ scoped_guard(mutex, &oxfw->mutex) {
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+ }
snd_oxfw_stream_lock_release(oxfw);
return 0;
@@ -71,12 +72,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_oxfw *oxfw = substream->rmidi->private_data;
- mutex_lock(&oxfw->mutex);
-
- oxfw->playback_substreams--;
- snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
-
- mutex_unlock(&oxfw->mutex);
+ scoped_guard(mutex, &oxfw->mutex) {
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+ }
snd_oxfw_stream_lock_release(oxfw);
return 0;
@@ -85,9 +84,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&oxfw->lock);
if (up)
amdtp_am824_midi_trigger(&oxfw->tx_stream,
@@ -95,16 +93,13 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
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);
+ guard(spinlock_irqsave)(&oxfw->lock);
if (up)
amdtp_am824_midi_trigger(&oxfw->rx_stream,
@@ -112,8 +107,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
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,
@@ -122,9 +115,9 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
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);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ oxfw->card->shortname, subs->number + 1);
}
}
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 3dd46285c0e2..774b8a763795 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -1,8 +1,8 @@
+// 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>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -170,30 +170,48 @@ end:
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)
- goto end;
+ return err;
err = init_hw_params(oxfw, substream);
if (err < 0)
goto err_locked;
- /*
- * When any PCM streams are already running, the available sampling
- * rate is limited at current value.
- */
- if (amdtp_stream_pcm_running(&oxfw->tx_stream) ||
- amdtp_stream_pcm_running(&oxfw->rx_stream)) {
- err = limit_to_current_params(substream);
- if (err < 0)
- goto end;
+ scoped_guard(mutex, &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)
+ 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)
+ 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);
-end:
- return err;
+
+ return 0;
err_locked:
snd_oxfw_stream_lock_release(oxfw);
return err;
@@ -211,105 +229,101 @@ 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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&oxfw->mutex);
- oxfw->capture_substreams++;
- mutex_unlock(&oxfw->mutex);
+ 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);
+
+ guard(mutex)(&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;
}
- return 0;
+ 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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&oxfw->mutex);
- oxfw->playback_substreams++;
- mutex_unlock(&oxfw->mutex);
+ 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);
+
+ guard(mutex)(&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;
}
- return 0;
+ 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->status->state != SNDRV_PCM_STATE_OPEN)
- oxfw->capture_substreams--;
+ guard(mutex)(&oxfw->mutex);
- snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
- mutex_unlock(&oxfw->mutex);
+ snd_oxfw_stream_stop_duplex(oxfw);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
- mutex_lock(&oxfw->mutex);
+ guard(mutex)(&oxfw->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- oxfw->playback_substreams--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
- snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+ snd_oxfw_stream_stop_duplex(oxfw);
- mutex_unlock(&oxfw->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&oxfw->mutex);
- err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
- runtime->rate, runtime->channels);
- mutex_unlock(&oxfw->mutex);
- if (err < 0)
- goto end;
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ return err;
+ }
amdtp_stream_pcm_prepare(&oxfw->tx_stream);
-end:
- return err;
+ return 0;
}
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&oxfw->mutex);
- err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
- runtime->rate, runtime->channels);
- mutex_unlock(&oxfw->mutex);
- if (err < 0)
- goto end;
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ return err;
+ }
amdtp_stream_pcm_prepare(&oxfw->rx_stream);
-end:
- return err;
+ return 0;
}
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -353,27 +367,27 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
{
struct snd_oxfw *oxfw = sbstm->private_data;
- return amdtp_stream_pcm_pointer(&oxfw->tx_stream);
+ 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_stream_pcm_pointer(&oxfw->rx_stream);
+ 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_stream_pcm_ack(&oxfw->tx_stream);
+ 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_stream_pcm_ack(&oxfw->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
}
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
@@ -381,28 +395,22 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
unsigned int cap = 0;
@@ -416,10 +424,12 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
return err;
pcm->private_data = oxfw;
- strcpy(pcm->name, oxfw->card->shortname);
+ pcm->nonatomic = true;
+ strscpy(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
index 8ba4f9f262b8..260c60364f39 100644
--- a/sound/firewire/oxfw/oxfw-proc.c
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* oxfw_proc.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./oxfw.h"
@@ -83,12 +82,8 @@ static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(oxfw->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, oxfw, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, oxfw, op);
}
void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
@@ -103,11 +98,7 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
oxfw->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- 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
index f33497cdc706..21412a3ca9f4 100644
--- a/sound/firewire/oxfw/oxfw-scs1x.c
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -372,8 +371,9 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
struct fw_scs1x *scs;
int err;
- scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL);
- if (scs == NULL)
+ 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;
diff --git a/sound/firewire/oxfw/oxfw-spkr.c b/sound/firewire/oxfw/oxfw-spkr.c
index cb905af0660d..f2767fb1965c 100644
--- a/sound/firewire/oxfw/oxfw-spkr.c
+++ b/sound/firewire/oxfw/oxfw-spkr.c
@@ -1,8 +1,8 @@
+// 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>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -270,8 +270,9 @@ int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
unsigned int i, first_ch;
int err;
- spkr = kzalloc(sizeof(struct fw_spkr), GFP_KERNEL);
- if (spkr == NULL)
+ spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
+ GFP_KERNEL);
+ if (!spkr)
return -ENOMEM;
oxfw->spec = spkr;
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index d9361f352133..5e36d7153a7b 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* oxfw_stream.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) 2014 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
#include <linux/delay.h>
#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
-#define CALLBACK_TIMEOUT 200
+#define READY_TIMEOUT_MS 600
/*
* According to datasheet of Oxford Semiconductor:
@@ -101,85 +100,28 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
return 0;
}
-static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
-{
- amdtp_stream_pcm_abort(stream);
- amdtp_stream_stop(stream);
-
- if (stream == &oxfw->tx_stream)
- cmp_connection_break(&oxfw->out_conn);
- else
- cmp_connection_break(&oxfw->in_conn);
-}
-
-static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
- unsigned int rate, unsigned int pcm_channels)
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
- u8 **formats;
struct cmp_connection *conn;
- struct snd_oxfw_stream_formation formation;
- unsigned int i, midi_ports;
int err;
- if (stream == &oxfw->rx_stream) {
- formats = oxfw->rx_stream_formats;
+ if (stream == &oxfw->rx_stream)
conn = &oxfw->in_conn;
- } else {
- formats = oxfw->tx_stream_formats;
+ else
conn = &oxfw->out_conn;
- }
- /* Get stream format */
- 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)
- goto end;
- if (rate != formation.rate)
- continue;
- if (pcm_channels == 0 || pcm_channels == formation.pcm)
- break;
- }
- if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
- err = -EINVAL;
- goto end;
- }
-
- pcm_channels = formation.pcm;
- midi_ports = formation.midi * 8;
-
- /* The stream should have one pcm channels at least */
- if (pcm_channels == 0) {
- err = -EINVAL;
- goto end;
- }
- err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
- false);
+ err = cmp_connection_establish(conn);
if (err < 0)
- goto end;
-
- err = cmp_connection_establish(conn,
- amdtp_stream_get_max_payload(stream));
- if (err < 0)
- goto end;
+ return err;
- err = amdtp_stream_start(stream,
- conn->resources.channel,
- conn->speed);
+ err = amdtp_domain_add_stream(&oxfw->domain, stream,
+ conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
- goto end;
+ return err;
}
- /* Wait first packet */
- if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
- stop_stream(oxfw, stream);
- err = -ETIMEDOUT;
- }
-end:
- return err;
+ return 0;
}
static int check_connection_used_by_others(struct snd_oxfw *oxfw,
@@ -206,18 +148,37 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw,
return err;
}
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+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;
+ if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS)
+ flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS;
} else {
conn = &oxfw->in_conn;
c_dir = CMP_INPUT;
@@ -226,135 +187,238 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
if (err < 0)
- goto end;
+ return err;
- err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, flags);
if (err < 0) {
- amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
- goto end;
+ return err;
}
- /*
- * OXFW starts to transmit packets with non-zero dbc.
- * OXFW postpone transferring packets till handling any asynchronous
- * packets. As a result, next isochronous packet includes more data
- * blocks than IEC 61883-6 defines.
- */
- if (stream == &oxfw->tx_stream) {
- oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
- if (oxfw->wrong_dbs)
- oxfw->tx_stream.flags |= CIP_WRONG_DBS;
- }
-end:
- return err;
+ return 0;
}
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream,
- unsigned int rate, unsigned int pcm_channels)
+static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
- struct amdtp_stream *opposite;
- struct snd_oxfw_stream_formation formation;
enum avc_general_plug_dir dir;
- unsigned int substreams, opposite_substreams;
- int err = 0;
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ struct cmp_connection *conn;
+ int i;
+ int err;
- if (stream == &oxfw->tx_stream) {
- substreams = oxfw->capture_substreams;
- opposite = &oxfw->rx_stream;
- opposite_substreams = oxfw->playback_substreams;
- dir = AVC_GENERAL_PLUG_DIR_OUT;
+ if (stream == &oxfw->rx_stream) {
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
} else {
- substreams = oxfw->playback_substreams;
- opposite_substreams = oxfw->capture_substreams;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
- if (oxfw->has_output)
- opposite = &oxfw->rx_stream;
- else
- opposite = NULL;
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
- dir = AVC_GENERAL_PLUG_DIR_IN;
+ 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;
- if (substreams == 0)
- goto end;
+ // The stream should have one pcm channels at least.
+ if (formation.pcm == 0)
+ return -EINVAL;
- /*
- * Considering JACK/FFADO streaming:
- * TODO: This can be removed hwdep functionality becomes popular.
- */
- err = check_connection_used_by_others(oxfw, stream);
+ err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
+ formation.midi * 8, false);
if (err < 0)
- goto end;
+ 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;
- /* packet queueing error */
- if (amdtp_streaming_error(stream))
- stop_stream(oxfw, stream);
+ // 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)
- goto end;
- if (rate == 0)
+ return err;
+ if (rate == 0) {
rate = formation.rate;
- if (pcm_channels == 0)
pcm_channels = formation.pcm;
+ }
+ if (formation.rate != rate || formation.pcm != pcm_channels) {
+ amdtp_domain_stop(&oxfw->domain);
- if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
- if (opposite != NULL) {
- err = check_connection_used_by_others(oxfw, opposite);
- if (err < 0)
- goto end;
- stop_stream(oxfw, opposite);
+ 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);
}
- stop_stream(oxfw, stream);
+ }
+ 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);
- goto end;
+ return err;
}
- /* Start opposite stream if needed. */
- if (opposite && !amdtp_stream_running(opposite) &&
- (opposite_substreams > 0)) {
- err = start_stream(oxfw, opposite, rate, 0);
+ 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) {
- dev_err(&oxfw->unit->device,
- "fail to restart stream: %d\n", err);
- goto end;
+ 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;
+ }
}
- /* Start requested stream. */
- if (!amdtp_stream_running(stream)) {
- err = start_stream(oxfw, stream, rate, pcm_channels);
- if (err < 0)
+ 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 start stream: %d\n", err);
+ "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;
+ }
}
-end:
+
+ 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_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
{
- if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
- ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
- return;
+ if (oxfw->substreams_count == 0) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
- stop_stream(oxfw, stream);
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
}
-/*
- * This function should be called before starting the stream or after stopping
- * the streams.
- */
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
@@ -367,46 +431,114 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
cmp_connection_destroy(conn);
}
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
{
- struct cmp_connection *conn;
+ int err;
- if (stream == &oxfw->tx_stream)
- conn = &oxfw->out_conn;
- else
- conn = &oxfw->in_conn;
+ err = init_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
- if (cmp_connection_update(conn) < 0)
- stop_stream(oxfw, stream);
- else
- amdtp_stream_update(stream);
+ 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;
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ u8 *format;
+ unsigned int len;
- err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
- if (err < 0)
- goto end;
- if (len < 3) {
- err = -EIO;
- goto end;
+ 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) {
+ if (len < 3)
+ err = -EIO;
+ else
+ err = snd_oxfw_stream_parse_format(format, formation);
+ }
+
+ kfree(format);
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the duplicated hard-coded format, instead.
+ unsigned int rate;
+ u8 *const *formats;
+ int i;
+
+ err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0);
+ if (err < 0)
+ return err;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ formats = oxfw->rx_stream_formats;
+ else
+ formats = oxfw->tx_stream_formats;
+
+ for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) {
+ if (!formats[i])
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], formation);
+ if (err < 0)
+ continue;
+
+ if (formation->rate == rate)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EIO;
}
- err = snd_oxfw_stream_parse_format(format, formation);
-end:
- kfree(format);
return err;
}
@@ -416,7 +548,7 @@ end:
* 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,
+int snd_oxfw_stream_parse_format(const u8 *format,
struct snd_oxfw_stream_formation *formation)
{
unsigned int i, e, channels, type;
@@ -429,7 +561,7 @@ int snd_oxfw_stream_parse_format(u8 *format,
* Level 1: AM824 Compound (0x40)
*/
if ((format[0] != 0x90) || (format[1] != 0x40))
- return -ENOSYS;
+ return -ENXIO;
/* check the sampling rate */
for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
@@ -437,7 +569,7 @@ int snd_oxfw_stream_parse_format(u8 *format,
break;
}
if (i == ARRAY_SIZE(avc_stream_rate_table))
- return -ENOSYS;
+ return -ENXIO;
formation->rate = oxfw_rate_table[i];
@@ -481,13 +613,13 @@ int snd_oxfw_stream_parse_format(u8 *format,
/* Don't care */
case 0xff:
default:
- return -ENOSYS; /* not supported */
+ return -ENXIO; /* not supported */
}
}
if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
- return -ENOSYS;
+ return -ENXIO;
return 0;
}
@@ -501,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
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;
+ // get format at current sampling rate.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ 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;
+ }
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the hard-coded format, instead.
+ buf[0] = 0x90;
+ buf[1] = 0x40;
+ buf[2] = avc_stream_rate_table[0];
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[5] = 0x08;
+ else
+ buf[5] = 0x02;
+
+ buf[6] = 0x06;
+
+ *len = 7;
}
/* parse and set stream format */
@@ -517,8 +668,9 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
if (err < 0)
goto end;
- formats[eid] = kmemdup(buf, *len, GFP_KERNEL);
- if (formats[eid] == NULL) {
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
err = -ENOMEM;
goto end;
}
@@ -535,7 +687,8 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
continue;
eid++;
- formats[eid] = kmemdup(buf, *len, GFP_KERNEL);
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
if (formats[eid] == NULL) {
err = -ENOMEM;
goto end;
@@ -570,7 +723,7 @@ static int fill_stream_formats(struct snd_oxfw *oxfw,
/* get first entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
- if (err == -ENOSYS) {
+ if (err == -ENXIO) {
/* LIST subfunction is not implemented */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
err = assume_stream_formats(oxfw, dir, pid, buf, &len,
@@ -597,8 +750,9 @@ static int fill_stream_formats(struct snd_oxfw *oxfw,
if (err < 0)
break;
- formats[eid] = kmemdup(buf, len, GFP_KERNEL);
- if (formats[eid] == NULL) {
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
err = -ENOMEM;
break;
}
@@ -641,49 +795,63 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
err);
goto end;
} else if ((plugs[0] == 0) && (plugs[1] == 0)) {
- err = -ENOSYS;
+ 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)
- goto end;
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
- 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;
+ // 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;
+ }
- /* Add one MIDI port. */
- if (formation.midi > 0)
- oxfw->midi_input_ports = 1;
+ oxfw->has_output = true;
}
-
- 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)
- goto end;
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
- 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;
+ // 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;
+ }
- /* Add one MIDI port. */
- if (formation.midi > 0)
- oxfw->midi_output_ports = 1;
+ oxfw->has_input = true;
}
}
end:
@@ -698,33 +866,24 @@ void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
{
- int err;
-
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
/* user land lock this */
- if (oxfw->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (oxfw->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
{
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
if (WARN_ON(oxfw->dev_lock_count <= 0))
- goto end;
+ return;
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
index 413ab6313bb6..5039bd79b18e 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -1,8 +1,8 @@
+// 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>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "oxfw.h"
@@ -20,15 +20,20 @@
#define VENDOR_LACIE 0x00d04b
#define VENDOR_TASCAM 0x00022e
#define OUI_STANTON 0x001260
+#define OUI_APOGEE 0x0003db
+#define OUI_OXFORD 0x0030e0
#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_LICENSE("GPL");
MODULE_ALIAS("snd-firewire-speakers");
MODULE_ALIAS("snd-scs1x");
@@ -40,16 +45,13 @@ struct compat_info {
static bool detect_loud_models(struct fw_unit *unit)
{
- const char *const models[] = {
+ static const char *const models[] = {
"Onyxi",
"Onyx-i",
"Onyx 1640i",
"d.Pro",
- "Mackie Onyx Satellite",
- "Tapco LINK.firewire 4x6",
"U.420"};
char model[32];
- unsigned int i;
int err;
err = fw_csr_string(unit->directory, CSR_MODEL,
@@ -57,15 +59,10 @@ static bool detect_loud_models(struct fw_unit *unit)
if (err < 0)
return false;
- for (i = 0; i < ARRAY_SIZE(models); i++) {
- if (strcmp(models[i], model) == 0)
- break;
- }
-
- return (i < ARRAY_SIZE(models));
+ return match_string(models, ARRAY_SIZE(models), model) >= 0;
}
-static int name_card(struct snd_oxfw *oxfw)
+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;
@@ -93,10 +90,12 @@ static int name_card(struct snd_oxfw *oxfw)
goto end;
be32_to_cpus(&firmware);
+ if (firmware >> 20 == 0x970)
+ oxfw->quirks |= SND_OXFW_QUIRK_JUMBO_PAYLOAD;
+
/* to apply card definitions */
- if (oxfw->entry->vendor_id == VENDOR_GRIFFIN ||
- oxfw->entry->vendor_id == VENDOR_LACIE) {
- info = (const struct compat_info *)oxfw->entry->driver_data;
+ 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;
@@ -106,50 +105,31 @@ static int name_card(struct snd_oxfw *oxfw)
m = model;
}
- strcpy(oxfw->card->driver, d);
- strcpy(oxfw->card->mixername, m);
- strcpy(oxfw->card->shortname, m);
+ strscpy(oxfw->card->driver, d);
+ strscpy(oxfw->card->mixername, m);
+ strscpy(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);
+ scnprintf(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_free(struct snd_oxfw *oxfw)
+static void oxfw_card_free(struct snd_card *card)
{
- unsigned int i;
+ struct snd_oxfw *oxfw = card->private_data;
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+ if (oxfw->has_output || oxfw->has_input)
+ snd_oxfw_stream_destroy_duplex(oxfw);
- fw_unit_put(oxfw->unit);
-
- for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
- kfree(oxfw->tx_stream_formats[i]);
- kfree(oxfw->rx_stream_formats[i]);
- }
-
- kfree(oxfw->spec);
mutex_destroy(&oxfw->mutex);
+ fw_unit_put(oxfw->unit);
}
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void oxfw_card_free(struct snd_card *card)
-{
- oxfw_free(card->private_data);
-}
-
-static int detect_quirks(struct snd_oxfw *oxfw)
+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;
@@ -160,31 +140,37 @@ static int detect_quirks(struct snd_oxfw *oxfw)
* Add ALSA control elements for two models to keep compatibility to
* old firewire-speaker module.
*/
- if (oxfw->entry->vendor_id == VENDOR_GRIFFIN)
+ if (entry->vendor_id == VENDOR_GRIFFIN)
return snd_oxfw_add_spkr(oxfw, false);
- if (oxfw->entry->vendor_id == VENDOR_LACIE)
+ if (entry->vendor_id == VENDOR_LACIE)
return snd_oxfw_add_spkr(oxfw, true);
/*
* Stanton models supports asynchronous transactions for unique MIDI
* messages.
*/
- if (oxfw->entry->vendor_id == OUI_STANTON) {
- /* No physical MIDI ports. */
+ 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;
- /* Output stream exists but no data channels are useful. */
- oxfw->has_output = false;
-
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 (oxfw->entry->vendor_id == VENDOR_TASCAM) {
+ if (entry->vendor_id == VENDOR_TASCAM) {
oxfw->midi_input_ports++;
oxfw->midi_output_ports++;
return 0;
@@ -200,155 +186,117 @@ static int detect_quirks(struct snd_oxfw *oxfw)
model = val;
}
- /*
- * Mackie Onyx Satellite with base station has a quirk to report a wrong
- * value in 'dbs' field of CIP header against its format information.
- */
- if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
- oxfw->wrong_dbs = true;
+ 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 void do_registration(struct work_struct *work)
+static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
+ struct snd_card *card;
+ struct snd_oxfw *oxfw;
int err;
- if (oxfw->registered)
- return;
+ if (entry->vendor_id == VENDOR_LOUD && entry->model_id == 0 && !detect_loud_models(unit))
+ return -ENODEV;
- err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
- &oxfw->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*oxfw), &card);
if (err < 0)
- return;
+ 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;
- err = name_card(oxfw);
+ 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;
+ if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) {
+ oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED |
+ SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS;
+ }
+
err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
- err = detect_quirks(oxfw);
+ err = detect_quirks(oxfw, entry);
if (err < 0)
goto error;
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
- if (err < 0)
- goto error;
- if (oxfw->has_output) {
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+ 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;
+ err = snd_oxfw_create_pcm(oxfw);
+ if (err < 0)
+ goto error;
- snd_oxfw_proc_init(oxfw);
+ snd_oxfw_proc_init(oxfw);
- err = snd_oxfw_create_midi(oxfw);
- if (err < 0)
- goto error;
+ err = snd_oxfw_create_midi(oxfw);
+ if (err < 0)
+ goto error;
- err = snd_oxfw_create_hwdep(oxfw);
- if (err < 0)
- goto error;
+ err = snd_oxfw_create_hwdep(oxfw);
+ if (err < 0)
+ goto error;
+ }
- err = snd_card_register(oxfw->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- /*
- * After registered, oxfw instance can be released corresponding to
- * releasing the sound card instance.
- */
- oxfw->card->private_free = oxfw_card_free;
- oxfw->card->private_data = oxfw;
- oxfw->registered = true;
-
- return;
-error:
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
- snd_card_free(oxfw->card);
- dev_info(&oxfw->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int oxfw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_oxfw *oxfw;
-
- if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
- return -ENODEV;
-
- /* Allocate this independent of sound card instance. */
- oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
- if (oxfw == NULL)
- return -ENOMEM;
-
- oxfw->entry = entry;
- oxfw->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, oxfw);
-
- mutex_init(&oxfw->mutex);
- spin_lock_init(&oxfw->lock);
- init_waitqueue_head(&oxfw->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
- snd_fw_schedule_registration(unit, &oxfw->dwork);
-
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);
- if (!oxfw->registered)
- snd_fw_schedule_registration(unit, &oxfw->dwork);
-
fcp_bus_reset(oxfw->unit);
- if (oxfw->registered) {
- mutex_lock(&oxfw->mutex);
-
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
-
- mutex_unlock(&oxfw->mutex);
-
- if (oxfw->entry->vendor_id == OUI_STANTON)
- snd_oxfw_scs1x_update(oxfw);
+ if (oxfw->has_output || oxfw->has_input) {
+ guard(mutex)(&oxfw->mutex);
+ snd_oxfw_stream_update_duplex(oxfw);
}
+
+ 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);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&oxfw->dwork);
-
- if (oxfw->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(oxfw->card);
- } else {
- /* Don't forget this case. */
- oxfw_free(oxfw);
- }
+ // Block till all of ALSA character devices are released.
+ snd_card_free(oxfw->card);
}
static const struct compat_info griffin_firewave = {
@@ -363,75 +311,69 @@ static const struct compat_info lacie_speakers = {
.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[] = {
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID |
- IEEE1394_MATCH_SPECIFIER_ID |
- IEEE1394_MATCH_VERSION,
- .vendor_id = VENDOR_GRIFFIN,
- .model_id = 0x00f970,
- .specifier_id = SPECIFIER_1394TA,
- .version = VERSION_AVC,
- .driver_data = (kernel_ulong_t)&griffin_firewave,
- },
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID |
- IEEE1394_MATCH_SPECIFIER_ID |
- IEEE1394_MATCH_VERSION,
- .vendor_id = VENDOR_LACIE,
- .model_id = 0x00f970,
- .specifier_id = SPECIFIER_1394TA,
- .version = VERSION_AVC,
- .driver_data = (kernel_ulong_t)&lacie_speakers,
- },
- /* Behringer,F-Control Audio 202 */
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = VENDOR_BEHRINGER,
- .model_id = 0x00fc22,
- },
- /*
- * Any Mackie(Loud) models (name string/model id):
- * Onyx-i series (former models): 0x081216
- * Mackie Onyx Satellite: 0x00200f
- * Tapco LINK.firewire 4x6: 0x000460
- * d.2 pro: Unknown
- * d.4 pro: Unknown
- * U.420: Unknown
- * U.420d: Unknown
- */
+ //
+ // 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),
+ // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the
+ // default value of ASIC.
+ OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL),
+ // 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),
+
+ //
+ // 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 */
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = VENDOR_TASCAM,
- .model_id = 0x800007,
- },
- /* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = OUI_STANTON,
- .model_id = 0x001000,
- },
- /* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = OUI_STANTON,
- .model_id = 0x002000,
- },
+ // 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);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index d54d4a9ac4a1..39ea9a6dde33 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -1,8 +1,8 @@
+/* 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>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/device.h>
@@ -32,6 +32,33 @@
#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,
+ // Miglia Harmony Audio does not support AV/C Stream Format Information command.
+ SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40,
+ // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number
+ // of accumulated payload quadlets including the packet.
+ SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80,
+};
+
/* This is an arbitrary number for convinience. */
#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
struct snd_oxfw {
@@ -40,11 +67,10 @@ struct snd_oxfw {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
- bool wrong_dbs;
+ // 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;
@@ -52,8 +78,7 @@ struct snd_oxfw {
struct cmp_connection in_conn;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
- unsigned int capture_substreams;
- unsigned int playback_substreams;
+ unsigned int substreams_count;
unsigned int midi_input_ports;
unsigned int midi_output_ports;
@@ -62,8 +87,9 @@ struct snd_oxfw {
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
- const struct ieee1394_device_id *entry;
void *spec;
+
+ struct amdtp_domain domain;
};
/*
@@ -99,24 +125,23 @@ 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_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream);
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream,
- unsigned int rate, unsigned int pcm_channels);
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream);
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream);
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream);
+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,
+int snd_oxfw_stream_parse_format(const 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,
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
index ea1506679c66..0ecafd0c6722 100644
--- a/sound/firewire/packets-buffer.c
+++ b/sound/firewire/packets-buffer.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* helpers for managing a buffer for many packets
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/firewire.h>
@@ -27,7 +27,7 @@ int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
void *p;
int err;
- b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL);
+ b->packets = kmalloc_array(count, sizeof(*b->packets), GFP_KERNEL);
if (!b->packets) {
err = -ENOMEM;
goto error;
@@ -37,7 +37,7 @@ int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
packets_per_page = PAGE_SIZE / packet_size;
if (WARN_ON(!packets_per_page)) {
err = -EINVAL;
- goto error;
+ goto err_packets;
}
pages = DIV_ROUND_UP(count, packets_per_page);
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
index 0fc955d5bd15..43fed14cf172 100644
--- a/sound/firewire/tascam/Makefile
+++ b/sound/firewire/tascam/Makefile
@@ -1,4 +1,5 @@
-snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-tascam-y := 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
index ab482423c165..59c339d9b5fb 100644
--- a/sound/firewire/tascam/amdtp-tascam.c
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* amdtp-tascam.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm.h>
@@ -30,22 +29,27 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
if (s->direction == AMDTP_IN_STREAM)
data_channels += 2;
- return amdtp_stream_set_parameters(s, rate, data_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,
- __be32 *buffer, unsigned int frames)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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) {
@@ -58,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+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 channels, remaining_frames, i, c;
+ 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;
- channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+ 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;
@@ -117,65 +126,123 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
-static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+static void read_status_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int data_blocks)
{
- struct snd_pcm_substream *pcm;
-
- pcm = READ_ONCE(s->pcm);
- if (data_blocks > 0 && pcm)
- read_pcm_s32(s, pcm, buffer, data_blocks);
-
- /* A place holder for control messages. */
+ 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];
+
+ scoped_guard(spinlock_irqsave, &tscm->lock) {
+ entry->index = index;
+ entry->before = before;
+ entry->after = after;
+ if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
+ tscm->push_pos = 0;
+ }
+
+ wake_up(&tscm->hwdep_wait);
+ }
+ }
- return data_blocks;
+ tscm->state[index] = after;
+ buffer += s->data_block_quadlets;
+ }
}
-static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
- __be32 *buffer,
- unsigned int data_blocks,
- unsigned int *syt)
+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_pcm_substream *pcm;
+ unsigned int pcm_frames = 0;
+ int i;
- /* This field is not used. */
- *syt = 0x0000;
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
- pcm = READ_ONCE(s->pcm);
- if (pcm)
- write_pcm_s32(s, pcm, buffer, data_blocks);
- else
- write_pcm_silence(s, buffer, data_blocks);
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
- return 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_data_blocks_t process_data_blocks;
+ 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_data_blocks = process_tx_data_blocks;
+ process_ctx_payloads = process_ir_ctx_payloads;
} else {
fmt = AMDTP_FMT_TSCM_RX;
- process_data_blocks = process_rx_data_blocks;
+ process_ctx_payloads = process_it_ctx_payloads;
}
- err = amdtp_stream_init(s, unit, dir,
- CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
- process_data_blocks, sizeof(struct amdtp_tscm));
+ err = amdtp_stream_init(s, unit, dir, flags, fmt,
+ process_ctx_payloads, sizeof(struct amdtp_tscm));
if (err < 0)
- return 0;
+ return err;
- /* Use fixed value for FDF field. */
- s->fdf = 0x00;
+ 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;
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
index 37b21647b471..867b4ea1096e 100644
--- a/sound/firewire/tascam/tascam-hwdep.c
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-hwdep.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
/*
@@ -16,18 +15,95 @@
#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);
- union snd_firewire_event event = {
- .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
- };
spin_lock_irq(&tscm->lock);
- while (!tscm->dev_lock_changed) {
+ 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();
@@ -37,15 +113,15 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&tscm->lock);
}
- event.lock_status.status = (tscm->dev_lock_count > 0);
- tscm->dev_lock_changed = false;
-
- spin_unlock_irq(&tscm->lock);
-
- count = min_t(long, count, sizeof(event.lock_status));
-
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ // 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;
}
@@ -54,18 +130,14 @@ 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)
- events = POLLIN | POLLRDNORM;
+ guard(spinlock_irq)(&tscm->lock);
+ if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
+ return EPOLLIN | EPOLLRDNORM;
else
- events = 0;
- spin_unlock_irq(&tscm->lock);
-
- return events;
+ return 0;
}
static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
@@ -78,7 +150,7 @@ static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
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]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -89,48 +161,43 @@ static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
static int hwdep_lock(struct snd_tscm *tscm)
{
- int err;
-
- spin_lock_irq(&tscm->lock);
+ guard(spinlock_irq)(&tscm->lock);
if (tscm->dev_lock_count == 0) {
tscm->dev_lock_count = -1;
- err = 0;
+ return 0;
} else {
- err = -EBUSY;
+ return -EBUSY;
}
-
- spin_unlock_irq(&tscm->lock);
-
- return err;
}
static int hwdep_unlock(struct snd_tscm *tscm)
{
- int err;
-
- spin_lock_irq(&tscm->lock);
+ guard(spinlock_irq)(&tscm->lock);
if (tscm->dev_lock_count == -1) {
tscm->dev_lock_count = 0;
- err = 0;
+ return 0;
} else {
- err = -EBADFD;
+ return -EBADFD;
}
+}
- spin_unlock_irq(&tscm->lock);
+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 err;
+ 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);
+ guard(spinlock_irq)(&tscm->lock);
if (tscm->dev_lock_count == -1)
tscm->dev_lock_count = 0;
- spin_unlock_irq(&tscm->lock);
return 0;
}
@@ -147,6 +214,8 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
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;
}
@@ -179,11 +248,13 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
if (err < 0)
return err;
- strcpy(hwdep->name, "Tascam");
+ strscpy(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
index 4a741570d536..1bf9d7b3da33 100644
--- a/sound/firewire/tascam/tascam-midi.c
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-midi.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "tascam.h"
@@ -44,30 +43,24 @@ static void midi_playback_drain(struct snd_rawmidi_substream *substream)
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);
+ guard(spinlock_irqsave)(&tscm->lock);
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);
+ guard(spinlock_irqsave)(&tscm->lock);
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)
@@ -109,9 +102,9 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
/* 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);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
}
}
@@ -124,9 +117,9 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
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);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
}
}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
index 6ec8ec634d4d..d885fef0c8ca 100644
--- a/sound/firewire/tascam/tascam-pcm.c
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-pcm.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "tascam.h"
@@ -44,32 +43,54 @@ static int pcm_init_hw_params(struct snd_tscm *tscm,
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;
- unsigned int rate;
int err;
err = snd_tscm_stream_lock_try(tscm);
if (err < 0)
- goto end;
+ return err;
err = pcm_init_hw_params(tscm, substream);
if (err < 0)
goto err_locked;
err = snd_tscm_stream_get_clock(tscm, &clock);
- if (clock != SND_TSCM_CLOCK_INTERNAL ||
- amdtp_stream_pcm_running(&tscm->rx_stream) ||
- amdtp_stream_pcm_running(&tscm->tx_stream)) {
- err = snd_tscm_stream_get_rate(tscm, &rate);
- if (err < 0)
- goto err_locked;
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
+ if (err < 0)
+ goto err_locked;
+
+ scoped_guard(mutex, &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)
+ 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)
+ 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);
-end:
- return err;
+
+ return 0;
err_locked:
snd_tscm_stream_lock_release(tscm);
return err;
@@ -84,76 +105,39 @@ static int pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&tscm->mutex);
- tscm->substreams_counter++;
- mutex_unlock(&tscm->mutex);
+ 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);
+
+ guard(mutex)(&tscm->mutex);
+ err = snd_tscm_stream_reserve_duplex(tscm, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++tscm->substreams_counter;
}
- return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_tscm *tscm = substream->private_data;
- int err;
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- mutex_lock(&tscm->mutex);
- tscm->substreams_counter++;
- mutex_unlock(&tscm->mutex);
- }
-
- return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_tscm *tscm = substream->private_data;
-
- mutex_lock(&tscm->mutex);
-
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- tscm->substreams_counter--;
-
- snd_tscm_stream_stop_duplex(tscm);
-
- mutex_unlock(&tscm->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return err;
}
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_tscm *tscm = substream->private_data;
- mutex_lock(&tscm->mutex);
+ guard(mutex)(&tscm->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- tscm->substreams_counter--;
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --tscm->substreams_counter;
snd_tscm_stream_stop_duplex(tscm);
- mutex_unlock(&tscm->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ return 0;
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -162,14 +146,12 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&tscm->mutex);
+ guard(mutex)(&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;
}
@@ -179,14 +161,12 @@ static int pcm_playback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- mutex_lock(&tscm->mutex);
+ guard(mutex)(&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;
}
@@ -230,28 +210,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_tscm *tscm = sbstrm->private_data;
- return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+ 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_stream_pcm_pointer(&tscm->rx_stream);
+ 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_stream_pcm_ack(&tscm->tx_stream);
+ 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_stream_pcm_ack(&tscm->rx_stream);
+ return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
}
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
@@ -259,27 +239,22 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_capture_hw_params,
- .hw_free = pcm_capture_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pcm_playback_hw_params,
- .hw_free = pcm_playback_hw_free,
+ .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,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
@@ -289,10 +264,12 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
return err;
pcm->private_data = tscm;
+ pcm->nonatomic = true;
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
index bfd4a4c06914..53846aeff342 100644
--- a/sound/firewire/tascam/tascam-proc.c
+++ b/sound/firewire/tascam/tascam-proc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-proc.h - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./tascam.h"
@@ -58,12 +57,8 @@ static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(tscm->card, name, root);
- if (entry == NULL)
- return;
-
- snd_info_set_text_ops(entry, tscm, op);
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
+ if (entry)
+ snd_info_set_text_ops(entry, tscm, op);
}
void snd_tscm_proc_init(struct snd_tscm *tscm)
@@ -78,11 +73,7 @@ void snd_tscm_proc_init(struct snd_tscm *tscm)
tscm->card->proc_root);
if (root == NULL)
return;
- root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(root) < 0) {
- snd_info_free_entry(root);
- 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
index f1657a4e0621..9c8fddd7dee1 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -1,28 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-stream.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
#include "tascam.h"
-#define CALLBACK_TIMEOUT 500
+#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;
- err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
- TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
- &reg, sizeof(reg), 0);
- if (err >= 0)
+ 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;
- return err;
+ // 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,
@@ -35,7 +51,7 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
err = get_clock(tscm, &data);
if (err < 0)
return err;
- data &= 0x0000ffff;
+ data &= CLOCK_CONFIG_MASK;
if (rate > 0) {
data &= 0x000000ff;
@@ -80,17 +96,14 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
{
- u32 data = 0x0;
- unsigned int trials = 0;
+ u32 data;
int err;
- while (data == 0x0 || trials++ < 5) {
- err = get_clock(tscm, &data);
- if (err < 0)
- return err;
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
- data = (data & 0xff000000) >> 24;
- }
+ data = (data & 0xff000000) >> 24;
/* Check base rate. */
if ((data & 0x0f) == 0x01)
@@ -166,7 +179,7 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
__be32 reg;
int err;
- /* Set an option for unknown purpose. */
+ // 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,
@@ -174,11 +187,7 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
return err;
- err = enable_data_channels(tscm);
- if (err < 0)
- return err;
-
- return set_clock(tscm, rate, INT_MAX);
+ return enable_data_channels(tscm);
}
static void finish_session(struct snd_tscm *tscm)
@@ -195,6 +204,19 @@ static void finish_session(struct snd_tscm *tscm)
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)
@@ -202,6 +224,30 @@ 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,
@@ -216,7 +262,7 @@ static int begin_session(struct snd_tscm *tscm)
if (err < 0)
return err;
- /* Set an option for unknown purpose. */
+ // 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,
@@ -224,7 +270,7 @@ static int begin_session(struct snd_tscm *tscm)
if (err < 0)
return err;
- /* Start multiplexing PCM samples on packets. */
+ // Start multiplexing PCM samples on packets.
reg = cpu_to_be32(0x00000001);
return snd_fw_transaction(tscm->unit,
TCODE_WRITE_QUADLET_REQUEST,
@@ -232,170 +278,186 @@ static int begin_session(struct snd_tscm *tscm)
&reg, sizeof(reg), 0);
}
-static void release_resources(struct snd_tscm *tscm)
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
+ struct amdtp_stream *stream)
{
- __be32 reg;
+ struct fw_iso_resources *resources;
+ int err;
- /* 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);
+ 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;
- /* Release isochronous resources. */
- fw_iso_resources_free(&tscm->tx_resources);
- fw_iso_resources_free(&tscm->rx_resources);
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(tscm->unit)->max_speed);
}
-static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
{
- __be32 reg;
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ unsigned int pcm_channels;
int err;
- /* Keep resources for in-stream. */
- err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
- if (err < 0)
- return err;
- err = fw_iso_resources_allocate(&tscm->tx_resources,
- amdtp_stream_get_max_payload(&tscm->tx_stream),
- fw_parent_device(tscm->unit)->max_speed);
- if (err < 0)
- goto error;
+ 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;
+ }
- /* Keep resources for out-stream. */
- err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
- if (err < 0)
- return err;
- err = fw_iso_resources_allocate(&tscm->rx_resources,
- amdtp_stream_get_max_payload(&tscm->rx_stream),
- fw_parent_device(tscm->unit)->max_speed);
+ 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;
- /* 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);
+ err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
if (err < 0)
- goto error;
+ fw_iso_resources_free(resources);
- /* 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)
- goto error;
+ 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)
- goto error;
+static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
- return 0;
-error:
- release_resources(tscm);
- return err;
+ 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)
{
- unsigned int pcm_channels;
int err;
- /* For out-stream. */
- err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
- if (err < 0)
- return err;
- 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 = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
- pcm_channels);
+ err = init_stream(tscm, &tscm->tx_stream);
if (err < 0)
return err;
- /* For in-stream. */
- err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
- if (err < 0)
+ err = init_stream(tscm, &tscm->rx_stream);
+ if (err < 0) {
+ destroy_stream(tscm, &tscm->tx_stream);
return err;
- pcm_channels = tscm->spec->pcm_capture_analog_channels;
- if (tscm->spec->has_adat)
- pcm_channels += 8;
- if (tscm->spec->has_spdif)
- pcm_channels += 2;
- err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
- pcm_channels);
- if (err < 0)
- amdtp_stream_destroy(&tscm->rx_stream);
+ }
+
+ 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. */
+// At bus reset, streaming is stopped and some registers are clear.
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
{
- amdtp_stream_pcm_abort(&tscm->tx_stream);
- amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_domain_stop(&tscm->domain);
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_pcm_abort(&tscm->rx_stream);
- amdtp_stream_stop(&tscm->rx_stream);
}
-/*
- * This function should be called before starting streams or after stopping
- * streams.
- */
+// This function should be called before starting streams or after stopping
+// streams.
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
{
- amdtp_stream_destroy(&tscm->rx_stream);
- amdtp_stream_destroy(&tscm->tx_stream);
+ amdtp_domain_destroy(&tscm->domain);
- fw_iso_resources_destroy(&tscm->rx_resources);
- fw_iso_resources_destroy(&tscm->tx_resources);
+ destroy_stream(tscm, &tscm->rx_stream);
+ destroy_stream(tscm, &tscm->tx_stream);
}
-int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+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;
- if (tscm->substreams_counter == 0)
- return 0;
-
err = snd_tscm_stream_get_rate(tscm, &curr_rate);
if (err < 0)
return err;
- if (curr_rate != rate ||
- amdtp_streaming_error(&tscm->rx_stream) ||
- amdtp_streaming_error(&tscm->tx_stream)) {
+
+ if (tscm->substreams_counter == 0 || rate != curr_rate) {
+ amdtp_domain_stop(&tscm->domain);
+
finish_session(tscm);
- amdtp_stream_stop(&tscm->rx_stream);
- amdtp_stream_stop(&tscm->tx_stream);
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
- release_resources(tscm);
+ 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);
}
- if (!amdtp_stream_running(&tscm->rx_stream)) {
- err = keep_resources(tscm, 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;
@@ -404,28 +466,33 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
- err = amdtp_stream_start(&tscm->rx_stream,
- tscm->rx_resources.channel,
- fw_parent_device(tscm->unit)->max_speed);
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
+ tscm->rx_resources.channel, spd);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&tscm->rx_stream,
- CALLBACK_TIMEOUT)) {
- err = -ETIMEDOUT;
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
+ tscm->tx_resources.channel, spd);
+ if (err < 0)
goto error;
- }
- }
- if (!amdtp_stream_running(&tscm->tx_stream)) {
- err = amdtp_stream_start(&tscm->tx_stream,
- tscm->tx_resources.channel,
- fw_parent_device(tscm->unit)->max_speed);
+ 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_stream_wait_callback(&tscm->tx_stream,
- CALLBACK_TIMEOUT)) {
+ if (!amdtp_domain_wait_ready(&tscm->domain, READY_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
@@ -433,25 +500,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
error:
- amdtp_stream_stop(&tscm->rx_stream);
- amdtp_stream_stop(&tscm->tx_stream);
-
+ amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
- release_resources(tscm);
return err;
}
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
{
- if (tscm->substreams_counter > 0)
- return;
+ if (tscm->substreams_counter == 0) {
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
- amdtp_stream_stop(&tscm->tx_stream);
- amdtp_stream_stop(&tscm->rx_stream);
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
- finish_session(tscm);
- release_resources(tscm);
+ tscm->need_long_tx_init_skip = false;
+ }
}
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
@@ -462,33 +527,24 @@ void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
{
- int err;
-
- spin_lock_irq(&tscm->lock);
+ guard(spinlock_irq)(&tscm->lock);
/* user land lock this */
- if (tscm->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (tscm->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
{
- spin_lock_irq(&tscm->lock);
+ guard(spinlock_irq)(&tscm->lock);
if (WARN_ON(tscm->dev_lock_count <= 0))
- goto end;
+ return;
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
index 2ad692dd4b13..a073cece4a7d 100644
--- a/sound/firewire/tascam/tascam-transaction.c
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam-transaction.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "tascam.h"
@@ -210,7 +209,7 @@ static void midi_port_work(struct work_struct *work)
/* Set interval to next transaction. */
port->next_ktime = ktime_add_ns(ktime_get(),
- port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+ port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
/* Start this transaction. */
port->idling = false;
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
index 44ad41fb7374..f4092df8650c 100644
--- a/sound/firewire/tascam/tascam.c
+++ b/sound/firewire/tascam/tascam.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tascam.c - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "tascam.h"
MODULE_DESCRIPTION("TASCAM FireWire series Driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
static const struct snd_tscm_spec model_specs[] = {
{
@@ -74,9 +73,9 @@ static int identify_model(struct snd_tscm *tscm)
if (tscm->spec == NULL)
return -ENODEV;
- strcpy(tscm->card->driver, "FW-TASCAM");
- strcpy(tscm->card->shortname, model);
- strcpy(tscm->card->mixername, model);
+ strscpy(tscm->card->driver, "FW-TASCAM");
+ strscpy(tscm->card->shortname, model);
+ strscpy(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],
@@ -85,30 +84,37 @@ static int identify_model(struct snd_tscm *tscm)
return 0;
}
-static void tscm_free(struct snd_tscm *tscm)
+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);
- fw_unit_put(tscm->unit);
-
mutex_destroy(&tscm->mutex);
+ fw_unit_put(tscm->unit);
}
-static void tscm_card_free(struct snd_card *card)
-{
- tscm_free(card->private_data);
-}
-
-static void do_registration(struct work_struct *work)
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
{
- struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
+ struct snd_card *card;
+ struct snd_tscm *tscm;
int err;
- err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
- &tscm->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*tscm), &card);
if (err < 0)
- return;
+ 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)
@@ -136,101 +142,69 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(tscm->card);
+ err = snd_card_register(card);
if (err < 0)
goto error;
- /*
- * After registered, tscm instance can be released corresponding to
- * releasing the sound card instance.
- */
- tscm->card->private_free = tscm_card_free;
- tscm->card->private_data = tscm;
- tscm->registered = true;
-
- return;
-error:
- snd_tscm_transaction_unregister(tscm);
- snd_tscm_stream_destroy_duplex(tscm);
- snd_card_free(tscm->card);
- dev_info(&tscm->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int snd_tscm_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_tscm *tscm;
-
- /* Allocate this independent of sound card instance. */
- tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
- if (tscm == NULL)
- return -ENOMEM;
-
- /* initialize myself */
- tscm->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, tscm);
-
- mutex_init(&tscm->mutex);
- spin_lock_init(&tscm->lock);
- init_waitqueue_head(&tscm->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
- snd_fw_schedule_registration(unit, &tscm->dwork);
-
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);
- /* Postpone a workqueue for deferred registration. */
- if (!tscm->registered)
- snd_fw_schedule_registration(unit, &tscm->dwork);
-
snd_tscm_transaction_reregister(tscm);
- /*
- * After registration, userspace can start packet streaming, then this
- * code block works fine.
- */
- if (tscm->registered) {
- mutex_lock(&tscm->mutex);
- snd_tscm_stream_update_duplex(tscm);
- mutex_unlock(&tscm->mutex);
- }
+ guard(mutex)(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
}
static void snd_tscm_remove(struct fw_unit *unit)
{
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&tscm->dwork);
-
- if (tscm->registered) {
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(tscm->card);
- } else {
- /* Don't forget this case. */
- tscm_free(tscm);
- }
+ // 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_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
.vendor_id = 0x00022e,
.specifier_id = 0x00022e,
+ .version = 0x800004,
},
- /* FE-08 requires reverse-engineering because it just has faders. */
{}
};
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
@@ -238,7 +212,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
static struct fw_driver tscm_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "snd-firewire-tascam",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = snd_tscm_probe,
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index a5bd167eb5d9..d07ffcb27be6 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tascam.h - a part of driver for TASCAM FireWire series
*
* Copyright (c) 2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_TASCAM_H_INCLUDED
@@ -62,6 +61,8 @@ struct snd_fw_async_midi_port {
int consume_bytes;
};
+#define SND_TSCM_QUEUE_COUNT 16
+
struct snd_tscm {
struct snd_card *card;
struct fw_unit *unit;
@@ -69,8 +70,6 @@ struct snd_tscm {
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
const struct snd_tscm_spec *spec;
struct fw_iso_resources tx_resources;
@@ -89,6 +88,16 @@ struct snd_tscm {
/* 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
@@ -119,6 +128,26 @@ struct snd_tscm {
#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,
@@ -138,6 +167,9 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
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);
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 3129546398d0..7797f44b3d0c 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -1,29 +1,9 @@
-config SND_HDA_CORE
- tristate
- select REGMAP
+# SPDX-License-Identifier: GPL-2.0-only
+menu "HD-Audio"
-config SND_HDA_DSP_LOADER
- bool
+source "sound/hda/common/Kconfig"
+source "sound/hda/controllers/Kconfig"
+source "sound/hda/codecs/Kconfig"
+source "sound/hda/core/Kconfig"
-config SND_HDA_I915
- bool
- default y
- depends on DRM_I915
- depends on SND_HDA_CORE
-
-config SND_HDA_EXT_CORE
- tristate
- select SND_HDA_CORE
-
-config SND_HDA_PREALLOC_SIZE
- int "Pre-allocated buffer size for HD-audio driver"
- range 0 32768
- default 64
- 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.
-
- Note that the pre-allocation size can be changed dynamically
- via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+endmenu
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index e4e726f2ce98..d9a6def582ef 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -1,14 +1,8 @@
# 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_I915) += hdac_i915.o
-
-obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
-
-#extended hda
-obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+obj-y += core/
+obj-$(CONFIG_SND_HDA) += common/
+obj-$(CONFIG_SND_HDA) += codecs/
+# this must be the last entry after codec drivers;
+# otherwise the codec drivers won't be hooked before the PCI probe
+# when built in kernel
+obj-$(CONFIG_SND_HDA) += controllers/
diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig
new file mode 100644
index 000000000000..addbc9424336
--- /dev/null
+++ b/sound/hda/codecs/Kconfig
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if SND_HDA
+
+config SND_HDA_GENERIC_LEDS
+ bool
+
+config SND_HDA_CODEC_ANALOG
+ tristate "Build Analog Devices HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Analog Devices HD-audio codec support in
+ snd-hda-intel driver, such as AD1986A.
+
+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
+ tristate "Build IDT/Sigmatel HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
+ snd-hda-intel driver, such as STAC9200.
+
+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
+ tristate "Build VIA HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include VIA HD-audio codec support in
+ snd-hda-intel driver, such as VT1708.
+
+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_CONEXANT
+ tristate "Build Conexant HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include Conexant HD-audio codec support in
+ snd-hda-intel driver, such as CX20549.
+
+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_SENARYTECH
+ tristate "Build Senarytech HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include Senarytech HD-audio codec support in
+ snd-hda-intel driver, such as SN6186.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m
+
+config SND_HDA_CODEC_CA0110
+ tristate "Build Creative CA0110-IBG codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Creative CA0110-IBG codec support in
+ snd-hda-intel driver, found on some Creative X-Fi cards.
+
+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_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 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.
+
+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_CM9825
+ tristate "Build C-Media CM9825 HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include C-Media CM9825 HD-audio 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_CM9825=m
+
+config SND_HDA_CODEC_SI3054
+ tristate "Build Silicon Labs 3054 HD-modem codec support"
+ help
+ Say Y or M here to include Silicon Labs 3054 HD-modem codec
+ (and compatibles) 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_SI3054=m
+
+config SND_HDA_GENERIC
+ 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 or M here to enable the generic HD-audio codec parser
+ in snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_GENERIC=m
+
+source "sound/hda/codecs/realtek/Kconfig"
+source "sound/hda/codecs/cirrus/Kconfig"
+source "sound/hda/codecs/hdmi/Kconfig"
+source "sound/hda/codecs/side-codecs/Kconfig"
+
+endif # SND_HDA
diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile
new file mode 100644
index 000000000000..e7f03e281999
--- /dev/null
+++ b/sound/hda/codecs/Makefile
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../common
+
+snd-hda-codec-generic-y := generic.o
+snd-hda-codec-cmedia-y := cmedia.o
+snd-hda-codec-cm9825-y := cm9825.o
+snd-hda-codec-analog-y := analog.o
+snd-hda-codec-ca0110-y := ca0110.o
+snd-hda-codec-ca0132-y := ca0132.o
+snd-hda-codec-cmedia-y := cmedia.o
+snd-hda-codec-conexant-y := conexant.o
+snd-hda-codec-idt-y := sigmatel.o
+snd-hda-codec-senarytech-y := senarytech.o
+snd-hda-codec-si3054-y := si3054.o
+snd-hda-codec-via-y := via.o
+
+obj-y += cirrus/
+obj-y += hdmi/
+obj-y += realtek/
+obj-y += side-codecs/
+
+# codec drivers
+obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
+obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_CM9825) += snd-hda-codec-cm9825.o
+obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.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_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
+obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o
+obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o
+obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o
+obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
diff --git a/sound/pci/hda/patch_analog.c b/sound/hda/codecs/analog.c
index 757857313426..357ad5a6c0db 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/hda/codecs/analog.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
+ * HD audio codec driver 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>
@@ -24,16 +11,25 @@
#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"
+#include "generic.h"
+enum {
+ MODEL_AD1882,
+ MODEL_AD1884,
+ MODEL_AD1981,
+ MODEL_AD1983,
+ MODEL_AD1986A,
+ MODEL_AD1988,
+};
struct ad198x_spec {
struct hda_gen_spec gen;
+ int model;
/* for auto parser */
int smux_paths[4];
@@ -41,6 +37,7 @@ struct ad198x_spec {
hda_nid_t eapd_nid;
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
+ int num_smux_conns;
};
@@ -84,7 +81,6 @@ static int create_beep_ctls(struct hda_codec *codec)
#define create_beep_ctls(codec) 0
#endif
-
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
{
@@ -124,19 +120,12 @@ static void ad198x_power_eapd(struct hda_codec *codec)
}
}
-static void ad198x_shutup(struct hda_codec *codec)
+static int ad_codec_suspend(struct hda_codec *codec)
{
snd_hda_shutup_pins(codec);
ad198x_power_eapd(codec);
-}
-
-#ifdef CONFIG_PM
-static int ad198x_suspend(struct hda_codec *codec)
-{
- ad198x_shutup(codec);
return 0;
}
-#endif
/* follow EAPD via vmaster hook */
static void ad_vmaster_eapd_hook(void *private_data, int enabled)
@@ -148,7 +137,7 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled)
return;
if (codec->inv_eapd)
enabled = !enabled;
- snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
+ snd_hda_codec_write_cache(codec, spec->eapd_nid, 0,
AC_VERB_SET_EAPD_BTLENABLE,
enabled ? 0x02 : 0x00);
}
@@ -157,7 +146,7 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled)
* Automatic parse of I/O pins from the BIOS configuration
*/
-static int ad198x_auto_build_controls(struct hda_codec *codec)
+static int ad_codec_build_controls(struct hda_codec *codec)
{
int err;
@@ -170,20 +159,6 @@ static int ad198x_auto_build_controls(struct hda_codec *codec)
return 0;
}
-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
- .reboot_notify = ad198x_shutup,
-};
-
-
static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
{
struct ad198x_spec *spec = codec->spec;
@@ -221,7 +196,6 @@ static int alloc_ad_spec(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
snd_hda_gen_spec_init(&spec->gen);
- codec->patch_ops = ad198x_auto_patch_ops;
return 0;
}
@@ -368,8 +342,9 @@ static const struct hda_fixup ad1986a_fixups[] = {
},
};
-static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
+static const struct hda_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),
@@ -397,11 +372,11 @@ static const struct hda_model_fixup ad1986a_fixup_models[] = {
/*
*/
-static int patch_ad1986a(struct hda_codec *codec)
+static int ad1986a_probe(struct hda_codec *codec)
{
int err;
- struct ad198x_spec *spec;
- static hda_nid_t preferred_pairs[] = {
+ struct ad198x_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
0x1a, 0x03,
0x1b, 0x03,
0x1c, 0x04,
@@ -410,11 +385,6 @@ static int patch_ad1986a(struct hda_codec *codec)
0
};
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
/* AD1986A has the inverted EAPD implementation */
codec->inv_eapd = 1;
@@ -440,10 +410,8 @@ static int patch_ad1986a(struct hda_codec *codec)
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);
+ if (err < 0)
return err;
- }
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
@@ -465,8 +433,7 @@ static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
struct ad198x_spec *spec = codec->spec;
static const char * const texts2[] = { "PCM", "ADC" };
static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
- hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
- int num_conns = snd_hda_get_num_conns(codec, dig_out);
+ int num_conns = spec->num_smux_conns;
if (num_conns == 2)
return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
@@ -493,7 +460,7 @@ static int ad1983_auto_smux_enum_put(struct snd_kcontrol *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 = snd_hda_get_num_conns(codec, dig_out);
+ int num_conns = spec->num_smux_conns;
if (val >= num_conns)
return -EINVAL;
@@ -524,23 +491,19 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
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 int ad1983_probe(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
- static hda_nid_t conn_0c[] = { 0x08 };
- static hda_nid_t conn_0d[] = { 0x09 };
+ static const hda_nid_t conn_0c[] = { 0x08 };
+ static const hda_nid_t conn_0d[] = { 0x09 };
+ struct ad198x_spec *spec = codec->spec;
int err;
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
spec->gen.mixer_nid = 0x0e;
spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
@@ -551,15 +514,11 @@ static int patch_ad1983(struct hda_codec *codec)
err = ad198x_parse_auto_config(codec, false);
if (err < 0)
- goto error;
+ return err;
err = ad1983_add_spdif_mux_ctl(codec);
if (err < 0)
- goto error;
+ return err;
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
@@ -610,7 +569,7 @@ static const struct hda_fixup ad1981_fixups[] = {
},
};
-static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
+static const struct hda_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),
@@ -619,16 +578,11 @@ static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
{}
};
-static int patch_ad1981(struct hda_codec *codec)
+static int ad1981_probe(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
+ struct ad198x_spec *spec = codec->spec;
int err;
- err = alloc_ad_spec(codec);
- if (err < 0)
- return -ENOMEM;
- spec = codec->spec;
-
spec->gen.mixer_nid = 0x0e;
spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
@@ -638,18 +592,14 @@ static int patch_ad1981(struct hda_codec *codec)
err = ad198x_parse_auto_config(codec, false);
if (err < 0)
- goto error;
+ return err;
err = ad1983_add_spdif_mux_ctl(codec);
if (err < 0)
- goto error;
+ return err;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
@@ -742,10 +692,12 @@ 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;
static const char * const texts[] = {
"PCM", "ADC1", "ADC2", "ADC3",
};
- int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ int num_conns = spec->num_smux_conns;
+
if (num_conns > 4)
num_conns = 4;
return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
@@ -768,14 +720,14 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
struct ad198x_spec *spec = codec->spec;
unsigned int val = ucontrol->value.enumerated.item[0];
struct nid_path *path;
- int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ int num_conns = spec->num_smux_conns;
if (val >= num_conns)
return -EINVAL;
if (spec->cur_smux == val)
return 0;
- mutex_lock(&codec->control_mutex);
+ guard(mutex)(&codec->control_mutex);
path = snd_hda_get_path_from_idx(codec,
spec->smux_paths[spec->cur_smux]);
if (path)
@@ -784,7 +736,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
if (path)
snd_hda_activate_path(codec, path, true, true);
spec->cur_smux = val;
- mutex_unlock(&codec->control_mutex);
return 1;
}
@@ -796,7 +747,7 @@ static const struct snd_kcontrol_new ad1988_auto_smux_mixer = {
.put = ad1988_auto_smux_enum_put,
};
-static int ad1988_auto_init(struct hda_codec *codec)
+static int ad_codec_init(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
int i, err;
@@ -804,6 +755,8 @@ static int ad1988_auto_init(struct hda_codec *codec)
err = snd_hda_gen_init(codec);
if (err < 0)
return err;
+ if (spec->model != MODEL_AD1988)
+ return 0;
if (!spec->gen.autocfg.dig_outs)
return 0;
@@ -824,7 +777,7 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
/* we create four static faked paths, since AD codecs have odd
* widget connections regarding the SPDIF out source
*/
- static struct nid_path fake_paths[4] = {
+ static const struct nid_path fake_paths[4] = {
{
.depth = 3,
.path = { 0x02, 0x1d, 0x1b },
@@ -859,6 +812,7 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
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);
@@ -873,8 +827,6 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
return -ENOMEM;
- codec->patch_ops.init = ad1988_auto_init;
-
return 0;
}
@@ -908,16 +860,11 @@ static const struct hda_model_fixup ad1988_fixup_models[] = {
{}
};
-static int patch_ad1988(struct hda_codec *codec)
+static int ad1988_probe(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
+ struct ad198x_spec *spec = codec->spec;
int err;
- 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;
@@ -928,18 +875,14 @@ static int patch_ad1988(struct hda_codec *codec)
err = ad198x_parse_auto_config(codec, true);
if (err < 0)
- goto error;
+ return err;
err = ad1988_add_spdif_mux_ctl(codec);
if (err < 0)
- goto error;
+ return err;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
@@ -991,7 +934,7 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
if (spec->eapd_nid)
ad_vmaster_eapd_hook(private_data, enabled);
- snd_hda_codec_update_cache(codec, 0x01, 0,
+ snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA,
enabled ? 0x00 : 0x02);
}
@@ -1080,7 +1023,7 @@ static const struct hda_fixup ad1884_fixups[] = {
},
};
-static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
+static const struct hda_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),
@@ -1088,16 +1031,11 @@ static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
};
-static int patch_ad1884(struct hda_codec *codec)
+static int ad1884_probe(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
+ struct ad198x_spec *spec = codec->spec;
int err;
- 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;
@@ -1108,18 +1046,14 @@ static int patch_ad1884(struct hda_codec *codec)
err = ad198x_parse_auto_config(codec, true);
if (err < 0)
- goto error;
+ return err;
err = ad1983_add_spdif_mux_ctl(codec);
if (err < 0)
- goto error;
+ return err;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
/*
@@ -1134,53 +1068,99 @@ static int patch_ad1884(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-static int patch_ad1882(struct hda_codec *codec)
+static int ad1882_probe(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
+ struct ad198x_spec *spec = codec->spec;
int err;
- 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);
err = ad198x_parse_auto_config(codec, true);
if (err < 0)
- goto error;
+ return err;
err = ad1988_add_spdif_mux_ctl(codec);
if (err < 0)
- goto error;
+ return err;
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
-
/*
- * patch entries
+ * driver entries
*/
+static int ad_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct ad198x_spec *spec;
+ int err;
+
+ err = alloc_ad_spec(codec);
+ if (err < 0)
+ return -ENOMEM;
+ spec = codec->spec;
+ spec->model = id->driver_data;
+
+ switch (spec->model) {
+ case MODEL_AD1882:
+ err = ad1882_probe(codec);
+ break;
+ case MODEL_AD1884:
+ err = ad1884_probe(codec);
+ break;
+ case MODEL_AD1981:
+ err = ad1981_probe(codec);
+ break;
+ case MODEL_AD1983:
+ err = ad1983_probe(codec);
+ break;
+ case MODEL_AD1986A:
+ err = ad1986a_probe(codec);
+ break;
+ case MODEL_AD1988:
+ err = ad1988_probe(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops ad_codec_ops = {
+ .probe = ad_codec_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = ad_codec_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = ad_codec_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = ad_codec_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
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),
+ HDA_CODEC_ID_MODEL(0x11d4184a, "AD1884A", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41882, "AD1882", MODEL_AD1882),
+ HDA_CODEC_ID_MODEL(0x11d41883, "AD1883", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41884, "AD1884", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d4194a, "AD1984A", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d4194b, "AD1984B", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41981, "AD1981", MODEL_AD1981),
+ HDA_CODEC_ID_MODEL(0x11d41983, "AD1983", MODEL_AD1983),
+ HDA_CODEC_ID_MODEL(0x11d41984, "AD1984", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41986, "AD1986A", MODEL_AD1986A),
+ HDA_CODEC_ID_MODEL(0x11d41988, "AD1988", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4198b, "AD1988B", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4882a, "AD1882A", MODEL_AD1882),
+ HDA_CODEC_ID_MODEL(0x11d4989a, "AD1989A", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4989b, "AD1989B", MODEL_AD1988),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
@@ -1190,6 +1170,7 @@ MODULE_DESCRIPTION("Analog Devices HD-audio codec");
static struct hda_codec_driver analog_driver = {
.id = snd_hda_id_analog,
+ .ops = &ad_codec_ops,
};
module_hda_codec_driver(analog_driver);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/hda/codecs/ca0110.c
index c2d9ee9cfdc0..c75a9ff9460d 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/hda/codecs/ca0110.c
@@ -1,41 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HD audio interface patch for Creative X-Fi CA0110-IBG chip
+ * HD audio codec driver 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/slab.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"
-
-
-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,
-};
+#include "generic.h"
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
@@ -52,8 +30,7 @@ static int ca0110_parse_auto_config(struct hda_codec *codec)
return 0;
}
-
-static int patch_ca0110(struct hda_codec *codec)
+static int ca0110_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
struct hda_gen_spec *spec;
int err;
@@ -63,10 +40,9 @@ static int patch_ca0110(struct hda_codec *codec)
return -ENOMEM;
snd_hda_gen_spec_init(spec);
codec->spec = spec;
- codec->patch_ops = ca0110_patch_ops;
spec->multi_cap_vol = 1;
- codec->bus->needs_damn_long_delay = 1;
+ codec->bus->core.needs_damn_long_delay = 1;
err = ca0110_parse_auto_config(codec);
if (err < 0)
@@ -75,18 +51,27 @@ static int patch_ca0110(struct hda_codec *codec)
return 0;
error:
- snd_hda_gen_free(codec);
+ snd_hda_gen_remove(codec);
return err;
}
+static const struct hda_codec_ops ca0110_codec_ops = {
+ .probe = ca0110_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
+
/*
- * patch entries
+ * driver entries
*/
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),
+ HDA_CODEC_ID(0x1102000a, "CA0110-IBG"),
+ HDA_CODEC_ID(0x1102000b, "CA0110-IBG"),
+ HDA_CODEC_ID(0x1102000d, "SB0880 X-Fi"),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
@@ -96,6 +81,7 @@ MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_driver ca0110_driver = {
.id = snd_hda_id_ca0110,
+ .ops = &ca0110_codec_ops,
};
module_hda_codec_driver(ca0110_driver);
diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c
new file mode 100644
index 000000000000..dd054aedd501
--- /dev/null
+++ b/sound/hda/codecs/ca0132.c
@@ -0,0 +1,10078 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on 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 {
+ const char *name;
+ 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 {
+ const char *name;
+ 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 {
+ const char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_voicefx_preset {
+ const 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 {
+ const char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_eq_preset {
+ const 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 {
+ const 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 {
+ const 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 {
+ const 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;
+
+#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_ALIENWARE,
+ QUIRK_ALIENWARE_M17XR4,
+ QUIRK_SBZ,
+ QUIRK_ZXR,
+ QUIRK_ZXR_DBPRO,
+ QUIRK_R3DI,
+ QUIRK_R3D,
+ QUIRK_AE5,
+ QUIRK_AE7,
+ QUIRK_NONE = HDA_FIXUP_ID_NOT_SET,
+};
+
+#ifdef CONFIG_PCI
+#define ca0132_quirk(spec) ((spec)->codec->fixup_id)
+#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 hda_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, 0x104b, "EVGA X299 Dark", 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),
+ {}
+};
+
+static const struct hda_model_fixup ca0132_quirk_models[] = {
+ { .id = QUIRK_ALIENWARE, .name = "alienware" },
+ { .id = QUIRK_ALIENWARE_M17XR4, .name = "alienware-m17xr4" },
+ { .id = QUIRK_SBZ, .name = "sbz" },
+ { .id = QUIRK_ZXR, .name = "zxr" },
+ { .id = QUIRK_ZXR_DBPRO, .name = "zxr-dbpro" },
+ { .id = QUIRK_R3DI, .name = "r3di" },
+ { .id = QUIRK_R3D, .name = "r3d" },
+ { .id = QUIRK_AE5, .name = "ae5" },
+ { .id = QUIRK_AE7, .name = "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;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ return err;
+
+ return chipio_write_data(codec, data);
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&spec->chipio_mutex);
+ status = chipio_write_address(codec, chip_addx);
+ if (status < 0)
+ return status;
+
+ return chipio_write_data_multiple(codec, data, count);
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ return err;
+
+ return chipio_read_data(codec, data);
+}
+
+/*
+ * 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 {
+ guard(mutex)(&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);
+ }
+ }
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr);
+ chipio_8051_set_data(codec, data);
+}
+
+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;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr & 0xff);
+ chipio_8051_set_data_pll(codec, data);
+}
+
+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;
+
+ guard(mutex)(&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);
+}
+
+/*
+ * 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);
+
+ guard(mutex)(&spec->chipio_mutex);
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+ scp_data & 0xffff);
+ if (status < 0)
+ return status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+ scp_data >> 16);
+ if (status < 0)
+ return status;
+
+ /* OK, now check if the write itself has executed*/
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+
+ 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_stream_format(channels, 32, sample_rate);
+
+ 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;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ if (nid == ca0132_tuning_ctls[i].nid) {
+ CLASS(snd_hda_power, pm)(codec);
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20,
+ ca0132_tuning_ctls[i].req,
+ &(lookup[idx]), sizeof(unsigned int));
+ return 1;
+ }
+ }
+
+ return -EINVAL;
+}
+
+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 = NULL;
+ knew.tlv.p = NULL;
+ 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);
+ snprintf(namestr, sizeof(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");
+
+ CLASS(snd_hda_power_pm, 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)
+ return err;
+ /*enable speaker EQ*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ return err;
+
+ /* 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)
+ return err;
+ /*disable speaker EQ*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ return err;
+
+ /* 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);
+ }
+
+ return 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__);
+
+ CLASS(snd_hda_power_pm, 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)
+ return err;
+
+ err = ca0132_alt_select_out_quirk_set(codec);
+ if (err < 0)
+ return err;
+
+ 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)
+ return err;
+
+ 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)
+ return err;
+ 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)
+ return err;
+
+ /*
+ * 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)
+ return err;
+
+ 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);
+ if (err < 0)
+ return err;
+
+ /* 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)
+ return err;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ err = ca0132_alt_set_full_range_speaker(codec);
+ if (err < 0)
+ return err;
+ }
+
+ return 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");
+
+ CLASS(snd_hda_power_pm, 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);
+ }
+
+ 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__);
+
+ CLASS(snd_hda_power_pm, 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);
+
+ 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;
+
+ guard(mutex)(&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;
+ }
+
+ return ret;
+}
+/* End of control change helpers. */
+
+static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec,
+ long idx)
+{
+ CLASS(snd_hda_power, pm)(codec);
+
+ dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ,
+ &(float_xbass_xover_lookup[idx]), sizeof(unsigned int));
+}
+
+/*
+ * 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;
+
+ CLASS(snd_hda_power, pm)(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));
+ }
+
+ 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)
+{
+ const 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);
+ strscpy(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)
+{
+ const 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);
+ strscpy(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);
+ strscpy(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;
+ strscpy(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;
+ strscpy(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;
+ strscpy(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;
+ strscpy(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;
+ strscpy(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;
+ strscpy(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;
+
+ codec_dbg(codec, "ca0132_switch_put: nid=0x%x, val=%ld\n",
+ nid, *valp);
+
+ CLASS(snd_hda_power, pm)(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++;
+ }
+ return ca0132_vnode_switch_set(kcontrol, ucontrol);
+ }
+
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ return ca0132_pe_switch_set(codec);
+ }
+
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ return ca0132_cvoice_switch_set(codec);
+ }
+
+ /* 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;
+ return ca0132_effects_set(codec, nid, *valp);
+ }
+
+ /* 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)
+ return ca0132_mic_boost_set(codec, *valp);
+ } else {
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ return ca0132_mic_boost_set(codec, *valp);
+ }
+
+ return 1;
+ }
+
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ spec->zxr_gain_set = *valp;
+ if (spec->cur_out_type == HEADPHONE_OUT)
+ return zxr_headphone_gain_set(codec, *valp);
+ else
+ return 0;
+ }
+
+ 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);
+
+ return 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);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * 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;
+ scoped_guard(mutex, &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;
+ }
+ break;
+ case VNID_MIC:
+ /* follow shared_mic info */
+ nid = spec->shared_mic_nid;
+ scoped_guard(mutex, &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;
+ }
+ 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;
+
+ CLASS(snd_hda_power, pm)(codec);
+ guard(mutex)(&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;
+ }
+
+ 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;
+
+ 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++;
+ }
+
+ CLASS(snd_hda_power, pm)(codec);
+ ca0132_alt_dsp_volume_put(codec, vnid);
+ guard(mutex)(&codec->control_mutex);
+ return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+}
+
+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;
+ scoped_guard(mutex, &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;
+ }
+ break;
+ case VNID_MIC:
+ /* follow shared_mic tlv */
+ nid = spec->shared_mic_nid;
+ scoped_guard(mutex, &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;
+ }
+ 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;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_write_exram_no_mutex(codec, 0x1920, 0x00);
+ chipio_8051_write_exram_no_mutex(codec, 0x192d, 0x00);
+}
+
+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.
+ */
+ scoped_guard(mutex, &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);
+ }
+ }
+ }
+
+ /*
+ * 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);
+
+ guard(mutex)(&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);
+ }
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&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");
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&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");
+}
+
+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;
+
+ guard(mutex)(&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);
+}
+
+static void ae5_post_dsp_startup_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&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);
+}
+
+static void ae7_post_dsp_setup_ports(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&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);
+}
+
+static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&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);
+}
+
+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;
+
+ guard(mutex)(&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);
+}
+
+/*
+ * 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");
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (spec->wait_scp) {
+ if (dspio_get_response_data(codec) >= 0)
+ spec->wait_scp = 0;
+ }
+
+ dspio_clear_response_queue(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;
+ snd_hda_codec_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);
+
+ CLASS(snd_hda_power_pm, 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);
+ }
+
+ 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);
+}
+
+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)
+{
+ switch (codec->core.subsystem_id) {
+ case 0x11020033:
+ codec->fixup_id = QUIRK_ZXR;
+ break;
+ case 0x1102003f:
+ codec->fixup_id = QUIRK_ZXR_DBPRO;
+ break;
+ default:
+ codec->fixup_id = QUIRK_SBZ;
+ break;
+ }
+}
+
+static void ca0132_codec_remove(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_free(codec);
+ else
+ return ca0132_free(codec);
+}
+
+static int ca0132_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct ca0132_spec *spec;
+ int err;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->codec = codec;
+
+ /* Detect codec quirk */
+ snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL);
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ sbz_detect_quirk(codec);
+
+ 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.");
+ codec->fixup_id = 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_codec_remove(codec);
+ return err;
+}
+
+static int ca0132_codec_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_build_controls(codec);
+ else
+ return ca0132_build_controls(codec);
+}
+
+static int ca0132_codec_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_build_pcms(codec);
+ else
+ return ca0132_build_pcms(codec);
+}
+
+static int ca0132_codec_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_init(codec);
+ else
+ return ca0132_init(codec);
+}
+
+static int ca0132_codec_suspend(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ return 0;
+}
+
+static const struct hda_codec_ops ca0132_codec_ops = {
+ .probe = ca0132_codec_probe,
+ .remove = ca0132_codec_remove,
+ .build_controls = ca0132_codec_build_controls,
+ .build_pcms = ca0132_codec_build_pcms,
+ .init = ca0132_codec_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = ca0132_codec_suspend,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ID(0x11020011, "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,
+ .ops = &ca0132_codec_ops,
+};
+
+module_hda_codec_driver(ca0132_driver);
diff --git a/sound/pci/hda/ca0132_regs.h b/sound/hda/codecs/ca0132_regs.h
index 8371274aa811..dc0153df3d5c 100644
--- a/sound/pci/hda/ca0132_regs.h
+++ b/sound/hda/codecs/ca0132_regs.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * HD audio interface patch for Creative CA0132 chip.
+ * HD audio codec driver for Creative CA0132 chip.
* CA0132 registers defines.
*
* Copyright (c) 2011, Creative Technology Ltd.
- *
- * 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 __CA0132_REGS_H
diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig
new file mode 100644
index 000000000000..ec6cbcaf64f0
--- /dev/null
+++ b/sound/hda/codecs/cirrus/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_CIRRUS
+ tristate "Cirrus Logic HD-audio codec support"
+ help
+ Say Y or M here to include Cirrus Logic HD-audio codec support.
+
+ This will enable both CS420x and CS421x HD-audio codec drivers
+ as default, but you can enable/disable each codec driver
+ individually, too (only when CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_CIRRUS
+
+config SND_HDA_CODEC_CS420X
+ tristate "Build Cirrus Logic CS420x codec support" if EXPERT
+ select SND_HDA_GENERIC
+ default y
+ help
+ Say Y or M here to include Cirrus Logic CS420x codec support
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS420X=m
+
+config SND_HDA_CODEC_CS421X
+ tristate "Build Cirrus Logic CS421x codec support" if EXPERT
+ select SND_HDA_GENERIC
+ default y
+ help
+ Say Y or M here to include Cirrus Logic CS421x codec support
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS421X=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
+ 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
+
+endif
diff --git a/sound/hda/codecs/cirrus/Makefile b/sound/hda/codecs/cirrus/Makefile
new file mode 100644
index 000000000000..dda1873ebcf5
--- /dev/null
+++ b/sound/hda/codecs/cirrus/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-cs420x-y := cs420x.o
+snd-hda-codec-cs421x-y := cs421x.o
+snd-hda-codec-cs8409-y := cs8409.o cs8409-tables.o
+
+obj-$(CONFIG_SND_HDA_CODEC_CS420X) += snd-hda-codec-cs420x.o
+obj-$(CONFIG_SND_HDA_CODEC_CS421X) += snd-hda-codec-cs421x.o
+obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/hda/codecs/cirrus/cs420x.c
index d6e079f4ec09..823220d5cada 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/hda/codecs/cirrus/cs420x.c
@@ -1,36 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HD audio interface patch for Cirrus Logic CS420x chip
+ * Cirrus Logic CS420x HD-audio codec
*
* 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/slab.h>
#include <linux/module.h>
#include <sound/core.h>
+#include <linux/pci.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_jack.h"
-#include "hda_generic.h"
-
-/*
- */
+#include "../generic.h"
struct cs_spec {
struct hda_gen_spec gen;
@@ -41,10 +26,6 @@ struct cs_spec {
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 */
@@ -68,13 +49,6 @@ enum {
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
@@ -117,31 +91,10 @@ enum {
/* 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)
{
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, spec->vendor_nid, 0,
@@ -152,6 +105,7 @@ static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
unsigned int coef)
{
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, spec->vendor_nid, 0,
@@ -168,9 +122,6 @@ static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- /* mute HPs if spdif jack (SENSE_B) is present */
- spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
-
snd_hda_gen_update_outputs(codec);
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
@@ -188,6 +139,7 @@ static void cs_automute(struct hda_codec *codec)
static bool 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);
}
@@ -206,7 +158,7 @@ static void init_input_coef(struct hda_codec *codec)
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);
}
@@ -280,13 +232,6 @@ static const struct hda_verb cs_errata_init_verbs[] = {
{0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
{0x11, AC_VERB_SET_PROC_COEF, 0x0008},
{0x11, AC_VERB_SET_PROC_STATE, 0x00},
-
-#if 0 /* Don't to set to D3 as we are in power-up sequence */
- {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 */
-#endif
-
{} /* terminator */
};
@@ -347,16 +292,6 @@ static int cs_build_controls(struct hda_codec *codec)
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 cs_parse_auto_config(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
@@ -374,8 +309,10 @@ static int cs_parse_auto_config(struct hda_codec *codec)
/* 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,
@@ -399,7 +336,7 @@ static const struct hda_model_fixup cs420x_models[] = {
{}
};
-static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
+static const struct hda_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),
@@ -409,6 +346,7 @@ static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
/* 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),
@@ -509,6 +447,7 @@ static void cs420x_fixup_gpio_13(struct hda_codec *codec,
{
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 =
@@ -521,6 +460,7 @@ static void cs420x_fixup_gpio_23(struct hda_codec *codec,
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
struct cs_spec *spec = codec->spec;
+
spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
spec->gpio_mask = spec->gpio_dir =
@@ -595,17 +535,10 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
return spec;
}
-static int patch_cs420x(struct hda_codec *codec)
+static int cs420x_probe(struct hda_codec *codec)
{
- struct cs_spec *spec;
int err;
- spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
- if (!spec)
- return -ENOMEM;
-
- codec->patch_ops = cs_patch_ops;
- spec->gen.automute_hook = cs_automute;
codec->single_adc_amp = 1;
snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
@@ -614,15 +547,11 @@ static int patch_cs420x(struct hda_codec *codec)
err = cs_parse_auto_config(codec);
if (err < 0)
- goto error;
+ return err;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-
- error:
- cs_free(codec);
- return err;
}
/*
@@ -645,13 +574,13 @@ static const struct hda_model_fixup cs4208_models[] = {
{}
};
-static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
+static const struct hda_quirk cs4208_fixup_tbl[] = {
SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
{} /* terminator */
};
/* codec SSID matching */
-static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
+static const struct hda_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),
@@ -665,6 +594,7 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec,
{
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 =
@@ -776,17 +706,11 @@ static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
}
-static int patch_cs4208(struct hda_codec *codec)
+static int cs4208_probe(struct hda_codec *codec)
{
- struct cs_spec *spec;
+ struct cs_spec *spec = codec->spec;
int err;
- spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
- if (!spec)
- return -ENOMEM;
-
- 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;
@@ -802,466 +726,60 @@ static int patch_cs4208(struct hda_codec *codec)
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)
-*/
-
-/* CS4210 board names */
-static const struct hda_model_fixup cs421x_models[] = {
- { .id = CS421X_CDB4210, .name = "cdb4210" },
- { .id = CS421X_STUMPY, .name = "stumpy" },
- {}
-};
-
-static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
- /* Test Intel board + CDB2410 */
- SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
- {} /* terminator */
-};
-
-/* 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 */
-};
-
-/* 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 (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->sense_b = 1;
-}
-
-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 const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
- {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
-
- {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 */
-
- {} /* 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)
-{
- 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 int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- 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;
-
- coef &= ~0x0003;
- coef |= (vol & 0x0003);
- if (original_coef == coef)
- return 0;
- else {
- cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
- return 1;
- }
-}
-
-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;
- unsigned int def_conf, coef;
-
- /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
- coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
-
- if (spec->gpio_mask)
- coef |= 0x0008; /* B1,B2 are GPIOs */
- else
- coef &= ~0x0008;
-
- if (spec->sense_b)
- coef |= 0x0010; /* B2 is SENSE_B, not inverted */
- else
- coef &= ~0x0010;
-
- cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
-
- if ((spec->gpio_mask || spec->sense_b) &&
- is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
-
- /*
- 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);
- }
-}
-
-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];
-
- /* detect on spdif is specific to CS4210 */
- if (!spec->spdif_detect ||
- spec->vendor_nid != CS4210_VENDOR_NID)
- return;
-
- spdif_present = snd_hda_jack_detect(codec, spdif_pin);
- if (spdif_present == spec->spdif_present)
- return;
-
- spec->spdif_present = spdif_present;
- /* SPDIF TX on/off */
- snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0);
-
- cs_automute(codec);
-}
-
-static void parse_cs421x_digital(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- int i;
-
- 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 cs421x_init(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- 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_gen_init(codec);
-
- 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);
- }
-
- init_input_coef(codec);
-
- cs4210_spdif_automute(codec, NULL);
-
- return 0;
-}
-
-static int cs421x_build_controls(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
return err;
- if (spec->gen.autocfg.speaker_outs &&
- spec->vendor_nid == CS4210_VENDOR_NID) {
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs421x_speaker_boost_ctl, codec));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-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;
-
- fix_volume_caps(codec, dac);
-
- 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;
-
- parse_cs421x_digital(codec);
- return 0;
-}
-
-#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;
- unsigned int coef;
-
- 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);
- }
-
- return 0;
-}
-#endif
-
-static const struct hda_codec_ops cs421x_patch_ops = {
- .build_controls = cs421x_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;
-
- spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
- if (!spec)
- return -ENOMEM;
-
- codec->patch_ops = cs421x_patch_ops;
- spec->gen.automute_hook = cs_automute;
-
- snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
- cs421x_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /*
- 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);
-
- err = cs421x_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;
}
-static int patch_cs4213(struct hda_codec *codec)
+static int cs_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
{
struct cs_spec *spec;
int err;
- spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
+ spec = cs_alloc_spec(codec, id->driver_data);
if (!spec)
return -ENOMEM;
+ spec->gen.automute_hook = cs_automute;
- codec->patch_ops = cs421x_patch_ops;
-
- err = cs421x_parse_auto_config(codec);
+ if (spec->vendor_nid == CS4208_VENDOR_NID)
+ err = cs4208_probe(codec);
+ else
+ err = cs420x_probe(codec);
if (err < 0)
- goto error;
-
- return 0;
-
- error:
- cs_free(codec);
+ snd_hda_gen_remove(codec);
return err;
}
+static const struct hda_codec_ops cs_codec_ops = {
+ .probe = cs_codec_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = cs_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
/*
- * patch entries
+ * driver entries
*/
-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),
+static const struct hda_device_id snd_hda_id_cs420x[] = {
+ HDA_CODEC_ID_MODEL(0x10134206, "CS4206", CS420X_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134207, "CS4207", CS420X_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134208, "CS4208", CS4208_VENDOR_NID),
{} /* terminator */
};
-MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs420x);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
+MODULE_DESCRIPTION("Cirrus Logic CS420x HD-audio codec");
-static struct hda_codec_driver cirrus_driver = {
- .id = snd_hda_id_cirrus,
+static struct hda_codec_driver cs420x_driver = {
+ .id = snd_hda_id_cs420x,
+ .ops = &cs_codec_ops,
};
-module_hda_codec_driver(cirrus_driver);
+module_hda_codec_driver(cs420x_driver);
diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c
new file mode 100644
index 000000000000..a93e2e0bb391
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs421x.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Cirrus Logic CS421x HD-audio codec
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.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 "../generic.h"
+
+struct cs_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ 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);
+};
+
+/* CS421x boards */
+enum {
+ CS421X_CDB4210,
+ CS421X_SENSE_B,
+ CS421X_STUMPY,
+};
+
+/* Vendor-specific processing widget */
+#define CS_DIG_OUT1_PIN_NID 0x10
+#define CS_DIG_OUT2_PIN_NID 0x15
+#define CS_DMIC1_PIN_NID 0x0e
+#define CS_DMIC2_PIN_NID 0x12
+
+/* coef indices */
+#define IDX_SPDIF_STAT 0x0000
+#define IDX_SPDIF_CTL 0x0001
+#define IDX_ADC_CFG 0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define CS_COEF_ADC_SZC_MASK (3 << 0)
+#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
+#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
+#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG 0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
+#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
+#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG 0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Transmitter(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)
+{
+ 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, 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)
+{
+ 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, spec->vendor_nid, 0,
+ AC_VERB_SET_PROC_COEF, coef);
+}
+
+/*
+ * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
+ */
+
+static void cs_automute(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
+
+ snd_hda_gen_update_outputs(codec);
+
+ 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 bool 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);
+}
+
+static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
+{
+ struct cs_spec *spec;
+
+ 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);
+
+ return spec;
+}
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Transmitter(sense)
+ */
+
+/* CS4210 board names */
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
+ {}
+};
+
+static const struct hda_quirk cs421x_fixup_tbl[] = {
+ /* Test Intel board + CDB2410 */
+ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+ {} /* terminator */
+};
+
+/* 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 */
+};
+
+/* 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 (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
+}
+
+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 const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+
+ {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 */
+
+ {} /* 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)
+{
+ 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 int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ 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;
+
+ coef &= ~0x0003;
+ coef |= (vol & 0x0003);
+ if (original_coef != coef) {
+ cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+ return 1;
+ }
+
+ return 0;
+}
+
+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;
+ unsigned int def_conf, coef;
+
+ /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+
+ if (spec->gpio_mask)
+ coef |= 0x0008; /* B1,B2 are GPIOs */
+ else
+ coef &= ~0x0008;
+
+ if (spec->sense_b)
+ coef |= 0x0010; /* B2 is SENSE_B, not inverted */
+ else
+ coef &= ~0x0010;
+
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+ if ((spec->gpio_mask || spec->sense_b) &&
+ is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
+
+ /*
+ * 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);
+ }
+}
+
+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];
+
+ /* detect on spdif is specific to CS4210 */
+ if (!spec->spdif_detect ||
+ spec->vendor_nid != CS4210_VENDOR_NID)
+ return;
+
+ spdif_present = snd_hda_jack_detect(codec, spdif_pin);
+ if (spdif_present == spec->spdif_present)
+ return;
+
+ spec->spdif_present = spdif_present;
+ /* SPDIF TX on/off */
+ snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0);
+
+ cs_automute(codec);
+}
+
+static void parse_cs421x_digital(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
+
+ 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 cs421x_init(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ 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_gen_init(codec);
+
+ 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);
+ }
+
+ cs4210_spdif_automute(codec, NULL);
+
+ return 0;
+}
+
+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;
+
+ fix_volume_caps(codec, dac);
+
+ 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;
+
+ parse_cs421x_digital(codec);
+
+ 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;
+ }
+
+ return 0;
+}
+
+/*
+ * 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;
+ unsigned int coef;
+
+ 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);
+ }
+
+ return 0;
+}
+
+static int cs421x_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct cs_spec *spec;
+ int err;
+
+ spec = cs_alloc_spec(codec, id->driver_data);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->gen.automute_hook = cs_automute;
+
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /*
+ * 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);
+ }
+
+ err = cs421x_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cs421x_codec_ops = {
+ .probe = cs421x_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs421x_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cs421x_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cs421x[] = {
+ HDA_CODEC_ID_MODEL(0x10134210, "CS4210", CS4210_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134213, "CS4213", CS4213_VENDOR_NID),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs421x);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic CS421x HD-audio codec");
+
+static struct hda_codec_driver cs421x_driver = {
+ .id = snd_hda_id_cs421x,
+ .ops = &cs421x_codec_ops,
+};
+
+module_hda_codec_driver(cs421x_driver);
diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c
new file mode 100644
index 000000000000..8c703b714a71
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409-tables.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs8409-tables.c -- HD audio codec driver 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 "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, 0x0D },
+ { 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, 10000 },
+ { 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, 0x0D },
+ { 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, 10000 },
+ { 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, 0x0D },
+ { 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, 10000 },
+ { 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 hda_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),
+ SND_PCI_QUIRK(0x1028, 0x0C73, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C75, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7D, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7F, "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/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c
new file mode 100644
index 000000000000..2c02d3be89ee
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409.c
@@ -0,0 +1,1475 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver 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 "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;
+
+ guard(mutex)(&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;
+ }
+}
+
+/*
+ * 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;
+
+ guard(mutex)(&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);
+
+ return read_data & 0x0ff;
+
+error:
+ 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;
+
+ guard(mutex)(&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;
+ }
+
+ return 0;
+
+error:
+ 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;
+
+ guard(mutex)(&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;
+
+ return 0;
+
+error:
+ 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;
+
+ guard(mutex)(&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;
+ /* Certain use cases may require a delay
+ * after a write operation before proceeding.
+ */
+ if (seq[i].delay)
+ fsleep(seq[i].delay);
+ }
+
+ return 0;
+
+error:
+ 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 },
+ };
+ unsigned int fsv;
+
+ /* 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);
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
+
+ fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL);
+ if (cs42l42->full_scale_vol) {
+ // Set the full scale volume bit
+ fsv |= CS42L42_FULL_SCALE_VOL_MASK;
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
+ }
+ // Unmute analog channels A and B
+ fsv = (fsv & ~CS42L42_ANA_MUTE_AB);
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
+
+ /* 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);
+}
+
+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, 0x0D },
+ { 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);
+}
+
+static void cs8409_remove(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_remove(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);
+ }
+}
+
+static void cs8409_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->unsol_event)
+ spec->unsol_event(codec, res);
+ else
+ cs8409_cs42l42_jack_unsol_event(codec, res);
+}
+
+/* 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;
+}
+
+/* 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 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;
+
+ 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 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;
+ spec->gen.suppress_vmaster = 1;
+
+ spec->unsol_event = dolphin_jack_unsol_event;
+
+ /* 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 */
+ if (kctrl)
+ 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 cs8409_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ 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_remove(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ return 0;
+}
+
+static const struct hda_codec_ops cs8409_codec_ops = {
+ .probe = cs8409_probe,
+ .remove = cs8409_remove,
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_init,
+ .unsol_event = cs8409_unsol_event,
+ .suspend = cs8409_cs42l42_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+static const struct hda_device_id snd_hda_id_cs8409[] = {
+ HDA_CODEC_ID(0x10138409, "CS8409"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409);
+
+static struct hda_codec_driver cs8409_driver = {
+ .id = snd_hda_id_cs8409,
+ .ops = &cs8409_codec_ops,
+};
+module_hda_codec_driver(cs8409_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h
new file mode 100644
index 000000000000..7fe56f4a73bc
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409.h
@@ -0,0 +1,377 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio codec driver 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 "../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_ANA_MUTE_AB (0x0C)
+#define CS42L42_FULL_SCALE_VOL_MASK (2)
+#define CS42L42_FULL_SCALE_VOL_0DB (0)
+#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1)
+
+/* 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;
+ unsigned int delay;
+};
+
+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);
+ /* unsol_event op override */
+ void (*unsol_event)(struct hda_codec *codec, 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 hda_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/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c
new file mode 100644
index 000000000000..5c474ce44348
--- /dev/null
+++ b/sound/hda/codecs/cm9825.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CM9825 HD-audio codec
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+/* CM9825 Offset Definitions */
+
+#define CM9825_VERB_SET_HPF_1 0x781
+#define CM9825_VERB_SET_HPF_2 0x785
+#define CM9825_VERB_SET_PLL 0x7a0
+#define CM9825_VERB_SET_NEG 0x7a1
+#define CM9825_VERB_SET_ADCL 0x7a2
+#define CM9825_VERB_SET_DACL 0x7a3
+#define CM9825_VERB_SET_MBIAS 0x7a4
+#define CM9825_VERB_SET_VNEG 0x7a8
+#define CM9825_VERB_SET_D2S 0x7a9
+#define CM9825_VERB_SET_DACTRL 0x7aa
+#define CM9825_VERB_SET_PDNEG 0x7ac
+#define CM9825_VERB_SET_VDO 0x7ad
+#define CM9825_VERB_SET_CDALR 0x7b0
+#define CM9825_VERB_SET_MTCBA 0x7b1
+#define CM9825_VERB_SET_OTP 0x7b2
+#define CM9825_VERB_SET_OCP 0x7b3
+#define CM9825_VERB_SET_GAD 0x7b4
+#define CM9825_VERB_SET_TMOD 0x7b5
+#define CM9825_VERB_SET_SNR 0x7b6
+
+struct cmi_spec {
+ struct hda_gen_spec gen;
+ const struct hda_verb *chip_d0_verbs;
+ const struct hda_verb *chip_d3_verbs;
+ const struct hda_verb *chip_hp_present_verbs;
+ const struct hda_verb *chip_hp_remove_verbs;
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+ int quirk;
+};
+
+static const struct hda_verb cm9825_std_d3_verbs[] = {
+ /* chip sleep verbs */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */
+ {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */
+ {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */
+ {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */
+ {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */
+ {}
+};
+
+static const struct hda_verb cm9825_std_d0_verbs[] = {
+ /* chip init verbs */
+ {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */
+ {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */
+ {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */
+ {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */
+ {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */
+ {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */
+ {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */
+ {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */
+ {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */
+ {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */
+ {0x3C, AC_VERB_SET_AMP_GAIN_MUTE |
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */
+ {0x3C, AC_VERB_SET_AMP_GAIN_MUTE |
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */
+ {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */
+ {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */
+ {}
+};
+
+static const struct hda_verb cm9825_hp_present_verbs[] = {
+ {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */
+ {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */
+ {}
+};
+
+static const struct hda_verb cm9825_hp_remove_verbs[] = {
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */
+ {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */
+ {}
+};
+
+static void cm9825_unsol_hp_delayed(struct work_struct *work)
+{
+ struct cmi_spec *spec =
+ container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work);
+ struct hda_jack_tbl *jack;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_jack_plugin = false;
+ int err = 0;
+
+ hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin);
+
+ codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n",
+ (int)hp_jack_plugin, hp_pin);
+
+ if (!hp_jack_plugin) {
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+ if (err)
+ codec_dbg(spec->codec, "codec_write err %d\n", err);
+
+ snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs);
+ } else {
+ snd_hda_sequence_write(spec->codec,
+ spec->chip_hp_present_verbs);
+ }
+
+ jack = snd_hda_jack_tbl_get(spec->codec, hp_pin);
+ if (jack) {
+ jack->block_report = 0;
+ snd_hda_jack_report_sync(spec->codec);
+ }
+}
+
+static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct cmi_spec *spec = codec->spec;
+ struct hda_jack_tbl *tbl;
+
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+
+ codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid);
+
+ 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(200));
+}
+
+static void cm9825_setup_unsol(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+
+ snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback);
+}
+
+static int cm9825_init(struct hda_codec *codec)
+{
+ snd_hda_gen_init(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+
+static void cm9825_remove(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ snd_hda_gen_remove(codec);
+}
+
+static int cm9825_suspend(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+
+ snd_hda_sequence_write(codec, spec->chip_d3_verbs);
+
+ return 0;
+}
+
+static int cm9825_resume(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ hda_nid_t hp_pin = 0;
+ bool hp_jack_plugin = false;
+ int err;
+
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+ if (err)
+ codec_dbg(codec, "codec_write err %d\n", err);
+
+ msleep(150); /* for depop noise */
+
+ snd_hda_codec_init(codec);
+
+ hp_pin = spec->gen.autocfg.hp_pins[0];
+ hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin);
+
+ codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n",
+ (int)hp_jack_plugin, hp_pin);
+
+ if (!hp_jack_plugin) {
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+ if (err)
+ codec_dbg(codec, "codec_write err %d\n", err);
+
+ snd_hda_sequence_write(codec, cm9825_hp_remove_verbs);
+ }
+
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+
+ return 0;
+}
+
+static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct cmi_spec *spec;
+ struct auto_pin_cfg *cfg;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed);
+ codec->spec = spec;
+ spec->codec = codec;
+ cfg = &spec->gen.autocfg;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->chip_d0_verbs = cm9825_std_d0_verbs;
+ spec->chip_d3_verbs = cm9825_std_d3_verbs;
+ spec->chip_hp_present_verbs = cm9825_hp_present_verbs;
+ spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs;
+
+ snd_hda_sequence_write(codec, spec->chip_d0_verbs);
+
+ 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;
+
+ cm9825_setup_unsol(codec);
+
+ return 0;
+
+ error:
+ cm9825_remove(codec);
+
+ codec_info(codec, "Enter err %d\n", err);
+
+ return err;
+}
+
+static const struct hda_codec_ops cm9825_codec_ops = {
+ .probe = cm9825_probe,
+ .remove = cm9825_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cm9825_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cm9825_suspend,
+ .resume = cm9825_resume,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cm9825[] = {
+ HDA_CODEC_ID(0x13f69825, "CM9825"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cm9825);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CM9825 HD-audio codec");
+
+static struct hda_codec_driver cm9825_driver = {
+ .id = snd_hda_id_cm9825,
+ .ops = &cm9825_codec_ops,
+};
+
+module_hda_codec_driver(cm9825_driver);
diff --git a/sound/hda/codecs/cmedia.c b/sound/hda/codecs/cmedia.c
new file mode 100644
index 000000000000..15e5a1118a6e
--- /dev/null
+++ b/sound/hda/codecs/cmedia.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal codec driver for Intel High Definition Audio Codec
+ *
+ * HD audio codec driver for C-Media CMI9880
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct hda_gen_spec *spec;
+ struct auto_pin_cfg *cfg;
+ bool is_cmi8888 = id->vendor_id == 0x13f68888;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ cfg = &spec->autocfg;
+ snd_hda_gen_spec_init(spec);
+
+ if (is_cmi8888) {
+ /* mask NID 0x10 from the playback volume selection;
+ * it's a headphone boost volume handled manually below
+ */
+ spec->out_vol_mask = (1ULL << 0x10);
+ }
+
+ 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;
+
+ 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 (is_cmi8888) {
+ 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, NULL, &amp_kctl)) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+ }
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cmedia_codec_ops = {
+ .probe = cmedia_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ID(0x13f68888, "CMI8888"),
+ HDA_CODEC_ID(0x13f69880, "CMI9880"),
+ HDA_CODEC_ID(0x434d4980, "CMI9880"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("C-Media HD-audio codec");
+
+static struct hda_codec_driver cmedia_driver = {
+ .id = snd_hda_id_cmedia,
+ .ops = &cmedia_codec_ops,
+};
+
+module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/hda/codecs/conexant.c
index 37e1cf8218ff..5fcbc1312c69 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/hda/codecs/conexant.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HD audio interface patch for Conexant HDA audio codec
+ * HD audio codec driver 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>
@@ -27,18 +14,16 @@
#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"
#include "hda_jack.h"
-#include "hda_generic.h"
+#include "generic.h"
struct conexant_spec {
struct hda_gen_spec gen;
- unsigned int beep_amp;
-
/* extra EAPD pins */
unsigned int num_eapds;
hda_nid_t eapds[4];
@@ -47,7 +32,7 @@ struct conexant_spec {
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
- /* OPLC XO specific */
+ /* OLPC XO specific */
bool recording;
bool dc_enable;
unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
@@ -57,70 +42,53 @@ struct conexant_spec {
unsigned int gpio_led;
unsigned int gpio_mute_led_mask;
unsigned int gpio_mic_led_mask;
-
+ bool is_cx11880_sn6140;
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
- int idx, int dir)
-{
- spec->gen.beep_nid = nid;
- spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
-}
-/* additional beep mixers; the actual parameters are overwritten at build */
+/* 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 */
};
-/* create beep controls if needed */
-static int add_beep_ctls(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;
- int err;
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
- if (spec->beep_amp) {
- const 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;
}
return 0;
}
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define add_beep_ctls(codec) 0
-#endif
-/*
- * Automatic parser for CX20641 & co
- */
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(struct hda_codec *codec)
+static int cx_auto_parse_beep(struct hda_codec *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) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
- break;
- }
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
}
#else
-#define cx_auto_parse_beep(codec)
+#define cx_auto_parse_beep(codec) 0
#endif
+/*
+ * Automatic parser for CX20641 & co
+ */
+
/* parse EAPDs */
static void cx_auto_parse_eapd(struct hda_codec *codec)
{
@@ -148,7 +116,7 @@ static void cx_auto_parse_eapd(struct hda_codec *codec)
}
static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on)
+ const hda_nid_t *pins, bool on)
{
int i;
for (i = 0; i < num_pins; i++) {
@@ -169,81 +137,132 @@ static void cx_auto_vmaster_hook(void *private_data, int enabled)
}
/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
-static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
+static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- struct hda_codec *codec = private_data;
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct conexant_spec *spec = codec->spec;
snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
AC_VERB_SET_EAPD_BTLENABLE,
- enabled ? 0x00 : 0x02);
+ brightness ? 0x02 : 0x00);
+ return 0;
}
-static int cx_auto_build_controls(struct hda_codec *codec)
+static void cxt_init_gpio_led(struct hda_codec *codec)
{
- int err;
+ struct conexant_spec *spec = codec->spec;
+ unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
+ 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);
+ }
+}
- err = add_beep_ctls(codec);
- if (err < 0)
- return err;
+static void cx_fixup_headset_recog(struct hda_codec *codec)
+{
+ unsigned int mic_present;
- return 0;
+ /* fix some headset type recognize fail issue, such as EDIFIER headset */
+ /* set micbias output current comparator threshold from 66% to 55%. */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
+ /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register
+ * value adjustment trim from 2.2K ohms to 2.0K ohms.
+ */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10);
+ /* fix reboot headset type recognize fail issue */
+ mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (mic_present & AC_PINSENSE_PRESENCE)
+ /* enable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ else
+ /* disable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
}
-static int cx_auto_init(struct hda_codec *codec)
+static int cx_init(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
snd_hda_gen_init(codec);
if (!spec->dynamic_eapd)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+ cxt_init_gpio_led(codec);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+ if (spec->is_cx11880_sn6140)
+ cx_fixup_headset_recog(codec);
+
return 0;
}
-static void cx_auto_reboot_notify(struct hda_codec *codec)
+static void cx_auto_shutdown(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- switch (codec->core.vendor_id) {
- case 0x14f150f2: /* CX20722 */
- case 0x14f150f4: /* CX20724 */
- break;
- default:
- return;
- }
-
- /* Turn the CX20722 codec into D3 to avoid spurious noises
+ /* 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);
+}
- snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
- snd_hda_codec_write(codec, codec->core.afg, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+static void cx_remove(struct hda_codec *codec)
+{
+ cx_auto_shutdown(codec);
+ snd_hda_gen_remove(codec);
}
-static void cx_auto_free(struct hda_codec *codec)
+static void cx_process_headset_plugin(struct hda_codec *codec)
{
- cx_auto_reboot_notify(codec);
- snd_hda_gen_free(codec);
+ unsigned int val;
+ unsigned int count = 0;
+
+ /* Wait headset detect done. */
+ do {
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xca0, 0x0);
+ if (val & 0x080) {
+ codec_dbg(codec, "headset type detect done!\n");
+ break;
+ }
+ msleep(20);
+ count++;
+ } while (count < 3);
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0);
+ if (val & 0x800) {
+ codec_dbg(codec, "headset plugin, type is CTIA\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else if (val & 0x400) {
+ codec_dbg(codec, "headset plugin, type is OMTP\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else {
+ codec_dbg(codec, "headphone plugin\n");
+ }
}
-static const struct hda_codec_ops cx_auto_patch_ops = {
- .build_controls = cx_auto_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = cx_auto_init,
- .reboot_notify = cx_auto_reboot_notify,
- .free = cx_auto_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .check_power_status = snd_hda_gen_check_power_status,
-#endif
-};
+static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_callback *event)
+{
+ unsigned int mic_present;
+
+ /* In cx11880 and sn6140, the node 16 can only be configured to headphone or disabled,
+ * the node 19 can only be configured to microphone or disabled.
+ * Check hp&mic tag to process headset plugin & plugout.
+ */
+ mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
+ else
+ cx_process_headset_plugin(codec);
+}
+
+static int cx_suspend(struct hda_codec *codec)
+{
+ cx_auto_shutdown(codec);
+ return 0;
+}
/*
* pin fix-up
@@ -255,12 +274,14 @@ enum {
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_LENOVO_XPAD_ACPI,
CXT_FIXUP_OLPC_XO,
CXT_FIXUP_CAP_MIX_AMP,
CXT_FIXUP_TOSHIBA_P105,
@@ -271,12 +292,20 @@ enum {
CXT_FIXUP_HP_SPECTRE,
CXT_FIXUP_HP_GATE_MIC,
CXT_FIXUP_MUTE_LED_GPIO,
+ CXT_FIXUP_HP_ELITEONE_OUT_DIS,
+ CXT_FIXUP_HP_ZBOOK_MUTE_LED,
CXT_FIXUP_HEADSET_MIC,
CXT_FIXUP_HP_MIC_NO_PRESENCE,
+ CXT_PINCFG_SWS_JS201D,
+ CXT_PINCFG_TOP_SPEAKER,
+ CXT_FIXUP_HP_A_U,
};
/* for hda_fixup_thinkpad_acpi() */
-#include "thinkpad_helper.c"
+#include "helpers/thinkpad.c"
+
+/* for hda_fixup_ideapad_acpi() */
+#include "helpers/ideapad_hotkey_led.c"
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
@@ -285,6 +314,19 @@ static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
spec->gen.inv_dmic_split = 1;
}
+/* fix widget control pin settings */
+static void cxt_fixup_update_pinctl(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* Unset OUT_EN for this Node pin, leaving only HP_EN.
+ * This is the value stored in the codec register after
+ * the correct initialization of the previous windows boot.
+ */
+ snd_hda_set_pin_ctl_cache(codec, 0x1d, AC_PINCTL_HP_EN);
+ }
+}
+
static void cxt5066_increase_mic_boost(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -343,6 +385,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
break;
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;
@@ -364,7 +407,7 @@ static void cxt_fixup_headset_mic(struct hda_codec *codec,
}
}
-/* OPLC XO 1.5 fixup */
+/* OLPC XO 1.5 fixup */
/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
* through the microphone jack.
@@ -374,7 +417,7 @@ static void cxt_fixup_headset_mic(struct hda_codec *codec,
* control. */
#define update_mic_pin(codec, nid, val) \
- snd_hda_codec_update_cache(codec, nid, 0, \
+ snd_hda_codec_write_cache(codec, nid, 0, \
AC_VERB_SET_PIN_WIDGET_CONTROL, val)
static const struct hda_input_mux olpc_xo_dc_bias = {
@@ -588,6 +631,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctl;
int i;
if (action != HDA_FIXUP_ACT_PROBE)
@@ -606,9 +650,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
/* override mic boost control */
- for (i = 0; i < spec->gen.kctls.used; i++) {
- struct snd_kcontrol_new *kctl =
- snd_array_elem(&spec->gen.kctls, i);
+ snd_array_for_each(&spec->gen.kctls, i, kctl) {
if (!strcmp(kctl->name, "Mic Boost Volume")) {
kctl->put = olpc_xo_mic_boost_put;
break;
@@ -623,8 +665,8 @@ static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mute_led_eapd = 0x1b;
- spec->dynamic_eapd = 1;
- spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
+ spec->dynamic_eapd = true;
+ snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led);
}
}
@@ -669,71 +711,98 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
/* update LED status via GPIO */
static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
- bool enabled)
+ bool led_on)
{
struct conexant_spec *spec = codec->spec;
unsigned int oldval = spec->gpio_led;
if (spec->mute_led_polarity)
- enabled = !enabled;
+ led_on = !led_on;
- if (enabled)
- spec->gpio_led &= ~mask;
- else
+ 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);
}
/* turn on/off mute LED via GPIO per vmaster hook */
-static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
+static int cxt_gpio_mute_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- struct hda_codec *codec = private_data;
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct conexant_spec *spec = codec->spec;
- cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
+ cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness);
+ return 0;
}
/* turn on/off mic-mute LED via GPIO per capture hook */
-static void cxt_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+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;
+
+ cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness);
+ return 0;
+}
+
+static void cxt_setup_mute_led(struct hda_codec *codec,
+ unsigned int mute, unsigned int mic_mute)
{
struct conexant_spec *spec = codec->spec;
- if (ucontrol)
- cxt_update_gpio_led(codec, spec->gpio_mic_led_mask,
- ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1]);
+ 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;
+ }
+ if (mic_mute) {
+ snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update);
+ spec->gpio_mic_led_mask = mic_mute;
+ }
}
+static void cxt_setup_gpio_unmute(struct hda_codec *codec,
+ unsigned int gpio_mute_mask)
+{
+ if (gpio_mute_mask) {
+ // set gpio data to 0.
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0);
+ }
+}
static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
- struct conexant_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x03 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 },
- {}
- };
- codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x01, 0x02);
+}
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = cxt_fixup_gpio_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x01;
- spec->gpio_mic_led_mask = 0x02;
- }
- snd_hda_add_verbs(codec, gpio_init);
- if (spec->gpio_led)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_led);
+static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x10, 0x20);
}
+static void cxt_fixup_hp_a_u(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ // Init vers in BIOS mute the spk/hp by set gpio high to avoid pop noise,
+ // so need to unmute once by clearing the gpio data when runs into the system.
+ if (action == HDA_FIXUP_ACT_INIT)
+ cxt_setup_gpio_unmute(codec, 0x2);
+}
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
@@ -764,6 +833,17 @@ static const struct hda_pintbl cxt_pincfg_lemote[] = {
{}
};
+/* SuoWoSi/South-holding JS201D with sn6140 */
+static const struct hda_pintbl cxt_pincfg_sws_js201d[] = {
+ { 0x16, 0x03211040 }, /* hp out */
+ { 0x17, 0x91170110 }, /* SPK/Class_D */
+ { 0x18, 0x95a70130 }, /* Internal mic */
+ { 0x19, 0x03a11020 }, /* Headset Mic */
+ { 0x1a, 0x40f001f0 }, /* Not used */
+ { 0x21, 0x40f001f0 }, /* Not used */
+ {}
+};
+
static const struct hda_fixup cxt_fixups[] = {
[CXT_PINCFG_LENOVO_X200] = {
.type = HDA_FIXUP_PINS,
@@ -798,6 +878,14 @@ static const struct hda_fixup cxt_fixups[] = {
.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,
@@ -834,6 +922,12 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = hda_fixup_thinkpad_acpi,
},
+ [CXT_FIXUP_LENOVO_XPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_ideapad_acpi,
+ .chained = true,
+ .chain_id = CXT_FIXUP_THINKPAD_ACPI,
+ },
[CXT_FIXUP_OLPC_XO] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_olpc_xo,
@@ -894,6 +988,14 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_mute_led_gpio,
},
+ [CXT_FIXUP_HP_ELITEONE_OUT_DIS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_update_pinctl,
+ },
+ [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,
@@ -907,9 +1009,24 @@ static const struct hda_fixup cxt_fixups[] = {
.chained = true,
.chain_id = CXT_FIXUP_HEADSET_MIC,
},
+ [CXT_PINCFG_SWS_JS201D] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_sws_js201d,
+ },
+ [CXT_PINCFG_TOP_SPEAKER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x82170111 },
+ { }
+ },
+ },
+ [CXT_FIXUP_HP_A_U] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_a_u,
+ },
};
-static const struct snd_pci_quirk cxt5045_fixups[] = {
+static const struct hda_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
@@ -929,7 +1046,7 @@ static const struct hda_model_fixup cxt5045_fixup_models[] = {
{}
};
-static const struct snd_pci_quirk cxt5047_fixups[] = {
+static const struct hda_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),
@@ -941,7 +1058,7 @@ static const struct hda_model_fixup cxt5047_fixup_models[] = {
{}
};
-static const struct snd_pci_quirk cxt5051_fixups[] = {
+static const struct hda_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),
{}
@@ -952,35 +1069,62 @@ static const struct hda_model_fixup cxt5051_fixup_models[] = {
{}
};
-static const struct snd_pci_quirk cxt5066_fixups[] = {
+static const struct hda_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, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+ 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, 0x8231, "HP ProBook 450 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, 0x83e5, "HP EliteOne 1000 G2", CXT_FIXUP_HP_ELITEONE_OUT_DIS),
+ 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(0x14f1, 0x0252, "MBX-Z60MR100", CXT_FIXUP_HP_A_U),
+ SND_PCI_QUIRK(0x14f1, 0x0265, "SWS JS201D", CXT_PINCFG_SWS_JS201D),
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_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI),
SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
+ HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER),
+ HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER),
{}
};
@@ -990,12 +1134,19 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .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_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" },
{ .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" },
+ { .id = CXT_PINCFG_SWS_JS201D, .name = "sws-js201d" },
+ { .id = CXT_PINCFG_TOP_SPEAKER, .name = "sirius-top-speaker" },
+ { .id = CXT_FIXUP_HP_A_U, .name = "HP-U-support" },
{}
};
@@ -1005,10 +1156,10 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
static void add_cx5051_fake_mutes(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- static hda_nid_t out_nids[] = {
+ static const hda_nid_t out_nids[] = {
0x10, 0x11, 0
};
- hda_nid_t *p;
+ const hda_nid_t *p;
for (p = out_nids; *p; p++)
snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
@@ -1017,7 +1168,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec)
spec->gen.dac_min_mute = true;
}
-static int patch_conexant_auto(struct hda_codec *codec)
+static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
struct conexant_spec *spec;
int err;
@@ -1029,13 +1180,18 @@ static int patch_conexant_auto(struct hda_codec *codec)
return -ENOMEM;
snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
- codec->patch_ops = cx_auto_patch_ops;
- cx_auto_parse_beep(codec);
+ /* init cx11880/sn6140 flag and reset headset_present_flag */
+ switch (codec->core.vendor_id) {
+ case 0x14f11f86:
+ case 0x14f11f87:
+ spec->is_cx11880_sn6140 = true;
+ snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref);
+ break;
+ }
+
cx_auto_parse_eapd(codec);
spec->gen.own_eapd_ctl = 1;
- if (spec->dynamic_eapd)
- spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
switch (codec->core.vendor_id) {
case 0x14f15045:
@@ -1058,9 +1214,16 @@ static int patch_conexant_auto(struct hda_codec *codec)
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;
- /* Fall through */
+ fallthrough;
default:
codec->pin_amp_workaround = 1;
snd_hda_pick_fixup(codec, cxt5066_fixup_models,
@@ -1068,17 +1231,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
break;
}
- /* Show mute-led control only on HP laptops
- * This is a sort of white-list: on HP laptops, EAPD corresponds
- * only to the mute-LED without actualy amp function. Meanwhile,
- * others may use EAPD really as an amp switch, so it might be
- * not good to expose it blindly.
- */
- switch (codec->core.subsystem_id >> 16) {
- case 0x103c:
- spec->gen.vmaster_mute_enum = 1;
- break;
- }
+ if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd)
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -1087,6 +1241,10 @@ static int patch_conexant_auto(struct hda_codec *codec)
if (err < 0)
goto error;
+ err = cx_auto_parse_beep(codec);
+ if (err < 0)
+ goto error;
+
err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
goto error;
@@ -1107,43 +1265,59 @@ static int patch_conexant_auto(struct hda_codec *codec)
return 0;
error:
- cx_auto_free(codec);
+ cx_remove(codec);
return err;
}
+static const struct hda_codec_ops cx_codec_ops = {
+ .probe = cx_probe,
+ .remove = cx_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cx_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cx_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
/*
*/
static const struct hda_device_id snd_hda_id_conexant[] = {
- HDA_CODEC_ENTRY(0x14f12008, "CX8200", 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),
+ HDA_CODEC_ID(0x14f11f86, "CX11880"),
+ HDA_CODEC_ID(0x14f11f87, "SN6140"),
+ HDA_CODEC_ID(0x14f12008, "CX8200"),
+ HDA_CODEC_ID(0x14f120d0, "CX11970"),
+ HDA_CODEC_ID(0x14f120d1, "SN6180"),
+ HDA_CODEC_ID(0x14f15045, "CX20549 (Venice)"),
+ HDA_CODEC_ID(0x14f15047, "CX20551 (Waikiki)"),
+ HDA_CODEC_ID(0x14f15051, "CX20561 (Hermosa)"),
+ HDA_CODEC_ID(0x14f15066, "CX20582 (Pebble)"),
+ HDA_CODEC_ID(0x14f15067, "CX20583 (Pebble HSF)"),
+ HDA_CODEC_ID(0x14f15068, "CX20584"),
+ HDA_CODEC_ID(0x14f15069, "CX20585"),
+ HDA_CODEC_ID(0x14f1506c, "CX20588"),
+ HDA_CODEC_ID(0x14f1506e, "CX20590"),
+ HDA_CODEC_ID(0x14f15097, "CX20631"),
+ HDA_CODEC_ID(0x14f15098, "CX20632"),
+ HDA_CODEC_ID(0x14f150a1, "CX20641"),
+ HDA_CODEC_ID(0x14f150a2, "CX20642"),
+ HDA_CODEC_ID(0x14f150ab, "CX20651"),
+ HDA_CODEC_ID(0x14f150ac, "CX20652"),
+ HDA_CODEC_ID(0x14f150b8, "CX20664"),
+ HDA_CODEC_ID(0x14f150b9, "CX20665"),
+ HDA_CODEC_ID(0x14f150f1, "CX21722"),
+ HDA_CODEC_ID(0x14f150f2, "CX20722"),
+ HDA_CODEC_ID(0x14f150f3, "CX21724"),
+ HDA_CODEC_ID(0x14f150f4, "CX20724"),
+ HDA_CODEC_ID(0x14f1510f, "CX20751/2"),
+ HDA_CODEC_ID(0x14f15110, "CX20751/2"),
+ HDA_CODEC_ID(0x14f15111, "CX20753/4"),
+ HDA_CODEC_ID(0x14f15113, "CX20755"),
+ HDA_CODEC_ID(0x14f15114, "CX20756"),
+ HDA_CODEC_ID(0x14f15115, "CX20757"),
+ HDA_CODEC_ID(0x14f151d7, "CX20952"),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
@@ -1153,6 +1327,7 @@ MODULE_DESCRIPTION("Conexant HD-audio codec");
static struct hda_codec_driver conexant_driver = {
.id = snd_hda_id_conexant,
+ .ops = &cx_codec_ops,
};
module_hda_codec_driver(conexant_driver);
diff --git a/sound/pci/hda/hda_generic.c b/sound/hda/codecs/generic.c
index 5cc65093d941..7bcf9aef8275 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/hda/codecs/generic.c
@@ -1,23 +1,10 @@
+// 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>
@@ -29,15 +16,16 @@
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/module.h>
+#include <linux/leds.h>
#include <sound/core.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_jack.h"
#include "hda_beep.h"
-#include "hda_generic.h"
+#include "generic.h"
/**
@@ -103,6 +91,12 @@ static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
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
}
/*
@@ -209,7 +203,7 @@ static void parse_user_hints(struct hda_codec *codec)
*/
#define update_pin_ctl(codec, pin, val) \
- snd_hda_codec_update_cache(codec, pin, 0, \
+ snd_hda_codec_write_cache(codec, pin, 0, \
AC_VERB_SET_PIN_WIDGET_CONTROL, val)
/* restore the pinctl based on the cached value */
@@ -264,10 +258,10 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
int anchor_nid)
{
struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
int i;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if (path->depth <= 0)
continue;
if ((!from_nid || path->path[0] == from_nid) &&
@@ -325,10 +319,10 @@ EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
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;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if (path->path[0] == nid)
return true;
}
@@ -351,11 +345,11 @@ static bool is_reachable_path(struct hda_codec *codec,
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;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
return true;
}
@@ -638,13 +632,13 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
{
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;
- for (n = 0; n < spec->paths.used; n++) {
- struct nid_path *path = snd_array_elem(&spec->paths, n);
+ snd_array_for_each(&spec->paths, n, path) {
if (!path->active)
continue;
if (codec->power_save_node) {
@@ -825,7 +819,7 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
}
}
-/* sync power of each widget in the the given path */
+/* 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)
@@ -898,7 +892,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
hda_nid_t nid = path->path[i];
if (enable && path->multi[i])
- snd_hda_codec_update_cache(codec, nid, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL,
path->idx[i]);
if (has_amp_in(codec, path, i))
@@ -930,7 +924,7 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
return;
if (codec->inv_eapd)
enable = !enable;
- snd_hda_codec_update_cache(codec, pin, 0,
+ snd_hda_codec_write_cache(codec, pin, 0,
AC_VERB_SET_EAPD_BTLENABLE,
enable ? 0x02 : 0x00);
}
@@ -993,6 +987,8 @@ add_control(struct hda_gen_spec *spec, int type, const char *name,
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;
}
@@ -1002,7 +998,11 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
const char *sfx, int cidx, unsigned long val)
{
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ int len;
+
+ len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ if (snd_BUG_ON(len >= sizeof(name)))
+ return -EINVAL;
if (!add_control(spec, type, name, cidx, val))
return -ENOMEM;
return 0;
@@ -1118,12 +1118,11 @@ static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
unsigned long pval;
int err;
- mutex_lock(&codec->control_mutex);
+ guard(mutex)(&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;
}
@@ -1136,7 +1135,7 @@ static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
sync_auto_mute_bits(kcontrol, ucontrol);
- mutex_lock(&codec->control_mutex);
+ guard(mutex)(&codec->control_mutex);
pval = kcontrol->private_value;
indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
for (i = 0; i < indices; i++) {
@@ -1148,7 +1147,6 @@ static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
change |= err;
}
kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
return err < 0 ? err : change;
}
@@ -1159,8 +1157,8 @@ static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
return path && path->ctls[ctl_type];
}
-static const char * const channel_name[4] = {
- "Front", "Surround", "CLFE", "Side"
+static const char * const channel_name[] = {
+ "Front", "Surround", "CLFE", "Side", "Back",
};
/* give some appropriate ctl name prefix for the given line out channel */
@@ -1186,7 +1184,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
/* multi-io channels */
if (ch >= cfg->line_outs)
- return channel_name[ch];
+ goto fixed_name;
switch (cfg->line_out_type) {
case AUTO_PIN_SPEAKER_OUT:
@@ -1214,11 +1212,17 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
*index = ch;
return "Headphone";
case AUTO_PIN_LINE_OUT:
- /* This deals with the case where we have two DACs and
- * one LO, one HP and one Speaker */
- if (!ch && cfg->speaker_outs && cfg->hp_outs) {
- bool hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type);
- bool spk_lo_shared = !path_has_mixer(codec, spec->speaker_paths[0], ctl_type);
+ /* 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)
@@ -1232,6 +1236,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
if (cfg->line_outs == 1 && !spec->multi_ios)
return "Line Out";
+ fixed_name:
if (ch >= ARRAY_SIZE(channel_name)) {
snd_BUG();
return "PCM";
@@ -1376,16 +1381,20 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
struct nid_path *path;
hda_nid_t pin = pins[i];
- path = snd_hda_get_path_from_idx(codec, path_idx[i]);
- if (path) {
- badness += assign_out_path_ctls(codec, path);
- continue;
+ if (!spec->preferred_dacs) {
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (path) {
+ badness += assign_out_path_ctls(codec, path);
+ continue;
+ }
}
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->preferred_dacs) {
+ badness += BAD_NO_PRIMARY_DAC;
}
if (!dacs[i])
@@ -1433,7 +1442,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
path = snd_hda_add_new_path(codec, dac, pin, 0);
}
if (!path) {
- dac = dacs[i] = 0;
+ dacs[i] = 0;
badness += bad->no_dac;
} else {
/* print_nid_path(codec, "output", path); */
@@ -1975,7 +1984,7 @@ 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;
+ struct auto_pin_cfg *best_cfg __free(kfree) = NULL;
unsigned int val;
int best_badness = INT_MAX;
int badness;
@@ -1991,10 +2000,8 @@ static int parse_output_paths(struct hda_codec *codec)
for (;;) {
badness = fill_and_eval_dacs(codec, fill_hardwired,
fill_mio_first);
- if (badness < 0) {
- kfree(best_cfg);
+ if (badness < 0)
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);
@@ -2065,7 +2072,7 @@ static int parse_output_paths(struct hda_codec *codec)
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, spec->vmaster_tlv);
if (spec->dac_min_mute)
- spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+ spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
}
}
@@ -2087,7 +2094,6 @@ static int parse_output_paths(struct hda_codec *codec)
if (spec->indep_hp && !indep_hp_possible(codec))
spec->indep_hp = 0;
- kfree(best_cfg);
return 0;
}
@@ -2238,11 +2244,9 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol,
unsigned int select = ucontrol->value.enumerated.item[0];
int ret = 0;
- mutex_lock(&spec->pcm_mutex);
- if (spec->active_streams) {
- ret = -EBUSY;
- goto unlock;
- }
+ guard(mutex)(&spec->pcm_mutex);
+ if (spec->active_streams)
+ return -EBUSY;
if (spec->indep_hp_enabled != select) {
hda_nid_t *dacp;
@@ -2274,8 +2278,6 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol,
call_hp_automute(codec, NULL);
ret = 1;
}
- unlock:
- mutex_unlock(&spec->pcm_mutex);
return ret;
}
@@ -2696,10 +2698,10 @@ static const struct snd_kcontrol_new out_jack_mode_enum = {
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;
- for (i = 0; i < spec->kctls.used; i++) {
- struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
+ snd_array_for_each(&spec->kctls, i, kctl) {
if (!strcmp(kctl->name, name) && kctl->index == idx)
return true;
}
@@ -2817,7 +2819,7 @@ static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
vref_texts);
/* set the right text */
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
return 0;
}
@@ -2930,7 +2932,7 @@ static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
text = "Mic In";
}
- strcpy(uinfo->value.enumerated.name, text);
+ strscpy(uinfo->value.enumerated.name, text);
return 0;
}
@@ -3460,22 +3462,24 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
struct hda_gen_spec *spec = codec->spec;
const struct hda_input_mux *imux;
struct nid_path *path;
- int i, adc_idx, err = 0;
+ int i, adc_idx, ret, err = 0;
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];
- err = func(kcontrol, ucontrol);
- if (err < 0)
- break;
+ scoped_guard(mutex, &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)
+ return ret;
+ if (ret > 0)
+ err = 1;
+ }
}
- mutex_unlock(&codec->control_mutex);
- if (err >= 0 && spec->cap_sync_hook)
+ if (spec->cap_sync_hook)
spec->cap_sync_hook(codec, kcontrol, ucontrol);
return err;
}
@@ -3520,6 +3524,7 @@ static int cap_sw_put(struct snd_kcontrol *kcontrol,
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,
@@ -3626,8 +3631,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
amp_val_replace_channels(ctl, chs));
if (!knew)
return -ENOMEM;
- if (is_switch)
+ 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;
@@ -3642,8 +3650,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
amp_val_replace_channels(ctl, 2));
if (!knew)
return -ENOMEM;
- if (is_switch)
+ if (is_switch) {
knew->put = cap_single_sw_put;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
return 0;
}
@@ -3684,6 +3695,8 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
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;
}
@@ -3899,6 +3912,101 @@ static int parse_mic_boost(struct hda_codec *codec)
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;
+
+ 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->flags = LED_CORE_SUSPENDRESUME;
+
+ err = led_classdev_register(&codec->core.dev, cdev);
+ if (err < 0)
+ return err;
+ spec->led_cdevs[idx] = cdev;
+ 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;
+
+ 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 digital I/Os and set up NIDs in BIOS auto-parse mode
*/
@@ -3909,7 +4017,7 @@ static void parse_digital(struct hda_codec *codec)
int i, nums;
hda_nid_t dig_nid, pin;
- /* support multiple SPDIFs; the secondary is set up as a slave */
+ /* 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];
@@ -3928,10 +4036,10 @@ static void parse_digital(struct hda_codec *codec)
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 (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ spec->multiout.follower_dig_outs = spec->follower_dig_outs;
+ if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1)
break;
- spec->slave_dig_outs[nums - 1] = dig_nid;
+ spec->follower_dig_outs[nums - 1] = dig_nid;
}
nums++;
}
@@ -4021,8 +4129,7 @@ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
struct nid_path *path;
int n;
- for (n = 0; n < spec->paths.used; n++) {
- path = snd_array_elem(&spec->paths, n);
+ snd_array_for_each(&spec->paths, n, path) {
if (!path->depth)
continue;
if (path->path[0] == nid ||
@@ -4248,7 +4355,7 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
*/
/* 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, hda_nid_t *pins)
+static bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins)
{
int i;
bool present = false;
@@ -4267,7 +4374,7 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
}
/* standard HP/line-out auto-mute helper */
-static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+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;
@@ -4387,7 +4494,7 @@ static void call_update_outputs(struct hda_codec *codec)
else
snd_hda_gen_update_outputs(codec);
- /* sync the whole vmaster slaves to reflect the new auto-mute status */
+ /* 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);
}
@@ -4828,7 +4935,7 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
* @nid: audio widget
* @on: power on/off flag
*
- * Set this in patch_ops.stream_pm. Only valid with power_save_node flag.
+ * Set this in hda_codec_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)
{
@@ -4837,6 +4944,69 @@ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
}
EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
+/* forcibly mute the speaker output without caching; return true if updated */
+static bool force_mute_output_path(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return false;
+ if (!nid_has_mute(codec, nid, HDA_OUTPUT))
+ return false; /* no mute, skip */
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+ snd_hda_codec_amp_read(codec, nid, 1, HDA_OUTPUT, 0) &
+ HDA_AMP_MUTE)
+ return false; /* both channels already muted, skip */
+
+ /* direct amp update without caching */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT |
+ AC_AMP_SET_RIGHT | HDA_AMP_MUTE);
+ return true;
+}
+
+/**
+ * snd_hda_gen_shutup_speakers - Forcibly mute the speaker outputs
+ * @codec: the HDA codec
+ *
+ * Forcibly mute the speaker outputs, to be called at suspend or shutdown.
+ *
+ * The mute state done by this function isn't cached, hence the original state
+ * will be restored at resume.
+ *
+ * Return true if the mute state has been changed.
+ */
+bool snd_hda_gen_shutup_speakers(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const int *paths;
+ const struct nid_path *path;
+ int i, p, num_paths;
+ bool updated = false;
+
+ /* if already powered off, do nothing */
+ if (!snd_hdac_is_power_on(&codec->core))
+ return false;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ paths = spec->out_paths;
+ num_paths = spec->autocfg.line_outs;
+ } else {
+ paths = spec->speaker_paths;
+ num_paths = spec->autocfg.speaker_outs;
+ }
+
+ for (i = 0; i < num_paths; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ for (p = 0; p < path->depth; p++)
+ if (force_mute_output_path(codec, path->path[p]))
+ updated = true;
+ }
+
+ return updated;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_shutup_speakers);
+
/**
* snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
* set up the hda_gen_spec
@@ -4854,6 +5024,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
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;
@@ -5006,8 +5179,6 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *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) {
@@ -5031,8 +5202,8 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config);
* Build control elements
*/
-/* slave controls for virtual master */
-static const char * const slave_pfxs[] = {
+/* 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",
@@ -5046,7 +5217,7 @@ static const char * const slave_pfxs[] = {
* snd_hda_gen_build_controls - Build controls from the parsed results
* @codec: the HDA codec
*
- * Pass this to build_controls patch_ops.
+ * Pass this to build_controls hda_codec_ops.
*/
int snd_hda_gen_build_controls(struct hda_codec *codec)
{
@@ -5084,22 +5255,23 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
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, slave_pfxs,
- "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, slave_pfxs,
- "Playback Switch",
- true, &spec->vmaster_mute.sw_kctl);
+ 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,
- spec->vmaster_mute_enum);
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
}
}
@@ -5149,17 +5321,17 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_gen_spec *spec = codec->spec;
int err;
- mutex_lock(&spec->pcm_mutex);
+ guard(mutex)(&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;
+ if (err < 0)
+ return err;
+
+ spec->active_streams |= 1 << STREAM_MULTI_OUT;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ return 0;
}
static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -5198,11 +5370,11 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
- mutex_lock(&spec->pcm_mutex);
+
+ guard(mutex)(&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;
}
@@ -5251,14 +5423,13 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_gen_spec *spec = codec->spec;
int err = 0;
- mutex_lock(&spec->pcm_mutex);
+ guard(mutex)(&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;
}
@@ -5267,11 +5438,11 @@ static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
- mutex_lock(&spec->pcm_mutex);
+
+ guard(mutex)(&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;
}
@@ -5515,7 +5686,7 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
if (*str)
return;
- strlcpy(str, chip_name, len);
+ strscpy(str, chip_name, len);
/* drop non-alnum chars after a space */
for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
@@ -5558,7 +5729,7 @@ static void setup_pcm_stream(struct hda_pcm_stream *str,
* snd_hda_gen_build_pcms - build PCM streams based on the parsed results
* @codec: the HDA codec
*
- * Pass this to build_pcms patch_ops.
+ * Pass this to build_pcms hda_codec_ops.
*/
int snd_hda_gen_build_pcms(struct hda_codec *codec)
{
@@ -5607,7 +5778,7 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
spec->stream_name_digital);
if (!info)
return -ENOMEM;
- codec->slave_dig_outs = spec->multiout.slave_dig_outs;
+ 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;
@@ -5831,14 +6002,14 @@ static void init_digital(struct hda_codec *codec)
*/
static void clear_unsol_on_unused_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
- 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) {
hda_nid_t nid = pin->nid;
if (is_jack_detectable(codec, nid) &&
!snd_hda_jack_tbl_get(codec, nid))
- snd_hda_codec_update_cache(codec, nid, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE, 0);
}
}
@@ -5847,7 +6018,7 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec)
* snd_hda_gen_init - initialize the generic spec
* @codec: the HDA codec
*
- * This can be put as patch_ops init function.
+ * This can be put as hda_codec_ops init function.
*/
int snd_hda_gen_init(struct hda_codec *codec)
{
@@ -5856,7 +6027,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
if (spec->init_hook)
spec->init_hook(codec);
- snd_hda_apply_verbs(codec);
+ if (!spec->skip_verbs)
+ snd_hda_apply_verbs(codec);
init_multi_out(codec);
init_extra_out(codec);
@@ -5873,7 +6045,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
@@ -5884,27 +6056,26 @@ int snd_hda_gen_init(struct hda_codec *codec)
EXPORT_SYMBOL_GPL(snd_hda_gen_init);
/**
- * snd_hda_gen_free - free the generic spec
+ * snd_hda_gen_remove - free the generic spec
* @codec: the HDA codec
*
- * This can be put as patch_ops free function.
+ * This can be put as hda_codec_ops remove function.
*/
-void snd_hda_gen_free(struct hda_codec *codec)
+void snd_hda_gen_remove(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);
+EXPORT_SYMBOL_GPL(snd_hda_gen_remove);
-#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.
+ * This can be put as hda_codec_ops check_power_status function.
*/
int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
@@ -5912,29 +6083,14 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
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 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
-};
-
-/*
- * snd_hda_parse_generic_codec - Generic codec parser
- * @codec: the HDA codec
- */
-static int snd_hda_parse_generic_codec(struct hda_codec *codec)
+static int snd_hda_gen_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
{
struct hda_gen_spec *spec;
int err;
@@ -5947,28 +6103,40 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec)
err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
if (err < 0)
- return err;
+ goto error;
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_gen_free(codec);
+ snd_hda_gen_remove(codec);
return err;
}
+static const struct hda_codec_ops generic_codec_ops = {
+ .probe = snd_hda_gen_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
static const struct hda_device_id snd_hda_id_generic[] = {
- HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
+ HDA_CODEC_ID(0x1af40021, "Generic"), /* QEMU */
+ HDA_CODEC_ID(HDA_CODEC_ID_GENERIC, "Generic"),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
static struct hda_codec_driver generic_driver = {
.id = snd_hda_id_generic,
+ .ops = &generic_codec_ops,
};
module_hda_codec_driver(generic_driver);
diff --git a/sound/pci/hda/hda_generic.h b/sound/hda/codecs/generic.h
index 61772317de46..524591821f8c 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/hda/codecs/generic.h
@@ -1,17 +1,18 @@
+/* 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>
- *
- * 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.
*/
#ifndef __SOUND_HDA_GENERIC_H
#define __SOUND_HDA_GENERIC_H
+#include <linux/leds.h>
+#include "hda_auto_parser.h"
+
+struct hda_jack_callback;
+
/* table entry for multi-io paths */
struct hda_multi_io {
hda_nid_t pin; /* multi-io widget pin NID */
@@ -109,7 +110,7 @@ struct hda_gen_spec {
* 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 */
+ hda_nid_t follower_dig_outs[3]; /* optional - for auto-parsing */
int dig_out_type;
/* capture */
@@ -185,7 +186,7 @@ struct hda_gen_spec {
struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
/* for pin sensing */
- /* current status; set in hda_geneic.c */
+ /* 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 */
@@ -222,7 +223,8 @@ struct hda_gen_spec {
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_enum:1; /* add vmaster mute mode enum */
+ 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 */
@@ -237,6 +239,7 @@ struct hda_gen_spec {
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;
@@ -293,6 +296,9 @@ struct hda_gen_spec {
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 */
@@ -305,7 +311,7 @@ enum {
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);
+void snd_hda_gen_remove(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);
@@ -333,13 +339,19 @@ 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));
+bool snd_hda_gen_shutup_speakers(struct hda_codec *codec);
+
#endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/hda/codecs/hdmi/Kconfig b/sound/hda/codecs/hdmi/Kconfig
new file mode 100644
index 000000000000..6ea3553ba9f8
--- /dev/null
+++ b/sound/hda/codecs/hdmi/Kconfig
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_HDMI
+ tristate "HD-audio HDMI codec support"
+ help
+ Say Y or M here to include HD-audio HDMI/DislayPort codec support.
+
+ This will enable all HDMI/DP codec drivers as default, but you can
+ enable/disable each codec driver individually, too (only when
+ CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_HDMI
+
+config SND_HDA_CODEC_HDMI_GENERIC
+ tristate "Generic HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_DYNAMIC_MINORS
+ select SND_PCM_ELD
+ default y
+ help
+ Say Y or M here to include Generic HDMI and DisplayPort HD-audio
+ codec support.
+
+ Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS
+ to assure the multiple streams for DP-MST support.
+
+config SND_HDA_CODEC_HDMI_SIMPLE
+ tristate "Simple HDMI/DisplayPort HD-audio codec support" if EXPERT
+ default y
+ help
+ Say Y or M here to include Simple HDMI and DisplayPort HD-audio
+ codec support for VIA and other codecs.
+
+config SND_HDA_CODEC_HDMI_INTEL
+ tristate "Intel HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include Intel graphics HDMI and DisplayPort
+ HD-audio codec support.
+
+config SND_HDA_INTEL_HDMI_SILENT_STREAM
+ bool "Enable Silent Stream always for HDMI"
+ depends on SND_HDA_CODEC_HDMI_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_CODEC_HDMI_ATI
+ tristate "AMD/ATI HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include AMD/ATI graphics HDMI and DisplayPort
+ HD-audio codec support.
+
+config SND_HDA_CODEC_HDMI_NVIDIA
+ tristate "Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for the recent Nvidia graphics cards.
+
+config SND_HDA_CODEC_HDMI_NVIDIA_MCP
+ tristate "Legacy Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_SIMPLE
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for the legacy Nvidia graphics like MCP73, MCP67, MCP77/78.
+
+config SND_HDA_CODEC_HDMI_TEGRA
+ tristate "Nvidia Tegra HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for Nvidia Tegra.
+
+endif
diff --git a/sound/hda/codecs/hdmi/Makefile b/sound/hda/codecs/hdmi/Makefile
new file mode 100644
index 000000000000..0e49a9421e3b
--- /dev/null
+++ b/sound/hda/codecs/hdmi/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-hdmi-y := hdmi.o eld.o
+snd-hda-codec-simplehdmi-y := simplehdmi.o
+snd-hda-codec-intelhdmi-y := intelhdmi.o
+snd-hda-codec-atihdmi-y := atihdmi.o
+snd-hda-codec-nvhdmi-y := nvhdmi.o
+snd-hda-codec-nvhdmi-mcp-y := nvhdmi-mcp.o
+snd-hda-codec-tegrahdmi-y := tegrahdmi.o
+
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_GENERIC) += snd-hda-codec-hdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_SIMPLE) += snd-hda-codec-simplehdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_INTEL) += snd-hda-codec-intelhdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_ATI) += snd-hda-codec-atihdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA) += snd-hda-codec-nvhdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA_MCP) += snd-hda-codec-nvhdmi-mcp.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_TEGRA) += snd-hda-codec-tegrahdmi.o
diff --git a/sound/hda/codecs/hdmi/atihdmi.c b/sound/hda/codecs/hdmi/atihdmi.c
new file mode 100644
index 000000000000..44366f75de33
--- /dev/null
+++ b/sound/hda/codecs/hdmi/atihdmi.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ATI/AMD codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+#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
+
+/* ATI/AMD specific 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 */
+};
+
+static int 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;
+}
+
+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 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: 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 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,
+ "%s: NID=0x%x, %shbr-ctl=0x%x\n",
+ __func__,
+ 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 snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+}
+
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, err;
+
+ err = snd_hda_hdmi_generic_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;
+
+ return 0;
+}
+
+/* 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 = snd_hda_hdmi_acomp_pin_eld_notify,
+ .master_bind = snd_hda_hdmi_acomp_master_bind,
+ .master_unbind = snd_hda_hdmi_acomp_master_unbind,
+};
+
+static int atihdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err, cvt_idx;
+
+ err = snd_hda_hdmi_generic_probe(codec);
+ if (err)
+ return err;
+
+ 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);
+ }
+
+ 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;
+
+ snd_hda_hdmi_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
+
+ return 0;
+}
+
+static const struct hda_codec_ops atihdmi_codec_ops = {
+ .probe = atihdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = atihdmi_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_atihdmi[] = {
+ HDA_CODEC_ID(0x1002793c, "RS600 HDMI"),
+ HDA_CODEC_ID(0x10027919, "RS600 HDMI"),
+ HDA_CODEC_ID(0x1002791a, "RS690/780 HDMI"),
+ HDA_CODEC_ID(0x1002aa01, "R6xx HDMI"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_atihdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD/ATI HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver atihdmi_driver = {
+ .id = snd_hda_id_atihdmi,
+ .ops = &atihdmi_codec_ops,
+};
+
+module_hda_codec_driver(atihdmi_driver);
diff --git a/sound/hda/codecs/hdmi/eld.c b/sound/hda/codecs/hdmi/eld.c
new file mode 100644
index 000000000000..1464fd1c675b
--- /dev/null
+++ b/sound/hda/codecs/hdmi/eld.c
@@ -0,0 +1,230 @@
+// 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>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+
+enum cea_edid_versions {
+ CEA_EDID_VER_NONE = 0,
+ CEA_EDID_VER_CEA861 = 1,
+ CEA_EDID_VER_CEA861A = 2,
+ CEA_EDID_VER_CEA861BCD = 3,
+ CEA_EDID_VER_RESERVED = 4,
+};
+
+/*
+ * The following two lists are shared between
+ * - HDMI audio InfoFrame (source to sink)
+ * - CEA E-EDID Extension (sink to source)
+ */
+
+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
+ codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+ return val;
+}
+
+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 hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
+{
+ int i;
+ int ret = 0;
+ int size;
+
+ /*
+ * 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 */
+ codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
+ size = 128;
+ }
+ if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+ codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
+ return -ERANGE;
+ }
+
+ /* 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;
+ }
+
+ *eld_size = size;
+error:
+ return ret;
+}
+
+#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)
+{
+ 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_print_eld_info(&eld->info, buffer);
+}
+
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_parsed_hdmi_eld *e = &eld->info;
+ char line[64];
+ char name[64];
+ char *sname;
+ long long val;
+ unsigned int n;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%s %llx", name, &val) != 2)
+ continue;
+ /*
+ * We don't allow modification to these fields:
+ * monitor_name manufacture_id product_id
+ * eld_version edid_version
+ */
+ if (!strcmp(name, "monitor_present"))
+ eld->monitor_present = val;
+ else if (!strcmp(name, "eld_valid"))
+ eld->eld_valid = val;
+ else if (!strcmp(name, "connection_type"))
+ e->conn_type = val;
+ else if (!strcmp(name, "port_id"))
+ e->port_id = val;
+ else if (!strcmp(name, "support_hdcp"))
+ e->support_hdcp = val;
+ else if (!strcmp(name, "support_ai"))
+ e->support_ai = val;
+ else if (!strcmp(name, "audio_sync_delay"))
+ e->aud_synch_delay = val;
+ else if (!strcmp(name, "speakers"))
+ e->spk_alloc = val;
+ else if (!strcmp(name, "sad_count"))
+ e->sad_count = val;
+ else if (!strncmp(name, "sad", 3)) {
+ sname = name + 4;
+ n = name[3] - '0';
+ if (name[4] >= '0' && name[4] <= '9') {
+ sname++;
+ n = 10 * n + name[4] - '0';
+ }
+ if (n >= ELD_MAX_SAD)
+ continue;
+ if (!strcmp(sname, "_coding_type"))
+ e->sad[n].format = val;
+ else if (!strcmp(sname, "_channels"))
+ e->sad[n].channels = val;
+ else if (!strcmp(sname, "_rates"))
+ e->sad[n].rates = val;
+ else if (!strcmp(sname, "_bits"))
+ e->sad[n].sample_bits = val;
+ else if (!strcmp(sname, "_max_bitrate"))
+ e->sad[n].max_bitrate = val;
+ else if (!strcmp(sname, "_profile"))
+ e->sad[n].profile = val;
+ if (n >= e->sad_count)
+ e->sad_count = n + 1;
+ }
+ }
+}
+#endif /* CONFIG_SND_PROC_FS */
+
+/* update PCM info based on ELD */
+void snd_hdmi_eld_update_pcm_info(struct snd_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) */
+ 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 snd_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 & ELD_PCM_BITS_20) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 20)
+ maxbps = 20;
+ }
+ if (a->sample_bits & ELD_PCM_BITS_24) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 24)
+ maxbps = 24;
+ }
+ }
+ }
+
+ /* restrict the parameters by the values the codec provides */
+ hinfo->rates &= rates;
+ hinfo->formats &= formats;
+ hinfo->maxbps = min(hinfo->maxbps, maxbps);
+ hinfo->channels_max = min(hinfo->channels_max, channels_max);
+}
diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c
new file mode 100644
index 000000000000..111c9b5335af
--- /dev/null
+++ b/sound/hda/codecs/hdmi/hdmi.c
@@ -0,0 +1,2363 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * hdmi.c - routines for HDMI/DisplayPort codecs
+ *
+ * Copyright(c) 2008-2010 Intel Corporation
+ * 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>
+ */
+
+#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 <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"
+#include "hdmi_local.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_all_pins;
+module_param(enable_all_pins, bool, 0444);
+MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins");
+
+int snd_hda_hdmi_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;
+
+ /*
+ * (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;
+ }
+
+ codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_pin_id_to_pin_index, "SND_HDA_CODEC_HDMI");
+
+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;
+
+ 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 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; 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;
+
+ codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid);
+ return -EINVAL;
+}
+
+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;
+ guard(mutex)(&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;
+ return 0;
+ }
+ eld = &per_pin->sink_eld;
+ uinfo->count = eld->eld_valid ? eld->eld_size : 0;
+ 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;
+
+ pcm_idx = kcontrol->private_value;
+ guard(mutex)(&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));
+ return 0;
+ }
+
+ eld = &per_pin->sink_eld;
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ 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);
+ return 0;
+}
+
+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)
+{
+ 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
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+ int *packet_index, int *byte_index)
+{
+ int val;
+
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+ *packet_index = val >> 5;
+ *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
+ unsigned char val)
+{
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+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);
+
+ 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;
+
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
+}
+
+/*
+ * ELD proc files
+ */
+
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ guard(mutex)(&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);
+}
+
+static void write_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ guard(mutex)(&per_pin->lock);
+ snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
+}
+
+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;
+
+ 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_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 0;
+}
+
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+ if (!per_pin->codec->bus->shutdown) {
+ snd_info_free_entry(per_pin->proc_entry);
+ per_pin->proc_entry = NULL;
+ }
+}
+#else
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
+ int index)
+{
+ return 0;
+}
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+}
+#endif
+
+/*
+ * Audio InfoFrame routines
+ */
+
+/*
+ * Enable Audio InfoFrame Transmission
+ */
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
+{
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_BEST);
+}
+
+/*
+ * Disable Audio InfoFrame Transmission
+ */
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
+{
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_DISABLE);
+}
+
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int size;
+
+ size = snd_hdmi_get_eld_size(codec, pin_nid);
+ 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);
+ codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+ }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef BE_PARANOID
+ int i, j;
+ int size;
+ int pi, bi;
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ if (size == 0)
+ continue;
+
+ hdmi_set_dip_index(codec, pin_nid, i, 0x0);
+ for (j = 1; j < 1000; j++) {
+ hdmi_write_dip_byte(codec, pin_nid, 0x0);
+ hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
+ if (pi != i)
+ codec_dbg(codec, "dip index %d: %d != %d\n",
+ bi, pi, i);
+ if (bi == 0) /* byte index wrapped around */
+ break;
+ }
+ codec_dbg(codec,
+ "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+ i, size, j);
+ }
+#endif
+}
+
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
+{
+ u8 *bytes = (u8 *)hdmi_ai;
+ u8 sum = 0;
+ int i;
+
+ hdmi_ai->checksum = 0;
+
+ for (i = 0; i < sizeof(*hdmi_ai); i++)
+ sum += bytes[i];
+
+ hdmi_ai->checksum = -sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ u8 *dip, int size)
+{
+ int i;
+
+ hdmi_debug_dip_size(codec, pin_nid);
+ hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ for (i = 0; i < size; i++)
+ hdmi_write_dip_byte(codec, pin_nid, dip[i]);
+}
+
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+ u8 *dip, int size)
+{
+ 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;
+
+ for (i = 0; i < size; i++) {
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_DATA, 0);
+ if (val != dip[i])
+ return false;
+ }
+
+ return true;
+}
+
+static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
+{
+ snd_hda_set_dev_select(codec, nid, dev_id);
+
+ return snd_hdmi_get_eld(codec, nid, buf, eld_size);
+}
+
+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 ((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;
+
+ if (conn_type == 0) { /* HDMI */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ } 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;
+ }
+
+ 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);
+ }
+}
+
+void snd_hda_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_audio_infoframe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
+
+void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, nid, dev_id);
+
+ if (pin_idx < 0)
+ return;
+ guard(mutex)(&spec->pcm_lock);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_check_presence_and_report,
+ "SND_HDA_CODEC_HDMI");
+
+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;
+
+ snd_hda_hdmi_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;
+
+ 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));
+
+ snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id);
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+ int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+ 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_ready) {
+ ;
+ }
+}
+
+void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ 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 (!jack) {
+ codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
+ return;
+ }
+
+ if (subtag == 0)
+ hdmi_intrinsic_event(codec, res, jack);
+ else
+ hdmi_non_intrinsic_event(codec, res);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_unsol_event, "SND_HDA_CODEC_HDMI");
+
+/*
+ * Callbacks
+ */
+
+/* HBR should be Non-PCM, 8 channels */
+#define is_hbr_format(format) \
+ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
+
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr)
+{
+ int pinctl, new_pinctl;
+
+ 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 (hbr)
+ new_pinctl |= AC_PINCTL_EPT_HBR;
+ else
+ new_pinctl |= AC_PINCTL_EPT_NATIVE;
+
+ 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, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ new_pinctl);
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+int snd_hda_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 (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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_stream, "SND_HDA_CODEC_HDMI");
+
+/* 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;
+}
+
+/* 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 */
+
+ /* 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;
+}
+
+/*
+ * HDA PCM callbacks
+ */
+static int hdmi_pcm_open(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 pin_idx, cvt_idx, pcm_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ /* Validate hinfo */
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
+ return -EINVAL;
+
+ guard(mutex)(&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)
+ return hdmi_pcm_open_no_pin(hinfo, codec, substream);
+
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false);
+ if (err < 0)
+ return err;
+
+ 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) {
+ per_cvt->assigned = false;
+ hinfo->nid = 0;
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ return -ENODEV;
+ }
+ }
+
+ /* 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;
+}
+
+/*
+ * HDA/HDMI auto parsing
+ */
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
+{
+ struct hdmi_spec *spec = codec->spec;
+ 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)) {
+ 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;
+ }
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ 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);
+ }
+
+ /* all the device entries on the same pin have the same conn list */
+ per_pin->num_mux_nids = conns;
+
+ return 0;
+}
+
+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 idx;
+
+ /* 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;
+
+ 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 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;
+
+ snd_hda_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_parse_eld(hda_codec_dev(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_show_eld(hda_codec_dev(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);
+ snd_hda_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;
+
+#ifdef CONFIG_PM
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm_runtime_suspended(dev))
+ return;
+
+ present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
+
+ guard(mutex)(&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);
+}
+
+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;
+
+ /*
+ * Power-up will call hdmi_present_sense, so the PM calls
+ * have to be done without mutex held.
+ */
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm.err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream enable ret=[%d]\n", pm.err);
+ return;
+ }
+
+ guard(mutex)(&per_pin->lock);
+
+ if (per_pin->setup) {
+ codec_dbg(codec, "hdmi: PCM already open, no silent stream\n");
+ return;
+ }
+
+ 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");
+ return;
+ }
+
+ 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);
+
+ spec->ops.silent_stream(codec, per_pin, true);
+}
+
+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;
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm.err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream disable ret=[%d]\n",
+ pm.err);
+ return;
+ }
+
+ guard(mutex)(&per_pin->lock);
+ if (!per_pin->silent_stream)
+ return;
+
+ 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;
+ }
+
+ spec->ops.silent_stream(codec, per_pin, false);
+
+ per_pin->cvt_nid = 0;
+ per_pin->silent_stream = false;
+}
+
+/* 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;
+
+ scoped_guard(mutex, &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;
+ }
+
+ 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;
+
+ guard(mutex)(&spec->pcm_lock);
+ hdmi_present_sense(per_pin, per_pin->repoll_count);
+}
+
+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;
+
+ caps = snd_hda_query_pin_caps(codec, pin_nid);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ return 0;
+
+ /*
+ * 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;
+
+ /*
+ * To simplify the implementation, malloc all
+ * the virtual pins in the initialization statically
+ */
+ 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 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);
+
+ 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;
+ }
+
+ err = snd_hda_query_supported_pcm(codec, cvt_nid,
+ &per_cvt->rates,
+ &per_cvt->formats,
+ NULL,
+ &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, 0x83e2, "HP EliteDesk 800 G4", 1),
+ SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1),
+ SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1),
+ SND_PCI_QUIRK(0x103c, 0x83f3, "HP ProDesk 400", 1),
+ 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(0x1043, 0x86ae, "ASUS", 1), /* Z170 PRO */
+ SND_PCI_QUIRK(0x1043, 0x86c7, "ASUS", 1), /* Z170M PLUS */
+ SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
+ SND_PCI_QUIRK(0x8086, 0x2060, "Intel NUC5CPYB", 1),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
+ {}
+};
+
+int snd_hda_hdmi_parse_codec(struct hda_codec *codec)
+{
+ 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->core.afg, &start_nid);
+ if (!start_nid || nodes < 0) {
+ codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+
+ 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 = get_wcaps(codec, nid);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
+ hdmi_add_cvt(codec, nid);
+ }
+
+ /* 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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_parse_codec, "SND_HDA_CODEC_HDMI");
+
+/*
+ */
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hda_spdif_out *spdif;
+
+ guard(mutex)(&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))
+ return true;
+ return !!(spdif->status & IEC958_AES0_NONAUDIO);
+}
+
+/*
+ * HDMI callbacks
+ */
+
+int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ 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;
+
+ guard(mutex)(&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);
+ return 0;
+ }
+
+ 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);
+ scoped_guard(mutex, &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);
+ }
+
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+ }
+ 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);
+ }
+
+ /* snd_hda_set_dev_select() has been called before */
+ return spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
+ per_pin->dev_id, stream_tag, format);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_prepare, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_cleanup, "SND_HDA_CODEC_HDMI");
+
+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;
+
+ guard(mutex)(&spec->pcm_lock);
+ if (hinfo->nid) {
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (snd_BUG_ON(pcm_idx < 0))
+ return -EINVAL;
+ cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
+ if (snd_BUG_ON(cvt_idx < 0))
+ return -EINVAL;
+ 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)
+ return 0;
+
+ 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);
+ }
+
+ guard(mutex)(&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;
+ }
+
+ return 0;
+}
+
+static const struct hda_pcm_ops generic_ops = {
+ .open = hdmi_pcm_open,
+ .close = hdmi_pcm_close,
+ .prepare = snd_hda_hdmi_generic_pcm_prepare,
+ .cleanup = snd_hda_hdmi_generic_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;
+ guard(mutex)(&per_pin->lock);
+ per_pin->chmap_set = true;
+ memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
+ if (prepared)
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+}
+
+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;
+}
+
+int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int idx, pcm_num;
+
+ /* 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 (idx = 0; idx < pcm_num; idx++) {
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hda_pcm *info;
+ struct hda_pcm_stream *pstr;
+
+ info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
+ if (!info)
+ return -ENOMEM;
+
+ 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];
+ pstr->substreams = 1;
+ pstr->ops = generic_ops;
+
+ per_cvt = get_cvt(spec, 0);
+ pstr->channels_min = per_cvt->channels_min;
+ pstr->channels_max = per_cvt->channels_max;
+
+ /* 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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_pcms, "SND_HDA_CODEC_HDMI");
+
+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;
+
+ 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;
+}
+
+int snd_hda_hdmi_generic_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;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_controls, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init_per_pins, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ guard(mutex)(&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);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init, "SND_HDA_CODEC_HDMI");
+
+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);
+}
+
+void snd_hda_hdmi_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_spec_free, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_generic_remove(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, pcm_idx;
+
+ 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;
+
+ 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);
+ }
+
+ snd_hda_hdmi_generic_spec_free(codec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_remove, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_suspend, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ snd_hda_codec_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_resume, "SND_HDA_CODEC_HDMI");
+
+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 = snd_hda_hdmi_setup_stream,
+};
+
+/* allocate codec->spec and assign/initialize generic parser ops */
+int snd_hda_hdmi_generic_alloc(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ 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;
+ hdmi_array_init(spec, 4);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_alloc, "SND_HDA_CODEC_HDMI");
+
+/* generic HDMI parser */
+int snd_hda_hdmi_generic_probe(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_probe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * 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);
+ guard(mutex)(&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++)
+ reprogram_jack_detect(spec->codec,
+ get_pin(spec, i)->pin_nid,
+ get_pin(spec, i)->dev_id,
+ use_acomp);
+}
+
+/* enable / disable the notifier via master bind / unbind */
+int snd_hda_hdmi_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, true);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_bind, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, false);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_unbind, "SND_HDA_CODEC_HDMI");
+
+/* 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 */
+void snd_hda_hdmi_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;
+
+ snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_pin_eld_notify, "SND_HDA_CODEC_HDMI");
+
+/* set up the private drm_audio_ops from the template */
+void snd_hda_hdmi_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_drm_audio_ops, "SND_HDA_CODEC_HDMI");
+
+/* initialize the generic HDMI audio component */
+void snd_hda_hdmi_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;
+ snd_hda_hdmi_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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI");
+
+/*
+ */
+
+enum {
+ MODEL_GENERIC,
+ MODEL_GF,
+};
+
+static int generichdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ int err;
+
+ err = snd_hda_hdmi_generic_probe(codec);
+ if (err < 0)
+ return err;
+ /*
+ * Glenfly GPUs have two codecs, stream switches from one codec to
+ * another, need to do actual clean-ups in codec_cleanup_stream
+ */
+ if (id->driver_data == MODEL_GF)
+ codec->no_sticky_stream = 1;
+
+ return 0;
+}
+
+static const struct hda_codec_ops generichdmi_codec_ops = {
+ .probe = generichdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+/*
+ */
+static const struct hda_device_id snd_hda_id_generichdmi[] = {
+ HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x11069f85, "VX11 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x17e80047, "Chrontel HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x1d179f86, "ZX-100S HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f87, "ZX-100S HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f88, "KX-5000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f89, "KX-5000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8a, "KX-6000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8b, "KX-6000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8c, "KX-6000G HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8d, "KX-6000G HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8e, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8f, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f90, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d82, "Arise 82 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d83, "Arise 83 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d84, "Arise 84 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d85, "Arise 85 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d86, "Arise 86 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d87, "Arise 87 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x80862801, "Bearlake HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862802, "Cantiga HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862803, "Eaglelake HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862880, "CedarTrail HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x808629fb, "Crestline HDMI", MODEL_GENERIC),
+ /* special ID for generic HDMI */
+ HDA_CODEC_ID_MODEL(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", MODEL_GENERIC),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generichdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic HDMI HD-audio codec");
+
+static struct hda_codec_driver generichdmi_driver = {
+ .id = snd_hda_id_generichdmi,
+ .ops = &generichdmi_codec_ops,
+};
+
+module_hda_codec_driver(generichdmi_driver);
diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h
new file mode 100644
index 000000000000..548241ad3fa9
--- /dev/null
+++ b/sound/hda/codecs/hdmi/hdmi_local.h
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD-audio HDMI codec driver
+ */
+
+#ifndef __HDA_HDMI_LOCAL_H
+#define __HDA_HDMI_LOCAL_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+
+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;
+};
+
+/* 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 codec drivers */
+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);
+
+ void (*silent_stream)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable);
+};
+
+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;
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
+
+ /*
+ * 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
+ */
+ int num_pins;
+ /*
+ * num_nids is the number of real pins
+ * In the above example, num_nids is 3
+ */
+ int num_nids;
+ /*
+ * 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.
+ */
+ 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 */
+ /*
+ * Non-generic VIA/NVIDIA specific
+ */
+ struct hda_multi_out multiout;
+ 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 *codec, int port); /* reverse port/pin mapping */
+
+ struct hdac_chmap chmap;
+ hda_nid_t vendor_nid;
+ const int *port_map;
+ int port_num;
+ int silent_stream_type;
+
+ const struct snd_pcm_hw_constraint_list *hw_constraints_channels;
+};
+
+#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 */
+ u8 ver; /* 0x01 */
+ u8 len; /* 0x0a */
+
+ u8 checksum;
+
+ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+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;
+};
+
+union audio_infoframe {
+ struct hdmi_audio_infoframe hdmi;
+ struct dp_audio_infoframe dp;
+ DECLARE_FLEX_ARRAY(u8, bytes);
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS 16
+#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_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 SUPPORTED_MAXBPS 24
+#define SUPPORTED_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+/*
+ * HDMI routines
+ */
+
+#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)
+
+/* Generic HDMI codec support */
+int snd_hda_hdmi_generic_alloc(struct hda_codec *codec);
+int snd_hda_hdmi_parse_codec(struct hda_codec *codec);
+int snd_hda_hdmi_generic_probe(struct hda_codec *codec);
+void snd_hda_hdmi_generic_remove(struct hda_codec *codec);
+
+int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec);
+int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec);
+int snd_hda_hdmi_generic_init(struct hda_codec *codec);
+int snd_hda_hdmi_generic_suspend(struct hda_codec *codec);
+int snd_hda_hdmi_generic_resume(struct hda_codec *codec);
+void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res);
+
+int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id);
+#define pin_id_to_pin_index(codec, pin, dev) \
+ snd_hda_hdmi_pin_id_to_pin_index(codec, pin, dev)
+int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec);
+void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec);
+int snd_hda_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);
+
+int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream);
+int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
+
+void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id);
+void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm);
+
+/* Audio component support */
+void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops);
+void snd_hda_hdmi_acomp_init(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops,
+ int (*port2pin)(struct hda_codec *, int));
+void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id);
+int snd_hda_hdmi_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp);
+void snd_hda_hdmi_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp);
+
+/* Simple / legacy HDMI codec support */
+int snd_hda_hdmi_simple_probe(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid);
+void snd_hda_hdmi_simple_remove(struct hda_codec *codec);
+
+int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec);
+int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec);
+int snd_hda_hdmi_simple_init(struct hda_codec *codec);
+void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec,
+ unsigned int res);
+int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
+
+#endif /* __HDA_HDMI_LOCAL_H */
diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c
new file mode 100644
index 000000000000..23237d527430
--- /dev/null
+++ b/sound/hda/codecs/hdmi/intelhdmi.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Intel HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+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");
+
+enum {
+ MODEL_HSW,
+ MODEL_GLK,
+ MODEL_ICL,
+ MODEL_TGL,
+ MODEL_ADLP,
+ MODEL_BYT,
+ MODEL_CPT,
+};
+
+#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)
+{
+ /* check codec->spec: it can be called before the probe gets called */
+ if (codec->spec) {
+ 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);
+}
+
+/* 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);
+ snd_hda_hdmi_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;
+ snd_hda_hdmi_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;
+}
+
+#define I915_SILENT_RATE 48000
+#define I915_SILENT_CHANNELS 2
+#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_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS,
+ I915_SILENT_RATE);
+ 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;
+ snd_hda_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 i915_set_silent_stream(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ switch (spec->silent_stream_type) {
+ case SILENT_STREAM_KAE:
+ if (enable) {
+ silent_stream_enable_i915(codec, per_pin);
+ silent_stream_set_kae(codec, per_pin, true);
+ } else {
+ silent_stream_set_kae(codec, per_pin, false);
+ }
+ break;
+ case SILENT_STREAM_I915:
+ if (enable) {
+ silent_stream_enable_i915(codec, per_pin);
+ snd_hda_power_up_pm(codec);
+ } else {
+ /* release ref taken in silent_stream_enable() */
+ snd_hda_power_down_pm(codec);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+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);
+ }
+}
+
+/* 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);
+}
+
+/* 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 = snd_hda_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);
+ }
+}
+
+static int i915_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ bool silent_streams = false;
+ int pin_idx, res;
+
+ res = snd_hda_hdmi_generic_suspend(codec);
+ if (spec->silent_stream_type != SILENT_STREAM_KAE)
+ 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 (per_pin->silent_stream) {
+ silent_streams = true;
+ break;
+ }
+ }
+
+ if (silent_streams) {
+ /*
+ * 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_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, res;
+
+ res = snd_hda_hdmi_generic_resume(codec);
+ if (spec->silent_stream_type != SILENT_STREAM_KAE)
+ return res;
+
+ /* 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;
+}
+
+/* precondition and allocation for Intel codecs */
+static int alloc_intel_hdmi(struct hda_codec *codec)
+{
+ /* 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;
+ }
+
+ return snd_hda_hdmi_generic_alloc(codec);
+}
+
+/* parse and post-process for Intel codecs */
+static int parse_intel_hdmi(struct hda_codec *codec)
+{
+ int err, retries = 3;
+
+ do {
+ err = snd_hda_hdmi_parse_codec(codec);
+ } while (err < 0 && retries--);
+
+ if (err < 0)
+ return err;
+
+ snd_hda_hdmi_generic_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;
+
+ 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->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;
+ spec->ops.silent_stream = i915_set_silent_stream;
+
+ /*
+ * 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 probe_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ return intel_hsw_common_init(codec, 0x08, NULL, 0, 3,
+ enable_silent_stream);
+}
+
+static int probe_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 probe_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 probe_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 probe_i915_adlp_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int res;
+
+ res = probe_i915_tgl_hdmi(codec);
+ if (!res) {
+ spec = codec->spec;
+
+ if (spec->silent_stream_type)
+ spec->silent_stream_type = SILENT_STREAM_KAE;
+ }
+
+ return res;
+}
+
+/* Intel Baytrail and Braswell; with eld notifier */
+static int probe_i915_byt_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ 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 probe_i915_cpt_hdmi(struct hda_codec *codec)
+{
+ return parse_intel_hdmi(codec);
+}
+
+/*
+ * common driver probe
+ */
+static int intelhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ err = alloc_intel_hdmi(codec);
+ if (err < 0)
+ return err;
+
+ switch (id->driver_data) {
+ case MODEL_HSW:
+ err = probe_i915_hsw_hdmi(codec);
+ break;
+ case MODEL_GLK:
+ err = probe_i915_glk_hdmi(codec);
+ break;
+ case MODEL_ICL:
+ err = probe_i915_icl_hdmi(codec);
+ break;
+ case MODEL_TGL:
+ err = probe_i915_tgl_hdmi(codec);
+ break;
+ case MODEL_ADLP:
+ err = probe_i915_adlp_hdmi(codec);
+ break;
+ case MODEL_BYT:
+ err = probe_i915_byt_hdmi(codec);
+ break;
+ case MODEL_CPT:
+ err = probe_i915_cpt_hdmi(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops intelhdmi_codec_ops = {
+ .probe = intelhdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = i915_hdmi_suspend,
+ .resume = i915_hdmi_resume,
+ .set_power_state = haswell_set_power_state,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_intelhdmi[] = {
+ HDA_CODEC_ID_MODEL(0x80860054, "IbexPeak HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862800, "Geminilake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x80862804, "IbexPeak HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862805, "CougarPoint HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862806, "PantherPoint HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862807, "Haswell HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x80862808, "Broadwell HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x80862809, "Skylake HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280a, "Broxton HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280b, "Kabylake HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280c, "Cannonlake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x8086280d, "Geminilake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x8086280f, "Icelake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x80862812, "Tigerlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862814, "DG1 HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862815, "Alderlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862816, "Rocketlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862818, "Raptorlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862819, "DG2 HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x8086281a, "Jasperlake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x8086281b, "Elkhartlake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x8086281c, "Alderlake-P HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281d, "Meteor Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281e, "Battlemage HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281f, "Raptor Lake P HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862820, "Lunar Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862822, "Panther Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862823, "Wildcat Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862882, "Valleyview2 HDMI", MODEL_BYT),
+ HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_intelhdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver intelhdmi_driver = {
+ .id = snd_hda_id_intelhdmi,
+ .ops = &intelhdmi_codec_ops,
+};
+
+module_hda_codec_driver(intelhdmi_driver);
diff --git a/sound/hda/codecs/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c
new file mode 100644
index 000000000000..8fd8d76fa72f
--- /dev/null
+++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Legacy Nvidia HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum { MODEL_2CH, MODEL_8CH };
+
+#define Nv_VERB_SET_Channel_Allocation 0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
+#define Nv_VERB_SET_Audio_Protection_On 0xF98
+#define Nv_VERB_SET_Audio_Protection_Off 0xF99
+
+#define nvhdmi_master_con_nid_7x 0x04
+#define nvhdmi_master_pin_nid_7x 0x05
+
+static const hda_nid_t nvhdmi_con_nids_7x[4] = {
+ /*front, rear, clfe, rear_surr */
+ 0x6, 0x8, 0xa, 0xc,
+};
+
+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 */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static int nvhdmi_mcp_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->multiout.max_channels == 2)
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
+ else
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
+ 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)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+ 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ for (i = 0; i < 4; i++) {
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ 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);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int chs;
+ 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;
+
+ guard(mutex)(&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;
+
+ dataDCC2 = 0x2;
+
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+ 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,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
+
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
+
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ 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,
+ spdif->ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (chs == 2)
+ channel_id = 0;
+ else
+ channel_id = i * 2;
+
+ /* turn off SPDIF once;
+ *otherwise the IEC958 bits won't be updated
+ */
+ if (codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
+ /* set the stream id */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ (stream_tag << 4) | channel_id);
+ /* set the stream format */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+ }
+
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
+
+ return 0;
+}
+
+static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = snd_hda_hdmi_simple_pcm_open,
+ .close = nvhdmi_8ch_7x_pcm_close,
+ .prepare = nvhdmi_8ch_7x_pcm_prepare
+ },
+};
+
+static int nvhdmi_mcp_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_hdmi_simple_build_pcms(codec);
+ if (!err && spec->multiout.max_channels == 8) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+
+ info->own_chmap = true;
+ }
+ return err;
+}
+
+static int nvhdmi_mcp_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ struct snd_pcm_chmap *chmap;
+ int err;
+
+ err = snd_hda_hdmi_simple_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ if (spec->multiout.max_channels != 8)
+ return 0;
+
+ /* 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 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 nvhdmi_mcp_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
+ if (err < 0)
+ return err;
+
+ /* 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;
+
+ if (id->driver_data == MODEL_2CH)
+ return 0;
+
+ spec->multiout.max_channels = 8;
+ spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
+
+ switch (codec->preset->vendor_id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ spec->hw_constraints_channels = &hw_constraints_2_8_channels;
+ break;
+ case 0x10de0007:
+ spec->hw_constraints_channels = &hw_constraints_2_6_8_channels;
+ break;
+ default:
+ break;
+ }
+
+ /* Initialize the audio infoframe channel mask and checksum to something
+ * valid
+ */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
+ return 0;
+}
+
+static const struct hda_codec_ops nvhdmi_mcp_codec_ops = {
+ .probe = nvhdmi_mcp_probe,
+ .remove = snd_hda_hdmi_simple_remove,
+ .build_controls = nvhdmi_mcp_build_pcms,
+ .build_pcms = nvhdmi_mcp_build_controls,
+ .init = nvhdmi_mcp_init,
+ .unsol_event = snd_hda_hdmi_simple_unsol_event,
+};
+
+static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = {
+ HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver nvhdmi_mcp_driver = {
+ .id = snd_hda_id_nvhdmi_mcp,
+ .ops = &nvhdmi_mcp_codec_ops,
+};
+
+module_hda_codec_driver(nvhdmi_mcp_driver);
diff --git a/sound/hda/codecs/hdmi/nvhdmi.c b/sound/hda/codecs/hdmi/nvhdmi.c
new file mode 100644
index 000000000000..94671ad24b5e
--- /dev/null
+++ b/sound/hda/codecs/hdmi/nvhdmi.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nvidia HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum {
+ MODEL_GENERIC,
+ MODEL_LEGACY,
+};
+
+/*
+ * 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;
+}
+
+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;
+}
+
+/* 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 = snd_hda_hdmi_acomp_pin_eld_notify,
+ .master_bind = snd_hda_hdmi_acomp_master_bind,
+ .master_unbind = snd_hda_hdmi_acomp_master_unbind,
+};
+
+static int probe_generic(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+ codec->dp_mst = true;
+
+ spec = codec->spec;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ snd_hda_hdmi_generic_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;
+
+ snd_hda_hdmi_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
+
+ return 0;
+}
+
+static int probe_legacy(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_probe(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;
+}
+
+static int nvhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ if (id->driver_data == MODEL_LEGACY)
+ return probe_legacy(codec);
+ else
+ return probe_generic(codec);
+}
+
+static const struct hda_codec_ops nvhdmi_codec_ops = {
+ .probe = nvhdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+static const struct hda_device_id snd_hda_id_nvhdmi[] = {
+ HDA_CODEC_ID_MODEL(0x10de0008, "GPU 08 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0009, "GPU 09 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000a, "GPU 0a HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000b, "GPU 0b HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000c, "MCP89 HDMI", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000d, "GPU 0d HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0010, "GPU 10 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0011, "GPU 11 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0012, "GPU 12 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0013, "GPU 13 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0014, "GPU 14 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0015, "GPU 15 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0016, "GPU 16 HDMI/DP", MODEL_LEGACY),
+ /* 17 is known to be absent */
+ HDA_CODEC_ID_MODEL(0x10de0018, "GPU 18 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0019, "GPU 19 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001a, "GPU 1a HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001b, "GPU 1b HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001c, "GPU 1c HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0040, "GPU 40 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0041, "GPU 41 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0042, "GPU 42 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0043, "GPU 43 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0044, "GPU 44 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0045, "GPU 45 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0050, "GPU 50 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0051, "GPU 51 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0052, "GPU 52 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0060, "GPU 60 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0061, "GPU 61 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0062, "GPU 62 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0070, "GPU 70 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0071, "GPU 71 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0072, "GPU 72 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0073, "GPU 73 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0074, "GPU 74 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0076, "GPU 76 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007b, "GPU 7b HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007c, "GPU 7c HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007d, "GPU 7d HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007e, "GPU 7e HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0080, "GPU 80 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0081, "GPU 81 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0082, "GPU 82 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0083, "GPU 83 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0084, "GPU 84 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0090, "GPU 90 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0091, "GPU 91 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0092, "GPU 92 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0093, "GPU 93 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0094, "GPU 94 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0095, "GPU 95 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0097, "GPU 97 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0098, "GPU 98 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0099, "GPU 99 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009a, "GPU 9a HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009b, "GPU 9b HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009c, "GPU 9c HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009d, "GPU 9d HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009e, "GPU 9e HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009f, "GPU 9f HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a0, "GPU a0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a1, "GPU a1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a3, "GPU a3 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a4, "GPU a4 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a5, "GPU a5 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a6, "GPU a6 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a7, "GPU a7 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a8, "GPU a8 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a9, "GPU a9 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00aa, "GPU aa HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ab, "GPU ab HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ad, "GPU ad HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ae, "GPU ae HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00af, "GPU af HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00b0, "GPU b0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00b1, "GPU b1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c0, "GPU c0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c1, "GPU c1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c3, "GPU c3 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c4, "GPU c4 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c5, "GPU c5 HDMI/DP", MODEL_GENERIC),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver nvhdmi_driver = {
+ .id = snd_hda_id_nvhdmi,
+ .ops = &nvhdmi_codec_ops,
+};
+
+module_hda_codec_driver(nvhdmi_driver);
diff --git a/sound/hda/codecs/hdmi/simplehdmi.c b/sound/hda/codecs/hdmi/simplehdmi.c
new file mode 100644
index 000000000000..193c8dc882af
--- /dev/null
+++ b/sound/hda/codecs/hdmi/simplehdmi.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Non-generic simple HDMI codec support
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "hdmi_local.h"
+#include "hda_jack.h"
+
+int snd_hda_hdmi_simple_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_pcms, "SND_HDA_CODEC_HDMI");
+
+/* unsolicited event for jack sensing */
+void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_unsol_event, "SND_HDA_CODEC_HDMI");
+
+static void free_hdmi_jack_priv(struct snd_jack *jack)
+{
+ struct hdmi_pcm *pcm = jack->private_data;
+
+ pcm->jack = NULL;
+}
+
+static int simple_hdmi_build_jack(struct hda_codec *codec)
+{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *jack;
+ struct hdmi_pcm *pcmp = get_hdmi_pcm(spec, 0);
+ int pcmdev = pcmp->pcm->device;
+ int err;
+
+ 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;
+
+ pcmp->jack = jack;
+ jack->private_data = pcmp;
+ jack->private_free = free_hdmi_jack_priv;
+ return 0;
+}
+
+int snd_hda_hdmi_simple_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);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_controls, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_simple_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;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_init, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_simple_remove(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+ kfree(spec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_remove, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->hw_constraints_channels) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ spec->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);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_pcm_open, "SND_HDA_CODEC_HDMI");
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_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 hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static const struct hda_pcm_stream simple_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = snd_hda_hdmi_simple_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+int snd_hda_hdmi_simple_probe(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;
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), 1);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), 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) {
+ snd_hda_hdmi_simple_remove(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
+ spec->pcm_playback = simple_pcm_playback;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_probe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * driver entries
+ */
+
+enum { MODEL_VIA };
+
+/* VIA HDMI Implementation */
+#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
+#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
+
+static int simplehdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ switch (id->driver_data) {
+ case MODEL_VIA:
+ return snd_hda_hdmi_simple_probe(codec, VIAHDMI_CVT_NID,
+ VIAHDMI_PIN_NID);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct hda_codec_ops simplehdmi_codec_ops = {
+ .probe = simplehdmi_probe,
+ .remove = snd_hda_hdmi_simple_remove,
+ .build_controls = snd_hda_hdmi_simple_build_controls,
+ .build_pcms = snd_hda_hdmi_simple_build_pcms,
+ .init = snd_hda_hdmi_simple_init,
+ .unsol_event = snd_hda_hdmi_simple_unsol_event,
+};
+
+static const struct hda_device_id snd_hda_id_simplehdmi[] = {
+ HDA_CODEC_ID_MODEL(0x11069f80, "VX900 HDMI/DP", MODEL_VIA),
+ HDA_CODEC_ID_MODEL(0x11069f81, "VX900 HDMI/DP", MODEL_VIA),
+ {} /* terminator */
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simple HDMI HD-audio codec support");
+
+static struct hda_codec_driver simplehdmi_driver = {
+ .id = snd_hda_id_simplehdmi,
+ .ops = &simplehdmi_codec_ops,
+};
+
+module_hda_codec_driver(simplehdmi_driver);
diff --git a/sound/hda/codecs/hdmi/tegrahdmi.c b/sound/hda/codecs/hdmi/tegrahdmi.c
new file mode 100644
index 000000000000..5f6fe31aa202
--- /dev/null
+++ b/sound/hda/codecs/hdmi/tegrahdmi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nvidia Tegra HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum {
+ MODEL_TEGRA,
+ MODEL_TEGRA234,
+};
+
+/*
+ * 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.
+ *
+ * | 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;
+
+ /*
+ * 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 = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag,
+ format, substream);
+ if (err < 0)
+ return err;
+
+ /* 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 snd_hda_hdmi_generic_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 = snd_hda_hdmi_generic_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;
+}
+
+/*
+ * 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;
+}
+
+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 tegra_hdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i, err;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_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);
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+
+ codec->depop_delay = 10;
+ 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 tegrahdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+
+ if (id->driver_data == MODEL_TEGRA234) {
+ codec->dp_mst = true;
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+ spec->hdmi_intr_trig_ctrl = true;
+ }
+
+ return tegra_hdmi_init(codec);
+}
+
+static const struct hda_codec_ops tegrahdmi_codec_ops = {
+ .probe = tegrahdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = tegra_hdmi_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+static const struct hda_device_id snd_hda_id_tegrahdmi[] = {
+ HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0033, "SoC 33 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0035, "SoC 35 HDMI/DP", MODEL_TEGRA234),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver tegrahdmi_driver = {
+ .id = snd_hda_id_tegrahdmi,
+ .ops = &tegrahdmi_codec_ops,
+};
+
+module_hda_codec_driver(tegrahdmi_driver);
diff --git a/sound/hda/codecs/helpers/hp_x360.c b/sound/hda/codecs/helpers/hp_x360.c
new file mode 100644
index 000000000000..969542c57358
--- /dev/null
+++ b/sound/hda/codecs/helpers/hp_x360.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/hda/codecs/helpers/ideapad_hotkey_led.c b/sound/hda/codecs/helpers/ideapad_hotkey_led.c
new file mode 100644
index 000000000000..c10d97964d49
--- /dev/null
+++ b/sound/hda/codecs/helpers/ideapad_hotkey_led.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ideapad helper functions for Lenovo Ideapad LED control,
+ * It should be included from codec driver.
+ */
+
+#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP)
+
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+static bool is_ideapad(struct hda_codec *codec)
+{
+ return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+ (acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004"));
+}
+
+static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (!is_ideapad(codec))
+ return;
+ snd_hda_gen_add_mute_led_cdev(codec, NULL);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+#else /* CONFIG_IDEAPAD_LAPTOP */
+
+static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+}
+
+#endif /* CONFIG_IDEAPAD_LAPTOP */
diff --git a/sound/hda/codecs/helpers/ideapad_s740.c b/sound/hda/codecs/helpers/ideapad_s740.c
new file mode 100644
index 000000000000..564b9086e52d
--- /dev/null
+++ b/sound/hda/codecs/helpers/ideapad_s740.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/hda/codecs/helpers/thinkpad.c b/sound/hda/codecs/helpers/thinkpad.c
new file mode 100644
index 000000000000..de4d8deed102
--- /dev/null
+++ b/sound/hda/codecs/helpers/thinkpad.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/hda/codecs/realtek/Kconfig b/sound/hda/codecs/realtek/Kconfig
new file mode 100644
index 000000000000..cdc6d9509a01
--- /dev/null
+++ b/sound/hda/codecs/realtek/Kconfig
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_REALTEK
+ tristate "Realtek HD-audio codec support"
+ help
+ Say Y or M here to include Realtek HD-audio codec support.
+
+ This will enable all Realtek HD-audio codec drivers as default,
+ but you can enable/disable each codec driver individually, too
+ (only when CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_REALTEK
+
+config SND_HDA_CODEC_REALTEK_LIB
+ tristate
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ select SND_HDA_SCODEC_COMPONENT
+
+config SND_HDA_CODEC_ALC260
+ tristate "Build Realtek ALC260 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC260 HD-audio codec support
+
+config SND_HDA_CODEC_ALC262
+ tristate "Build Realtek ALC262 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC262 HD-audio codec support
+
+config SND_HDA_CODEC_ALC268
+ tristate "Build Realtek ALC268 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC268 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC269
+ tristate "Build Realtek ALC269 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC269 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC662
+ tristate "Build Realtek ALC662 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC662 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC680
+ tristate "Build Realtek ALC680 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC680 HD-audio codec support
+
+config SND_HDA_CODEC_ALC861
+ tristate "Build Realtek ALC861 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC861 HD-audio codec support
+
+config SND_HDA_CODEC_ALC861VD
+ tristate "Build Realtek ALC861-VD HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC861-VD HD-audio codec support
+
+config SND_HDA_CODEC_ALC880
+ tristate "Build Realtek ALC880 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC880 HD-audio codec support
+
+config SND_HDA_CODEC_ALC882
+ tristate "Build Realtek ALC882 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC882 and compatible HD-audio
+ codec support
+
+endif
diff --git a/sound/hda/codecs/realtek/Makefile b/sound/hda/codecs/realtek/Makefile
new file mode 100644
index 000000000000..c6ee4e526a40
--- /dev/null
+++ b/sound/hda/codecs/realtek/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-realtek-lib-y := realtek.o
+snd-hda-codec-alc260-y := alc260.o
+snd-hda-codec-alc262-y := alc262.o
+snd-hda-codec-alc268-y := alc268.o
+snd-hda-codec-alc269-y := alc269.o
+snd-hda-codec-alc662-y := alc662.o
+snd-hda-codec-alc680-y := alc680.o
+snd-hda-codec-alc861-y := alc861.o
+snd-hda-codec-alc861vd-y := alc861vd.o
+snd-hda-codec-alc880-y := alc880.o
+snd-hda-codec-alc882-y := alc882.o
+
+obj-$(CONFIG_SND_HDA_CODEC_REALTEK_LIB) += snd-hda-codec-realtek-lib.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC260) += snd-hda-codec-alc260.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC262) += snd-hda-codec-alc262.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC268) += snd-hda-codec-alc268.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC269) += snd-hda-codec-alc269.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC662) += snd-hda-codec-alc662.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC680) += snd-hda-codec-alc680.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC861) += snd-hda-codec-alc861.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC861VD) += snd-hda-codec-alc861vd.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC880) += snd-hda-codec-alc880.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC882) += snd-hda-codec-alc882.o
diff --git a/sound/hda/codecs/realtek/alc260.c b/sound/hda/codecs/realtek/alc260.c
new file mode 100644
index 000000000000..8bd47079dccb
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc260.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC260 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc260_parse_auto_config(struct hda_codec *codec)
+{
+ 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);
+}
+
+/*
+ * Pin config fixes
+ */
+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,
+};
+
+static void alc260_gpio1_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present);
+}
+
+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 },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ spec->init_amp = ALC_INIT_NONE;
+ break;
+ }
+}
+
+static void alc260_fixup_fsc_s7020(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->init_amp = ALC_INIT_NONE;
+}
+
+static void alc260_fixup_fsc_s7020_jwse(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->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
+ }
+}
+
+static const struct hda_fixup alc260_fixups[] = {
+ [ALC260_FIXUP_HP_DC5750] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
+ },
+ [ALC260_FIXUP_HP_PIN_0F] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x01214000 }, /* HP */
+ { }
+ }
+ },
+ [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 },
+ { }
+ },
+ },
+ [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 const struct hda_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),
+ {}
+};
+
+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 alc260_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
+ return err;
+
+ 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;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc260_codec_ops = {
+ .probe = alc260_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc260[] = {
+ HDA_CODEC_ID(0x10ec0260, "ALC260"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc260_driver = {
+ .id = snd_hda_id_alc260,
+ .ops = &alc260_codec_ops,
+};
+
+module_hda_codec_driver(alc260_driver);
diff --git a/sound/hda/codecs/realtek/alc262.c b/sound/hda/codecs/realtek/alc262.c
new file mode 100644
index 000000000000..3ec06cf5d2a6
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc262.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC262 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+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);
+}
+
+/*
+ * Pin config fixes
+ */
+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 */
+ { }
+ }
+ },
+ [ALC262_FIXUP_FSC_S7110] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x90170110 }, /* speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_HP_Z200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130120 }, /* internal speaker */
+ { }
+ }
+ },
+ [ALC262_FIXUP_TYAN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x1993e1f0 }, /* int AUX */
+ { }
+ }
+ },
+ [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 },
+ {}
+ }
+ },
+ [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 },
+ {}
+ }
+ },
+ [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,
+ },
+};
+
+static const struct hda_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 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"},
+ {}
+};
+
+/*
+ */
+static int alc262_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+
+ spec->shutup = alc_eapd_shutup;
+
+#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);
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc262_codec_ops = {
+ .probe = alc262_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc262[] = {
+ HDA_CODEC_ID(0x10ec0262, "ALC262"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc262);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC262 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc262_driver = {
+ .id = snd_hda_id_alc262,
+ .ops = &alc262_codec_ops,
+};
+
+module_hda_codec_driver(alc262_driver);
diff --git a/sound/hda/codecs/realtek/alc268.c b/sound/hda/codecs/realtek/alc268.c
new file mode 100644
index 000000000000..4b565fb7bd1c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc268.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/* 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)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int err;
+
+ guard(mutex)(&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;
+ return err;
+}
+
+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)
+ },
+};
+
+/* 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)},
+ { }
+};
+
+enum {
+ ALC268_FIXUP_INV_DMIC,
+ ALC268_FIXUP_HP_EAPD,
+ ALC268_FIXUP_SPDIF,
+};
+
+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 */
+ {}
+ }
+ },
+};
+
+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"},
+ {}
+};
+
+static const struct hda_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
+ */
+ SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD),
+ {}
+};
+
+/*
+ * 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);
+}
+
+/*
+ */
+static int alc268_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int i, err;
+
+ /* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* 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);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc268_codec_ops = {
+ .probe = alc268_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc268[] = {
+ HDA_CODEC_ID(0x10ec0267, "ALC267"),
+ HDA_CODEC_ID(0x10ec0268, "ALC268"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC267/268 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc268_driver = {
+ .id = snd_hda_id_alc268,
+ .ops = &alc268_codec_ops,
+};
+
+module_hda_codec_driver(alc268_driver);
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
new file mode 100644
index 000000000000..214eb9df6ef8
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -0,0 +1,8193 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC269 and compatible codecs
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
+
+static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+};
+
+static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+};
+
+/* 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,
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
+{
+ 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;
+
+ 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;
+ }
+
+ return alc_parse_auto_config(codec, alc269_ignore, ssids);
+}
+
+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 },
+ {}
+};
+
+static void alc_headset_btn_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ int report = 0;
+
+ if (jack->unsol_res & (7 << 13))
+ report |= SND_JACK_BTN_0;
+
+ if (jack->unsol_res & (1 << 16 | 3 << 8))
+ report |= SND_JACK_BTN_1;
+
+ /* Volume up key */
+ if (jack->unsol_res & (7 << 23))
+ report |= SND_JACK_BTN_2;
+
+ /* Volume down key */
+ if (jack->unsol_res & (7 << 10))
+ report |= SND_JACK_BTN_3;
+
+ snd_hda_jack_set_button_state(codec, jack->nid, report);
+}
+
+static void alc_disable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ 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 0x10ec0257:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ break;
+ }
+}
+
+static void alc_enable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ 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 0x10ec0257:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ break;
+ }
+}
+
+static void alc_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
+{
+ alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
+}
+
+static void alc269_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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 */
+ {}
+};
+
+static void alc282_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc282_coefs);
+}
+
+static void alc282_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;
+ int coef78;
+
+ alc282_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
+
+ /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ 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);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
+
+static void alc282_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;
+ int coef78;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ 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 void alc283_restore_default_value(struct hda_codec *codec)
+{
+ 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;
+
+ alc283_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+
+ msleep(30);
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ 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 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;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ /*depop hp during suspend*/
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
+
+ if (hp_pin_sense)
+ msleep(100);
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
+
+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;
+
+ 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);
+ }
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ msleep(30);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ msleep(75);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+ }
+ alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
+ 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 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;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ /* 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 (spec->en_3kpull_low)
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+
+ 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);
+ }
+}
+
+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;
+
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */
+ 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);
+
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+
+ 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);
+
+ 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);
+
+ 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 */
+
+ 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);
+
+ msleep(50);
+ alc_update_coef_idx(codec, 0x4a, 1<<15, 0);
+}
+
+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;
+
+ 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);
+ }
+
+ 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;
+ }
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+ msleep(30);
+
+ 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);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ if (hp1_pin_sense)
+ 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);
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+ }
+}
+
+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;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ 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) {
+ alc_disable_headset_jack_key(codec);
+ /* 3k pull low control for Headset jack. */
+ alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
+ msleep(2);
+
+ if (hp1_pin_sense)
+ 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);
+
+ msleep(75);
+
+ if (hp1_pin_sense)
+ 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);
+
+ msleep(75);
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_enable_headset_jack_key(codec);
+ }
+ 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 void alc222_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;
+
+ if (!hp_pin)
+ return;
+
+ msleep(30);
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ msleep(2);
+
+ if (hp1_pin_sense)
+ 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, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ }
+}
+
+static void alc222_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;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ msleep(2);
+
+ if (hp1_pin_sense)
+ 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, 0x14, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+}
+
+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;
+
+ msleep(30);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ msleep(75);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ msleep(75);
+ }
+}
+
+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;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+ 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;
+
+ if (!hp_pin)
+ return;
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(100);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ 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 */
+
+ /* 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);
+}
+
+static void alc294_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* required only at boot or S4 resume time */
+ if (!spec->done_hp_init || is_s4_resume(codec)) {
+ alc294_hp_init(codec);
+ spec->done_hp_init = true;
+ }
+ alc_default_init(codec);
+}
+
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
+{
+ 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 int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
+{
+ unsigned int val;
+
+ 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 alc5505_dsp_halt(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ 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 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);
+}
+
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ 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
+}
+
+#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
+
+static int alc269_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_suspend(codec);
+
+ return alc_suspend(codec);
+}
+
+static int alc269_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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);
+ }
+
+ snd_hda_codec_init(codec);
+
+ 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);
+ }
+
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+
+ /* 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);
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_resume(codec);
+
+ return 0;
+}
+
+static void alc269_fixup_pincfg_no_hp_to_lineout(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->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+}
+
+static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ 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));
+}
+
+static void alc269_fixup_hweq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x1e, 0, 0x80);
+}
+
+static void alc271_fixup_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_verb verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+ {}
+ };
+ unsigned int cfg;
+
+ 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);
+}
+
+/* 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)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000);
+}
+
+static void alc269_fixup_pcm_44k(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ /* 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 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);
+}
+
+static void alc269_quanta_automute(struct hda_codec *codec)
+{
+ snd_hda_gen_update_outputs(codec);
+
+ alc_write_coef_idx(codec, 0x0c, 0x680);
+ alc_write_coef_idx(codec, 0x0c, 0x480);
+}
+
+static void alc269_fixup_quanta_mute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ spec->gen.automute_hook = alc269_quanta_automute;
+}
+
+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);
+
+ 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);
+}
+
+/*
+ * Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
+ */
+struct hda_alc298_mbxinit {
+ unsigned char value_0x23;
+ unsigned char value_0x25;
+};
+
+static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
+ const struct hda_alc298_mbxinit *initval,
+ bool first)
+{
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
+ alc_write_coef_idx(codec, 0x26, 0xb000);
+
+ if (first)
+ snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+
+ 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_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 alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /* 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;
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ /* 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);
+
+ for (seq = dac_init; seq->value_0x23; seq++)
+ alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
+}
+
+static void alc269_fixup_x101_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
+}
+
+static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin,
+ bool polarity, bool on)
+{
+ unsigned int pinval;
+
+ 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 */
+ CLASS(snd_hda_power_pm, pm)(codec);
+ snd_hda_set_pin_ctl_cache(codec, pin, pinval);
+}
+
+/* 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;
+
+ alc_update_vref_led(codec, spec->mute_led_nid,
+ spec->mute_led_polarity, brightness);
+ return 0;
+}
+
+/* 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)
+{
+ 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 alc269_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
+ }
+}
+
+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;
+
+ 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 alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
+}
+
+static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
+}
+
+static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
+}
+
+static void alc236_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01);
+}
+
+static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
+}
+
+static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
+}
+
+static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20);
+}
+
+static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
+}
+
+static void alc245_fixup_hp_gpio_led(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->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+/* 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)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_vref_led(codec, spec->cap_mute_led_nid,
+ spec->micmute_led_polarity, brightness);
+ return 0;
+}
+
+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;
+
+ 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 alc280_fixup_hp_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+/* 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;
+
+ 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;
+ }
+}
+
+/* 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)
+{
+ 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;
+ }
+}
+
+static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x04;
+ spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook;
+ }
+}
+
+static void alc_update_coef_led(struct hda_codec *codec,
+ struct alc_coef_led *led,
+ bool polarity, bool on)
+{
+ 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);
+}
+
+/* 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;
+
+ alc_update_coef_led(codec, &spec->mute_led_coef,
+ spec->mute_led_polarity, brightness);
+ return 0;
+}
+
+static void alc285_fixup_hp_mute_led_coefbit(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->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);
+ }
+}
+
+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 (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);
+ }
+}
+
+static void alc236_fixup_hp_mute_led_coefbit2(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->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x07;
+ spec->mute_led_coef.mask = 1;
+ spec->mute_led_coef.on = 1;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc245_fixup_hp_mute_led_coefbit(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->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 3 << 2;
+ spec->mute_led_coef.on = 2 << 2;
+ spec->mute_led_coef.off = 1 << 2;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc245_fixup_hp_mute_led_v1_coefbit(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->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 3 << 2;
+ 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);
+ }
+}
+
+/* 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;
+
+ 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);
+ }
+}
+
+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;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+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;
+
+ 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 alc295_fixup_hp_mute_led_coefbit11(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->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0xb;
+ spec->mute_led_coef.mask = 3 << 3;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 1 << 4;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
+}
+
+static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+static void alc236_fixup_hp_micmute_led_vref(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->cap_mute_led_nid = 0x1a;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
+ }
+}
+
+static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_micmute_led_vref(codec, fix, action);
+}
+
+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);
+}
+
+struct alc298_samsung_amp_desc {
+ unsigned char nid;
+ unsigned short init_seq[2][2];
+};
+
+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 } } }
+ };
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ alc_write_coef_idx(codec, 0x22, amps[i].nid);
+
+ for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]);
+
+ for (j = 0; j < ARRAY_SIZE(init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, init_seq[j]);
+ }
+}
+
+struct alc298_samsung_v2_amp_desc {
+ unsigned short nid;
+ int init_seq_size;
+ unsigned short init_seq[18][2];
+};
+
+static const struct alc298_samsung_v2_amp_desc
+alc298_samsung_v2_amp_desc_tbl[] = {
+ { 0x38, 18, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
+ { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
+ { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
+ }},
+ { 0x39, 18, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
+ { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
+ { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
+ }},
+ { 0x3c, 15, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
+ { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
+ }},
+ { 0x3d, 15, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
+ { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
+ }}
+};
+
+static void alc298_samsung_v2_enable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ static const unsigned short enable_seq[][2] = {
+ { 0x203a, 0x0081 }, { 0x23ff, 0x0001 },
+ };
+ int i, j;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < ARRAY_SIZE(enable_seq); j++)
+ alc298_samsung_write_coef_pack(codec, enable_seq[j]);
+ codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+}
+
+static void alc298_samsung_v2_disable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ static const unsigned short disable_seq[][2] = {
+ { 0x23ff, 0x0000 }, { 0x203a, 0x0080 },
+ };
+ int i, j;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < ARRAY_SIZE(disable_seq); j++)
+ alc298_samsung_write_coef_pack(codec, disable_seq[j]);
+ codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+}
+
+static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ /* Dynamically enable/disable speaker amps before and after playback */
+ if (action == HDA_GEN_PCM_ACT_OPEN)
+ alc298_samsung_v2_enable_amps(codec);
+ if (action == HDA_GEN_PCM_ACT_CLOSE)
+ alc298_samsung_v2_disable_amps(codec);
+}
+
+static void alc298_samsung_v2_init_amps(struct hda_codec *codec,
+ int num_speaker_amps)
+{
+ struct alc_spec *spec = codec->spec;
+ int i, j;
+
+ /* Set spec's num_speaker_amps before doing anything else */
+ spec->num_speaker_amps = num_speaker_amps;
+
+ /* Disable speaker amps before init to prevent any physical damage */
+ alc298_samsung_v2_disable_amps(codec);
+
+ /* Initialize the speaker amps */
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) {
+ alc298_samsung_write_coef_pack(codec,
+ alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]);
+ }
+ alc_write_coef_idx(codec, 0x89, 0x0);
+ codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+
+ /* register hook to enable speaker amps only when they are needed */
+ spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook;
+}
+
+static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc298_samsung_v2_init_amps(codec, 2);
+}
+
+static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc298_samsung_v2_init_amps(codec, 4);
+}
+
+static void gpio2_mic_hotkey_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* 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);
+}
+
+static int alc_register_micmute_input_device(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
+ }
+
+ spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
+
+ 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);
+
+ 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;
+ }
+
+ return 0;
+}
+
+/* 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)
+{
+ 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;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
+/* 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;
+
+ 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;
+
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
+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;
+
+ 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 void alc233_fixup_lenovo_low_en_micmute_led(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->micmute_led_polarity = 1;
+ alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action);
+}
+
+static void alc255_set_default_jack_type(struct hda_codec *codec)
+{
+ /* 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);
+}
+
+static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc255_set_default_jack_type(codec);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 void alc288_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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);
+}
+
+static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 void alc_fixup_no_shutup(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->no_shutup_pins = 1;
+ }
+}
+
+/* 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)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x16, 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;
+ codec->power_save_node = 0; /* avoid click noises */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
+static void alc_fixup_tpt470_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 void alc_fixup_tpt470_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* 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;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static void alc295_fixup_asus_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t preferred_pairs[] = {
+ 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int mic_pin = alc_find_ext_mic_pin(codec);
+ int hp_pin = alc_get_hp_pin(spec);
+
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
+ return;
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
+ }
+}
+
+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;
+
+ /* The mic boosts on level 2 and 3 are too noisy
+ on the internal mic input.
+ Therefore limit the boost to 0 or 1. */
+
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ 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;
+
+ 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 void alc283_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);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *codec)
+{
+ 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 alc282_fixup_asus_tx300(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
+ }
+}
+
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 alc298_fixup_speaker_volume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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);
+ }
+}
+
+/* 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)
+{
+ 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);
+ }
+}
+
+/* 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)
+{
+ 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);
+ }
+}
+
+/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */
+static void alc294_fixup_bass_speaker_15(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+/* 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;
+
+ 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);
+}
+
+/* Manage GPIOs for HP EliteBook Folio 9480m.
+ *
+ * GPIO4 is the headphone amplifier power control
+ * GPIO3 is the audio output mute indicator LED
+ */
+
+static void alc280_fixup_hp_9480m(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+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 */
+ }
+}
+
+/* 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 void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 (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 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;
+ }
+}
+
+static void alc225_fixup_s3_pop_noise(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->power_save_node = 1;
+}
+
+/* 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)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02,
+ 0
+ };
+
+ 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;
+}
+
+/* avoid DAC 0x06 for speaker switch 0x17; it has no volume control */
+static void alc274_fixup_hp_aio_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ /* The speaker is routed to the Node 0x06 by a mistake, thus the
+ * speaker's volume can't be adjusted since the node doesn't have
+ * Amp-out capability. Assure the speaker and lineout pin to be
+ * coupled with DAC NID 0x02.
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x16, 0x02, 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+/* 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;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+/* 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)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
+static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec)
+{
+ 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 0x10ec0257:
+ case 0x19e58326:
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15);
+ break;
+ }
+}
+
+static void alc295_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+static void alc256_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ if (codec->core.subsystem_id == 0x10280d76)
+ spec->gen.suppress_auto_mute = 0;
+ else
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ spec->en_3kpull_low = false;
+ break;
+ }
+}
+
+static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+}
+
+
+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
+ */
+ 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 void alc294_fixup_gx502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Pin 0x21: headphones/headset mic */
+ if (!is_jack_detectable(codec, 0x21))
+ return;
+
+ 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;
+ }
+}
+
+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);
+}
+
+static void alc294_fixup_gu502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (!is_jack_detectable(codec, 0x21))
+ return;
+
+ 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;
+ }
+}
+
+static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ msleep(100);
+ alc_write_coef_idx(codec, 0x65, 0x0);
+}
+
+static void alc274_fixup_hp_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ switch (action) {
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
+
+static void alc_fixup_no_int_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+/* 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)
+{
+ static const hda_nid_t conn[] = { 0x02 };
+
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* front/high speakers */
+ { 0x17, 0x90170130 }, /* back/bass speakers */
+ { }
+ };
+
+ //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;
+ }
+}
+
+/* GPIO1 = amplifier on/off */
+static void alc285_fixup_hp_spectre_x360_df1(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* front/high speakers */
+ { 0x17, 0x90170130 }, /* back/bass speakers */
+ { }
+ };
+
+ // enable mute led
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* 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 alc285_fixup_hp_spectre_x360(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 alc285_fixup_hp_envy_x360(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const struct coef_fw coefs[] = {
+ WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023),
+ WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03),
+ WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a),
+ WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014),
+ WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15),
+ WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489),
+ WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0),
+ WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000),
+ WRITE_COEF(0x6e, 0x1005), { }
+ };
+
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x12, 0xb7a60130 }, /* Internal microphone*/
+ { 0x14, 0x90170150 }, /* B&O soundbar speakers */
+ { 0x17, 0x90170153 }, /* Side speakers */
+ { 0x19, 0x03a11040 }, /* Headset microphone */
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+
+ /* Fixes volume control problem for side speakers */
+ alc295_fixup_disable_dac3(codec, fix, action);
+
+ /* Fixes no sound from headset speaker */
+ snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0);
+
+ /* Auto-enable headset mic when plugged */
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x21);
+
+ /* Headset mic volume enhancement */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, coefs);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ rename_ctl(codec, "Bass Speaker Playback Volume",
+ "B&O-Tuned Playback Volume");
+ rename_ctl(codec, "Front Playback Switch",
+ "B&O Soundbar Playback Switch");
+ rename_ctl(codec, "Bass Speaker Playback Switch",
+ "Side Speaker Playback Switch");
+ break;
+ }
+}
+
+static void alc285_fixup_hp_beep(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->beep_just_power_on = true;
+ } else if (action == HDA_FIXUP_ACT_INIT) {
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ /*
+ * Just enable loopback to internal speaker and headphone jack.
+ * Disable amplification to get about the same beep volume as
+ * was on pure BIOS setup before loading the driver.
+ */
+ alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13));
+
+ snd_hda_enable_beep_device(codec, 1);
+
+#if !IS_ENABLED(CONFIG_INPUT_PCSPKR)
+ dev_warn_once(hda_codec_dev(codec),
+ "enable CONFIG_INPUT_PCSPKR to get PC beeps\n");
+#endif
+#endif
+ }
+}
+
+/* for hda_fixup_thinkpad_acpi() */
+#include "../helpers/thinkpad.c"
+
+static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */
+ hda_fixup_thinkpad_acpi(codec, fix, action);
+}
+
+/* for hda_fixup_ideapad_acpi() */
+#include "../helpers/ideapad_hotkey_led.c"
+
+static void alc_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ hda_fixup_ideapad_acpi(codec, fix, action);
+}
+
+/* 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;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.suppress_auto_mute = 1;
+ break;
+ }
+}
+
+static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct hda_codec *cdc = data;
+ struct alc_spec *spec = cdc->spec;
+
+ codec_info(cdc, "ACPI Notification %d\n", event);
+
+ hda_component_acpi_device_notify(&spec->comps, handle, event, data);
+}
+
+static int comp_bind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+ int ret;
+
+ ret = hda_component_manager_bind(cdc, &spec->comps);
+ if (ret)
+ return ret;
+
+ return hda_component_manager_bind_acpi_notifications(cdc,
+ &spec->comps,
+ comp_acpi_device_notify, cdc);
+}
+
+static void comp_unbind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify);
+ hda_component_manager_unbind(cdc, &spec->comps);
+}
+
+static const struct component_master_ops comp_master_ops = {
+ .bind = comp_bind,
+ .unbind = comp_unbind,
+};
+
+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;
+
+ hda_component_manager_playback_hook(&spec->comps, action);
+}
+
+static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+ const char *hid, const char *match_str, int count)
+{
+ struct alc_spec *spec = cdc->spec;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid,
+ match_str, &comp_master_ops);
+ if (ret)
+ return;
+
+ spec->gen.pcm_playback_hook = comp_generic_playback_hook;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ hda_component_manager_free(&spec->comps, &comp_master_ops);
+ break;
+ }
+}
+
+static void find_cirrus_companion_amps(struct hda_codec *cdc)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct acpi_device *adev;
+ struct fwnode_handle *fwnode __free(fwnode_handle) = NULL;
+ const char *bus = NULL;
+ static const struct {
+ const char *hid;
+ const char *name;
+ } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" },
+ { "CSC3556", "cs35l56-hda" },
+ { "CSC3557", "cs35l57-hda" }};
+ char *match;
+ int i, count = 0, count_devindex = 0;
+
+ for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) {
+ adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1);
+ if (adev)
+ break;
+ }
+ if (!adev) {
+ codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n");
+ return;
+ }
+
+ count = i2c_acpi_client_count(adev);
+ if (count > 0) {
+ bus = "i2c";
+ } else {
+ count = acpi_spi_count_resources(adev);
+ if (count > 0)
+ bus = "spi";
+ }
+
+ fwnode = fwnode_handle_get(acpi_fwnode_handle(adev));
+ acpi_dev_put(adev);
+
+ if (!bus) {
+ codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ if (!fwnode) {
+ codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ /*
+ * When available the cirrus,dev-index property is an accurate
+ * count of the amps in a system and is used in preference to
+ * the count of bus devices that can contain additional address
+ * alias entries.
+ */
+ count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index");
+ if (count_devindex > 0)
+ count = count_devindex;
+
+ match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name);
+ if (!match)
+ return;
+ codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match);
+ comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count);
+}
+
+static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
+}
+
+static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1);
+}
+
+static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
+}
+
+static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ /*
+ * The same SSID has been re-used in different hardware, they have
+ * different codecs and the newer GA403U has a ALC285.
+ */
+ if (cdc->core.vendor_id != 0x10ec0285)
+ alc_fixup_inv_dmic(cdc, fix, action);
+}
+
+static void tas2781_fixup_tias_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
+}
+
+static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2);
+}
+
+static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1);
+}
+
+static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1);
+}
+
+static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+
+/* for alc295_fixup_hp_top_speakers */
+#include "../helpers/hp_x360.c"
+
+/* for alc285_fixup_ideapad_s740_coef() */
+#include "../helpers/ideapad_s740.c"
+
+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)
+{
+ /*
+ * 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.
+ */
+ alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs);
+}
+
+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),
+ {}
+};
+
+static void alc233_fixup_no_audio_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /*
+ * 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 void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /*
+ * 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);
+ }
+}
+
+static void alc256_decrease_headphone_amp_val(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ u32 caps;
+ u8 nsteps, offs;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ caps = query_amp_caps(codec, 0x3, HDA_OUTPUT);
+ nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10;
+ offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10;
+ caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET;
+ caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT);
+
+ if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps))
+ codec_warn(codec, "failed to override amp caps for NID 0x3\n");
+}
+
+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;
+
+ 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;
+ }
+ }
+ break;
+ }
+}
+
+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;
+
+ /* Support Audio mute LED and Mic mute LED on keyboard */
+ hda_fixup_ideapad_acpi(codec, fix, action);
+
+ 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;
+ }
+}
+
+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;
+
+ alc_fixup_no_shutup(codec, fix, action);
+
+ 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;
+ }
+}
+
+/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */
+static void alc287_fixup_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x17, 0x02, 0x21, 0x03, 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ spec->gen.auto_mute_via_amp = 1;
+ if (spec->gen.autocfg.speaker_pins[0] != 0x14) {
+ snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ 0x0); /* Make sure 0x14 was disable */
+ }
+}
+
+/* Fix none verb table of Headset Mic pin */
+static void alc2xx_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x03a1103c },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ break;
+ }
+}
+
+static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /*
+ * The Pin Complex 0x14 for the treble speakers is wrongly reported as
+ * unconnected.
+ * The Pin Complex 0x17 for the bass speakers has the lowest association
+ * and sequence values so shift it up a bit to squeeze 0x14 in.
+ */
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, // top/treble
+ { 0x17, 0x90170111 }, // bottom/bass
+ { }
+ };
+
+ /*
+ * Force DAC 0x02 for the bass speakers 0x17.
+ */
+ static const hda_nid_t conn[] = { 0x02 };
+
+ 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);
+ break;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc245_fixup_hp_gpio_led(codec, fix, action);
+}
+
+/* some changes for Spectre x360 16, 2024 model */
+static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /*
+ * The Pin Complex 0x14 for the treble speakers is wrongly reported as
+ * unconnected.
+ * The Pin Complex 0x17 for the bass speakers has the lowest association
+ * and sequence values so shift it up a bit to squeeze 0x14 in.
+ */
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, // top/treble
+ { 0x17, 0x90170111 }, // bottom/bass
+ { }
+ };
+
+ /*
+ * Force DAC 0x02 for the bass speakers 0x17.
+ */
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* needed for amp of back speakers */
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ 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;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc245_fixup_hp_gpio_led(codec, fix, action);
+}
+
+static void alc245_fixup_hp_zbook_firefly_g12a(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.auto_mute_via_amp = 1;
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+/*
+ * ALC287 PCM hooks
+ */
+static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ break;
+ }
+}
+
+static void alc287_s4_power_gpio3_default(struct hda_codec *codec)
+{
+ if (is_s4_suspend(codec)) {
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ }
+}
+
+static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coefs[] = {
+ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300),
+ WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023),
+ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301),
+ WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023),
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11);
+ alc_process_coef_fw(codec, coefs);
+ spec->power_hook = alc287_s4_power_gpio3_default;
+ spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook;
+}
+
+/*
+ * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly
+ * at PM resume
+ */
+static void alc283_fixup_dell_hp_resume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_write_coef_idx(codec, 0xd, 0x2800);
+}
+
+enum {
+ ALC269_FIXUP_GPIO2,
+ ALC269_FIXUP_SONY_VAIO,
+ ALC275_FIXUP_SONY_VAIO_GPIO2,
+ ALC269_FIXUP_DELL_M101Z,
+ 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_DELL1_LIMIT_INT_MIC_BOOST,
+ 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,
+ ALC295_FIXUP_HP_MUTE_LED_COEFBIT11,
+ ALC269_FIXUP_THINKPAD_ACPI,
+ ALC269_FIXUP_LENOVO_XPAD_ACPI,
+ ALC269_FIXUP_DMIC_THINKPAD_ACPI,
+ ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
+ ALC269VC_FIXUP_INFINIX_Y4_MAX,
+ ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO,
+ ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ 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,
+ ALC285_FIXUP_HP_SPECTRE_X360_DF1,
+ ALC285_FIXUP_HP_ENVY_X360,
+ 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,
+ ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED,
+ 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,
+ ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS,
+ ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_I2C_HEADSET_MIC,
+ 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_I2C_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,
+ ALC255_FIXUP_PREDATOR_SUBWOOFER,
+ ALC299_FIXUP_PREDATOR_SPK,
+ ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE,
+ ALC289_FIXUP_DELL_SPK1,
+ ALC289_FIXUP_DELL_SPK2,
+ ALC289_FIXUP_DUAL_SPK,
+ ALC289_FIXUP_RTK_AMP_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_ALLY,
+ ALC294_FIXUP_ASUS_ALLY_PINS,
+ ALC294_FIXUP_ASUS_ALLY_VERBS,
+ ALC294_FIXUP_ASUS_ALLY_SPEAKER,
+ 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,
+ ALC285_FIXUP_HP_BEEP_MICMUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_COEFBIT2,
+ ALC236_FIXUP_HP_GPIO_LED,
+ ALC236_FIXUP_HP_MUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
+ ALC236_FIXUP_LENOVO_INV_DMIC,
+ ALC298_FIXUP_SAMSUNG_AMP,
+ ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS,
+ ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS,
+ 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,
+ ALC274_FIXUP_ASUS_ZEN_AIO_27,
+ 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,
+ ALC287_FIXUP_CS35L41_I2C_4,
+ ALC245_FIXUP_CS35L41_SPI_1,
+ 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,
+ ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN,
+ ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
+ ALC236_FIXUP_DELL_DUAL_CODECS,
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ ALC287_FIXUP_TAS2781_I2C,
+ ALC295_FIXUP_DELL_TAS2781_I2C,
+ ALC245_FIXUP_TAS2781_SPI_2,
+ ALC287_FIXUP_TXNW2781_I2C,
+ ALC287_FIXUP_YOGA7_14ARB7_I2C,
+ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
+ ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT,
+ ALC245_FIXUP_HP_X360_MUTE_LEDS,
+ ALC287_FIXUP_THINKPAD_I2S_SPK,
+ ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
+ ALC2XX_FIXUP_HEADSET_MIC,
+ ALC289_FIXUP_DELL_CS35L41_SPI_2,
+ ALC294_FIXUP_CS35L41_I2C_2,
+ ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
+ ALC256_FIXUP_HEADPHONE_AMP_VOL,
+ ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX,
+ ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX,
+ ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A,
+ ALC285_FIXUP_ASUS_GA403U,
+ ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1,
+ ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318,
+ ALC256_FIXUP_CHROME_BOOK,
+ ALC245_FIXUP_CLEVO_NOISY_MIC,
+ ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE,
+ ALC233_FIXUP_MEDION_MTL_SPK,
+ ALC294_FIXUP_BASS_SPEAKER_15,
+ ALC283_FIXUP_DELL_HP_RESUME,
+ ALC294_FIXUP_ASUS_CS35L41_SPI_2,
+ ALC274_FIXUP_HP_AIO_BIND_DACS,
+ ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2,
+ ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1,
+ ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC,
+ ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK,
+};
+
+/* 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 hda_fixup alc269_fixups[] = {
+ [ALC269_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC269_FIXUP_SONY_VAIO] = {
+ .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] = {
+ .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_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC269_FIXUP_ASUS_G73JW] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x99130111 }, /* subwoofer */
+ { }
+ }
+ },
+ [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 = alc_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,
+ },
+ [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */
+ { 0x1b, 0x90170152 }, /* use as internal speaker (back) */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC269VC_FIXUP_INFINIX_Y4_MAX] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170150 }, /* use as internal speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a19020 }, /* headset mic */
+ { 0x1b, 0x90170150 }, /* speaker */
+ { }
+ },
+ },
+ [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_DELL1_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [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_LENOVO_XPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_ideapad_acpi,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [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_DELL1_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [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_LENOVO_L2MH_LOW_ENLED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_low_en_micmute_led,
+ },
+ [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
+ },
+ [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_CS35L41_SPI_2
+ },
+ [ALC285_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170120 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2
+ },
+ [ALC285_FIXUP_ASUS_I2C_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1
+ },
+ [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_I2C_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a19020 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2
+ },
+ [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
+ },
+ [ALC255_FIXUP_PREDATOR_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */
+ { 0x1b, 0x90170152 } /* use as internal speaker (back) */
+ }
+ },
+ [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 */
+ { }
+ }
+ },
+ [ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_PREDATOR_SUBWOOFER
+ },
+ [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_SPK1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170140 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [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
+ },
+ [ALC289_FIXUP_RTK_AMP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK1
+ },
+ [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
+ },
+ [ALC294_FIXUP_ASUS_ALLY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
+ },
+ [ALC294_FIXUP_ASUS_ALLY_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1a, 0x03a11c30 },
+ { 0x21, 0x03211420 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_VERBS
+ },
+ [ALC294_FIXUP_ASUS_ALLY_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x46 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0004 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x47 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xa47a },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0049},
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x201b },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4278},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER
+ },
+ [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_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,
+ },
+ [ALC295_FIXUP_DELL_TAS2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_tias_i2c,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DUAL_SPK
+ },
+ [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,
+ },
+ [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_beep,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_coefbit2,
+ },
+ [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,
+ },
+ [ALC236_FIXUP_LENOVO_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_INT_MIC,
+ },
+ [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_hp_mute_led_coefbit11,
+ },
+ [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_AMP_V2_2_AMPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp_v2_2_amps
+ },
+ [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp_v2_4_amps
+ },
+ [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,
+ },
+ [ALC274_FIXUP_ASUS_ZEN_AIO_27] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x202b },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC2XX_FIXUP_HEADSET_MIC,
+ },
+ [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
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_DF1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_df1
+ },
+ [ALC285_FIXUP_HP_ENVY_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_envy_x360,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ },
+ [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,
+ },
+ [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,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_four,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_one,
+ },
+ [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,
+ },
+ [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2,
+ },
+ [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,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_TAS2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_tias_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC245_FIXUP_TAS2781_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_spi,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC287_FIXUP_TXNW2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_txnw_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = yoga7_14arb7_fixup_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ },
+ [ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_v1_coefbit,
+ },
+ [ALC245_FIXUP_HP_X360_MUTE_LEDS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
+ [ALC287_FIXUP_THINKPAD_I2S_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ },
+ [ALC2XX_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc2xx_fixup_headset_mic,
+ },
+ [ALC289_FIXUP_DELL_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DUAL_SPK
+ },
+ [ALC294_FIXUP_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ },
+ [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_acer_sfg16_micmute_led,
+ },
+ [ALC256_FIXUP_HEADPHONE_AMP_VOL] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_decrease_headphone_amp_val,
+ },
+ [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_spectre_x360_eu0xxx,
+ },
+ [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx,
+ },
+ [ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_zbook_firefly_g12a,
+ },
+ [ALC285_FIXUP_ASUS_GA403U] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_asus_ga403u,
+ },
+ [ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
+ },
+ [ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ },
+ [ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA403U,
+ },
+ [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC256_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_chromebook,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK
+ },
+ [ALC245_FIXUP_CLEVO_NOISY_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ },
+ [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x20a11040 }, /* dock mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC233_FIXUP_MEDION_MTL_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 },
+ { }
+ },
+ },
+ [ALC294_FIXUP_BASS_SPEAKER_15] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_bass_speaker_15,
+ },
+ [ALC283_FIXUP_DELL_HP_RESUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_dell_hp_resume,
+ },
+ [ALC294_FIXUP_ASUS_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC,
+ },
+ [ALC274_FIXUP_HP_AIO_BIND_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_aio_bind_dacs,
+ },
+ [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ },
+ [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
+ },
+ [ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170151 }, /* Internal Speaker LFE */
+ { 0x1e, 0x90170150 }, /* Internal Speaker */
+ { }
+ },
+ }
+};
+
+static const struct hda_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, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
+ 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, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER),
+ SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER),
+ 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, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ 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, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ 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(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ 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, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME),
+ 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, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ 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, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
+ 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, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ 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, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4),
+ SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC295_FIXUP_DELL_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC295_FIXUP_DELL_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc0, "Dell Oasis 13", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ 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", ALC295_FIXUP_HP_X360),
+ 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, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11),
+ SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_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, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1),
+ 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 EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ 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, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87cc, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_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, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ 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, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
+ SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED),
+ 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, 0x89a0, "HP Laptop 15-dw4xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ 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, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a26, "HP Victus 16-d1xxx (MB 8A26)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
+ SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ 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, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_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, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_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, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ 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, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ 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, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2),
+ 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_4_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, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c99, "HP Victus 16-r1xxx (MB 8C99)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d07, "HP Victus 15-fb2xxx (MB 8D07)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS),
+ SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS),
+ SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
+ SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
+ SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ded, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dfb, "HP EliteBook 6 G1a 14", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1d, "HP ZBook X Gli 16 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ 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, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK),
+ SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C),
+ 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, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C),
+ 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, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ 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, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1454, "ASUS PM3406CKA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1493, "ASUS GV601VV/VU/VJ/VQ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G614JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS G513PI/PU/PV", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x14f2, "ASUS VivoBook X515JA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1503, "ASUS G733PY/PZ/PZV/PYV", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
+ SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS UX7602VI/BZ", ALC245_FIXUP_CS35L41_SPI_2),
+ 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, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY),
+ SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2),
+ 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, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2),
+ 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, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ 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/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1c80, "ASUS VivoBook TP401", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1ccf, "ASUS G814JU/JV/JI", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1cdf, "ASUS G814JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1cef, "ASUS G834JY/JZ/JI/JG", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS G713PI/PU/PV/PVN", ALC287_FIXUP_CS35L41_I2C_2),
+ 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, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15),
+ SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15),
+ SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ 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, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC),
+ 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, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
+ 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, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
+ SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27),
+ SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ 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(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1),
+ 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, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ 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(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ 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, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", 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, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
+ SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ 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(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ 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, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x35a1, "Clevo V3[56]0EN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x35b1, "Clevo V3[57]0WN[MNP]Q", ALC256_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, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", 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, 0x7724, "Clevo L140AU", 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, 0xa554, "VAIO VJFH52", ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ SND_PCI_QUIRK(0x1558, 0xa743, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ 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, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C),
+ 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_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C),
+ 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, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C),
+ 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),
+ HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ 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, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1),
+ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */
+ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ 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, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ 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(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL),
+ SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL),
+ SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ 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(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC),
+ 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, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ 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(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ 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(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13),
+ SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
+ SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
+ SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
+ SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX),
+ SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX),
+ SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK),
+ 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(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x000b, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x000c, "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
+ {}
+};
+
+static const struct hda_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, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI),
+ SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED),
+ {}
+};
+
+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 = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"},
+ {.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_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"},
+ {.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 = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"},
+ {.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 = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"},
+ {.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 = ALC285_FIXUP_HP_SPECTRE_X360_DF1, .name = "alc285-hp-spectre-x360-df1"},
+ {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"},
+ {.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"},
+ {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"},
+ {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"},
+ {}
+};
+#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(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
+ {0x17, 0x90170110},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
+ {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ 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(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}),
+ {}
+};
+
+/* 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(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1b, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET,
+ {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_LIMIT_INT_MIC_BOOST,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ {}
+};
+
+static void alc269_fill_coef(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int val;
+
+ 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_get_coef0(codec) & 0x00ff) == 0x016) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8814);
+ }
+
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
+ /* Power up output pin */
+ alc_update_coef_idx(codec, 0x04, 0, 1<<11);
+ }
+
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ val = alc_read_coef_idx(codec, 0xd);
+ if (val != -1 && (val & 0x0c00) >> 10 != 0x1) {
+ /* Capless ramp up clock control */
+ alc_write_coef_idx(codec, 0xd, val | (1<<10));
+ }
+ val = alc_read_coef_idx(codec, 0x17);
+ if (val != -1 && (val & 0x01c0) >> 6 != 0x4) {
+ /* Class D power on reset */
+ alc_write_coef_idx(codec, 0x17, val | (1<<7));
+ }
+ }
+
+ /* HP */
+ alc_update_coef_idx(codec, 0x4, 0, 1<<11);
+}
+
+static void alc269_remove(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec)
+ hda_component_manager_free(&spec->comps, &comp_master_ops);
+
+ snd_hda_gen_remove(codec);
+}
+
+/*
+ */
+static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+ codec->power_save_node = 0;
+ spec->en_3kpull_low = true;
+
+ spec->shutup = alc_default_shutup;
+ spec->init_hook = alc_default_init;
+
+ 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);
+ }
+ if (err < 0)
+ goto error;
+ spec->shutup = alc269_shutup;
+ spec->init_hook = alc269_fill_coef;
+ alc269_fill_coef(codec);
+ break;
+
+ 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 */
+ if (codec->core.vendor_id == 0x10ec0236 &&
+ codec->bus->pci->vendor != PCI_VENDOR_ID_AMD)
+ spec->en_3kpull_low = false;
+ break;
+ case 0x10ec0257:
+ spec->codec_variant = ALC269_TYPE_ALC257;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ spec->gen.mixer_nid = 0;
+ spec->en_3kpull_low = false;
+ 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 0x10ec0222:
+ case 0x10ec0623:
+ spec->codec_variant = ALC269_TYPE_ALC623;
+ spec->shutup = alc222_shutup;
+ spec->init_hook = alc222_init;
+ 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 (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = 1;
+ spec->init_hook = alc5505_dsp_init;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ /*
+ * Check whether ACPI describes companion amplifiers that require
+ * component binding
+ */
+ find_cirrus_companion_amps(codec);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc269_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ 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;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ alc269_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc269_codec_ops = {
+ .probe = alc269_probe,
+ .remove = alc269_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = alc269_suspend,
+ .resume = alc269_resume,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc269[] = {
+ HDA_CODEC_ID(0x10ec0215, "ALC215"),
+ HDA_CODEC_ID(0x10ec0221, "ALC221"),
+ HDA_CODEC_ID(0x10ec0222, "ALC222"),
+ HDA_CODEC_ID(0x10ec0225, "ALC225"),
+ HDA_CODEC_ID(0x10ec0230, "ALC236"),
+ HDA_CODEC_ID(0x10ec0231, "ALC231"),
+ HDA_CODEC_ID(0x10ec0233, "ALC233"),
+ HDA_CODEC_ID(0x10ec0234, "ALC234"),
+ HDA_CODEC_ID(0x10ec0235, "ALC233"),
+ HDA_CODEC_ID(0x10ec0236, "ALC236"),
+ HDA_CODEC_ID(0x10ec0245, "ALC245"),
+ HDA_CODEC_ID(0x10ec0255, "ALC255"),
+ HDA_CODEC_ID(0x10ec0256, "ALC256"),
+ HDA_CODEC_ID(0x10ec0257, "ALC257"),
+ HDA_CODEC_ID(0x10ec0269, "ALC269"),
+ HDA_CODEC_ID(0x10ec0270, "ALC270"),
+ HDA_CODEC_ID(0x10ec0274, "ALC274"),
+ HDA_CODEC_ID(0x10ec0275, "ALC275"),
+ HDA_CODEC_ID(0x10ec0276, "ALC276"),
+ HDA_CODEC_ID(0x10ec0280, "ALC280"),
+ HDA_CODEC_ID(0x10ec0282, "ALC282"),
+ HDA_CODEC_ID(0x10ec0283, "ALC283"),
+ HDA_CODEC_ID(0x10ec0284, "ALC284"),
+ HDA_CODEC_ID(0x10ec0285, "ALC285"),
+ HDA_CODEC_ID(0x10ec0286, "ALC286"),
+ HDA_CODEC_ID(0x10ec0287, "ALC287"),
+ HDA_CODEC_ID(0x10ec0288, "ALC288"),
+ HDA_CODEC_ID(0x10ec0289, "ALC289"),
+ HDA_CODEC_ID(0x10ec0290, "ALC290"),
+ HDA_CODEC_ID(0x10ec0292, "ALC292"),
+ HDA_CODEC_ID(0x10ec0293, "ALC293"),
+ HDA_CODEC_ID(0x10ec0294, "ALC294"),
+ HDA_CODEC_ID(0x10ec0295, "ALC295"),
+ HDA_CODEC_ID(0x10ec0298, "ALC298"),
+ HDA_CODEC_ID(0x10ec0299, "ALC299"),
+ HDA_CODEC_ID(0x10ec0300, "ALC300"),
+ HDA_CODEC_ID(0x10ec0623, "ALC623"),
+ HDA_CODEC_ID(0x10ec0700, "ALC700"),
+ HDA_CODEC_ID(0x10ec0701, "ALC701"),
+ HDA_CODEC_ID(0x10ec0703, "ALC703"),
+ HDA_CODEC_ID(0x10ec0711, "ALC711"),
+ HDA_CODEC_ID(0x19e58326, "HW8326"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc269);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC269 and compatible HD-audio codecs");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
+
+static struct hda_codec_driver alc269_driver = {
+ .id = snd_hda_id_alc269,
+ .ops = &alc269_codec_ops,
+};
+
+module_hda_codec_driver(alc269_driver);
diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c
new file mode 100644
index 000000000000..5073165d1f3c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc662.c
@@ -0,0 +1,1116 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC662 and compatible codecs
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/*
+ * ALC662 support
+ *
+ * ALC662 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).
+ */
+
+/*
+ * BIOS auto configuration
+ */
+
+static int alc662_parse_auto_config(struct hda_codec *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;
+
+ 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
+ ssids = alc662_ssids;
+ return alc_parse_auto_config(codec, alc662_ignore, ssids);
+}
+
+static void alc272_fixup_mario(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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");
+}
+
+/* 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;
+ if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data)
+ return AC_PWRST_D0;
+ return power_state;
+}
+
+static void alc662_fixup_led_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+
+static void alc662_usi_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);
+
+ 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);
+}
+
+static void alc662_fixup_usi_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc662_usi_automute_hook;
+ }
+}
+
+static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* 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);
+ }
+}
+
+static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Pin 0x1b: shared headphones jack and surround speakers */
+ if (!is_jack_detectable(codec, 0x1b))
+ return;
+
+ 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 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;
+ }
+}
+
+static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */
+ { 0x1b, 0x0181304f },
+ { }
+ };
+
+ 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;
+ }
+}
+
+static void alc897_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+
+ snd_hda_gen_hp_automute(codec, jack);
+ vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP;
+ snd_hda_set_pin_ctl(codec, 0x1b, vref);
+}
+
+static void alc897_fixup_lenovo_headset_mic(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->gen.hp_automute_hook = alc897_hp_automute_hook;
+ spec->no_shutup_pins = 1;
+ }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100);
+ }
+}
+
+static void alc897_fixup_lenovo_headset_mode(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+ }
+}
+
+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),
+ {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc668_coefs);
+}
+
+static void alc_fixup_headset_mode_alc662(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */
+
+ /* 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 void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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);
+}
+
+enum {
+ ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_LED_GPIO1,
+ ALC662_FIXUP_IDEAPAD,
+ 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,
+ ALC897_FIXUP_UNIS_H3C_X500S,
+ ALC897_FIXUP_HEADSET_MIC_PIN3,
+};
+
+static const struct hda_fixup alc662_fixups[] = {
+ [ALC662_FIXUP_ASPIRE] = {
+ .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] = {
+ .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 = alc_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
+ },
+ [ALC897_FIXUP_UNIS_H3C_X500S] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 },
+ {}
+ },
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* use as headset mic */
+ { }
+ },
+ },
+};
+
+static const struct hda_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3),
+ 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, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", 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, 0x1064, "Lenovo P3 Tower", 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, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", 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),
+ SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB),
+
+#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"},
+ {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"},
+ {}
+};
+
+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 alc662_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+
+ spec->shutup = alc_eapd_shutup;
+
+ /* handle multiple HPs as is */
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
+ }
+
+ alc_pre_init(codec);
+
+ 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);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ 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;
+ }
+
+ /* automatic parse from the BIOS config */
+ err = alc662_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ switch (codec->core.vendor_id) {
+ case 0x10ec0662:
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ break;
+ case 0x10ec0272:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0668:
+ err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+ break;
+ case 0x10ec0273:
+ err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
+ break;
+ }
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc662_codec_ops = {
+ .probe = alc662_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc662[] = {
+ HDA_CODEC_ID(0x10ec0272, "ALC272"),
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100101, "ALC662 rev1"),
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100300, "ALC662 rev3"),
+ HDA_CODEC_ID(0x10ec0663, "ALC663"),
+ HDA_CODEC_ID(0x10ec0665, "ALC665"),
+ HDA_CODEC_ID(0x10ec0667, "ALC667"),
+ HDA_CODEC_ID(0x10ec0668, "ALC668"),
+ HDA_CODEC_ID(0x10ec0670, "ALC670"),
+ HDA_CODEC_ID(0x10ec0671, "ALC671"),
+ HDA_CODEC_ID(0x10ec0867, "ALC891"),
+ HDA_CODEC_ID(0x10ec0892, "ALC892"),
+ HDA_CODEC_ID(0x10ec0897, "ALC897"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc662);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC662 and compatible HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc662_driver = {
+ .id = snd_hda_id_alc662,
+ .ops = &alc662_codec_ops,
+};
+
+module_hda_codec_driver(alc662_driver);
diff --git a/sound/hda/codecs/realtek/alc680.c b/sound/hda/codecs/realtek/alc680.c
new file mode 100644
index 000000000000..8aab1026243c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc680.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC680 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+ return alc_parse_auto_config(codec, NULL, NULL);
+}
+
+/*
+ */
+static int alc680_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ /* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ err = alc680_parse_auto_config(codec);
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops alc680_codec_ops = {
+ .probe = alc680_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc680[] = {
+ HDA_CODEC_ID(0x10ec0680, "ALC680"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc680);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC680 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc680_driver = {
+ .id = snd_hda_id_alc680,
+ .ops = &alc680_codec_ops,
+};
+
+module_hda_codec_driver(alc680_driver);
diff --git a/sound/hda/codecs/realtek/alc861.c b/sound/hda/codecs/realtek/alc861.c
new file mode 100644
index 000000000000..270037c6504a
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc861.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC861 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc861_parse_auto_config(struct hda_codec *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);
+}
+
+/* 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,
+};
+
+/* 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;
+ unsigned int val;
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ 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 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 const struct hda_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 alc861_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
+
+ spec->power_hook = alc_power_eapd;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc861_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc861_codec_ops = {
+ .probe = alc861_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc861[] = {
+ HDA_CODEC_ID_REV(0x10ec0861, 0x100340, "ALC660"),
+ HDA_CODEC_ID(0x10ec0861, "ALC861"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC861 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc861_driver = {
+ .id = snd_hda_id_alc861,
+ .ops = &alc861_codec_ops,
+};
+
+module_hda_codec_driver(alc861_driver);
diff --git a/sound/hda/codecs/realtek/alc861vd.c b/sound/hda/codecs/realtek/alc861vd.c
new file mode 100644
index 000000000000..44264e0d6e56
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc861vd.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC861-VD codec
+// Based on ALC882
+// In addition, an independent DAC
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc861vd_parse_auto_config(struct hda_codec *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);
+}
+
+enum {
+ ALC660VD_FIX_ASUS_GPIO1,
+ ALC861VD_FIX_DALLAS,
+};
+
+/* exclude VREF80 */
+static void alc861vd_fixup_dallas(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
+ snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
+ }
+}
+
+/* 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;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gpio_mask |= 0x02;
+ alc_fixup_gpio(codec, action, 0x01);
+}
+
+static const struct hda_fixup alc861vd_fixups[] = {
+ [ALC660VD_FIX_ASUS_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc660vd_fixup_asus_gpio1,
+ },
+ [ALC861VD_FIX_DALLAS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861vd_fixup_dallas,
+ },
+};
+
+static const struct hda_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 alc861vd_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc861vd_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc861vd_codec_ops = {
+ .probe = alc861vd_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc861vd[] = {
+ HDA_CODEC_ID(0x10ec0660, "ALC660-VD"),
+ HDA_CODEC_ID(0x10ec0862, "ALC861-VD"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861vd);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC861-VD HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc861vd_driver = {
+ .id = snd_hda_id_alc861vd,
+ .ops = &alc861vd_codec_ops,
+};
+
+module_hda_codec_driver(alc861vd_driver);
diff --git a/sound/hda/codecs/realtek/alc880.c b/sound/hda/codecs/realtek/alc880.c
new file mode 100644
index 000000000000..bf1bdf11ec2d
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc880.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC880 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ /* 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 alc880_parse_auto_config(struct hda_codec *codec)
+{
+ 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);
+}
+
+/*
+ * ALC880 fix-ups
+ */
+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 },
+ { }
+ }
+ },
+ [ALC880_FIXUP_LG_LW25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0181344f }, /* line-in */
+ { 0x1b, 0x0321403f }, /* headphone */
+ { }
+ }
+ },
+ [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 },
+ {}
+ },
+ },
+ [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 */
+ { }
+ },
+ },
+ [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 hda_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 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"},
+ {}
+};
+
+
+/*
+ * OK, here we have finally the probe for ALC880
+ */
+static int alc880_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc880_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc880_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc880_codec_ops = {
+ .probe = alc880_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = alc880_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc880[] = {
+ HDA_CODEC_ID(0x10ec0880, "ALC880"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC880 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc880_driver = {
+ .id = snd_hda_id_alc880,
+ .ops = &alc880_codec_ops,
+};
+
+module_hda_codec_driver(alc880_driver);
diff --git a/sound/hda/codecs/realtek/alc882.c b/sound/hda/codecs/realtek/alc882.c
new file mode 100644
index 000000000000..529fecd5baa0
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc882.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC882/883/885/888/889 codec 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).
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/*
+ * Pin config fixes
+ */
+enum {
+ 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,
+ ALC888VD_FIXUP_MIC_100VREF,
+};
+
+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);
+}
+
+/* set up GPIO at initialization */
+static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gpio_write_delay = true;
+ alc_fixup_gpio3(codec, fix, action);
+}
+
+/* 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)
+{
+ 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);
+ }
+}
+
+/* Set VREF on HP pin */
+static void alc889_fixup_mbp_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 };
+ struct alc_spec *spec = codec->spec;
+ int 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;
+ 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;
+ }
+}
+
+static void alc889_fixup_mac_pins(struct hda_codec *codec,
+ const hda_nid_t *nids, int num_nids)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ 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);
+ }
+ spec->gen.keep_vref_in_automute = 1;
+}
+
+/* Set VREF on speaker pins on imac91 */
+static void alc889_fixup_imac91_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18, 0x1a };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba11 */
+static void alc889_fixup_mba11_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba21 */
+static void alc889_fixup_mba21_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18, 0x19 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* 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)
+{
+ 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 void alc1220_fixup_gb_x570(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ 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;
+ }
+}
+
+static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const hda_nid_t conn1[] = { 0x0c };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ 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);
+}
+
+static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc1220_fixup_clevo_p950(codec, fix, action);
+ alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
+}
+
+static void alc887_asus_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int vref;
+
+ snd_hda_gen_hp_automute(codec, jack);
+
+ 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);
+}
+
+static void alc887_fixup_asus_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP);
+ spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook;
+}
+
+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 */
+ { }
+ }
+ },
+ [ALC882_FIXUP_LENOVO_Y530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x99130112 }, /* rear int speakers */
+ { 0x16, 0x99130111 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC882_FIXUP_PB_M5210] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
+ }
+ },
+ [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 */
+ { }
+ }
+ },
+ [ALC889_FIXUP_CD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1c, 0x993301f0 }, /* CD */
+ { }
+ }
+ },
+ [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 */
+ {}
+ }
+ },
+ [ALC888VD_FIXUP_MIC_100VREF] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, PIN_VREF100 }, /* headset mic */
+ {}
+ }
+ },
+};
+
+static const struct hda_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(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF),
+ 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, 0x5802, "Clevo X58[05]WN[RST]", 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, 0x66a6, "Clevo PE60SN[CDE]-[GS]", 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 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 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)
+{
+ 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);
+}
+
+/*
+ */
+static int alc882_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+
+ 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;
+ }
+
+ alc_pre_init(codec);
+
+ 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);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc882_codec_ops = {
+ .probe = alc882_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc882[] = {
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100002, "ALC662 rev2"),
+ HDA_CODEC_ID(0x10ec0882, "ALC882"),
+ HDA_CODEC_ID(0x10ec0883, "ALC883"),
+ HDA_CODEC_ID_REV(0x10ec0885, 0x100101, "ALC889A"),
+ HDA_CODEC_ID_REV(0x10ec0885, 0x100103, "ALC889A"),
+ HDA_CODEC_ID(0x10ec0885, "ALC885"),
+ HDA_CODEC_ID(0x10ec0887, "ALC887"),
+ HDA_CODEC_ID_REV(0x10ec0888, 0x100101, "ALC1200"),
+ HDA_CODEC_ID(0x10ec0888, "ALC888"),
+ HDA_CODEC_ID(0x10ec0889, "ALC889"),
+ HDA_CODEC_ID(0x10ec0899, "ALC898"),
+ HDA_CODEC_ID(0x10ec0900, "ALC1150"),
+ HDA_CODEC_ID(0x10ec0b00, "ALCS1200A"),
+ HDA_CODEC_ID(0x10ec1168, "ALC1220"),
+ HDA_CODEC_ID(0x10ec1220, "ALC1220"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc882);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC882 and compatible HD-audio codecs");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc882_driver = {
+ .id = snd_hda_id_alc882,
+ .ops = &alc882_codec_ops,
+};
+
+module_hda_codec_driver(alc882_driver);
diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c
new file mode 100644
index 000000000000..ca377a5adadb
--- /dev/null
+++ b/sound/hda/codecs/realtek/realtek.c
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek HD-audio codec support code
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_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;
+}
+
+int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx)
+{
+ guard(coef_mutex)(codec);
+ return __alc_read_coefex_idx(codec, nid, coef_idx);
+}
+EXPORT_SYMBOL_NS_GPL(alc_read_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
+{
+ 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);
+}
+
+void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
+{
+ guard(coef_mutex)(codec);
+ __alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+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 val = __alc_read_coefex_idx(codec, nid, coef_idx);
+
+ if (val != -1)
+ __alc_write_coefex_idx(codec, nid, coef_idx,
+ (val & ~mask) | bits_set);
+}
+
+void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set)
+{
+ guard(coef_mutex)(codec);
+ __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+/* a special bypass for COEF 0; read the cached value at the second time */
+unsigned int alc_get_coef0(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->coef0)
+ spec->coef0 = alc_read_coef_idx(codec, 0);
+ return spec->coef0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_get_coef0, "SND_HDA_CODEC_REALTEK");
+
+void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw)
+{
+ guard(coef_mutex)(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);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_process_coef_fw, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * GPIO setup tables, used in initialization
+ */
+
+/* Enable GPIO mask and set output */
+void alc_setup_gpio(struct hda_codec *codec, unsigned int mask)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gpio_mask |= mask;
+ spec->gpio_dir |= mask;
+ spec->gpio_data |= mask;
+}
+EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_write_gpio_data(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK");
+
+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;
+
+ if (on)
+ spec->gpio_data |= mask;
+ else
+ spec->gpio_data &= ~mask;
+ if (oldval != spec->gpio_data)
+ alc_write_gpio_data(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK");
+
+void alc_write_gpio(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ alc_setup_gpio(codec, mask);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x01);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio1, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x02);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio2, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x03);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio3, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x04);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio4, "SND_HDA_CODEC_REALTEK");
+
+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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_micmute_led, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Fix hardware PLL issue
+ * On some codecs, the analog PLL gating control must be off while
+ * the default value is 1.
+ */
+void alc_fix_pll(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->pll_nid)
+ alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx,
+ 1 << spec->pll_coef_bit, 0);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fix_pll, "SND_HDA_CODEC_REALTEK");
+
+void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_bit)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->pll_nid = nid;
+ spec->pll_coef_idx = coef_idx;
+ spec->pll_coef_bit = coef_bit;
+ alc_fix_pll(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fix_pll_init, "SND_HDA_CODEC_REALTEK");
+
+/* update the master volume per volume-knob's unsol event */
+void alc_update_knob_master(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ unsigned int val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
+
+ 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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_knob_master, "SND_HDA_CODEC_REALTEK");
+
+/* Change EAPD to verb control */
+void alc_fill_eapd_coef(struct hda_codec *codec)
+{
+ int coef;
+
+ coef = alc_get_coef0(codec);
+
+ 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 0x10ec0236:
+ case 0x10ec0245:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x10ec0285:
+ case 0x10ec0289:
+ alc_update_coef_idx(codec, 0x36, 1<<13, 0);
+ fallthrough;
+ case 0x10ec0230:
+ case 0x10ec0233:
+ case 0x10ec0235:
+ case 0x10ec0255:
+ case 0x19e58326:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ 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:
+ alc_write_coef_idx(codec, 0x6e, 0x0c25);
+ fallthrough;
+ 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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fill_eapd_coef, "SND_HDA_CODEC_REALTEK");
+
+/* 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);
+}
+
+/* turn on/off EAPD controls of the codec */
+void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
+{
+ /* 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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_setup_eapd, "SND_HDA_CODEC_REALTEK");
+
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+int alc_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;
+
+ 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;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_find_ext_mic_pin, "SND_HDA_CODEC_REALTEK");
+
+void alc_headset_mic_no_shutup(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int mic_pin = alc_find_ext_mic_pin(codec);
+ 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;
+
+ 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);
+ }
+
+ codec->pins_shutup = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_headset_mic_no_shutup, "SND_HDA_CODEC_REALTEK");
+
+void alc_shutup_pins(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->no_shutup_pins)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x19e58326:
+ case 0x10ec0283:
+ case 0x10ec0285:
+ case 0x10ec0286:
+ case 0x10ec0287:
+ case 0x10ec0288:
+ case 0x10ec0295:
+ case 0x10ec0298:
+ alc_headset_mic_no_shutup(codec);
+ break;
+ default:
+ snd_hda_shutup_pins(codec);
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_shutup_pins, "SND_HDA_CODEC_REALTEK");
+
+/* generic shutup callback;
+ * just turning off EAPD and a little pause for avoiding pop-noise
+ */
+void alc_eapd_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_auto_setup_eapd(codec, false);
+ if (!spec->no_depop_delay)
+ msleep(200);
+ alc_shutup_pins(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_eapd_shutup, "SND_HDA_CODEC_REALTEK");
+
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
+{
+ 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;
+ }
+}
+
+/* generic EAPD initialization */
+void alc_auto_init_amp(struct hda_codec *codec, int type)
+{
+ alc_auto_setup_eapd(codec, true);
+ alc_write_gpio(codec);
+ switch (type) {
+ case ALC_INIT_DEFAULT:
+ switch (codec->core.vendor_id) {
+ case 0x10ec0260:
+ alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
+ break;
+ case 0x10ec0880:
+ case 0x10ec0882:
+ case 0x10ec0883:
+ case 0x10ec0885:
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
+ break;
+ case 0x10ec0888:
+ alc888_coef_init(codec);
+ break;
+ }
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_init_amp, "SND_HDA_CODEC_REALTEK");
+
+/* get a primary headphone pin if available */
+hda_nid_t alc_get_hp_pin(struct alc_spec *spec)
+{
+ 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;
+}
+EXPORT_SYMBOL_NS_GPL(alc_get_hp_pin, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Realtek SSID verification
+ */
+
+/* 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)
+
+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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_sku_ignore, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->no_depop_delay = 1;
+ codec->depop_delay = 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_no_depop_delay, "SND_HDA_CODEC_REALTEK");
+
+int alc_auto_parse_customize_define(struct hda_codec *codec)
+{
+ unsigned int ass, tmp, i;
+ unsigned nid = 0;
+ struct alc_spec *spec = codec->spec;
+
+ spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return -1;
+ goto do_sku;
+ }
+
+ 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->core.vendor_id == 0x10ec0260)
+ nid = 0x17;
+ ass = snd_hda_codec_get_pincfg(codec, nid);
+
+ if (!(ass & 1)) {
+ codec_info(codec, "%s: SKU not ready 0x%08x\n",
+ codec->core.chip_name, ass);
+ return -1;
+ }
+
+ /* check sum */
+ tmp = 0;
+ for (i = 1; i < 16; i++) {
+ if ((ass >> i) & 1)
+ tmp++;
+ }
+ if (((ass >> 16) & 0xf) != tmp)
+ return -1;
+
+ spec->cdefine.port_connectivity = ass >> 30;
+ spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
+ spec->cdefine.check_sum = (ass >> 16) & 0xf;
+ spec->cdefine.customization = ass >> 8;
+do_sku:
+ spec->cdefine.sku_cfg = ass;
+ spec->cdefine.external_amp = (ass & 0x38) >> 3;
+ spec->cdefine.platform_type = (ass & 0x4) >> 2;
+ spec->cdefine.swap = (ass & 0x2) >> 1;
+ spec->cdefine.override = ass & 0x1;
+
+ codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n",
+ nid, spec->cdefine.sku_cfg);
+ codec_dbg(codec, "SKU: port_connectivity=0x%x\n",
+ spec->cdefine.port_connectivity);
+ 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;
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_parse_customize_define, "SND_HDA_CODEC_REALTEK");
+
+/* 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
+ */
+/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
+ * 31 ~ 16 : Manufacture ID
+ * 15 ~ 8 : SKU ID
+ * 7 ~ 0 : Assembly ID
+ * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
+ */
+int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports)
+{
+ unsigned int ass, tmp, i;
+ unsigned nid;
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return 0;
+ goto do_sku;
+ }
+
+ 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 */
+ /*
+ * 31~30 : port connectivity
+ * 29~21 : reserve
+ * 20 : PCBEEP input
+ * 19~16 : Check sum (15:1)
+ * 15~1 : Custom
+ * 0 : override
+ */
+ nid = 0x1d;
+ if (codec->core.vendor_id == 0x10ec0260)
+ nid = 0x17;
+ ass = snd_hda_codec_get_pincfg(codec, nid);
+ codec_dbg(codec,
+ "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n",
+ ass, nid);
+ if (!(ass & 1))
+ return 0;
+ if ((ass >> 30) != 1) /* no physical connection */
+ return 0;
+
+ /* check sum */
+ tmp = 0;
+ for (i = 1; i < 16; i++) {
+ if ((ass >> i) & 1)
+ tmp++;
+ }
+ if (((ass >> 16) & 0xf) != tmp)
+ return 0;
+do_sku:
+ 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
+ * 2 : 0 --> Desktop, 1 --> Laptop
+ * 3~5 : External Amplifier control
+ * 7~6 : Reserved
+ */
+ tmp = (ass & 0x38) >> 3; /* external Amp control */
+ 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
+ * when the external headphone out jack is plugged"
+ */
+ if (!(ass & 0x8000))
+ return 1;
+ /*
+ * 10~8 : Jack location
+ * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
+ * 14~13: Resvered
+ * 15 : 1 --> enable the function "Mute internal speaker
+ * when the external headphone out jack is plugged"
+ */
+ if (!alc_get_hp_pin(spec)) {
+ hda_nid_t nid;
+ tmp = (ass >> 11) & 0x3; /* HP to chassis */
+ nid = ports[tmp];
+ if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+ spec->gen.autocfg.line_outs))
+ return 1;
+ spec->gen.autocfg.hp_pins[0] = nid;
+ }
+ return 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_subsystem_id, "SND_HDA_CODEC_REALTEK");
+
+/* Check the validity of ALC subsystem-id
+ * ports contains an array of 4 pin NIDs for port-A, E, D and I */
+void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
+{
+ if (!alc_subsystem_id(codec, ports)) {
+ struct alc_spec *spec = codec->spec;
+ 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;
+ }
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_ssid_check, "SND_HDA_CODEC_REALTEK");
+
+/* inverted digital-mic */
+void alc_fixup_inv_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gen.inv_dmic_split = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_inv_dmic, "SND_HDA_CODEC_REALTEK");
+
+int alc_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;
+}
+EXPORT_SYMBOL_NS_GPL(alc_build_controls, "SND_HDA_CODEC_REALTEK");
+
+int alc_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* hibernation resume needs the full chip initialization */
+ if (is_s4_resume(codec))
+ alc_pre_init(codec);
+
+ 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 */
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_init, "SND_HDA_CODEC_REALTEK");
+
+void alc_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!snd_hda_get_bool_hint(codec, "shutup"))
+ return; /* disabled explicitly by hints */
+
+ if (spec && spec->shutup)
+ spec->shutup(codec);
+ else
+ alc_shutup_pins(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_shutup, "SND_HDA_CODEC_REALTEK");
+
+void alc_power_eapd(struct hda_codec *codec)
+{
+ alc_auto_setup_eapd(codec, false);
+}
+EXPORT_SYMBOL_NS_GPL(alc_power_eapd, "SND_HDA_CODEC_REALTEK");
+
+int alc_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_suspend, "SND_HDA_CODEC_REALTEK");
+
+int alc_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->no_depop_delay)
+ msleep(150); /* to avoid pop noise */
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_resume, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Rename codecs appropriately from COEF value or subvendor id
+ */
+struct alc_codec_rename_table {
+ unsigned int vendor_id;
+ unsigned short coef_mask;
+ unsigned short coef_bits;
+ const char *name;
+};
+
+struct alc_codec_rename_pci_table {
+ unsigned int codec_vendor_id;
+ unsigned short pci_subvendor;
+ unsigned short pci_subdevice;
+ const char *name;
+};
+
+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" },
+ { 0x10ec0257, 0x12f0, 0, "ALC3328" },
+ { } /* 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);
+ }
+
+ 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);
+ }
+
+ return 0;
+}
+
+/*
+ * Digital-beep handlers
+ */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+
+/* 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),
+};
+
+/* set up and create beep controls */
+int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ int i;
+
+ 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;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_set_beep_amp, "SND_HDA_CODEC_REALTEK");
+
+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),
+ {}
+};
+
+int alc_has_cdefine_beep(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_allow_list);
+ if (q)
+ return q->value;
+ return spec->cdefine.enable_pcbeep;
+}
+EXPORT_SYMBOL_NS_GPL(alc_has_cdefine_beep, "SND_HDA_CODEC_REALTEK");
+
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
+/* 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
+ */
+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;
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
+ spec->parse_flags);
+ if (err < 0)
+ return err;
+
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
+
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_parse_auto_config, "SND_HDA_CODEC_REALTEK");
+
+/* common preparation job for alc_spec */
+int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+ struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ int err;
+
+ 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;
+ mutex_init(&spec->coef_mutex);
+
+ err = alc_codec_rename_from_preset(codec);
+ if (err < 0) {
+ kfree(spec);
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_alloc_spec, "SND_HDA_CODEC_REALTEK");
+
+/* For dual-codec configuration, we need to disable some features to avoid
+ * conflicts of kctls and PCM streams
+ */
+void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+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 */
+ { }
+};
+
+/* override the 2.1 chmap */
+void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_bass_chmap, "SND_HDA_CODEC_REALTEK");
+
+/* exported as it's used by multiple codecs */
+void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strscpy(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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc1220_fixup_gb_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strscpy(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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc233_alc662_fixup_lenovo_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+static void alc_shutup_dell_xps13(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int hp_pin = alc_get_hp_pin(spec);
+
+ /* 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);
+}
+
+void alc_fixup_dell_xps13(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;
+
+ 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;
+ }
+ }
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_dell_xps13, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * headset handling
+ */
+
+static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay)
+{
+ if (delay <= 0)
+ delay = 75;
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(delay);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(delay);
+}
+
+static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay)
+{
+ if (delay <= 0)
+ delay = 75;
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(delay);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ msleep(delay);
+}
+
+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),
+ {}
+};
+
+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),
+ {}
+ };
+
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_hp_mute_disable(codec, 75);
+ 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_hp_mute_disable(codec, 75);
+ 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");
+}
+
+
+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 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),
+ {}
+ };
+
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ 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");
+}
+
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ int val;
+
+ 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),
+ {}
+ };
+
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ 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");
+}
+
+/* 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),
+ {}
+ };
+
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ 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);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
+}
+
+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),
+ {}
+ };
+
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
+
+ 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);
+
+ alc_process_coef_fw(codec, coef0255);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ if (!is_ctia) {
+ alc_write_coef_idx(codec, 0x45, 0xe089);
+ msleep(100);
+ val = alc_read_coef_idx(codec, 0x46);
+ if ((val & 0x0070) == 0x0070)
+ is_ctia = false;
+ else
+ is_ctia = true;
+ }
+ alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3);
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ 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);
+
+ 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:
+ 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;
+ }
+ if (!is_ctia) {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8);
+ msleep(100);
+ val = alc_read_coef_idx(codec, 0x46);
+ if ((val & 0x00f0) == 0x00f0)
+ is_ctia = false;
+ else
+ is_ctia = true;
+ }
+ 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);
+ break;
+ case 0x10ec0867:
+ is_ctia = true;
+ break;
+ }
+
+ codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
+ str_yes_no(is_ctia));
+ spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
+}
+
+static void alc_update_headset_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+
+ int new_headset_mode;
+
+ 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;
+
+ if (new_headset_mode == spec->current_headset_mode) {
+ snd_hda_gen_update_outputs(codec);
+ return;
+ }
+
+ 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;
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+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);
+}
+
+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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_headset_jack_cb, "SND_HDA_CODEC_REALTEK");
+
+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;
+
+ /* 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;
+ }
+
+ 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;
+}
+
+void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mic, "SND_HDA_CODEC_REALTEK");
+
+/* update LED status via GPIO */
+void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ int polarity, bool enabled)
+{
+ if (polarity)
+ enabled = !enabled;
+ alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_gpio_led, "SND_HDA_CODEC_REALTEK");
+
+/* 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;
+
+ alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ spec->micmute_led_polarity, !brightness);
+ return 0;
+}
+
+/* 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;
+
+ alc_update_gpio_led(codec, spec->gpio_mute_led_mask,
+ spec->mute_led_polarity, !brightness);
+ return 0;
+}
+
+/* setup mute and mic-mute GPIO bits, add hooks appropriately */
+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;
+
+ 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);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_hp_gpio_led, "SND_HDA_CODEC_REALTEK");
+
+/* suppress the jack-detection */
+void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_no_jack_detect, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_disable_aamix, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.auto_mute_via_amp = 1;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_auto_mute_via_amp, "SND_HDA_CODEC_REALTEK");
+
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek HD-audio codec helper");
diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h
new file mode 100644
index 000000000000..b2a919904c4c
--- /dev/null
+++ b/sound/hda/codecs/realtek/realtek.h
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek HD-audio codec support code
+//
+
+#ifndef __HDA_REALTEK_H
+#define __HDA_REALTEK_H
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#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/i2c.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "../side-codecs/hda_component.h"
+
+/* extra amp-initialization sequence types */
+enum {
+ ALC_INIT_UNDEFINED,
+ ALC_INIT_NONE,
+ ALC_INIT_DEFAULT,
+};
+
+enum {
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
+};
+
+enum {
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
+};
+
+enum {
+ ALC_KEY_MICMUTE_INDEX,
+};
+
+struct alc_customize_define {
+ unsigned int sku_cfg;
+ unsigned char port_connectivity;
+ unsigned char check_sum;
+ unsigned char customization;
+ unsigned char external_amp;
+ unsigned int enable_pcbeep:1;
+ unsigned int platform_type:1;
+ unsigned int swap:1;
+ unsigned int override:1;
+ 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 alc_customize_define cdefine;
+ 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 (*power_hook)(struct hda_codec *codec);
+ void (*shutup)(struct hda_codec *codec);
+
+ int init_amp;
+ int codec_variant; /* flag for other variants */
+ 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;
+ unsigned int en_3kpull_low:1;
+ int num_speaker_amps;
+
+ /* 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];
+
+ /* component binding */
+ struct hda_component_parent comps;
+};
+
+int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx);
+void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val);
+void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set);
+#define alc_read_coef_idx(codec, coef_idx) \
+ alc_read_coefex_idx(codec, 0x20, coef_idx)
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+ alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
+#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \
+ alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set)
+
+unsigned int alc_get_coef0(struct hda_codec *codec);
+
+/* coef writes/updates batch */
+struct coef_fw {
+ unsigned char nid;
+ unsigned char idx;
+ unsigned short mask;
+ unsigned short val;
+};
+
+#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)
+
+void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw);
+
+/*
+ * GPIO helpers
+ */
+void alc_setup_gpio(struct hda_codec *codec, unsigned int mask);
+void alc_write_gpio_data(struct hda_codec *codec);
+void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
+ bool on);
+void alc_write_gpio(struct hda_codec *codec);
+
+/* common GPIO fixups */
+void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask);
+void alc_fixup_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/*
+ * Common init code, callbacks and helpers
+ */
+void alc_fix_pll(struct hda_codec *codec);
+void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_bit);
+void alc_fill_eapd_coef(struct hda_codec *codec);
+void alc_auto_setup_eapd(struct hda_codec *codec, bool on);
+
+int alc_find_ext_mic_pin(struct hda_codec *codec);
+void alc_headset_mic_no_shutup(struct hda_codec *codec);
+void alc_shutup_pins(struct hda_codec *codec);
+void alc_eapd_shutup(struct hda_codec *codec);
+void alc_auto_init_amp(struct hda_codec *codec, int type);
+hda_nid_t alc_get_hp_pin(struct alc_spec *spec);
+int alc_auto_parse_customize_define(struct hda_codec *codec);
+int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports);
+void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports);
+int alc_build_controls(struct hda_codec *codec);
+void alc_update_knob_master(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+
+static inline void alc_pre_init(struct hda_codec *codec)
+{
+ alc_fill_eapd_coef(codec);
+}
+
+#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)
+#define is_s4_suspend(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE)
+
+int alc_init(struct hda_codec *codec);
+void alc_shutup(struct hda_codec *codec);
+void alc_power_eapd(struct hda_codec *codec);
+int alc_suspend(struct hda_codec *codec);
+int alc_resume(struct hda_codec *codec);
+
+int alc_parse_auto_config(struct hda_codec *codec,
+ const hda_nid_t *ignore_nids,
+ const hda_nid_t *ssid_nids);
+int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid);
+
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir);
+int alc_has_cdefine_beep(struct hda_codec *codec);
+#define set_beep_amp alc_set_beep_amp
+#define has_cdefine_beep alc_has_cdefine_beep
+#else
+#define set_beep_amp(spec, nid, idx, dir) 0
+#define has_cdefine_beep(codec) 0
+#endif
+
+static inline void rename_ctl(struct hda_codec *codec, const char *oldname,
+ const char *newname)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_hda_find_mixer_ctl(codec, oldname);
+ if (kctl)
+ snd_ctl_rename(codec->card, kctl, newname);
+}
+
+/* Common fixups */
+void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_inv_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ int polarity, bool enabled);
+void alc_fixup_hp_gpio_led(struct hda_codec *codec,
+ int action,
+ unsigned int mute_mask,
+ unsigned int micmute_mask);
+void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/* device-specific, but used by multiple codec drivers */
+void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/*
+ * COEF access helper functions
+ */
+static inline void coef_mutex_lock(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_power_up_pm(codec);
+ mutex_lock(&spec->coef_mutex);
+}
+
+static inline void coef_mutex_unlock(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ mutex_unlock(&spec->coef_mutex);
+ snd_hda_power_down_pm(codec);
+}
+
+DEFINE_GUARD(coef_mutex, struct hda_codec *, coef_mutex_lock(_T), coef_mutex_unlock(_T))
+
+#endif /* __HDA_REALTEK_H */
diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c
new file mode 100644
index 000000000000..9aa1e9bcd9ec
--- /dev/null
+++ b/sound/hda/codecs/senarytech.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Senary HDA audio codec
+ *
+ * Initially based on conexant.c
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+struct senary_spec {
+ struct hda_gen_spec gen;
+
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ hda_nid_t mute_led_eapd;
+
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new senary_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 set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
+
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &senary_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+
+static int senary_auto_parse_beep(struct hda_codec *codec)
+{
+ struct senary_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) &&
+ (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD)))
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
+}
+#else
+#define senary_auto_parse_beep(codec) 0
+#endif
+
+/* parse EAPDs */
+static void senary_auto_parse_eapd(struct hda_codec *codec)
+{
+ struct senary_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_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;
+ }
+}
+
+static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ 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);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void senary_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct senary_spec *spec = codec->spec;
+
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
+static void senary_init_gpio_led(struct hda_codec *codec)
+{
+ struct senary_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);
+ }
+}
+
+static int senary_init(struct hda_codec *codec)
+{
+ snd_hda_gen_init(codec);
+ senary_init_gpio_led(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+
+static void senary_shutdown(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+
+ /* Turn the problematic codec into D3 to avoid spurious noises
+ * from the internal speaker during (and after) reboot
+ */
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
+}
+
+static void senary_remove(struct hda_codec *codec)
+{
+ senary_shutdown(codec);
+ snd_hda_gen_remove(codec);
+}
+
+static int senary_suspend(struct hda_codec *codec)
+{
+ senary_shutdown(codec);
+ return 0;
+}
+
+static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct senary_spec *spec;
+ int err;
+
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+
+ senary_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
+
+ if (!spec->gen.vmaster_mute.hook)
+ spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
+
+ err = senary_auto_parse_beep(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ goto error;
+
+ /* Some laptops with Senary 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;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ senary_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops senary_codec_ops = {
+ .probe = senary_probe,
+ .remove = senary_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = senary_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = senary_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ */
+
+static const struct hda_device_id snd_hda_id_senary[] = {
+ HDA_CODEC_ID(0x1fa86186, "SN6186"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Senarytech HD-audio codec");
+
+static struct hda_codec_driver senary_driver = {
+ .id = snd_hda_id_senary,
+ .ops = &senary_codec_ops,
+};
+
+module_hda_codec_driver(senary_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/hda/codecs/si3054.c
index f63acb1b965c..87cf9da9f3bf 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/hda/codecs/si3054.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for Silicon Labs 3054/5 modem codec
+ * HD audio codec driver for Silicon Labs 3054/5 modem codec
*
* 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>
@@ -27,7 +13,7 @@
#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 */
@@ -260,50 +246,48 @@ static int si3054_init(struct hda_codec *codec)
return 0;
}
-static void si3054_free(struct hda_codec *codec)
+static void si3054_remove(struct hda_codec *codec)
{
kfree(codec->spec);
}
-
/*
*/
-static const struct hda_codec_ops si3054_patch_ops = {
- .build_controls = si3054_build_controls,
- .build_pcms = si3054_build_pcms,
- .init = si3054_init,
- .free = si3054_free,
-};
-
-static int patch_si3054(struct hda_codec *codec)
+static int si3054_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
- struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ codec->spec = kzalloc(sizeof(struct si3054_spec), GFP_KERNEL);
+ if (!codec->spec)
return -ENOMEM;
- codec->spec = spec;
- codec->patch_ops = si3054_patch_ops;
return 0;
}
+static const struct hda_codec_ops si3054_codec_ops = {
+ .probe = si3054_probe,
+ .remove = si3054_remove,
+ .build_controls = si3054_build_controls,
+ .build_pcms = si3054_build_pcms,
+ .init = si3054_init,
+};
+
/*
- * patch entries
+ * driver entries
*/
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),
+ HDA_CODEC_ID(0x163c3055, "Si3054"),
+ HDA_CODEC_ID(0x163c3155, "Si3054"),
+ HDA_CODEC_ID(0x11c13026, "Si3054"),
+ HDA_CODEC_ID(0x11c13055, "Si3054"),
+ HDA_CODEC_ID(0x11c13155, "Si3054"),
+ HDA_CODEC_ID(0x10573055, "Si3054"),
+ HDA_CODEC_ID(0x10573057, "Si3054"),
+ HDA_CODEC_ID(0x10573155, "Si3054"),
/* VIA HDA on Clevo m540 */
- HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
+ HDA_CODEC_ID(0x11063288, "Si3054"),
/* Asus A8J Modem (SM56) */
- HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
+ HDA_CODEC_ID(0x15433155, "Si3054"),
/* LG LW20 modem */
- HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
+ HDA_CODEC_ID(0x18540018, "Si3054"),
{}
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
@@ -313,6 +297,7 @@ MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_driver si3054_driver = {
.id = snd_hda_id_si3054,
+ .ops = &si3054_codec_ops,
};
module_hda_codec_driver(si3054_driver);
diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig
new file mode 100644
index 000000000000..cbf1847896bc
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/Kconfig
@@ -0,0 +1,128 @@
+config SND_HDA_CIRRUS_SCODEC
+ tristate
+
+config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST
+ tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS
+ depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the cirrus side-codec library.
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
+config SND_HDA_SCODEC_CS35L41
+ tristate
+ select SND_HDA_GENERIC
+ select REGMAP_IRQ
+ select FW_CS_DSP
+
+config SND_HDA_SCODEC_COMPONENT
+ tristate
+
+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 EFI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_SOC_CS_AMP_LIB
+ 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 EFI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_SOC_CS_AMP_LIB
+ 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_SCODEC_CS35L56
+ tristate
+
+config SND_HDA_SCODEC_CS35L56_I2C
+ tristate "Build CS35L56 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on SND_SOC
+ select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CIRRUS_SCODEC
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ I2C control.
+
+config SND_HDA_SCODEC_CS35L56_SPI
+ tristate "Build CS35L56 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on SND_SOC
+ select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CIRRUS_SCODEC
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ SPI control.
+
+config SND_HDA_SCODEC_TAS2781
+ tristate
+ select SND_HDA_GENERIC
+
+config SND_HDA_SCODEC_TAS2781_I2C
+ tristate "Build TAS2781 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_HDA_SCODEC_TAS2781
+ select SND_SOC_TAS2781_COMLIB_I2C
+ select SND_SOC_TAS2781_FMWLIB
+ select CRC32
+ help
+ Say Y or M here to include TAS2781 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_TAS2781_I2C=m
+
+config SND_HDA_SCODEC_TAS2781_SPI
+ tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_HDA_SCODEC_TAS2781
+ select SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_FMWLIB
+ select CRC8
+ select CRC32
+ help
+ Say Y or M here to include TAS2781 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_TAS2781_SPI=m
diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile
new file mode 100644
index 000000000000..245e84f6a121
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-cirrus-scodec-y := cirrus_scodec.o
+snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o
+snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o
+snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o
+snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o
+snd-hda-scodec-cs35l56-y := cs35l56_hda.o
+snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o
+snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
+snd-hda-scodec-component-y := hda_component.o
+snd-hda-scodec-tas2781-y := tas2781_hda.o
+snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
+snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o
+
+obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o
+obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o
+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_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
+obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec.c b/sound/hda/codecs/side-codecs/cirrus_scodec.c
new file mode 100644
index 000000000000..3c670207ba30
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus side-codecs.
+//
+// Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/dev_printk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+
+#include "cirrus_scodec.h"
+
+int cirrus_scodec_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 = -ENOENT;
+
+ 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);
+ } 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;
+}
+EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, "SND_HDA_CIRRUS_SCODEC");
+
+MODULE_DESCRIPTION("HDA Cirrus side-codec library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec.h b/sound/hda/codecs/side-codecs/cirrus_scodec.h
new file mode 100644
index 000000000000..ba2041d8ef24
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CIRRUS_SCODEC_H
+#define CIRRUS_SCODEC_H
+
+int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index,
+ int num_amps, int fixed_gpio_id);
+
+#endif /* CIRRUS_SCODEC_H */
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
new file mode 100644
index 000000000000..3cca750857b6
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus side-codec library.
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <linux/device.h>
+#include <linux/device/faux.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "cirrus_scodec.h"
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
+ device_remove_software_node,
+ struct device *)
+
+struct cirrus_scodec_test_gpio {
+ unsigned int pin_state;
+ struct gpio_chip chip;
+};
+
+struct cirrus_scodec_test_priv {
+ struct faux_device *amp_dev;
+ struct platform_device *gpio_pdev;
+ struct cirrus_scodec_test_gpio *gpio_priv;
+};
+
+static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return 0;
+}
+
+static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip);
+
+ return !!(gpio_priv->pin_state & BIT(offset));
+}
+
+static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned long config)
+{
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_LEVEL:
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ return -EOPNOTSUPP;
+ default:
+ return 0;
+ }
+}
+
+static const struct gpio_chip cirrus_scodec_test_gpio_chip = {
+ .label = "cirrus_scodec_test_gpio",
+ .owner = THIS_MODULE,
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .get_direction = cirrus_scodec_test_gpio_get_direction,
+ .direction_input = cirrus_scodec_test_gpio_direction_in,
+ .get = cirrus_scodec_test_gpio_get,
+ .direction_output = cirrus_scodec_test_gpio_direction_out,
+ .set = cirrus_scodec_test_gpio_set,
+ .set_config = cirrus_scodec_test_gpio_set_config,
+ .base = -1,
+ .ngpio = 32,
+};
+
+static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev)
+{
+ struct cirrus_scodec_test_gpio *gpio_priv;
+ int ret;
+
+ gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL);
+ if (!gpio_priv)
+ return -ENOMEM;
+
+ /* GPIO core modifies our struct gpio_chip so use a copy */
+ gpio_priv->chip = cirrus_scodec_test_gpio_chip;
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n");
+
+ dev_set_drvdata(&pdev->dev, gpio_priv);
+
+ return 0;
+}
+
+static struct platform_driver cirrus_scodec_test_gpio_driver = {
+ .driver.name = "cirrus_scodec_test_gpio_drv",
+ .driver.owner = THIS_MODULE,
+ .probe = cirrus_scodec_test_gpio_probe,
+};
+
+/* software_node referencing the gpio driver */
+static const struct software_node cirrus_scodec_test_gpio_swnode = {
+ .name = "cirrus_scodec_test_gpio",
+};
+
+static void cirrus_scodec_test_create_gpio(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver));
+
+ priv->gpio_pdev = kunit_platform_device_alloc(test,
+ cirrus_scodec_test_gpio_driver.driver.name,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev);
+
+ KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev,
+ &cirrus_scodec_test_gpio_swnode));
+ KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,
+ device_remove_software_node_wrapper,
+ &priv->gpio_pdev->dev));
+
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev));
+
+ priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev);
+ KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);
+}
+
+static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg,
+ int gpio_num)
+{
+ struct software_node_ref_args template =
+ SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0);
+
+ *arg = template;
+}
+
+static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test,
+ struct device *dev,
+ struct software_node_ref_args *args,
+ int num_args)
+{
+ const struct property_entry props_template[] = {
+ PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args),
+ { }
+ };
+ struct property_entry *props;
+ struct software_node *node;
+
+ node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL);
+ if (!props)
+ return -ENOMEM;
+
+ memcpy(props, props_template, sizeof(props_template));
+ node->properties = props;
+
+ return device_add_software_node(dev, node);
+}
+
+struct cirrus_scodec_test_spkid_param {
+ int num_amps;
+ int gpios_per_amp;
+ int num_amps_sharing;
+};
+
+static void cirrus_scodec_test_spkid_parse(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+ const struct cirrus_scodec_test_spkid_param *param = test->param_value;
+ int num_spk_id_refs = param->num_amps * param->gpios_per_amp;
+ struct software_node_ref_args *refs;
+ struct device *dev = &priv->amp_dev->dev;
+ unsigned int v;
+ int i, ret;
+
+ refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, refs);
+
+ for (i = 0, v = 0; i < num_spk_id_refs; ) {
+ cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++);
+
+ /*
+ * If amps are sharing GPIOs repeat the last set of
+ * GPIOs until we've done that number of amps.
+ * We have done all GPIOs for an amp when i is a multiple
+ * of gpios_per_amp.
+ * We have done all amps sharing the same GPIOs when i is
+ * a multiple of (gpios_per_amp * num_amps_sharing).
+ */
+ if (!(i % param->gpios_per_amp) &&
+ (i % (param->gpios_per_amp * param->num_amps_sharing)))
+ v -= param->gpios_per_amp;
+ }
+
+ ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs);
+ KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n");
+
+ for (i = 0; i < param->num_amps; ++i) {
+ for (v = 0; v < (1 << param->gpios_per_amp); ++v) {
+ /* Set only the GPIO bits used by this amp */
+ priv->gpio_priv->pin_state =
+ v << (param->gpios_per_amp * (i / param->num_amps_sharing));
+
+ ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1);
+ KUNIT_EXPECT_EQ_MSG(test, ret, v,
+ "get_speaker_id failed amp:%d pin_state:%#x\n",
+ i, priv->gpio_priv->pin_state);
+ }
+ }
+}
+
+static void cirrus_scodec_test_no_spkid(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ int ret;
+
+ ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+static int cirrus_scodec_test_case_init(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ /* Create dummy GPIO */
+ cirrus_scodec_test_create_gpio(test);
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ return 0;
+}
+
+static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = {
+ { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+
+ /* Same GPIO shared by all amps */
+ { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 },
+ { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 },
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 },
+
+ /* Two sets of shared GPIOs */
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 },
+};
+
+static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d",
+ param->num_amps, param->gpios_per_amp, param->num_amps_sharing);
+}
+
+KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases,
+ cirrus_scodec_test_spkid_param_desc);
+
+static struct kunit_case cirrus_scodec_test_cases[] = {
+ KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params),
+ KUNIT_CASE(cirrus_scodec_test_no_spkid),
+ { } /* terminator */
+};
+
+static struct kunit_suite cirrus_scodec_test_suite = {
+ .name = "snd-hda-scodec-cs35l56-test",
+ .init = cirrus_scodec_test_case_init,
+ .test_cases = cirrus_scodec_test_cases,
+};
+
+kunit_test_suite(cirrus_scodec_test_suite);
+
+MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");
+MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
new file mode 100644
index 000000000000..c04208e685a0
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -0,0 +1,2096 @@
+// 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 <linux/spi/spi.h>
+#include <linux/vmalloc.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "hda_component.h"
+#include "cs35l41_hda.h"
+#include "cs35l41_hda_property.h"
+
+#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
+#define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d"
+#define CS35L41_DSM_GET_MUTE 5
+#define CS35L41_NOTIFY_EVENT 0x91
+#define CS35L41_TUNING_SIG 0x109A4A35
+
+enum cs35l41_tuning_param_types {
+ TUNING_PARAM_GAIN,
+};
+
+struct cs35l41_tuning_param_hdr {
+ __le32 tuning_index;
+ __le32 type;
+ __le32 size;
+} __packed;
+
+struct cs35l41_tuning_param {
+ struct cs35l41_tuning_param_hdr hdr;
+ union {
+ __le32 gain;
+ };
+} __packed;
+
+struct cs35l41_tuning_params {
+ __le32 signature;
+ __le32 version;
+ __le32 size;
+ __le32 num_entries;
+ u8 data[];
+} __packed;
+
+/* Firmware calibration controls */
+static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = {
+ .alg_id = CAL_DSP_CTL_ALG,
+ .mem_region = CAL_DSP_CTL_TYPE,
+ .ambient = CAL_AMBIENT_DSP_CTL_NAME,
+ .calr = CAL_R_DSP_CTL_NAME,
+ .status = CAL_STATUS_DSP_CTL_NAME,
+ .checksum = CAL_CHECKSUM_DSP_CTL_NAME,
+};
+
+enum cs35l41_hda_fw_id {
+ CS35L41_HDA_FW_SPK_PROT,
+ CS35L41_HDA_FW_SPK_CALI,
+ CS35L41_HDA_FW_SPK_DIAG,
+ CS35L41_HDA_FW_MISC,
+ CS35L41_HDA_NUM_FW
+};
+
+static const char * const cs35l41_hda_fw_ids[CS35L41_HDA_NUM_FW] = {
+ [CS35L41_HDA_FW_SPK_PROT] = "spk-prot",
+ [CS35L41_HDA_FW_SPK_CALI] = "spk-cali",
+ [CS35L41_HDA_FW_SPK_DIAG] = "spk-diag",
+ [CS35L41_HDA_FW_MISC] = "misc",
+};
+
+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 char channel_name[3] = { 'L', 'R', 'C' };
+
+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_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
+ { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON
+ { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
+};
+
+static const struct reg_sequence cs35l41_hda_config_no_dsp[] = {
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1
+ { CS35L41_ASP_TX3_SRC, 0x00000000 }, // ASPTX3 SRC = ZERO FILL
+ { CS35L41_ASP_TX4_SRC, 0x00000000 }, // ASPTX4 SRC = ZERO FILL
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL
+ { CS35L41_DSP1_RX6_SRC, 0x00000021 }, // DSP1RX6 SRC = CLASSH_TGT
+};
+
+static const struct reg_sequence cs35l41_hda_config_dsp[] = {
+ { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled
+ { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1
+ { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON
+ { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON
+ { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON
+};
+
+static const struct reg_sequence cs35l41_hda_unmute[] = {
+ { 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_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 const struct cs_dsp_client_ops client_ops = {
+ /* cs_dsp requires the client to provide this even if it is empty */
+};
+
+static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename,
+ const struct firmware **firmware, char **filename,
+ const char *ssid)
+{
+ int ret = 0;
+
+ /* Filename is the same as the tuning file with "cfg" suffix */
+ *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename);
+ if (*filename == NULL)
+ return -ENOMEM;
+
+ 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_file(struct cs35l41_hda *cs35l41,
+ const struct firmware **firmware, char **filename,
+ 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, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, amp_name, filetype);
+ else if (spkid > -1 && ssid)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, filetype);
+ else if (ssid && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, amp_name, filetype);
+ else if (ssid)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_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->acpi_subsystem_id, cs35l41->amp_name,
+ 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->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ 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->acpi_subsystem_id,
+ cs35l41->amp_name, cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ 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->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+ }
+
+ return ret;
+coeff_err:
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ return ret;
+}
+
+static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ /* Handle fallback */
+ dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
+
+ /* fallback try cirrus/part-dspN-fwtype.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ NULL, NULL, -1, "wmfw");
+ if (ret)
+ goto err;
+
+ /* fallback try cirrus/part-dspN-fwtype.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ NULL, NULL, -1, "bin");
+ if (ret) {
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ goto err;
+ }
+ return 0;
+
+err:
+ dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
+ 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->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->acpi_subsystem_id, cs35l41->amp_name,
+ -1, "bin");
+ if (ret)
+ goto coeff_err;
+
+ goto out;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ 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->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->acpi_subsystem_id, NULL, -1,
+ "bin");
+ if (ret)
+ goto coeff_err;
+ }
+
+out:
+ if (ret)
+ /* if all attempts at finding firmware fail, try fallback */
+ goto fallback;
+
+ return 0;
+
+coeff_err:
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+fallback:
+ return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+}
+
+
+static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41)
+{
+ int ret;
+
+ if (!cs35l41->cal_data_valid)
+ return;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls,
+ &cs35l41->cal_data);
+ if (ret < 0)
+ dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret);
+ else
+ dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR);
+}
+
+static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid)
+{
+ u32 tmp;
+ int ret;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret);
+ return ret;
+ }
+
+ *uid = tmp;
+ *uid <<= 32;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret);
+ return ret;
+ }
+
+ *uid |= tmp;
+
+ dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid);
+
+ return 0;
+}
+
+static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41)
+{
+ u64 silicon_uid;
+ int ret;
+
+ ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid);
+ if (ret < 0)
+ return ret;
+
+ ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid,
+ cs35l41->index,
+ &cs35l41->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
+ return ret;
+
+ cs35l41->cal_data_valid = true;
+
+ return 0;
+}
+
+
+static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41)
+{
+ cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM;
+}
+
+static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware)
+{
+ struct cs35l41_tuning_params *params;
+ unsigned int offset = 0;
+ unsigned int end;
+ int i;
+
+ params = (void *)&firmware->data[0];
+
+ if (le32_to_cpu(params->size) != firmware->size) {
+ dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n",
+ le32_to_cpu(params->size), firmware->size);
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->version) != 1) {
+ dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n",
+ le32_to_cpu(params->version));
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) {
+ dev_err(cs35l41->dev,
+ "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n",
+ CS35L41_TUNING_SIG, le32_to_cpu(params->signature));
+ return -EINVAL;
+ }
+
+ end = firmware->size - sizeof(struct cs35l41_tuning_params);
+
+ for (i = 0; i < le32_to_cpu(params->num_entries); i++) {
+ struct cs35l41_tuning_param *param;
+
+ if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end))
+ return -EFAULT;
+
+ param = (void *)&params->data[offset];
+ offset += le32_to_cpu(param->hdr.size);
+
+ if (offset > end)
+ return -EFAULT;
+
+ switch (le32_to_cpu(param->hdr.type)) {
+ case TUNING_PARAM_GAIN:
+ cs35l41->tuning_gain = le32_to_cpu(param->gain);
+ dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename)
+{
+ const struct firmware *tuning_param_file = NULL;
+ char *tuning_param_filename = NULL;
+ int ret;
+
+ ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file,
+ &tuning_param_filename, cs35l41->acpi_subsystem_id);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename,
+ ret);
+ return 0;
+ }
+
+ ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n",
+ tuning_param_filename, ret);
+ /* Reset to default Tuning Parameters */
+ cs35l41_set_default_tuning_params(cs35l41);
+ }
+
+ release_firmware(tuning_param_file);
+ kfree(tuning_param_filename);
+
+ return ret;
+}
+
+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;
+ }
+
+ cs35l41_set_default_tuning_params(cs35l41);
+
+ 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);
+ ret = cs35l41_load_tuning_params(cs35l41, coeff_filename);
+ if (ret)
+ dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret);
+ } else {
+ dev_warn(cs35l41->dev, "No Coefficient File available.\n");
+ }
+
+ ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
+ cs35l41_hda_fw_ids[cs35l41->firmware_type]);
+ if (ret)
+ goto err;
+
+ cs35l41_hda_apply_calibration(cs35l41);
+
+err:
+ if (ret)
+ cs35l41_set_default_tuning_params(cs35l41);
+ 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;
+
+ cs35l41_set_default_tuning_params(cs35l41);
+ cs_dsp_stop(dsp);
+ cs_dsp_power_down(dsp);
+ 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);
+
+ guard(mutex)(&cs35l41->fw_mutex);
+ cs35l41_shutdown_dsp(cs35l41);
+ cs_dsp_remove(dsp);
+ cs35l41->halo_initialized = false;
+}
+
+/* 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_update_mixer(struct cs35l41_hda *cs35l41)
+{
+ struct regmap *reg = cs35l41->regmap;
+ unsigned int asp_en = 0;
+ unsigned int dsp1rx2_src = 0;
+
+ regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
+
+ if (cs35l41->cs_dsp.running) {
+ asp_en |= CS35L41_ASP_TX1_EN_MASK; // ASP_TX1_EN = 1
+ regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_dsp));
+ if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST)
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON);
+ else
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_config_no_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_no_dsp));
+ }
+
+ if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) {
+ asp_en |= CS35L41_ASP_RX2_EN_MASK; // ASP_RX2_EN = 1
+ dsp1rx2_src = 0x00000009; // DSP1RX2 SRC = ASPRX2
+ } else {
+ dsp1rx2_src = 0x00000008; // DSP1RX2 SRC = ASPRX1
+ }
+
+ asp_en |= CS35L41_ASP_RX1_EN_MASK; // ASP_RX1_EN = 1
+
+ regmap_write(reg, CS35L41_SP_ENABLES, asp_en);
+ regmap_write(reg, CS35L41_DSP1_RX1_SRC, 0x00000008); // DSP1RX1 SRC = ASPRX1
+ regmap_write(reg, CS35L41_DSP1_RX2_SRC, dsp1rx2_src);
+}
+
+static void cs35l41_hda_play_start(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Play (Start)\n");
+
+ if (cs35l41->playback_started) {
+ dev_dbg(dev, "Playback already started.");
+ return;
+ }
+
+ cs35l41->playback_started = true;
+
+ cs35l41_update_mixer(cs35l41);
+
+ if (cs35l41->cs_dsp.running) {
+ regmap_update_bits(reg, 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, reg, CSPL_MBOX_CMD_RESUME);
+ }
+ 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);
+
+}
+
+static void cs35l41_mute(struct device *dev, bool mute)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+ unsigned int amp_gain;
+
+ dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override,
+ cs35l41->playback_started);
+
+ if (cs35l41->playback_started) {
+ if (mute || cs35l41->mute_override) {
+ dev_dbg(dev, "Muting\n");
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ } else {
+ dev_dbg(dev, "Unmuting\n");
+ if (cs35l41->cs_dsp.running) {
+ dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain);
+ amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) |
+ (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT);
+
+ /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */
+ regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000);
+ regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_unmute,
+ ARRAY_SIZE(cs35l41_hda_unmute));
+ }
+ }
+ }
+}
+
+static void cs35l41_hda_play_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Play (Complete)\n");
+
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1,
+ &cs35l41->cs_dsp);
+ cs35l41_mute(dev, false);
+}
+
+static void cs35l41_hda_pause_start(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Start)\n");
+
+ cs35l41_mute(dev, true);
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0,
+ &cs35l41->cs_dsp);
+}
+
+static void cs35l41_hda_pause_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Complete)\n");
+
+ 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->cs_dsp.running) {
+ cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE);
+ regmap_update_bits(reg, 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;
+}
+
+static void cs35l41_hda_pre_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_pause_start(dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
+static void cs35l41_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ /*
+ * All amps must be resumed before we can start playing back.
+ * This ensures, for external boost, that all amps are in AMP_SAFE mode.
+ * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the
+ * other actions.
+ */
+ pm_runtime_get_sync(dev);
+ break;
+ case HDA_GEN_PCM_ACT_PREPARE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_play_start(dev);
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_pause_done(dev);
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ if (!cs35l41->cs_dsp.running && cs35l41->request_fw_load &&
+ !cs35l41->fw_request_ongoing) {
+ dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n");
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
+ }
+ }
+
+ /*
+ * Playback must be finished for all amps before we start runtime suspend.
+ * This ensures no amps are playing back when we start putting them to sleep.
+ */
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs35l41_hda_post_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_play_done(dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs35l41_hda_channel_map(struct cs35l41_hda *cs35l41)
+{
+ unsigned int tx_num = 0;
+ unsigned int *tx_slot = NULL;
+ unsigned int rx_num;
+ unsigned int *rx_slot;
+ unsigned int mono = 0;
+
+ if (!cs35l41->amp_name) {
+ if (cs35l41->hw_cfg.spk_pos >= ARRAY_SIZE(channel_name))
+ return -EINVAL;
+
+ cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%c%d",
+ channel_name[cs35l41->hw_cfg.spk_pos],
+ cs35l41->channel_index);
+ if (!cs35l41->amp_name)
+ return -ENOMEM;
+ }
+
+ rx_num = 1;
+ if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER)
+ rx_slot = &mono;
+ else
+ rx_slot = &cs35l41->hw_cfg.spk_pos;
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
+ rx_slot);
+}
+
+static int cs35l41_verify_id(struct cs35l41_hda *cs35l41, unsigned int *regid, unsigned int *reg_revid)
+{
+ unsigned int mtl_revid, chipid;
+ int ret;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, regid);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
+ return ret;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, reg_revid);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
+ return ret;
+ }
+
+ 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);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
+{
+ guard(mutex)(&cs35l41->fw_mutex);
+ if (cs35l41->cs_dsp.running) {
+ cs35l41->cs_dsp.running = false;
+ cs35l41->cs_dsp.booted = false;
+ }
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int cs35l41_system_suspend_prep(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System Suspend Prepare\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 */
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_start(dev);
+
+ return 0;
+}
+
+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 */
+ }
+
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_done(dev);
+ }
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret);
+ return ret;
+ }
+
+ /* Shutdown DSP before system suspend */
+ ret = cs35l41_ready_for_reset(cs35l41);
+ if (ret)
+ dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret);
+
+ if (cs35l41->reset_gpio) {
+ dev_info(cs35l41->dev, "Asserting Reset\n");
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ }
+
+ dev_dbg(cs35l41->dev, "System Suspended\n");
+
+ return ret;
+}
+
+static int cs35l41_wait_boot_done(struct cs35l41_hda *cs35l41)
+{
+ unsigned int int_status;
+ int ret;
+
+ 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\n");
+ return ret;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+ if (ret || (int_status & CS35L41_OTP_BOOT_ERR)) {
+ dev_err(cs35l41->dev, "OTP Boot status %x error\n",
+ int_status & CS35L41_OTP_BOOT_ERR);
+ if (!ret)
+ ret = -EIO;
+ return ret;
+ }
+
+ 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) {
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ usleep_range(2000, 2100);
+
+ ret = cs35l41_wait_boot_done(cs35l41);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(cs35l41->regmap, true);
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret);
+ return ret;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
+ }
+
+ 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;
+
+ 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;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ if (cs35l41->cs_dsp.running) {
+ ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
+ cs35l41->hw_cfg.bst_type);
+ if (ret)
+ return ret;
+ } else {
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ }
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ unsigned int regid, reg_revid;
+ int ret;
+
+ 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;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ if (cs35l41->cs_dsp.running) {
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
+ return ret;
+ }
+ }
+
+ ret = cs35l41_verify_id(cs35l41, &regid, &reg_revid);
+ 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;
+ }
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
+ dev_dbg(cs35l41->dev, "CS35L41 Resumed (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+}
+
+static int cs35l41_hda_read_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ guard(mutex)(&dsp->pwr_lock);
+ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp, name, type, alg), 0, buf, len);
+}
+
+static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
+{
+ unsigned int fw_status;
+ __be32 halo_sts;
+ int ret;
+
+ if (cs35l41->bypass_fw) {
+ dev_warn(cs35l41->dev, "Bypassing Firmware.\n");
+ return 0;
+ }
+
+ 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(cs35l41_hda_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: %u\n",
+ halo_sts);
+ goto clean_dsp;
+ }
+
+ 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);
+ goto clean_dsp;
+ }
+
+ 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);
+ ret = -EINVAL;
+ goto clean_dsp;
+ }
+
+ ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret);
+ goto clean_dsp;
+ }
+
+ dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n",
+ cs35l41_hda_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain);
+
+ return 0;
+
+clean_dsp:
+ cs35l41_shutdown_dsp(cs35l41);
+ return ret;
+}
+
+static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
+{
+ if (cs35l41->cs_dsp.running && !load) {
+ dev_dbg(cs35l41->dev, "Unloading Firmware\n");
+ cs35l41_shutdown_dsp(cs35l41);
+ } else if (!cs35l41->cs_dsp.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 int cs35l41_mute_override_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->mute_override;
+ 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);
+
+ scoped_guard(mutex, &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;
+ }
+
+ 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);
+
+ if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (cs35l41->fw_request_ongoing) {
+ dev_dbg(cs35l41->dev, "Existing request not complete\n");
+ return -EBUSY;
+ }
+
+ /* 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");
+ return -EBUSY;
+ }
+
+ cs35l41->fw_request_ongoing = true;
+ cs35l41->request_fw_load = ucontrol->value.integer.value[0];
+ schedule_work(&cs35l41->fw_load_work);
+
+ return 1;
+}
+
+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] < CS35L41_HDA_NUM_FW) {
+ if (cs35l41->firmware_type != ucontrol->value.enumerated.item[0]) {
+ cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
+ return 1;
+ } else {
+ 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(cs35l41_hda_fw_ids), cs35l41_hda_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];
+ char mute_override_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,
+ };
+ struct snd_kcontrol_new mute_override_ctl = {
+ .name = mute_override_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_ctl_boolean_mono_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .get = cs35l41_mute_override_ctl_get,
+ };
+ 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);
+ scnprintf(mute_override_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s Forced Mute Status",
+ 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);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&mute_override_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", mute_override_ctl.name,
+ ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", mute_override_ctl.name);
+
+ return 0;
+}
+
+static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands)
+{
+ guid_t guid;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ return acpi_check_dsm(handle, &guid, 0, BIT(commands));
+}
+
+static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle handle)
+{
+ guid_t guid;
+ union acpi_object *ret;
+ int mute = -ENODEV;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) {
+ ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL);
+ mute = *ret->buffer.pointer;
+ dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute);
+ }
+
+ dev_dbg(cs35l41->dev, "%s: %d\n", __func__, mute);
+
+ return mute;
+}
+
+static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int mute;
+
+ if (event != CS35L41_NOTIFY_EVENT)
+ return;
+
+ mute = cs35l41_get_acpi_mute_state(cs35l41, handle);
+ if (mute < 0) {
+ dev_warn(cs35l41->dev, "Unable to retrieve mute state: %d\n", mute);
+ return;
+ }
+
+ dev_dbg(cs35l41->dev, "Requesting mute value: %d\n", mute);
+ cs35l41->mute_override = (mute > 0);
+ cs35l41_mute(cs35l41->dev, cs35l41->mute_override);
+}
+
+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_parent *parent = master_data;
+ struct hda_component *comp;
+ unsigned int sleep_flags;
+ int ret = 0;
+
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ comp->dev = dev;
+ cs35l41->codec = parent->codec;
+ if (!cs35l41->acpi_subsystem_id)
+ cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
+ cs35l41->codec->core.subsystem_id);
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ cs35l41->firmware_type = CS35L41_HDA_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);
+
+ comp->playback_hook = cs35l41_hda_playback_hook;
+ comp->pre_playback_hook = cs35l41_hda_pre_playback_hook;
+ comp->post_playback_hook = cs35l41_hda_post_playback_hook;
+ comp->acpi_notify = cs35l41_acpi_device_notify;
+ comp->adev = cs35l41->dacpi;
+
+ comp->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comp->adev),
+ CS35L41_DSM_GET_MUTE);
+
+ cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41,
+ acpi_device_handle(cs35l41->dacpi)) > 0;
+
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ sleep_flags = lock_system_sleep();
+ if (!device_link_add(&cs35l41->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
+ dev_warn(dev, "Unable to create device link\n");
+ unlock_system_sleep(sleep_flags);
+
+ pm_runtime_put_autosuspend(dev);
+
+ dev_info(cs35l41->dev,
+ "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n",
+ cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type,
+ cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH,
+ channel_name[cs35l41->hw_cfg.spk_pos],
+ cs35l41->cs_dsp.running, cs35l41->speaker_id);
+
+ 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_parent *parent = master_data;
+ struct hda_component *comp;
+ unsigned int sleep_flags;
+
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
+ return;
+
+ if (comp->dev == dev) {
+ sleep_flags = lock_system_sleep();
+ device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev);
+ unlock_system_sleep(sleep_flags);
+ memset(comp, 0, sizeof(*comp));
+ }
+}
+
+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 const 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 void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol)
+{
+ int irq;
+ int ret;
+ int i;
+
+ if (!cs35l41->irq) {
+ dev_warn(cs35l41->dev, "No Interrupt Found");
+ goto err;
+ }
+
+ 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) {
+ dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0) {
+ ret = irq;
+ dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name,
+ ret);
+ goto err;
+ }
+
+ 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) {
+ dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.",
+ cs35l41_irqs[i].name, ret);
+ goto err;
+ }
+ }
+ return;
+err:
+ dev_warn(cs35l41->dev,
+ "IRQ Config Failed. Amp errors may not be recoverable without reboot.");
+}
+
+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_pol;
+ int ret;
+
+ 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 (using_irq)
+ cs35l41_configure_interrupt(cs35l41, irq_pol);
+
+ return cs35l41_hda_channel_map(cs35l41);
+}
+
+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;
+}
+
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ u32 values[HDA_MAX_COMPONENTS];
+ char *property;
+ size_t nval;
+ int i, ret;
+
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0)
+ goto err;
+
+ 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(cs35l41->dacpi), "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;
+
+ return 0;
+err:
+ dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
+ hw_cfg->valid = false;
+ hw_cfg->gpio1.valid = false;
+ hw_cfg->gpio2.valid = false;
+ acpi_dev_put(cs35l41->dacpi);
+
+ return ret;
+}
+
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
+{
+ struct acpi_device *adev;
+ struct device *physdev;
+ struct spi_device *spi;
+ const char *sub;
+ int 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;
+ }
+
+ cs35l41->dacpi = adev;
+ physdev = get_device(acpi_get_first_physical_node(adev));
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub))
+ sub = NULL;
+ cs35l41->acpi_subsystem_id = sub;
+
+ ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
+ if (!ret) {
+ dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
+ goto out;
+ }
+
+ ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+ if (ret) {
+ put_device(physdev);
+ return ret;
+ }
+out:
+ put_device(physdev);
+
+ cs35l41->bypass_fw = false;
+ if (cs35l41->control_bus == SPI) {
+ spi = to_spi_device(cs35l41->dev);
+ if (spi->max_speed_hz < CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ) {
+ dev_warn(cs35l41->dev,
+ "SPI speed is too slow to support firmware download: %d Hz.\n",
+ spi->max_speed_hz);
+ cs35l41->bypass_fw = true;
+ }
+ }
+
+ return 0;
+}
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap, enum control_bus control_bus)
+{
+ unsigned int regid, reg_revid;
+ 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;
+ cs35l41->control_bus = control_bus;
+ 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) {
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+ regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ usleep_range(2000, 2100);
+
+ ret = cs35l41_wait_boot_done(cs35l41);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_verify_id(cs35l41, &regid, &reg_revid);
+ if (ret)
+ 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_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
+ goto err;
+ }
+
+ ret = cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_get_calibration(cs35l41);
+ if (ret && ret != -ENOENT)
+ goto err;
+
+ cs35l41_mute(cs35l41->dev, true);
+
+ 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_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_probe(cs35l41->dev, ret, "Register component failed\n");
+ goto err_pm;
+ }
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+
+err_pm:
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ 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);
+ gpiod_put(cs35l41->cs_gpio);
+ acpi_dev_put(cs35l41->dacpi);
+ 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);
+
+ component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
+ if (cs35l41->halo_initialized)
+ cs35l41_remove_dsp(cs35l41);
+
+ acpi_dev_put(cs35l41->dacpi);
+
+ 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);
+ gpiod_put(cs35l41->cs_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)
+ .prepare = cs35l41_system_suspend_prep,
+ 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_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
+MODULE_FIRMWARE("cirrus/cs35l41-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l41-*.bin");
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h
new file mode 100644
index 000000000000..7d003c598e93
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.h
@@ -0,0 +1,110 @@
+/* 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/acpi.h>
+#include <linux/efi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <sound/cs35l41.h>
+#include <sound/cs-amp-lib.h>
+
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000
+#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */
+#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */
+
+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,
+ CS35L41_CENTER,
+};
+
+enum cs35l41_hda_gpio_function {
+ CS35L41_NOT_USED,
+ CS35l41_VSPK_SWITCH,
+ CS35L41_INTERRUPT,
+ CS35l41_SYNC,
+};
+
+enum control_bus {
+ I2C,
+ SPI
+};
+
+struct cs35l41_hda {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *cs_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;
+ struct acpi_device *dacpi;
+ bool mute_override;
+ enum control_bus control_bus;
+ bool bypass_fw;
+ unsigned int tuning_gain;
+ struct cirrus_amp_cal_data cal_data;
+ bool cal_data_valid;
+
+};
+
+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, enum control_bus control_bus);
+void cs35l41_hda_remove(struct device *dev);
+int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id);
+
+#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
new file mode 100644
index 000000000000..e77495413c21
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/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), 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" },
+ {}
+};
+
+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 = 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/hda/codecs/side-codecs/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
new file mode 100644
index 000000000000..16d5ea77192f
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35L41 ALSA HDA Property driver
+//
+// Copyright 2023 Cirrus Logic, Inc.
+//
+// Author: Stefan Binding <sbinding@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/string.h>
+#include "cs35l41_hda_property.h"
+#include <linux/spi/spi.h>
+
+#define MAX_AMPS 4
+
+struct cs35l41_config {
+ const char *ssid;
+ int num_amps;
+ enum {
+ INTERNAL,
+ EXTERNAL
+ } boost_type;
+ u8 channel[MAX_AMPS];
+ int reset_gpio_index; /* -1 if no reset gpio */
+ int spkid_gpio_index; /* -1 if no spkid gpio */
+ int cs_gpio_index; /* -1 if no cs gpio, or cs-gpios already exists, max num amps == 2 */
+ int boost_ind_nanohenry; /* Required if boost_type == Internal */
+ int boost_peak_milliamp; /* Required if boost_type == Internal */
+ int boost_cap_microfarad; /* Required if boost_type == Internal */
+};
+
+static const struct cs35l41_config cs35l41_config_table[] = {
+ { "10251826", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "1025182C", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10251844", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10280B27", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280B28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280BEB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10280C4D", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 },
+/*
+ * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
+ * We can override the _DSD to correct the boost type here.
+ * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
+ * in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD.
+ */
+ { "103C89C6", 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
+ { "103C8A28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A29", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2B", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2C", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 },
+ { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C4D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C4E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 },
+ { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431473", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431483", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431493", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314D3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314E3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431503", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431683", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "104316A3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431B93", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CEF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431D1F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431DA2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E02", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E12", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431EE2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10433A20", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A40", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A60", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "17AA3865", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA3929", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA392B", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ {}
+};
+
+static int cs35l41_add_gpios(struct cs35l41_hda *cs35l41, struct device *physdev, int reset_gpio,
+ int spkid_gpio, int cs_gpio_index, int num_amps)
+{
+ struct acpi_gpio_mapping *gpio_mapping = NULL;
+ struct acpi_gpio_params *reset_gpio_params = NULL;
+ struct acpi_gpio_params *spkid_gpio_params = NULL;
+ struct acpi_gpio_params *cs_gpio_params = NULL;
+ unsigned int num_entries = 0;
+ unsigned int reset_index, spkid_index, csgpio_index;
+ int i;
+
+ /*
+ * GPIO Mapping only needs to be done once, since it would be available for subsequent amps
+ */
+ if (cs35l41->dacpi->driver_gpios)
+ return 0;
+
+ if (reset_gpio >= 0) {
+ reset_index = num_entries;
+ num_entries++;
+ }
+
+ if (spkid_gpio >= 0) {
+ spkid_index = num_entries;
+ num_entries++;
+ }
+
+ if ((cs_gpio_index >= 0) && (num_amps == 2)) {
+ csgpio_index = num_entries;
+ num_entries++;
+ }
+
+ if (!num_entries)
+ return 0;
+
+ /* must include termination entry */
+ num_entries++;
+
+ gpio_mapping = devm_kcalloc(physdev, num_entries, sizeof(struct acpi_gpio_mapping),
+ GFP_KERNEL);
+
+ if (!gpio_mapping)
+ goto err;
+
+ if (reset_gpio >= 0) {
+ gpio_mapping[reset_index].name = "reset-gpios";
+ reset_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params),
+ GFP_KERNEL);
+ if (!reset_gpio_params)
+ goto err;
+
+ for (i = 0; i < num_amps; i++)
+ reset_gpio_params[i].crs_entry_index = reset_gpio;
+
+ gpio_mapping[reset_index].data = reset_gpio_params;
+ gpio_mapping[reset_index].size = num_amps;
+ }
+
+ if (spkid_gpio >= 0) {
+ gpio_mapping[spkid_index].name = "spk-id-gpios";
+ spkid_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params),
+ GFP_KERNEL);
+ if (!spkid_gpio_params)
+ goto err;
+
+ for (i = 0; i < num_amps; i++)
+ spkid_gpio_params[i].crs_entry_index = spkid_gpio;
+
+ gpio_mapping[spkid_index].data = spkid_gpio_params;
+ gpio_mapping[spkid_index].size = num_amps;
+ }
+
+ if ((cs_gpio_index >= 0) && (num_amps == 2)) {
+ gpio_mapping[csgpio_index].name = "cs-gpios";
+ /* only one GPIO CS is supported without using _DSD, obtained using index 0 */
+ cs_gpio_params = devm_kzalloc(physdev, sizeof(struct acpi_gpio_params), GFP_KERNEL);
+ if (!cs_gpio_params)
+ goto err;
+
+ cs_gpio_params->crs_entry_index = cs_gpio_index;
+
+ gpio_mapping[csgpio_index].data = cs_gpio_params;
+ gpio_mapping[csgpio_index].size = 1;
+ }
+
+ return devm_acpi_dev_add_driver_gpios(physdev, gpio_mapping);
+err:
+ devm_kfree(physdev, gpio_mapping);
+ devm_kfree(physdev, reset_gpio_params);
+ devm_kfree(physdev, spkid_gpio_params);
+ devm_kfree(physdev, cs_gpio_params);
+ return -ENOMEM;
+}
+
+static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ const struct cs35l41_config *cfg;
+ struct gpio_desc *cs_gpiod;
+ struct spi_device *spi;
+ bool dsd_found;
+ int ret;
+ int i;
+
+ for (cfg = cs35l41_config_table; cfg->ssid; cfg++) {
+ if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id))
+ break;
+ }
+
+ if (!cfg->ssid)
+ return -ENOENT;
+
+ if (!cs35l41->dacpi || cs35l41->dacpi != ACPI_COMPANION(physdev)) {
+ dev_err(cs35l41->dev, "ACPI Device does not match, cannot override _DSD.\n");
+ return -ENODEV;
+ }
+
+ dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id);
+
+ dsd_found = acpi_dev_has_props(cs35l41->dacpi);
+
+ if (!dsd_found) {
+ ret = cs35l41_add_gpios(cs35l41, physdev, cfg->reset_gpio_index,
+ cfg->spkid_gpio_index, cfg->cs_gpio_index,
+ cfg->num_amps);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret);
+ return ret;
+ }
+ } else if (cfg->reset_gpio_index >= 0 || cfg->spkid_gpio_index >= 0) {
+ dev_warn(cs35l41->dev, "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, "
+ "_DSD already exists.\n");
+ }
+
+ if (cs35l41->control_bus == SPI) {
+ cs35l41->index = id;
+
+ /*
+ * Manually set the Chip Select for the second amp <cs_gpio_index> in the node.
+ * This is only supported for systems with 2 amps, since we cannot expand the
+ * default number of chip selects without using cs-gpios
+ * The CS GPIO must be set high prior to communicating with the first amp (which
+ * uses a native chip select), to ensure the second amp does not clash with the
+ * first.
+ */
+ if (IS_ENABLED(CONFIG_SPI) && cfg->cs_gpio_index >= 0) {
+ spi = to_spi_device(cs35l41->dev);
+
+ if (cfg->num_amps != 2) {
+ dev_warn(cs35l41->dev,
+ "Cannot update SPI CS, Number of Amps (%d) != 2\n",
+ cfg->num_amps);
+ } else if (dsd_found) {
+ dev_warn(cs35l41->dev,
+ "Cannot update SPI CS, _DSD already exists.\n");
+ } else {
+ /*
+ * This is obtained using driver_gpios, since only one GPIO for CS
+ * exists, this can be obtained using index 0.
+ */
+ cs_gpiod = gpiod_get_index(physdev, "cs", 0, GPIOD_OUT_LOW);
+ if (IS_ERR(cs_gpiod)) {
+ dev_err(cs35l41->dev,
+ "Unable to get Chip Select GPIO descriptor\n");
+ return PTR_ERR(cs_gpiod);
+ }
+ if (id == 1) {
+ spi_set_csgpiod(spi, 0, cs_gpiod);
+ cs35l41->cs_gpio = cs_gpiod;
+ } else {
+ gpiod_set_value_cansleep(cs_gpiod, true);
+ gpiod_put(cs_gpiod);
+ }
+ spi_setup(spi);
+ }
+ }
+ } else {
+ if (cfg->num_amps > 2)
+ /*
+ * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43,
+ * subtracting 0x40 would give zero-based index
+ */
+ cs35l41->index = id - 0x40;
+ else
+ /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ }
+
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset",
+ cs35l41->index, GPIOD_OUT_LOW,
+ "cs35l41-reset");
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, cfg->num_amps, -1);
+
+ hw_cfg->spk_pos = cfg->channel[cs35l41->index];
+
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (cfg->channel[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
+ if (cfg->boost_type == INTERNAL) {
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ hw_cfg->bst_ind = cfg->boost_ind_nanohenry;
+ hw_cfg->bst_ipk = cfg->boost_peak_milliamp;
+ hw_cfg->bst_cap = cfg->boost_cap_microfarad;
+ hw_cfg->gpio1.func = CS35L41_NOT_USED;
+ hw_cfg->gpio1.valid = true;
+ } else {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->bst_ind = -1;
+ hw_cfg->bst_ipk = -1;
+ hw_cfg->bst_cap = -1;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ }
+
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
+/*
+ * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has
+ * its own speaker id.
+ */
+static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* If _DSD exists for this laptop, we cannot support it through here */
+ if (acpi_dev_has_props(cs35l41->dacpi))
+ return -ENOENT;
+
+ /* 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);
+ if (cs35l41->index == 0)
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1);
+ else
+ 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;
+
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ hw_cfg->bst_ind = 1000;
+ hw_cfg->bst_ipk = 4100;
+ hw_cfg->bst_cap = 24;
+ hw_cfg->gpio1.func = CS35L41_NOT_USED;
+ hw_cfg->gpio1.valid = true;
+
+ return 0;
+}
+
+/*
+ * 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 lenovo_legion_no_acpi(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 (strcmp(hid, "CLSA0100") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ } else if (strcmp(hid, "CLSA0101") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ }
+
+ return 0;
+}
+
+static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ int ret;
+
+ ret = cs35l41_add_gpios(cs35l41, physdev, -1, 2, -1, 2);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret);
+ return ret;
+ }
+
+ return cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+}
+
+struct cs35l41_prop_model {
+ const char *hid;
+ const char *ssid;
+ int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+};
+
+static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
+ { "CLSA0100", NULL, lenovo_legion_no_acpi },
+ { "CLSA0101", NULL, lenovo_legion_no_acpi },
+ { "CSC3551", "10251826", generic_dsd_config },
+ { "CSC3551", "1025182C", generic_dsd_config },
+ { "CSC3551", "10251844", generic_dsd_config },
+ { "CSC3551", "10280B27", generic_dsd_config },
+ { "CSC3551", "10280B28", generic_dsd_config },
+ { "CSC3551", "10280BEB", generic_dsd_config },
+ { "CSC3551", "10280C4D", generic_dsd_config },
+ { "CSC3551", "103C89C6", generic_dsd_config },
+ { "CSC3551", "103C8A28", generic_dsd_config },
+ { "CSC3551", "103C8A29", generic_dsd_config },
+ { "CSC3551", "103C8A2A", generic_dsd_config },
+ { "CSC3551", "103C8A2B", generic_dsd_config },
+ { "CSC3551", "103C8A2C", generic_dsd_config },
+ { "CSC3551", "103C8A2D", generic_dsd_config },
+ { "CSC3551", "103C8A2E", generic_dsd_config },
+ { "CSC3551", "103C8A30", generic_dsd_config },
+ { "CSC3551", "103C8A31", generic_dsd_config },
+ { "CSC3551", "103C8A6E", generic_dsd_config },
+ { "CSC3551", "103C8BB3", generic_dsd_config },
+ { "CSC3551", "103C8BB4", generic_dsd_config },
+ { "CSC3551", "103C8BDD", generic_dsd_config },
+ { "CSC3551", "103C8BDE", generic_dsd_config },
+ { "CSC3551", "103C8BDF", generic_dsd_config },
+ { "CSC3551", "103C8BE0", generic_dsd_config },
+ { "CSC3551", "103C8BE1", generic_dsd_config },
+ { "CSC3551", "103C8BE2", generic_dsd_config },
+ { "CSC3551", "103C8BE3", generic_dsd_config },
+ { "CSC3551", "103C8BE5", generic_dsd_config },
+ { "CSC3551", "103C8BE6", generic_dsd_config },
+ { "CSC3551", "103C8BE7", generic_dsd_config },
+ { "CSC3551", "103C8BE8", generic_dsd_config },
+ { "CSC3551", "103C8BE9", generic_dsd_config },
+ { "CSC3551", "103C8B3A", generic_dsd_config },
+ { "CSC3551", "103C8C15", generic_dsd_config },
+ { "CSC3551", "103C8C16", generic_dsd_config },
+ { "CSC3551", "103C8C17", generic_dsd_config },
+ { "CSC3551", "103C8C4D", generic_dsd_config },
+ { "CSC3551", "103C8C4E", generic_dsd_config },
+ { "CSC3551", "103C8C4F", generic_dsd_config },
+ { "CSC3551", "103C8C50", generic_dsd_config },
+ { "CSC3551", "103C8C51", generic_dsd_config },
+ { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8CDD", generic_dsd_config },
+ { "CSC3551", "103C8CDE", generic_dsd_config },
+ { "CSC3551", "104312AF", generic_dsd_config },
+ { "CSC3551", "10431433", generic_dsd_config },
+ { "CSC3551", "10431463", generic_dsd_config },
+ { "CSC3551", "10431473", generic_dsd_config },
+ { "CSC3551", "10431483", generic_dsd_config },
+ { "CSC3551", "10431493", generic_dsd_config },
+ { "CSC3551", "104314D3", generic_dsd_config },
+ { "CSC3551", "104314E3", generic_dsd_config },
+ { "CSC3551", "10431503", generic_dsd_config },
+ { "CSC3551", "10431533", generic_dsd_config },
+ { "CSC3551", "10431573", generic_dsd_config },
+ { "CSC3551", "10431663", generic_dsd_config },
+ { "CSC3551", "10431683", generic_dsd_config },
+ { "CSC3551", "104316A3", generic_dsd_config },
+ { "CSC3551", "104316D3", generic_dsd_config },
+ { "CSC3551", "104316F3", generic_dsd_config },
+ { "CSC3551", "104317F3", generic_dsd_config },
+ { "CSC3551", "10431863", generic_dsd_config },
+ { "CSC3551", "104318D3", generic_dsd_config },
+ { "CSC3551", "10431A63", missing_speaker_id_gpio2 },
+ { "CSC3551", "10431A83", generic_dsd_config },
+ { "CSC3551", "10431B93", generic_dsd_config },
+ { "CSC3551", "10431C9F", generic_dsd_config },
+ { "CSC3551", "10431CAF", generic_dsd_config },
+ { "CSC3551", "10431CCF", generic_dsd_config },
+ { "CSC3551", "10431CDF", generic_dsd_config },
+ { "CSC3551", "10431CEF", generic_dsd_config },
+ { "CSC3551", "10431D1F", generic_dsd_config },
+ { "CSC3551", "10431DA2", generic_dsd_config },
+ { "CSC3551", "10431E02", generic_dsd_config },
+ { "CSC3551", "10431E12", generic_dsd_config },
+ { "CSC3551", "10431EE2", generic_dsd_config },
+ { "CSC3551", "10431F12", generic_dsd_config },
+ { "CSC3551", "10431F1F", generic_dsd_config },
+ { "CSC3551", "10431F62", generic_dsd_config },
+ { "CSC3551", "10433A20", generic_dsd_config },
+ { "CSC3551", "10433A30", generic_dsd_config },
+ { "CSC3551", "10433A40", generic_dsd_config },
+ { "CSC3551", "10433A50", generic_dsd_config },
+ { "CSC3551", "10433A60", generic_dsd_config },
+ { "CSC3551", "17AA3865", generic_dsd_config },
+ { "CSC3551", "17AA3866", generic_dsd_config },
+ { "CSC3551", "17AA386E", generic_dsd_config },
+ { "CSC3551", "17AA386F", generic_dsd_config },
+ { "CSC3551", "17AA3877", generic_dsd_config },
+ { "CSC3551", "17AA3878", generic_dsd_config },
+ { "CSC3551", "17AA38A9", generic_dsd_config },
+ { "CSC3551", "17AA38AB", generic_dsd_config },
+ { "CSC3551", "17AA38B4", generic_dsd_config },
+ { "CSC3551", "17AA38B5", generic_dsd_config },
+ { "CSC3551", "17AA38B6", generic_dsd_config },
+ { "CSC3551", "17AA38B7", generic_dsd_config },
+ { "CSC3551", "17AA38C7", generic_dsd_config },
+ { "CSC3551", "17AA38C8", generic_dsd_config },
+ { "CSC3551", "17AA38F9", generic_dsd_config },
+ { "CSC3551", "17AA38FA", generic_dsd_config },
+ { "CSC3551", "17AA3929", generic_dsd_config },
+ { "CSC3551", "17AA392B", generic_dsd_config },
+ {}
+};
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ const struct cs35l41_prop_model *model;
+
+ for (model = cs35l41_prop_model_table; model->hid; model++) {
+ if (!strcmp(model->hid, hid) &&
+ (!model->ssid ||
+ (cs35l41->acpi_subsystem_id &&
+ !strcasecmp(model->ssid, cs35l41->acpi_subsystem_id))))
+ return model->add_prop(cs35l41, physdev, id, hid);
+ }
+
+ return -ENOENT;
+}
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.h b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h
new file mode 100644
index 000000000000..fd834042e2fd
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS35L41 ALSA HDA Property driver
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ * Author: Stefan Binding <sbinding@opensource.cirrus.com>
+ */
+
+#ifndef CS35L41_HDA_PROP_H
+#define CS35L41_HDA_PROP_H
+
+#include <linux/device.h>
+#include "cs35l41_hda.h"
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+#endif /* CS35L41_HDA_PROP_H */
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c
new file mode 100644
index 000000000000..2acbaf8467a0
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c
@@ -0,0 +1,64 @@
+// 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), 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 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l41_hda_spi_id);
+
+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/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
new file mode 100644
index 000000000000..5bb1c4ebeaf3
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -0,0 +1,1182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// HDA audio 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/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs-amp-lib.h>
+#include <sound/hda_codec.h>
+#include <sound/tlv.h>
+#include "cirrus_scodec.h"
+#include "cs35l56_hda.h"
+#include "hda_component.h"
+#include "../generic.h"
+
+ /*
+ * The cs35l56_hda_dai_config[] reg sequence configures the device as
+ * ASP1_BCLK_FREQ = 3.072 MHz
+ * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S
+ * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots
+ * ASP1_RX_WL = 24 bits per sample
+ * ASP1_TX_WL = 24 bits per sample
+ * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled
+ *
+ * Override any Windows-specific mixer settings applied by the firmware.
+ */
+static const struct reg_sequence cs35l56_hda_dai_config[] = {
+ { CS35L56_ASP1_CONTROL1, 0x00000021 },
+ { CS35L56_ASP1_CONTROL2, 0x20200200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000003 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000018 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000019 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000020 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+
+};
+
+static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
+static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
+ if (ret == 0) {
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->transducer_actual_ps,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
+ }
+ regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ cs35l56->asp_tx_mask);
+ cs35l56->playing = true;
+}
+
+static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56)
+{
+ cs35l56->playing = false;
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT));
+
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static void cs35l56_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ if (cs35l56->playing)
+ break;
+
+ /* If we're suspended: flag that resume should start playback */
+ if (cs35l56->suspended) {
+ cs35l56->playing = true;
+ break;
+ }
+
+ cs35l56_hda_play(cs35l56);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ if (!cs35l56->playing)
+ break;
+
+ cs35l56_hda_pause(cs35l56);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs35l56_hda_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ if (cs35l56->cs_dsp.booted)
+ cs_dsp_stop(&cs35l56->cs_dsp);
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int cs35l56_hda_runtime_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, false);
+ if (ret < 0)
+ return ret;
+
+ if (cs35l56->cs_dsp.booted) {
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+ regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ return ret;
+}
+
+static int cs35l56_hda_mixer_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 = CS35L56_NUM_INPUT_SRC;
+ if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC)
+ uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1;
+ strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int reg_val;
+ int i;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
+ reg_val &= CS35L56_ASP_TXn_SRC_MASK;
+
+ for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) {
+ if (cs35l56_tx_input_values[i] == reg_val) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ bool changed;
+
+ if (item >= CS35L56_NUM_INPUT_SRC)
+ return -EINVAL;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
+ CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
+ &changed);
+
+ return changed;
+}
+
+static int cs35l56_hda_posture_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 = CS35L56_MAIN_POSTURE_MIN;
+ uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX;
+ return 0;
+}
+
+static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int pos;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_read(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->posture_number, &pos);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = pos;
+
+ return 0;
+}
+
+static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned long pos = ucontrol->value.integer.value[0];
+ bool changed;
+ int ret;
+
+ if ((pos < CS35L56_MAIN_POSTURE_MIN) ||
+ (pos > CS35L56_MAIN_POSTURE_MAX))
+ return -EINVAL;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->posture_number,
+ CS35L56_MAIN_POSTURE_MASK, pos, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static const struct {
+ const char *name;
+ unsigned int reg;
+} cs35l56_hda_mixer_controls[] = {
+ { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT },
+ { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT },
+ { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT },
+ { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0);
+
+static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.step = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int raw_vol;
+ int vol;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_read(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, &raw_vol);
+
+ if (ret)
+ return ret;
+
+ vol = (s16)(raw_vol & 0xFFFF);
+ vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT))
+ vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1));
+
+ ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ long vol = ucontrol->value.integer.value[0];
+ unsigned int raw_vol;
+ bool changed;
+ int ret;
+
+ if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN)))
+ return -EINVAL;
+
+ raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MASK, raw_vol, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56)
+{
+ struct snd_kcontrol_new ctl_template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs35l56_hda_posture_info,
+ .get = cs35l56_hda_posture_get,
+ .put = cs35l56_hda_posture_put,
+ };
+ char name[64];
+ int i;
+
+ snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+
+ /* Mixer controls */
+ ctl_template.info = cs35l56_hda_mixer_info;
+ ctl_template.get = cs35l56_hda_mixer_get;
+ ctl_template.put = cs35l56_hda_mixer_put;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls));
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) {
+ snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name,
+ cs35l56_hda_mixer_controls[i].name);
+ ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg;
+ cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) {
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n",
+ ctl_template.name);
+ }
+ }
+
+ ctl_template.info = cs35l56_hda_vol_info;
+ ctl_template.get = cs35l56_hda_vol_get;
+ ctl_template.put = cs35l56_hda_vol_put;
+ ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ);
+ ctl_template.tlv.p = cs35l56_hda_vol_tlv;
+ snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+}
+
+static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--)
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]);
+
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl);
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl);
+}
+
+static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
+ /* cs_dsp requires the client to provide this even if it is empty */
+};
+
+static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
+ const struct firmware **firmware, char **filename,
+ const char *base_name, const char *system_name,
+ const char *amp_name,
+ const char *filetype)
+{
+ char *s, c;
+ int ret = 0;
+
+ if (system_name && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name,
+ system_name, amp_name, filetype);
+ else if (system_name)
+ *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name,
+ system_name, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype);
+
+ if (!*filename)
+ return -ENOMEM;
+
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and forward slash 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, cs35l56->base.dev);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename);
+
+ return 0;
+}
+
+static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
+ unsigned int preloaded_fw_ver,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ const char *system_name = cs35l56->system_name;
+ const char *amp_name = cs35l56->amp_name;
+ char base_name[37];
+ int ret;
+
+ if (preloaded_fw_ver) {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc",
+ cs35l56->base.type,
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ preloaded_fw_ver & 0xffffff);
+ } else {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l%02x-%02x%s-dsp1-misc",
+ cs35l56->base.type,
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "");
+ }
+
+ if (system_name && amp_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, system_name, amp_name, "wmfw")) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, amp_name, "bin");
+ return;
+ }
+ }
+
+ if (system_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, system_name, NULL, "wmfw")) {
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ base_name, system_name,
+ amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ base_name, system_name,
+ NULL, "bin");
+ return;
+ }
+
+ /*
+ * Check for system-specific bin files without wmfw before
+ * falling back to generic firmware
+ */
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, NULL, "bin");
+
+ if (*coeff_firmware)
+ return;
+ }
+
+ ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, NULL, NULL, "wmfw");
+ if (!ret) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, NULL, NULL, "bin");
+ return;
+ }
+
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, NULL, NULL, "bin");
+}
+
+static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
+ char *wmfw_filename,
+ const struct firmware *coeff_firmware,
+ char *coeff_filename)
+{
+ release_firmware(wmfw_firmware);
+ kfree(wmfw_filename);
+
+ release_firmware(coeff_firmware);
+ kfree(coeff_filename);
+}
+
+static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
+{
+ int ret;
+
+ if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
+ return;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
+ &cs35l56_calibration_controls,
+ &cs35l56->base.cal_data);
+ if (ret < 0)
+ dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
+ else
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+}
+
+static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
+{
+ const struct firmware *coeff_firmware = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ char *wmfw_filename = NULL;
+ unsigned int preloaded_fw_ver;
+ bool firmware_missing;
+ int ret;
+
+ /*
+ * Prepare for a new DSP power-up. If the DSP has had firmware
+ * downloaded previously then it needs to be powered down so that it
+ * can be updated.
+ */
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+
+ cs35l56->base.fw_patched = false;
+
+ ret = pm_runtime_resume_and_get(cs35l56->base.dev);
+ if (ret < 0) {
+ dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret);
+ return;
+ }
+
+ /*
+ * The firmware can only be upgraded if it is currently running
+ * from the built-in ROM. If not, the wmfw/bin must be for the
+ * version of firmware that is running on the chip.
+ */
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
+ if (ret)
+ goto err_pm_put;
+
+ if (firmware_missing)
+ preloaded_fw_ver = 0;
+
+ cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+
+ /*
+ * If the BIOS didn't patch the firmware a bin file is mandatory to
+ * enable the ASP·
+ */
+ if (!coeff_firmware && firmware_missing) {
+ dev_err(cs35l56->base.dev, ".bin file required but not found\n");
+ goto err_fw_release;
+ }
+
+ mutex_lock(&cs35l56->base.irq_lock);
+
+ /*
+ * If the firmware hasn't been patched it must be shutdown before
+ * doing a full patch and reset afterwards. If it is already
+ * running a patched version the firmware files only contain
+ * tunings and we can use the lower cost reinit sequence instead.
+ */
+ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
+ if (ret)
+ goto err;
+ }
+
+ ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename, "misc");
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret);
+ goto err;
+ }
+
+ if (wmfw_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename);
+
+ if (coeff_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
+
+ /* If we downloaded firmware, reset the device and wait for it to boot */
+ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
+ cs35l56_system_reset(&cs35l56->base, false);
+ regcache_mark_dirty(cs35l56->base.regmap);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err_powered_up;
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err_powered_up;
+
+ regcache_sync(cs35l56->base.regmap);
+
+ regmap_clear_bits(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->prot_sts,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret)
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+
+ cs35l56_hda_apply_calibration(cs35l56);
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ if (ret)
+ cs_dsp_stop(&cs35l56->cs_dsp);
+
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->cs_dsp);
+
+err_powered_up:
+ if (!cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+err:
+ mutex_unlock(&cs35l56->base.irq_lock);
+err_fw_release:
+ cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+err_pm_put:
+ pm_runtime_put(cs35l56->base.dev);
+}
+
+static void cs35l56_hda_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work);
+
+ cs35l56_hda_fw_load(cs35l56);
+}
+
+static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ comp->dev = dev;
+ cs35l56->codec = parent->codec;
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+ comp->playback_hook = cs35l56_hda_playback_hook;
+
+ queue_work(system_long_wq, &cs35l56->dsp_work);
+
+ cs35l56_hda_create_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
+ cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
+#endif
+
+ dev_dbg(cs35l56->base.dev, "Bound\n");
+
+ return 0;
+}
+
+static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ cancel_work_sync(&cs35l56->dsp_work);
+
+ cs35l56_hda_remove_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp);
+ debugfs_remove_recursive(cs35l56->debugfs_root);
+#endif
+
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (comp && (comp->dev == dev))
+ memset(comp, 0, sizeof(*comp));
+
+ cs35l56->codec = NULL;
+
+ dev_dbg(cs35l56->base.dev, "Unbound\n");
+}
+
+static const struct component_ops cs35l56_hda_comp_ops = {
+ .bind = cs35l56_hda_bind,
+ .unbind = cs35l56_hda_unbind,
+};
+
+static int cs35l56_hda_system_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ if (cs35l56->playing)
+ cs35l56_hda_pause(cs35l56);
+
+ cs35l56->suspended = true;
+
+ /*
+ * 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->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs35l56_hda_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * 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->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_early(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ cs35l56_wait_control_port_ready();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ if (ret)
+ return ret;
+
+ cs35l56->suspended = false;
+
+ if (!cs35l56->codec)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
+ if (ret > 0)
+ queue_work(system_long_wq, &cs35l56->dsp_work);
+
+ if (cs35l56->playing)
+ cs35l56_hda_play(cs35l56);
+
+ return 0;
+}
+
+static int cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr)
+{
+ /* The cirrus,dev-index property has the wrong values */
+ switch (*bus_addr) {
+ case 0x30:
+ cs35l56->index = 1;
+ return 0;
+ case 0x31:
+ cs35l56->index = 0;
+ return 0;
+ default:
+ /* There is a pseudo-address for broadcast to both amps - ignore it */
+ dev_dbg(cs35l56->base.dev, "Ignoring I2C address %#x\n", *bus_addr);
+ return 0;
+ }
+}
+
+static const struct {
+ const char *sub;
+ int (*fixup_fn)(struct cs35l56_hda *cs35l56, int *bus_addr);
+} cs35l56_hda_fixups[] = {
+ {
+ .sub = "17AA390B", /* Lenovo Yoga Book 9i GenX */
+ .fixup_fn = cs35l56_hda_fixup_yoga9,
+ },
+};
+
+static int cs35l56_hda_apply_platform_fixups(struct cs35l56_hda *cs35l56, const char *sub,
+ int *bus_addr)
+{
+ int i;
+
+ if (IS_ERR(sub))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_hda_fixups); i++) {
+ if (strcasecmp(cs35l56_hda_fixups[i].sub, sub) == 0) {
+ dev_dbg(cs35l56->base.dev, "Applying fixup for %s\n",
+ cs35l56_hda_fixups[i].sub);
+ return (cs35l56_hda_fixups[i].fixup_fn)(cs35l56, bus_addr);
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id)
+{
+ u32 values[HDA_MAX_COMPONENTS];
+ char hid_string[8];
+ struct acpi_device *adev;
+ const char *property, *sub;
+ size_t nval;
+ int i, ret;
+
+ /*
+ * ACPI_COMPANION isn't available when this driver was instantiated by
+ * the serial-multi-instantiate driver, so lookup the node by HID
+ */
+ if (!ACPI_COMPANION(cs35l56->base.dev)) {
+ snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid);
+ adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
+ dev_name(cs35l56->base.dev));
+ return -ENODEV;
+ }
+ ACPI_COMPANION_SET(cs35l56->base.dev, adev);
+ }
+
+ /* Initialize things that could be overwritten by a fixup */
+ cs35l56->index = -1;
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
+ ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id);
+ if (ret)
+ return ret;
+
+ if (cs35l56->index == -1) {
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(cs35l56->base.dev, property);
+ if (ret <= 0)
+ goto err;
+
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l56->index = i;
+ break;
+ }
+ }
+
+ /*
+ * It's not an error for the ID to be missing: for I2C there can be
+ * an alias address that is not a real device. So reject silently.
+ */
+ if (cs35l56->index == -1) {
+ dev_dbg(cs35l56->base.dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ if (IS_ERR(sub)) {
+ dev_info(cs35l56->base.dev,
+ "Read ACPI _SUB failed(%ld): fallback to generic firmware\n",
+ PTR_ERR(sub));
+ } else {
+ ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1);
+ if (ret == -ENOENT) {
+ cs35l56->system_name = sub;
+ } else if (ret >= 0) {
+ cs35l56->system_name = kasprintf(GFP_KERNEL, "%s-spkid%d", sub, ret);
+ kfree(sub);
+ if (!cs35l56->system_name)
+ return -ENOMEM;
+ } else {
+ return ret;
+ }
+ }
+
+ cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev,
+ "reset",
+ cs35l56->index,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.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->base.dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
+ }
+
+ return 0;
+
+err:
+ if (ret != -ENODEV)
+ dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
+
+ return ret;
+}
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
+{
+ int ret;
+
+ mutex_init(&cs35l56->base.irq_lock);
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
+
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work);
+
+ ret = cs35l56_hda_read_acpi(cs35l56, hid, id);
+ if (ret)
+ goto err;
+
+ cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d",
+ cs35l56->index + 1);
+ if (!cs35l56->amp_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ cs35l56->base.type = hid & 0xff;
+ cs35l56->base.cal_index = -1;
+
+ cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
+ cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
+
+ if (cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "Hard reset\n");
+
+ /*
+ * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the
+ * ACPI defines a different default state. So explicitly set low.
+ */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ }
+
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
+ goto err;
+
+ /* Reset the device and wait for it to boot */
+ cs35l56_system_reset(&cs35l56->base, false);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ regcache_sync(cs35l56->base.regmap);
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ ret = cs35l56_get_calibration(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
+ goto err;
+ }
+
+ dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n",
+ cs35l56->system_name, cs35l56->amp_name);
+
+ regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
+ ARRAY_SIZE(cs35l56_hda_dai_config));
+
+ /*
+ * By default only enable one ASP1TXn, where n=amplifier index,
+ * This prevents multiple amps trying to drive the same slot.
+ */
+ cs35l56->asp_tx_mask = BIT(cs35l56->index);
+
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
+
+ cs35l56->base.init_done = true;
+
+ ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret);
+ goto pm_err;
+ }
+
+ return 0;
+
+pm_err:
+ pm_runtime_disable(cs35l56->base.dev);
+ cs_dsp_remove(&cs35l56->cs_dsp);
+err:
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, "SND_HDA_SCODEC_CS35L56");
+
+void cs35l56_hda_remove(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_get_sync(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+
+ cs_dsp_remove(&cs35l56->cs_dsp);
+
+ kfree(cs35l56->system_name);
+ pm_runtime_put_noidle(cs35l56->base.dev);
+
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, "SND_HDA_SCODEC_CS35L56");
+
+const struct dev_pm_ops cs35l56_hda_pm_ops = {
+ RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late,
+ cs35l56_hda_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq,
+ cs35l56_hda_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, "SND_HDA_SCODEC_CS35L56");
+
+MODULE_DESCRIPTION("CS35L56 HDA Driver");
+MODULE_IMPORT_NS("FW_CS_DSP");
+MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("cirrus/cs35l54-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l54-*.bin");
+MODULE_FIRMWARE("cirrus/cs35l56-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l56-*.bin");
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h
new file mode 100644
index 000000000000..38d94fb213a5
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * HDA audio driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS35L56_HDA_H__
+#define __CS35L56_HDA_H__
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+
+struct dentry;
+
+struct cs35l56_hda {
+ struct cs35l56_base base;
+ struct hda_codec *codec;
+ struct work_struct dsp_work;
+
+ int index;
+ const char *system_name;
+ const char *amp_name;
+
+ struct cs_dsp cs_dsp;
+ bool playing;
+ bool suspended;
+ u8 asp_tx_mask;
+
+ struct snd_kcontrol *posture_ctl;
+ struct snd_kcontrol *volume_ctl;
+ struct snd_kcontrol *mixer_ctl[4];
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ struct dentry *debugfs_root;
+#endif
+};
+
+extern const struct dev_pm_ops cs35l56_hda_pm_ops;
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);
+void cs35l56_hda_remove(struct device *dev);
+
+#endif /*__CS35L56_HDA_H__*/
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c
new file mode 100644
index 000000000000..1072f17385ac
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(clt);
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&clt->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &clt->dev;
+
+#ifdef CS35L56_WAKE_HOLD_TIME_US
+ cs35l56->base.can_hibernate = true;
+#endif
+
+ cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr);
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, clt->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_i2c_remove(struct i2c_client *clt)
+{
+ cs35l56_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l56_hda_i2c_id[] = {
+ { "cs35l54-hda", 0x3554 },
+ { "cs35l56-hda", 0x3556 },
+ { "cs35l57-hda", 0x3557 },
+ {}
+};
+
+static const struct acpi_device_id cs35l56_acpi_hda_match[] = {
+ { "CSC3554", 0 },
+ { "CSC3556", 0 },
+ { "CSC3557", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match);
+
+static struct i2c_driver cs35l56_hda_i2c_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .acpi_match_table = cs35l56_acpi_hda_match,
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_i2c_id,
+ .probe = cs35l56_hda_i2c_probe,
+ .remove = cs35l56_hda_i2c_remove,
+};
+module_i2c_driver(cs35l56_hda_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 I2C driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56");
+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/hda/codecs/side-codecs/cs35l56_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c
new file mode 100644
index 000000000000..f802c83c57b4
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &spi->dev;
+ ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
+ if (ret)
+ return ret;
+
+#ifdef CS35L56_WAKE_HOLD_TIME_US
+ cs35l56->base.can_hibernate = true;
+#endif
+
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0));
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_spi_remove(struct spi_device *spi)
+{
+ cs35l56_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l56_hda_spi_id[] = {
+ { "cs35l54-hda", 0x3554 },
+ { "cs35l56-hda", 0x3556 },
+ { "cs35l57-hda", 0x3557 },
+ {}
+};
+
+static const struct acpi_device_id cs35l56_acpi_hda_match[] = {
+ { "CSC3554", 0 },
+ { "CSC3556", 0 },
+ { "CSC3557", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match);
+
+static struct spi_driver cs35l56_hda_spi_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .acpi_match_table = cs35l56_acpi_hda_match,
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_spi_id,
+ .probe = cs35l56_hda_spi_probe,
+ .remove = cs35l56_hda_spi_remove,
+};
+module_spi_driver(cs35l56_hda_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 SPI driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56");
+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/hda/codecs/side-codecs/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c
new file mode 100644
index 000000000000..bcf47a301697
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/hda_component.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/acpi.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/hda_codec.h>
+#include "hda_component.h"
+#include "hda_local.h"
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle, u32 event, void *data)
+{
+ struct hda_component *comp;
+ int i;
+
+ guard(mutex)(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->acpi_notify)
+ comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");
+
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler, void *data)
+{
+ bool support_notifications = false;
+ struct acpi_device *adev;
+ struct hda_component *comp;
+ int ret;
+ int i;
+
+ adev = parent->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ support_notifications = support_notifications ||
+ comp->acpi_notifications_supported;
+ }
+
+ if (support_notifications) {
+ ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+ handler, data);
+ if (ret < 0) {
+ codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
+ return 0;
+ }
+
+ codec_dbg(cdc, "Notify handler installed\n");
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
+
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ adev = parent->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ return;
+
+ ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);
+ if (ret < 0)
+ codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)
+{
+ struct hda_component *comp;
+ int i;
+
+ guard(mutex)(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->pre_playback_hook)
+ comp->pre_playback_hook(comp->dev, action);
+ }
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->playback_hook)
+ comp->playback_hook(comp->dev, action);
+ }
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->post_playback_hook)
+ comp->post_playback_hook(comp->dev, action);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");
+
+struct hda_scodec_match {
+ const char *bus;
+ const char *hid;
+ const char *match_str;
+ int index;
+};
+
+/* match the device name in a slightly relaxed manner */
+static int hda_comp_match_dev_name(struct device *dev, void *data)
+{
+ struct hda_scodec_match *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), p->match_str, p->hid, p->index);
+ return !strcmp(d + n, tmp);
+}
+
+int hda_component_manager_bind(struct hda_codec *cdc,
+ struct hda_component_parent *parent)
+{
+ /* Init shared and component specific data */
+ memset(parent->comps, 0, sizeof(parent->comps));
+
+ guard(mutex)(&parent->mutex);
+ return component_bind_all(hda_codec_dev(cdc), parent);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");
+
+int hda_component_manager_init(struct hda_codec *cdc,
+ struct hda_component_parent *parent, int count,
+ const char *bus, const char *hid,
+ const char *match_str,
+ const struct component_master_ops *ops)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct component_match *match = NULL;
+ struct hda_scodec_match *sm;
+ int ret, i;
+
+ if (parent->codec) {
+ codec_err(cdc, "Component binding already created (SSID: %x)\n",
+ cdc->core.subsystem_id);
+ return -EINVAL;
+ }
+ parent->codec = cdc;
+
+ mutex_init(&parent->mutex);
+
+ for (i = 0; i < count; i++) {
+ sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);
+ if (!sm)
+ return -ENOMEM;
+
+ sm->bus = bus;
+ sm->hid = hid;
+ sm->match_str = match_str;
+ sm->index = i;
+ component_match_add(dev, &match, hda_comp_match_dev_name, sm);
+ }
+
+ ret = component_master_add_with_match(dev, ops, match);
+ if (ret)
+ codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");
+
+void hda_component_manager_free(struct hda_component_parent *parent,
+ const struct component_master_ops *ops)
+{
+ struct device *dev;
+
+ if (!parent->codec)
+ return;
+
+ dev = hda_codec_dev(parent->codec);
+
+ component_master_del(dev, ops);
+
+ parent->codec = NULL;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");
+
+MODULE_DESCRIPTION("HD Audio component binding library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/hda_component.h b/sound/hda/codecs/side-codecs/hda_component.h
new file mode 100644
index 000000000000..075137a73bae
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/hda_component.h
@@ -0,0 +1,102 @@
+/* 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.
+ */
+
+#ifndef __HDA_COMPONENT_H__
+#define __HDA_COMPONENT_H__
+
+#include <linux/acpi.h>
+#include <linux/component.h>
+#include <linux/mutex.h>
+#include <sound/hda_codec.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 acpi_device *adev;
+ bool acpi_notifications_supported;
+ void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev);
+ void (*pre_playback_hook)(struct device *dev, int action);
+ void (*playback_hook)(struct device *dev, int action);
+ void (*post_playback_hook)(struct device *dev, int action);
+};
+
+struct hda_component_parent {
+ struct mutex mutex;
+ struct hda_codec *codec;
+ struct hda_component comps[HDA_MAX_COMPONENTS];
+};
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle, u32 event, void *data);
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler, void *data);
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler);
+#else
+static inline void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle,
+ u32 event,
+ void *data)
+{
+}
+
+static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler,
+ void *data)
+
+{
+ return 0;
+}
+
+static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler)
+{
+}
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action);
+
+int hda_component_manager_init(struct hda_codec *cdc,
+ struct hda_component_parent *parent, int count,
+ const char *bus, const char *hid,
+ const char *match_str,
+ const struct component_master_ops *ops);
+
+void hda_component_manager_free(struct hda_component_parent *parent,
+ const struct component_master_ops *ops);
+
+int hda_component_manager_bind(struct hda_codec *cdc, struct hda_component_parent *parent);
+
+static inline struct hda_component *hda_component_from_index(struct hda_component_parent *parent,
+ int index)
+{
+ if (!parent)
+ return NULL;
+
+ if (index < 0 || index >= ARRAY_SIZE(parent->comps))
+ return NULL;
+
+ return &parent->comps[index];
+}
+
+static inline void hda_component_manager_unbind(struct hda_codec *cdc,
+ struct hda_component_parent *parent)
+{
+ guard(mutex)(&parent->mutex);
+ component_unbind_all(hda_codec_dev(cdc), parent);
+}
+
+#endif /* ifndef __HDA_COMPONENT_H__ */
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c
new file mode 100644
index 000000000000..96e6d82dc69e
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA Shared Lib for I2C&SPI driver
+//
+// Copyright 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/component.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+
+#include "tas2781_hda.h"
+
+#define CALIBRATION_DATA_AREA_NUM 2
+
+const efi_guid_t tasdev_fct_efi_guid[] = {
+ /* DELL */
+ EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74,
+ 0x91, 0xea, 0x9f),
+ /* HP */
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a,
+ 0xa3, 0x5d, 0xb3),
+ /* LENOVO & OTHERS */
+ EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4,
+ 0x31, 0x0a, 0x92),
+};
+EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781");
+
+/*
+ * The order of calibrated-data writing function is a bit different from the
+ * order in UEFI. Here is the conversion to match the order of calibrated-data
+ * writing function.
+ */
+static void cali_cnv(unsigned char *data, unsigned int base, int offset)
+{
+ struct cali_reg reg_data;
+
+ memcpy(&reg_data, &data[base], sizeof(reg_data));
+ /* the data order has to be swapped between r0_low_reg and inv0_reg */
+ swap(reg_data.r0_low_reg, reg_data.invr0_reg);
+
+ cpu_to_be32_array((__force __be32 *)(data + offset + 1),
+ (u32 *)&reg_data, TASDEV_CALIB_N);
+}
+
+static void tas2781_apply_calib(struct tasdevice_priv *p)
+{
+ struct calidata *cali_data = &p->cali_data;
+ struct cali_reg *r = &cali_data->cali_reg_array;
+ unsigned char *data = cali_data->data;
+ unsigned int *tmp_val = (unsigned int *)data;
+ unsigned int cali_reg[TASDEV_CALIB_N] = {
+ TASDEVICE_REG(0, 0x17, 0x74),
+ TASDEVICE_REG(0, 0x18, 0x0c),
+ TASDEVICE_REG(0, 0x18, 0x14),
+ TASDEVICE_REG(0, 0x13, 0x70),
+ TASDEVICE_REG(0, 0x18, 0x7c),
+ };
+ unsigned int crc, oft, node_num;
+ unsigned char *buf;
+ int i, j, k, l;
+
+ if (tmp_val[0] == 2781) {
+ /*
+ * New features were added in calibrated Data V3:
+ * 1. Added calibration registers address define in
+ * a node, marked as Device id == 0x80.
+ * New features were added in calibrated Data V2:
+ * 1. Added some the fields to store the link_id and
+ * uniqie_id for multi-link solutions
+ * 2. Support flexible number of devices instead of
+ * fixed one in V1.
+ * Layout of calibrated data V2 in UEFI(total 256 bytes):
+ * ChipID (2781, 4 bytes)
+ * Data-Group-Sum (4 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * for (i = 0; i < Data-Group-Sum; i++) {
+ * if (Data type != 0x80) (4 bytes)
+ * Calibrated Data of Device #i (20 bytes)
+ * else
+ * Calibration registers address (5*4 = 20 bytes)
+ * # V2: No reg addr in data grp section.
+ * # V3: Normally the last grp is the reg addr.
+ * }
+ * CRC (4 bytes)
+ * Reserved (the rest)
+ */
+ crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
+
+ if (crc != tmp_val[3 + tmp_val[1] * 6]) {
+ cali_data->total_sz = 0;
+ dev_err(p->dev, "%s: CRC error\n", __func__);
+ return;
+ }
+ node_num = tmp_val[1];
+
+ for (j = 0, k = 0; j < node_num; j++) {
+ oft = j * 6 + 3;
+ if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) {
+ for (i = 0; i < TASDEV_CALIB_N; i++) {
+ buf = &data[(oft + i + 1) * 4];
+ cali_reg[i] = TASDEVICE_REG(buf[1],
+ buf[2], buf[3]);
+ }
+ } else {
+ l = j * (cali_data->cali_dat_sz_per_dev + 1);
+ if (k >= p->ndev || l > oft * 4) {
+ dev_err(p->dev, "%s: dev sum error\n",
+ __func__);
+ cali_data->total_sz = 0;
+ return;
+ }
+
+ data[l] = k;
+ oft++;
+ cali_cnv(data, 4 * oft, l);
+ k++;
+ }
+ }
+ } else {
+ /*
+ * Calibration data is in V1 format.
+ * struct cali_data {
+ * char cali_data[20];
+ * }
+ *
+ * struct {
+ * struct cali_data cali_data[4];
+ * int TimeStamp of Calibration (4 bytes)
+ * int CRC (4 bytes)
+ * } ueft;
+ */
+ crc = crc32(~0, data, 84) ^ ~0;
+ if (crc != tmp_val[21]) {
+ cali_data->total_sz = 0;
+ dev_err(p->dev, "%s: V1 CRC error\n", __func__);
+ return;
+ }
+
+ for (j = p->ndev - 1; j >= 0; j--) {
+ l = j * (cali_data->cali_dat_sz_per_dev + 1);
+ cali_cnv(data, cali_data->cali_dat_sz_per_dev * j, l);
+ data[l] = j;
+ }
+ }
+
+ if (p->dspbin_typ == TASDEV_BASIC) {
+ r->r0_reg = cali_reg[0];
+ r->invr0_reg = cali_reg[1];
+ r->r0_low_reg = cali_reg[2];
+ r->pow_reg = cali_reg[3];
+ r->tlimit_reg = cali_reg[4];
+ }
+
+ p->is_user_space_calidata = true;
+ cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
+}
+
+/*
+ * Update the calibration data, including speaker impedance, f0, etc,
+ * into algo. Calibrate data is done by manufacturer in the factory.
+ * The data is used by Algo for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * Calibration data format in EFI is V2, since 2024.
+ */
+int tas2781_save_calibration(struct tas2781_hda *hda)
+{
+ /*
+ * GUID was used for data access in BIOS, it was provided by board
+ * manufactory.
+ */
+ efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
+ /*
+ * Some devices save the calibrated data into L"CALI_DATA",
+ * and others into L"SmartAmpCalibrationData".
+ */
+ static efi_char16_t *efi_name[CALIBRATION_DATA_AREA_NUM] = {
+ L"CALI_DATA",
+ L"SmartAmpCalibrationData",
+ };
+ struct tasdevice_priv *p = hda->priv;
+ struct calidata *cali_data = &p->cali_data;
+ unsigned long total_sz = 0;
+ unsigned int attr, size;
+ unsigned char *data;
+ efi_status_t status;
+ int i;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
+ dev_err(p->dev, "%s: NO EFI FOUND!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (hda->catlog_id < LENOVO)
+ efi_guid = tasdev_fct_efi_guid[hda->catlog_id];
+
+ cali_data->cali_dat_sz_per_dev = 20;
+ size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
+ for (i = 0; i < CALIBRATION_DATA_AREA_NUM; i++) {
+ /* Get real size of UEFI variable */
+ status = efi.get_variable(efi_name[i], &efi_guid, &attr,
+ &total_sz, NULL);
+ cali_data->total_sz = total_sz > size ? total_sz : size;
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ /* Allocate data buffer of data_size bytes */
+ data = cali_data->data = devm_kzalloc(p->dev,
+ cali_data->total_sz, GFP_KERNEL);
+ if (!data) {
+ status = -ENOMEM;
+ continue;
+ }
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name[i], &efi_guid,
+ &attr, &cali_data->total_sz, data);
+ }
+ /* Check whether get the calibrated data */
+ if (status == EFI_SUCCESS)
+ break;
+ }
+
+ if (status != EFI_SUCCESS) {
+ cali_data->total_sz = 0;
+ return status;
+ }
+
+ tas2781_apply_calib(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781");
+
+void tas2781_hda_remove(struct device *dev,
+ const struct component_ops *ops)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ component_del(tas_hda->dev, ops);
+
+ pm_runtime_get_sync(tas_hda->dev);
+ pm_runtime_disable(tas_hda->dev);
+
+ pm_runtime_put_noidle(tas_hda->dev);
+
+ tasdevice_remove(tas_hda->priv);
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_hda_remove, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_profile, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_programs, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_fw->nr_configurations - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_config, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->rcabin.profile_cfg_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_get_profile_id, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int profile_id = ucontrol->value.integer.value[0];
+ int max = tas_priv->rcabin.ncfgs - 1;
+ int val, ret = 0;
+
+ val = clamp(profile_id, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->rcabin.profile_cfg_id, val);
+
+ if (tas_priv->rcabin.profile_cfg_id != val) {
+ tas_priv->rcabin.profile_cfg_id = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_set_profile_id, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_prog);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_program_get, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_program = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_programs - 1;
+ int val, ret = 0;
+
+ val = clamp(nr_program, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_prog, val);
+
+ if (tas_priv->cur_prog != val) {
+ tas_priv->cur_prog = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_program_put, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_conf);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_get, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_config = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_configurations - 1;
+ int val, ret = 0;
+
+ val = clamp(nr_config, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_conf, val);
+
+ if (tas_priv->cur_conf != val) {
+ tas_priv->cur_conf = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_put, "SND_HDA_SCODEC_TAS2781");
+
+MODULE_DESCRIPTION("TAS2781 HDA Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.h b/sound/hda/codecs/side-codecs/tas2781_hda.h
new file mode 100644
index 000000000000..66188909a0bb
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * HDA audio driver for Texas Instruments TAS2781 smart amp
+ *
+ * Copyright (C) 2025 Texas Instruments, Inc.
+ */
+#ifndef __TAS2781_HDA_H__
+#define __TAS2781_HDA_H__
+
+#include <sound/asound.h>
+
+/* Flag of calibration registers address. */
+#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
+
+#define TASDEV_CALIB_N 5
+
+/*
+ * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+ xhandler_get, xhandler_put, tlv_array) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) { \
+ .reg = xreg, .rreg = xreg, \
+ .shift = xshift, .rshift = xshift,\
+ .min = xmin, .max = xmax, .invert = xinvert, \
+ } \
+}
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
+ .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xdata, \
+}
+
+enum device_catlog_id {
+ DELL = 0,
+ HP,
+ LENOVO,
+ OTHERS
+};
+
+struct tas2781_hda {
+ struct device *dev;
+ struct tasdevice_priv *priv;
+ struct snd_kcontrol *dsp_prog_ctl;
+ struct snd_kcontrol *dsp_conf_ctl;
+ struct snd_kcontrol *prof_ctl;
+ enum device_catlog_id catlog_id;
+ void *hda_priv;
+};
+
+extern const efi_guid_t tasdev_fct_efi_guid[];
+
+int tas2781_save_calibration(struct tas2781_hda *p);
+void tas2781_hda_remove(struct device *dev,
+ const struct component_ops *ops);
+int tasdevice_info_profile(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_info_programs(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_info_config(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_set_profile_id(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_get_profile_id(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_program_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_program_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_config_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_config_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+
+#endif
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
new file mode 100644
index 000000000000..a126f04c3ed7
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -0,0 +1,827 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA I2C driver
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Current maintainer: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pci_ids.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+#include <sound/tlv.h>
+#include <sound/tas2770-tlv.h>
+#include <sound/tas2781-tlv.h>
+#include <sound/tas5825-tlv.h>
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "tas2781_hda.h"
+
+#define TAS2563_CAL_VAR_NAME_MAX 16
+#define TAS2563_CAL_ARRAY_SIZE 80
+#define TAS2563_CAL_DATA_SIZE 4
+#define TAS2563_MAX_CHANNELS 4
+#define TAS2563_CAL_CH_SIZE 20
+
+#define TAS2563_CAL_R0_LOW TASDEVICE_REG(0, 0x0f, 0x48)
+#define TAS2563_CAL_POWER TASDEVICE_REG(0, 0x0d, 0x3c)
+#define TAS2563_CAL_INVR0 TASDEVICE_REG(0, 0x0f, 0x40)
+#define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14)
+#define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34)
+
+enum device_chip_id {
+ HDA_TAS2563,
+ HDA_TAS2770,
+ HDA_TAS2781,
+ HDA_TAS5825,
+ HDA_OTHERS
+};
+
+struct tas2781_hda_i2c_priv {
+ struct snd_kcontrol *snd_ctls[2];
+ int (*save_calibration)(struct tas2781_hda *h);
+
+ int hda_chip_id;
+};
+
+static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
+{
+ struct tasdevice_priv *tas_priv = data;
+ struct acpi_resource_i2c_serialbus *sb;
+
+ if (i2c_acpi_get_i2c_resource(ares, &sb)) {
+ if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS &&
+ sb->slave_address != tas_priv->global_addr) {
+ tas_priv->tasdevice[tas_priv->ndev].dev_addr =
+ (unsigned int)sb->slave_address;
+ tas_priv->ndev++;
+ }
+ }
+ return 1;
+}
+
+static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false };
+
+static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = {
+ { "speakerid-gpios", &speakerid_gpios, 1 },
+ { }
+};
+
+static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
+{
+ struct acpi_device *adev;
+ struct device *physdev;
+ LIST_HEAD(resources);
+ const char *sub;
+ uint32_t subid;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev,
+ "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p);
+ if (ret < 0) {
+ dev_err(p->dev, "Failed to get ACPI resource.\n");
+ goto err;
+ }
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub)) {
+ /* No subsys id in older tas2563 projects. */
+ if (!strncmp(hid, "INT8866", sizeof("INT8866")))
+ goto end_2563;
+ dev_err(p->dev, "Failed to get SUBSYS ID.\n");
+ ret = PTR_ERR(sub);
+ goto err;
+ }
+ /* Speaker id was needed for ASUS projects. */
+ ret = kstrtou32(sub, 16, &subid);
+ if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) {
+ ret = devm_acpi_dev_add_driver_gpios(p->dev,
+ tas2781_speaker_id_gpios);
+ if (ret < 0)
+ dev_err(p->dev, "Failed to add driver gpio %d.\n",
+ ret);
+ p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN);
+ if (IS_ERR(p->speaker_id)) {
+ dev_err(p->dev, "Failed to get Speaker id.\n");
+ ret = PTR_ERR(p->speaker_id);
+ goto err;
+ }
+ } else {
+ p->speaker_id = NULL;
+ }
+
+end_2563:
+ acpi_dev_free_resource_list(&resources);
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return 0;
+
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "%s: action = %d\n", __func__, action);
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ pm_runtime_get_sync(dev);
+ scoped_guard(mutex, &tas_hda->priv->codec_lock) {
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+ tas_hda->priv->playback_started = true;
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ scoped_guard(mutex, &tas_hda->priv->codec_lock) {
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+ tas_hda->priv->playback_started = false;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ret;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %ld\n",
+ __func__, kcontrol->id.name, ucontrol->value.integer.value[0]);
+
+ return ret;
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: -> %ld\n",
+ __func__, kcontrol->id.name, ucontrol->value.integer.value[0]);
+
+ /* The check of the given value is in tasdevice_amp_putvol. */
+ return tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n",
+ __func__, kcontrol->id.name, tas_priv->force_fwload_status);
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n",
+ __func__, kcontrol->id.name,
+ tas_priv->force_fwload_status, val);
+
+ if (tas_priv->force_fwload_status == val)
+ change = false;
+ else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2770_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2770_AMP_LEVEL,
+ 0, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2770_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2770_DVC_LEVEL,
+ 0, 0, 200, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2770_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2781_amp_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas5825_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS5825_AMP_LEVEL,
+ 0, 0, 31, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS5825_DVC_LEVEL,
+ 0, 0, 254, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tasdevice_prof_ctrl = {
+ .name = "Speaker Profile Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+};
+
+static const struct snd_kcontrol_new tasdevice_dsp_prog_ctrl = {
+ .name = "Speaker Program Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+};
+
+static const struct snd_kcontrol_new tasdevice_dsp_conf_ctrl = {
+ .name = "Speaker Config Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+};
+
+static int tas2563_save_calibration(struct tas2781_hda *h)
+{
+ efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
+ char *vars[TASDEV_CALIB_N] = {
+ "R0_%d", "R0_Low_%d", "InvR0_%d", "Power_%d", "TLim_%d"
+ };
+ efi_char16_t efi_name[TAS2563_CAL_VAR_NAME_MAX];
+ unsigned long max_size = TAS2563_CAL_DATA_SIZE;
+ unsigned char var8[TAS2563_CAL_VAR_NAME_MAX];
+ struct tasdevice_priv *p = h->priv;
+ struct calidata *cd = &p->cali_data;
+ struct cali_reg *r = &cd->cali_reg_array;
+ unsigned int offset = 0;
+ unsigned char *data;
+ __be32 bedata;
+ efi_status_t status;
+ unsigned int attr;
+ int ret, i, j, k;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
+ dev_err(p->dev, "%s: NO EFI FOUND!\n", __func__);
+ return -EINVAL;
+ }
+
+ cd->cali_dat_sz_per_dev = TAS2563_CAL_DATA_SIZE * TASDEV_CALIB_N;
+
+ /* extra byte for each device is the device number */
+ cd->total_sz = (cd->cali_dat_sz_per_dev + 1) * p->ndev;
+ data = cd->data = devm_kzalloc(p->dev, cd->total_sz,
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < p->ndev; ++i) {
+ data[offset] = i;
+ offset++;
+ for (j = 0; j < TASDEV_CALIB_N; ++j) {
+ /* EFI name for calibration started with 1, not 0 */
+ ret = snprintf(var8, sizeof(var8), vars[j], i + 1);
+ if (ret < 0 || ret >= sizeof(var8) - 1) {
+ dev_err(p->dev, "%s: Read %s failed\n",
+ __func__, var8);
+ return -EINVAL;
+ }
+ /*
+ * Our variable names are ASCII by construction, but
+ * EFI names are wide chars. Convert and zero-pad.
+ */
+ memset(efi_name, 0, sizeof(efi_name));
+ for (k = 0; k < sizeof(var8) && var8[k]; k++)
+ efi_name[k] = var8[k];
+ status = efi.get_variable(efi_name,
+ &efi_guid, &attr, &max_size,
+ &data[offset]);
+ if (status != EFI_SUCCESS ||
+ max_size != TAS2563_CAL_DATA_SIZE) {
+ dev_warn(p->dev,
+ "Dev %d: Caldat[%d] read failed %ld\n",
+ i, j, status);
+ return -EINVAL;
+ }
+ bedata = cpu_to_be32(*(uint32_t *)&data[offset]);
+ memcpy(&data[offset], &bedata, sizeof(bedata));
+ offset += TAS2563_CAL_DATA_SIZE;
+ }
+ }
+
+ if (cd->total_sz != offset) {
+ dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) mismatch\n",
+ __func__, cd->total_sz, offset);
+ return -EINVAL;
+ }
+
+ r->r0_reg = TAS2563_CAL_R0;
+ r->invr0_reg = TAS2563_CAL_INVR0;
+ r->r0_low_reg = TAS2563_CAL_R0_LOW;
+ r->pow_reg = TAS2563_CAL_POWER;
+ r->tlimit_reg = TAS2563_CAL_TLIM;
+
+ /*
+ * TAS2781_FMWLIB supports two solutions of calibrated data. One is
+ * from the driver itself: driver reads the calibrated files directly
+ * during probe; The other from user space: during init of audio hal,
+ * the audio hal will pass the calibrated data via kcontrol interface.
+ * Driver will store this data in "struct calidata" for use. For hda
+ * device, calibrated data are usunally saved into UEFI. So Hda side
+ * codec driver use the mixture of these two solutions, driver reads
+ * the data from UEFI, then store this data in "struct calidata" for
+ * use.
+ */
+ p->is_user_space_calidata = true;
+
+ return 0;
+}
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_hda->priv->codec;
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(hda_priv->snd_ctls) - 1; i >= 0; i--)
+ snd_ctl_remove(codec->card, hda_priv->snd_ctls[i]);
+
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static void tasdev_add_kcontrols(struct tasdevice_priv *tas_priv,
+ struct snd_kcontrol **ctls, struct hda_codec *codec,
+ const struct snd_kcontrol_new *tas_snd_ctrls, int num_ctls)
+{
+ int i, ret;
+
+ for (i = 0; i < num_ctls; i++) {
+ ctls[i] = snd_ctl_new1(
+ &tas_snd_ctrls[i], tas_priv);
+ ret = snd_ctl_add(codec->card, ctls[i]);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas_snd_ctrls[i].name, ret);
+ break;
+ }
+ }
+}
+
+static void tasdevice_dspfw_init(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_priv->codec;
+ int ret, spk_id;
+
+ tasdevice_dsp_remove(tas_priv);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ if (tas_priv->speaker_id != NULL) {
+ // Speaker id need to be checked for ASUS only.
+ spk_id = gpiod_get_value(tas_priv->speaker_id);
+ if (spk_id < 0) {
+ // Speaker id is not valid, use default.
+ dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id);
+ spk_id = 0;
+ }
+ snprintf(tas_priv->coef_binaryname,
+ sizeof(tas_priv->coef_binaryname),
+ "TAS2XXX%04X%d.bin",
+ lower_16_bits(codec->core.subsystem_id),
+ spk_id);
+ } else {
+ snprintf(tas_priv->coef_binaryname,
+ sizeof(tas_priv->coef_binaryname),
+ "TAS2XXX%04X.bin",
+ lower_16_bits(codec->core.subsystem_id));
+ }
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return;
+ }
+ tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_prog_ctl, codec,
+ &tasdevice_dsp_prog_ctrl, 1);
+ tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_conf_ctl, codec,
+ &tasdevice_dsp_conf_ctrl, 1);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tasdevice_prmg_load(tas_priv, 0);
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->cur_conf = 0;
+
+ /* Init common setting for different audio profiles */
+ if (tas_priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv,
+ tas_priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ /* If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ hda_priv->save_calibration(tas_hda);
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_priv->codec;
+ int ret;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ tas_priv->fw_state = TASDEVICE_RCA_FW_OK;
+ tasdev_add_kcontrols(tas_priv, &tas_hda->prof_ctl, codec,
+ &tasdevice_prof_ctrl, 1);
+
+ switch (hda_priv->hda_chip_id) {
+ case HDA_TAS2770:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas2770_snd_controls[0],
+ ARRAY_SIZE(tas2770_snd_controls));
+ break;
+ case HDA_TAS2781:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas2781_snd_controls[0],
+ ARRAY_SIZE(tas2781_snd_controls));
+ tasdevice_dspfw_init(context);
+ break;
+ case HDA_TAS5825:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas5825_snd_controls[0],
+ ARRAY_SIZE(tas5825_snd_controls));
+ tasdevice_dspfw_init(context);
+ break;
+ case HDA_TAS2563:
+ tasdevice_dspfw_init(context);
+ break;
+ default:
+ break;
+ }
+
+out:
+ mutex_unlock(&tas_hda->priv->codec_lock);
+ release_firmware(fmw);
+ pm_runtime_put_autosuspend(tas_hda->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ struct hda_codec *codec;
+ unsigned int subid;
+ int ret;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ codec = parent->codec;
+ subid = codec->core.subsystem_id >> 16;
+
+ switch (subid) {
+ case 0x1028:
+ tas_hda->catlog_id = DELL;
+ break;
+ default:
+ tas_hda->catlog_id = LENOVO;
+ break;
+ }
+
+ pm_runtime_get_sync(dev);
+
+ comp->dev = dev;
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready);
+ if (!ret)
+ comp->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev,
+ struct device *master, void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_config_info_remove(tas_hda->priv);
+ tasdevice_dsp_remove(tas_hda->priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static int tas2781_hda_i2c_probe(struct i2c_client *clt)
+{
+ struct tas2781_hda_i2c_priv *hda_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret;
+
+ tas_hda = devm_kzalloc(&clt->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ hda_priv = devm_kzalloc(&clt->dev, sizeof(*hda_priv), GFP_KERNEL);
+ if (!hda_priv)
+ return -ENOMEM;
+
+ tas_hda->hda_priv = hda_priv;
+
+ dev_set_drvdata(&clt->dev, tas_hda);
+ tas_hda->dev = &clt->dev;
+
+ tas_hda->priv = tasdevice_kzalloc(clt);
+ if (!tas_hda->priv)
+ return -ENOMEM;
+
+ if (strstr(dev_name(&clt->dev), "TIAS2781")) {
+ /*
+ * TAS2781, integrated on-chip DSP with
+ * global I2C address supported.
+ */
+ device_name = "TIAS2781";
+ hda_priv->hda_chip_id = HDA_TAS2781;
+ hda_priv->save_calibration = tas2781_save_calibration;
+ tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
+ } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) {
+ /*
+ * TAS2770, has no on-chip DSP, so no calibration data
+ * required; has no global I2C address supported.
+ */
+ device_name = "TXNW2770";
+ hda_priv->hda_chip_id = HDA_TAS2770;
+ } else if (strstarts(dev_name(&clt->dev),
+ "i2c-TXNW2781:00-tas2781-hda.0")) {
+ device_name = "TXNW2781";
+ hda_priv->hda_chip_id = HDA_TAS2781;
+ hda_priv->save_calibration = tas2781_save_calibration;
+ tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
+ } else if (strstr(dev_name(&clt->dev), "INT8866")) {
+ /*
+ * TAS2563, integrated on-chip DSP with
+ * global I2C address supported.
+ */
+ device_name = "INT8866";
+ hda_priv->hda_chip_id = HDA_TAS2563;
+ hda_priv->save_calibration = tas2563_save_calibration;
+ tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
+ } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW5825")) {
+ /*
+ * TAS5825, integrated on-chip DSP without
+ * global I2C address and calibration supported.
+ */
+ device_name = "TXNW5825";
+ hda_priv->hda_chip_id = HDA_TAS5825;
+ } else {
+ return -ENODEV;
+ }
+
+ tas_hda->priv->irq = clt->irq;
+ ret = tas2781_read_acpi(tas_hda->priv, device_name);
+ if (ret)
+ return dev_err_probe(tas_hda->dev, ret,
+ "Platform not supported\n");
+
+ ret = tasdevice_init(tas_hda->priv);
+ if (ret)
+ goto err;
+
+ pm_runtime_set_autosuspend_delay(tas_hda->dev, 3000);
+ pm_runtime_use_autosuspend(tas_hda->dev);
+ pm_runtime_mark_last_busy(tas_hda->dev);
+ pm_runtime_set_active(tas_hda->dev);
+ pm_runtime_enable(tas_hda->dev);
+
+ tasdevice_reset(tas_hda->priv);
+
+ ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_hda->dev, "Register component failed: %d\n", ret);
+ pm_runtime_disable(tas_hda->dev);
+ }
+
+err:
+ if (ret)
+ tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops);
+ return ret;
+}
+
+static void tas2781_hda_i2c_remove(struct i2c_client *clt)
+{
+ tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "Runtime Suspend\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ /* The driver powers up the amplifiers at module load time.
+ * Stop the playback if it's unused.
+ */
+ if (tas_hda->priv->playback_started) {
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+ tas_hda->priv->playback_started = false;
+ }
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "Runtime Resume\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->priv->dev, "System Suspend\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ /* Shutdown chip before system suspend */
+ if (tas_hda->priv->playback_started)
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(tas_hda->priv->dev, "System Resume\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ for (i = 0; i < tas_hda->priv->ndev; i++) {
+ tas_hda->priv->tasdevice[i].cur_book = -1;
+ tas_hda->priv->tasdevice[i].cur_prog = -1;
+ tas_hda->priv->tasdevice[i].cur_conf = -1;
+ }
+ tasdevice_reset(tas_hda->priv);
+ tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ /* Init common setting for different audio profiles */
+ if (tas_hda->priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_hda->priv,
+ tas_hda->priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ if (tas_hda->priv->playback_started)
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct i2c_device_id tas2781_hda_i2c_id[] = {
+ { "tas2781-hda" },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"INT8866", 0 },
+ {"TIAS2781", 0 },
+ {"TXNW2770", 0 },
+ {"TXNW2781", 0 },
+ {"TXNW5825", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct i2c_driver tas2781_hda_i2c_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_i2c_id,
+ .probe = tas2781_hda_i2c_probe,
+ .remove = tas2781_hda_i2c_remove,
+};
+module_i2c_driver(tas2781_hda_i2c_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA Driver");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781");
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
new file mode 100644
index 000000000000..b9a55672bf15
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -0,0 +1,956 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 - 2025 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "tas2781_hda.h"
+
+#define TASDEVICE_RANGE_MAX_SIZE (256 * 128)
+#define TASDEVICE_WIN_LEN 128
+#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
+/* Flag of calibration registers address. */
+#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
+#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
+
+/* System Reset Check Register */
+#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
+#define TAS2781_REG_CLK_CONFIG_RESET 0x19
+
+struct tas2781_hda_spi_priv {
+ struct snd_kcontrol *snd_ctls[3];
+};
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = TASDEVICE_RANGE_MAX_SIZE,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TASDEVICE_WIN_LEN,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .zero_flag_mask = true,
+ .read_flag_mask = 0x01,
+ .reg_shift = -1,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = TASDEVICE_RANGE_MAX_SIZE,
+};
+
+static int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char data[2];
+
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
+ data, sizeof(data));
+ *val = data[1];
+ } else {
+ ret = tasdevice_dev_read(tas_priv, chn, reg, val);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char buf[TASDEVICE_WIN_LEN + 1];
+
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
+ buf, len + 1);
+ memcpy(data, buf + 1, len);
+ } else {
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, data, len);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int mask,
+ unsigned int value)
+{
+ int ret, val;
+
+ /*
+ * In our TAS2781 SPI mode, read/write was masked in last bit of
+ * address, it cause regmap_update_bits() not work as expected.
+ */
+ ret = tasdevice_dev_read(tas_priv, chn, reg, &val);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = tasdevice_dev_write(tas_priv, chn, TASDEVICE_PAGE_REG(reg),
+ (val & ~mask) | (mask & value));
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_change_chn_book(struct tasdevice_priv *p,
+ unsigned short chn, int book)
+{
+ int ret = 0;
+
+ if (chn == p->index) {
+ struct tasdevice *tasdev = &p->tasdevice[chn];
+ struct regmap *map = p->regmap;
+
+ if (tasdev->cur_book != book) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0)
+ dev_err(p->dev, "%s, E=%d\n", __func__, ret);
+ else
+ tasdev->cur_book = book;
+ }
+ } else {
+ ret = -EXDEV;
+ dev_dbg(p->dev, "Not error, %s ignore channel(%d)\n",
+ __func__, chn);
+ }
+
+ return ret;
+}
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ fsleep(800);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->index,
+ TASDEVICE_REG_SWRESET, TASDEVICE_REG_SWRESET_RESET);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
+ return;
+ }
+ fsleep(1000);
+ }
+}
+
+static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
+ void *codec, struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret;
+
+ /*
+ * Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ guard(mutex)(&tas_priv->codec_lock);
+
+ scnprintf(tas_priv->rca_binaryname,
+ sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->ndev);
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static void tasdevice_spi_init(struct tasdevice_priv *tas_priv)
+{
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
+
+ tas_priv->isspi = true;
+
+ tas_priv->update_bits = tasdevice_spi_dev_update_bits;
+ tas_priv->change_chn_book = tasdevice_spi_change_chn_book;
+ tas_priv->dev_read = tasdevice_spi_dev_read;
+ tas_priv->dev_bulk_read = tasdevice_spi_dev_bulk_read;
+
+ mutex_init(&tas_priv->codec_lock);
+}
+
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int val, ret;
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+
+ ret = tasdevice_spi_dev_update_bits(tas_priv, tas_priv->index,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret)
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret, val;
+
+ ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ return ret;
+ }
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *p,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int val, ret;
+
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_dev_write(p, p->index, mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(p->dev, "set digital vol err in dev %d\n", p->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *p,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ ret = tasdevice_spi_dev_read(p, p->index, mc->reg, &val);
+ if (ret) {
+ dev_err(p->dev, "%s, get digital vol err\n", __func__);
+ return ret;
+ }
+
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
+ const char *hid, int id)
+{
+ struct tasdevice_priv *p = tas_hda->priv;
+ struct acpi_device *adev;
+ struct device *physdev;
+ u32 values[HDA_MAX_COMPONENTS];
+ const char *property;
+ size_t nval;
+ int ret, i;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev, "Failed to find ACPI device: %s\n", hid);
+ return -ENODEV;
+ }
+
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ property = "ti,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0 || ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ p->ndev = nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ p->index = U8_MAX;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ p->index = i;
+ break;
+ }
+ }
+ if (p->index == U8_MAX) {
+ dev_dbg(p->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (p->index == 0) {
+ /* All of amps share same RESET pin. */
+ p->reset = devm_gpiod_get_index_optional(physdev, "reset",
+ p->index, GPIOD_OUT_LOW);
+ if (IS_ERR(p->reset)) {
+ ret = PTR_ERR(p->reset);
+ dev_err_probe(p->dev, ret, "Failed on reset GPIO\n");
+ goto err;
+ }
+ }
+ put_device(physdev);
+
+ return 0;
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ if (action == HDA_GEN_PCM_ACT_OPEN) {
+ pm_runtime_get_sync(dev);
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+ } else if (action == HDA_GEN_PCM_ACT_CLOSE) {
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
+ tasdevice_tuning_switch(tas_priv, 1);
+ pm_runtime_put_autosuspend(dev);
+ }
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ *
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ *
+ * Return 0 if succeeded.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val) {
+ change = false;
+ } else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return change;
+}
+
+static struct snd_kcontrol_new tas2781_snd_ctls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_AMP_LEVEL, 1, 0, 20, 0,
+ tas2781_amp_getvol, tas2781_amp_putvol,
+ tas2781_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_DVC_LVL, 0, 0, 200, 1,
+ tas2781_digital_getvol, tas2781_digital_putvol,
+ tas2781_dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT(NULL, 0, tas2781_force_fwload_get,
+ tas2781_force_fwload_put),
+};
+
+static struct snd_kcontrol_new tas2781_prof_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+};
+
+static struct snd_kcontrol_new tas2781_dsp_ctls[] = {
+ /* Speaker Program */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+ },
+ /* Speaker Config */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+ },
+};
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct hda_codec *codec = tas_hda->priv->codec;
+ struct tas2781_hda_spi_priv *h_priv = tas_hda->hda_priv;
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(h_priv->snd_ctls) - 1; i >= 0; i--)
+ snd_ctl_remove(codec->card, h_priv->snd_ctls[i]);
+
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static int tas2781_hda_spi_prf_ctl(struct tas2781_hda *h)
+{
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Profile Id", p->index);
+ tas2781_prof_ctl.name = name;
+ h->prof_ctl = snd_ctl_new1(&tas2781_prof_ctl, p);
+ rc = snd_ctl_add(c->card, h->prof_ctl);
+ if (rc)
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_prof_ctl.name, rc);
+ return rc;
+}
+
+static int tas2781_hda_spi_snd_ctls(struct tas2781_hda *h)
+{
+ struct tas2781_hda_spi_priv *h_priv = h->hda_priv;
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int i = 0;
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Analog Volume", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Speaker-%d Digital Volume", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Froce Speaker-%d FW Load", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ }
+ return rc;
+}
+
+static int tas2781_hda_spi_dsp_ctls(struct tas2781_hda *h)
+{
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int i = 0;
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Program Id", p->index);
+ tas2781_dsp_ctls[i].name = name;
+ h->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p);
+ rc = snd_ctl_add(c->card, h->dsp_prog_ctl);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_dsp_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Speaker-%d Config Id", p->index);
+ tas2781_dsp_ctls[i].name = name;
+ h->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p);
+ rc = snd_ctl_add(c->card, h->dsp_conf_ctl);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_dsp_ctls[i].name, rc);
+ }
+
+ return rc;
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct hda_codec *codec = tas_priv->codec;
+ int ret, val;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ /* Add control one time only. */
+ ret = tas2781_hda_spi_prf_ctl(tas_hda);
+ if (ret)
+ goto out;
+
+ ret = tas2781_hda_spi_snd_ctls(tas_hda);
+ if (ret)
+ goto out;
+
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X-%01d.bin",
+ lower_16_bits(codec->core.subsystem_id), tas_priv->index);
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ ret = tas2781_hda_spi_dsp_ctls(tas_hda);
+ if (ret)
+ goto out;
+ /* Perform AMP reset before firmware download. */
+ tas2781_spi_reset(tas_priv);
+ tas_priv->rcabin.profile_cfg_id = 0;
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index,
+ TAS2781_REG_CLK_CONFIG, &val);
+ if (ret < 0)
+ goto out;
+
+ if (val == TAS2781_REG_CLK_CONFIG_RESET) {
+ ret = tasdevice_prmg_load(tas_priv, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "FW download failed = %d\n",
+ ret);
+ goto out;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ }
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->tasdevice[tas_priv->index].cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->tasdevice[tas_priv->index].cur_conf = 0;
+
+ /*
+ * If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ tas2781_save_calibration(tas_hda);
+out:
+ release_firmware(fmw);
+ pm_runtime_put_autosuspend(tas_hda->priv->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ struct hda_codec *codec;
+ int ret;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ codec = parent->codec;
+
+ pm_runtime_get_sync(dev);
+
+ comp->dev = dev;
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE,
+ tasdev_fw_ready);
+ if (!ret)
+ comp->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static int tas2781_hda_spi_probe(struct spi_device *spi)
+{
+ struct tas2781_hda_spi_priv *hda_priv;
+ struct tasdevice_priv *tas_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret = 0;
+
+ tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ hda_priv = devm_kzalloc(&spi->dev, sizeof(*hda_priv), GFP_KERNEL);
+ if (!hda_priv)
+ return -ENOMEM;
+
+ tas_hda->hda_priv = hda_priv;
+ spi->max_speed_hz = TAS2781_SPI_MAX_FREQ;
+
+ tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ return -ENOMEM;
+ tas_priv->dev = &spi->dev;
+ tas_hda->priv = tas_priv;
+ tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
+ ret);
+ return ret;
+ }
+ if (strstr(dev_name(&spi->dev), "TXNW2781")) {
+ device_name = "TXNW2781";
+ } else {
+ dev_err(tas_priv->dev, "Unmatched spi dev %s\n",
+ dev_name(&spi->dev));
+ return -ENODEV;
+ }
+
+ tas_priv->irq = spi->irq;
+ dev_set_drvdata(&spi->dev, tas_hda);
+ ret = tas2781_read_acpi(tas_hda, device_name,
+ spi_get_chipselect(spi, 0));
+ if (ret)
+ return dev_err_probe(tas_priv->dev, ret,
+ "Platform not supported\n");
+
+ tasdevice_spi_init(tas_priv);
+
+ pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000);
+ pm_runtime_use_autosuspend(tas_priv->dev);
+ pm_runtime_set_active(tas_priv->dev);
+ pm_runtime_get_noresume(tas_priv->dev);
+ pm_runtime_enable(tas_priv->dev);
+
+ pm_runtime_put_autosuspend(tas_priv->dev);
+
+ ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_priv->dev, "Register component fail: %d\n", ret);
+ pm_runtime_disable(tas_priv->dev);
+ tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops);
+ }
+
+ return ret;
+}
+
+static void tas2781_hda_spi_remove(struct spi_device *spi)
+{
+ tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 1);
+
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 0);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown chip before system suspend */
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 1);
+
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ int ret, val;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ ret = tas_priv->dev_read(tas_priv, tas_priv->index,
+ TAS2781_REG_CLK_CONFIG, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val == TAS2781_REG_CLK_CONFIG_RESET) {
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
+
+ ret = tasdevice_prmg_load(tas_priv, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev,
+ "FW download failed = %d\n", ret);
+ return ret;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+
+ if (tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 0);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct spi_device_id tas2781_hda_spi_id[] = {
+ { "tas2781-hda", },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"TXNW2781", },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct spi_driver tas2781_hda_spi_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_spi_id,
+ .probe = tas2781_hda_spi_probe,
+ .remove = tas2781_hda_spi_remove,
+};
+module_spi_driver(tas2781_hda_spi_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
+MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781");
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/hda/codecs/sigmatel.c
index 63d15b545b33..ecbee408d771 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/hda/codecs/sigmatel.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for SigmaTel STAC92xx
+ * HD audio codec driver for SigmaTel STAC92xx
*
* Copyright (c) 2005 Embedded Alley Solutions, Inc.
* Matt Porter <mporter@embeddedalley.com>
*
- * Based on patch_cmedia.c and patch_realtek.c
+ * Based on cmedia.c and 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>
@@ -32,12 +19,12 @@
#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"
#include "hda_jack.h"
-#include "hda_generic.h"
+#include "generic.h"
enum {
STAC_REF,
@@ -77,6 +64,7 @@ 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,
@@ -221,6 +209,7 @@ struct sigmatel_spec {
/* beep widgets */
hda_nid_t anabeep_nid;
+ bool beep_power_on;
/* SPDIF-out mux */
const char * const *spdif_labels;
@@ -332,33 +321,18 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
}
/* hook for controlling mic-mute LED GPIO */
-static void stac_capture_led_hook(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+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;
- unsigned int mask;
- bool cur_mute, prev_mute;
- if (!kcontrol || !ucontrol)
- return;
-
- mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- prev_mute = !spec->mic_enabled;
- if (ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1])
- spec->mic_enabled |= mask;
+ if (brightness)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
else
- spec->mic_enabled &= ~mask;
- cur_mute = !spec->mic_enabled;
- if (cur_mute != prev_mute) {
- if (cur_mute)
- 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);
- }
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 0;
}
static int stac_vrefout_set(struct hda_codec *codec,
@@ -396,10 +370,9 @@ static unsigned int stac_vref_led_power_filter(struct hda_codec *codec,
}
/* update mute-LED accoring to the master switch */
-static void stac_update_led_status(struct hda_codec *codec, int enabled)
+static void stac_update_led_status(struct hda_codec *codec, bool muted)
{
struct sigmatel_spec *spec = codec->spec;
- int muted = !enabled;
if (!spec->gpio_led)
return;
@@ -423,9 +396,13 @@ static void stac_update_led_status(struct hda_codec *codec, int enabled)
}
/* vmaster hook to update mute LED */
-static void stac_vmaster_hook(void *private_data, int val)
+static int stac_vmaster_hook(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- stac_update_led_status(private_data, val);
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+
+ stac_update_led_status(codec, brightness);
+ return 0;
}
/* automute hook to handle GPIO mute and EAPD updates */
@@ -825,7 +802,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
static bool has_builtin_speaker(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t *nid_pin;
+ const hda_nid_t *nid_pin;
int nids, i;
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
@@ -855,14 +832,14 @@ static int stac_auto_create_beep_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
struct snd_kcontrol_new *knew;
- static struct snd_kcontrol_new abeep_mute_ctl =
+ static const struct snd_kcontrol_new abeep_mute_ctl =
HDA_CODEC_MUTE(NULL, 0, 0, 0);
- static struct snd_kcontrol_new dbeep_mute_ctl =
+ static const struct snd_kcontrol_new dbeep_mute_ctl =
HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0);
- static struct snd_kcontrol_new beep_vol_ctl =
+ static const struct snd_kcontrol_new beep_vol_ctl =
HDA_CODEC_VOLUME(NULL, 0, 0, 0);
- /* check for mute support for the the amp */
+ /* 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)
@@ -1005,15 +982,6 @@ static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
return 0;
}
-/*
- */
-
-static const struct hda_verb stac9200_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
static const struct hda_verb stac9200_eapd_init[] = {
/* set dac0mux for dac converter */
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -1494,7 +1462,7 @@ static const struct hda_model_fixup stac9200_models[] = {
{}
};
-static const struct snd_pci_quirk stac9200_fixup_tbl[] = {
+static const struct hda_quirk stac9200_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_REF),
@@ -1715,7 +1683,7 @@ static const struct hda_model_fixup stac925x_models[] = {
{}
};
-static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
+static const struct hda_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),
@@ -1739,6 +1707,7 @@ static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
};
static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
+ // Port A-H
{ 0x0a, 0x02214030 },
{ 0x0b, 0x02a19040 },
{ 0x0c, 0x01a19020 },
@@ -1747,9 +1716,12 @@ static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
{ 0x0f, 0x01014010 },
{ 0x10, 0x01014020 },
{ 0x11, 0x01014030 },
+ // CD in
{ 0x12, 0x02319040 },
+ // Digial Mic ins
{ 0x13, 0x90a000f0 },
{ 0x14, 0x90a000f0 },
+ // Digital outs
{ 0x22, 0x01452050 },
{ 0x23, 0x01452050 },
{}
@@ -1790,6 +1762,7 @@ static const struct hda_pintbl alienware_m17x_pin_configs[] = {
};
static const struct hda_pintbl intel_dg45id_pin_configs[] = {
+ // Analog outputs
{ 0x0a, 0x02214230 },
{ 0x0b, 0x02A19240 },
{ 0x0c, 0x01013214 },
@@ -1797,6 +1770,9 @@ static const struct hda_pintbl intel_dg45id_pin_configs[] = {
{ 0x0e, 0x01A19250 },
{ 0x0f, 0x01011212 },
{ 0x10, 0x01016211 },
+ // Digital output
+ { 0x22, 0x01451380 },
+ { 0x23, 0x40f000f0 },
{}
};
@@ -1897,6 +1873,18 @@ static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
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,
@@ -1922,6 +1910,10 @@ static const struct hda_fixup stac92hd73xx_fixups[] = {
.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,
@@ -1960,16 +1952,19 @@ static const struct hda_model_fixup stac92hd73xx_models[] = {
{ .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 const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
+static const struct hda_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,
@@ -2009,6 +2004,8 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
"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,
@@ -2157,10 +2154,8 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
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
}
}
@@ -2202,7 +2197,7 @@ static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec,
int action)
{
struct sigmatel_spec *spec = codec->spec;
- static hda_nid_t preferred_pairs[] = {
+ static const hda_nid_t preferred_pairs[] = {
0xd, 0x13,
0
};
@@ -2758,7 +2753,7 @@ static const struct hda_model_fixup stac92hd83xxx_models[] = {
{}
};
-static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
+static const struct hda_quirk stac92hd83xxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD83XXX_REF),
@@ -3149,7 +3144,7 @@ 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 slaved HP
+ * Lets turn it back into follower HP
*/
pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) |
(AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT);
@@ -3241,7 +3236,7 @@ static const struct hda_model_fixup stac92hd71bxx_models[] = {
{}
};
-static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = {
+static const struct hda_quirk stac92hd71bxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
@@ -3501,7 +3496,7 @@ static const struct hda_pintbl ecs202_pin_configs[] = {
};
/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */
-static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = {
+static const struct hda_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),
@@ -3645,7 +3640,7 @@ static const struct hda_model_fixup stac922x_models[] = {
{}
};
-static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
+static const struct hda_quirk stac922x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D945_REF),
@@ -3973,7 +3968,7 @@ static const struct hda_model_fixup stac927x_models[] = {
{}
};
-static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
+static const struct hda_quirk stac927x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D965_REF),
@@ -4183,7 +4178,7 @@ static const struct hda_model_fixup stac9205_models[] = {
{}
};
-static const struct snd_pci_quirk stac9205_fixup_tbl[] = {
+static const struct hda_quirk stac9205_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF),
@@ -4260,7 +4255,7 @@ static const struct hda_fixup stac92hd95_fixups[] = {
},
};
-static const struct snd_pci_quirk stac92hd95_fixup_tbl[] = {
+static const struct hda_quirk stac92hd95_fixup_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS),
{} /* terminator */
};
@@ -4291,6 +4286,9 @@ static int stac_parse_auto_config(struct hda_codec *codec)
spec->gen.automute_hook = stac_update_outputs;
+ if (spec->gpio_led)
+ snd_hda_gen_add_mute_led_cdev(codec, stac_vmaster_hook);
+
err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
@@ -4321,6 +4319,8 @@ static int stac_parse_auto_config(struct hda_codec *codec)
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)) {
@@ -4332,9 +4332,6 @@ static int stac_parse_auto_config(struct hda_codec *codec)
}
#endif
- if (spec->gpio_led)
- spec->gen.vmaster_mute.hook = stac_vmaster_hook;
-
if (spec->aloopback_ctl &&
snd_hda_get_bool_hint(codec, "loopback") == 1) {
unsigned int wr_verb =
@@ -4394,20 +4391,6 @@ static int stac_init(struct hda_codec *codec)
return 0;
}
-static void stac_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);
-}
-
-#define stac_free snd_hda_gen_free
-
#ifdef CONFIG_SND_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
@@ -4455,27 +4438,19 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#define stac927x_proc_hook NULL
#endif
-#ifdef CONFIG_PM
static int stac_suspend(struct hda_codec *codec)
{
- stac_shutup(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);
+
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
- .reboot_notify = stac_shutup,
-};
static int alloc_stac_spec(struct hda_codec *codec)
{
@@ -4488,19 +4463,14 @@ static int alloc_stac_spec(struct hda_codec *codec)
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;
}
-static int patch_stac9200(struct hda_codec *codec)
+static int probe_stac9200(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -4514,25 +4484,19 @@ static int patch_stac9200(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
-static int patch_stac925x(struct hda_codec *codec)
+static int probe_stac925x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -4544,26 +4508,20 @@ static int patch_stac925x(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
-static int patch_stac92hd73xx(struct hda_codec *codec)
+static int probe_stac92hd73xx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
int num_dacs;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
/* enable power_save_node only for new 92HD89xx chips, as it causes
* click noises on old 92HD73xx chips.
@@ -4618,10 +4576,8 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
snd_hda_add_verbs(codec, stac92hd73xx_core_init);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
/* Don't GPIO-mute speakers if there are no internal speakers, because
* the GPIO might be necessary for Headphone
@@ -4656,20 +4612,15 @@ static void stac_setup_gpio(struct hda_codec *codec)
spec->gpio_dir |= spec->mic_mute_led_gpio;
spec->mic_enabled = 0;
spec->gpio_data |= spec->mic_mute_led_gpio;
-
- spec->gen.cap_sync_hook = stac_capture_led_hook;
+ snd_hda_gen_add_micmute_led_cdev(codec, stac_capture_led_update);
}
}
-static int patch_stac92hd83xxx(struct hda_codec *codec)
+static int probe_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
/* longer delay needed for D3 */
codec->core.power_caps &= ~AC_PWRST_EPSS;
@@ -4694,10 +4645,8 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
stac_setup_gpio(codec);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
codec->proc_widget_hook = stac92hd_proc_hook;
@@ -4710,15 +4659,11 @@ static const hda_nid_t stac92hd95_pwr_nids[] = {
0x0a, 0x0b, 0x0c, 0x0d
};
-static int patch_stac92hd95(struct hda_codec *codec)
+static int probe_stac92hd95(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
/* longer delay needed for D3 */
codec->core.power_caps &= ~AC_PWRST_EPSS;
@@ -4740,10 +4685,8 @@ static int patch_stac92hd95(struct hda_codec *codec)
stac_setup_gpio(codec);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
codec->proc_widget_hook = stac92hd_proc_hook;
@@ -4752,16 +4695,12 @@ static int patch_stac92hd95(struct hda_codec *codec)
return 0;
}
-static int patch_stac92hd71bxx(struct hda_codec *codec)
+static int probe_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
/* disabled power_save_node since it causes noises on a Dell machine */
/* codec->power_save_node = 1; */
@@ -4824,10 +4763,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
stac_setup_gpio(codec);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
codec->proc_widget_hook = stac92hd7x_proc_hook;
@@ -4836,15 +4773,11 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
return 0;
}
-static int patch_stac922x(struct hda_codec *codec)
+static int probe_stac922x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -4863,10 +4796,8 @@ static int patch_stac922x(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
@@ -4878,15 +4809,11 @@ static const char * const stac927x_spdif_labels[] = {
"Analog Mux 2", "Analog Mux 3", NULL
};
-static int patch_stac927x(struct hda_codec *codec)
+static int probe_stac927x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -4912,10 +4839,8 @@ static int patch_stac927x(struct hda_codec *codec)
snd_hda_add_verbs(codec, stac927x_core_init);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
codec->proc_widget_hook = stac927x_proc_hook;
@@ -4929,22 +4854,18 @@ 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;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
-static int patch_stac9205(struct hda_codec *codec)
+static int probe_stac9205(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -4970,10 +4891,8 @@ static int patch_stac9205(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
+ if (err < 0)
return err;
- }
codec->proc_widget_hook = stac9205_proc_hook;
@@ -5017,21 +4936,17 @@ static const struct hda_fixup stac9872_fixups[] = {
},
};
-static const struct snd_pci_quirk stac9872_fixup_tbl[] = {
+static const struct hda_quirk stac9872_fixup_tbl[] = {
SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
"Sony VAIO F/S", STAC_9872_VAIO),
{} /* terminator */
};
-static int patch_stac9872(struct hda_codec *codec)
+static int probe_stac9872(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
spec = codec->spec;
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
@@ -5043,125 +4958,202 @@ static int patch_stac9872(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
- return -EINVAL;
- }
+ if (err < 0)
+ return err;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
+/*
+ * common driver probe
+ */
+
+enum {
+ MODEL_STAC9200,
+ MODEL_STAC9205,
+ MODEL_STAC922X,
+ MODEL_STAC925X,
+ MODEL_STAC927X,
+ MODEL_STAC9872,
+ MODEL_STAC92HD71BXX,
+ MODEL_STAC92HD73XX,
+ MODEL_STAC92HD83XXX,
+ MODEL_STAC92HD95,
+};
+
+static int stac_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
+
+ switch (id->driver_data) {
+ case MODEL_STAC9200:
+ err = probe_stac9200(codec);
+ break;
+ case MODEL_STAC9205:
+ err = probe_stac9205(codec);
+ break;
+ case MODEL_STAC922X:
+ err = probe_stac922x(codec);
+ break;
+ case MODEL_STAC925X:
+ err = probe_stac925x(codec);
+ break;
+ case MODEL_STAC927X:
+ err = probe_stac927x(codec);
+ break;
+ case MODEL_STAC9872:
+ err = probe_stac9872(codec);
+ break;
+ case MODEL_STAC92HD71BXX:
+ err = probe_stac92hd71bxx(codec);
+ break;
+ case MODEL_STAC92HD73XX:
+ err = probe_stac92hd73xx(codec);
+ break;
+ case MODEL_STAC92HD83XXX:
+ err = probe_stac92hd83xxx(codec);
+ break;
+ case MODEL_STAC92HD95:
+ err = probe_stac92hd95(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops stac_codec_ops = {
+ .probe = stac_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = stac_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = stac_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
/*
- * patch entries
+ * driver entries
*/
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),
+ HDA_CODEC_ID_MODEL(0x83847690, "STAC9200", MODEL_STAC9200),
+ HDA_CODEC_ID_MODEL(0x83847882, "STAC9220 A1", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847680, "STAC9221 A1", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847880, "STAC9220 A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847681, "STAC9220D/9223D A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847682, "STAC9221 A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847683, "STAC9221D A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847618, "STAC9227", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847619, "STAC9227", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847638, "STAC92HD700", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847616, "STAC9228", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847617, "STAC9228", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847614, "STAC9229", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847615, "STAC9229", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847620, "STAC9274", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847621, "STAC9274D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847622, "STAC9273X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847623, "STAC9273D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847624, "STAC9272X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847625, "STAC9272D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847626, "STAC9271X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847627, "STAC9271D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847628, "STAC9274X5NH", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847629, "STAC9274D5NH", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847632, "STAC9202", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847633, "STAC9202D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847634, "STAC9250", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847635, "STAC9250D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847636, "STAC9251", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847637, "STAC9250D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847645, "92HD206X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847646, "92HD206D", MODEL_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),
+ HDA_CODEC_ID_MODEL(0x83847661, "CXD9872RD/K", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847662, "STAC9872AK", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847664, "CXD9872AKD", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847698, "STAC9205", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a0, "STAC9205", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a1, "STAC9205D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a2, "STAC9204", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a3, "STAC9204D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a4, "STAC9255", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a5, "STAC9255D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a6, "STAC9254", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a7, "STAC9254D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x111d7603, "92HD75B3X5", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d7604, "92HD83C1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d4, "92HD83C1C5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7605, "92HD81B1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d5, "92HD81B1C5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d1, "92HD87B1/3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d9, "92HD87B2/4", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7666, "92HD88B3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7667, "92HD88B1", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7668, "92HD88B2", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7669, "92HD88B4", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7608, "92HD75B2X5", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d7674, "92HD73D1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7675, "92HD73C1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7676, "92HD73E1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7695, "92HD95", MODEL_STAC92HD95),
+ HDA_CODEC_ID_MODEL(0x111d76b0, "92HD71B8X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b1, "92HD71B8X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b2, "92HD71B7X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b3, "92HD71B7X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b4, "92HD71B6X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b5, "92HD71B6X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b6, "92HD71B5X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b7, "92HD71B5X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76c0, "92HD89C3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c1, "92HD89C2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c2, "92HD89C1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c3, "92HD89B3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c4, "92HD89B2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c5, "92HD89B1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c6, "92HD89E3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c7, "92HD89E2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c8, "92HD89E1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c9, "92HD89D3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76ca, "92HD89D2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cb, "92HD89D1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cc, "92HD89F3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cd, "92HD89F2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76ce, "92HD89F1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76df, "92HD93BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e0, "92HD91BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e3, "92HD98BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e5, "92HD99BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e7, "92HD90BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e8, "92HD66B1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e9, "92HD66B2X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ea, "92HD66B3X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76eb, "92HD66C1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ec, "92HD66C2X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ed, "92HD66C3X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ee, "92HD66B1X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ef, "92HD66B2X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f0, "92HD66B3X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f1, "92HD66C1X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f2, "92HD66C2X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f3, "92HD66C3/65", MODEL_STAC92HD83XXX),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
@@ -5171,6 +5163,7 @@ MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
static struct hda_codec_driver sigmatel_driver = {
.id = snd_hda_id_sigmatel,
+ .ops = &stac_codec_ops,
};
module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/pci/hda/patch_via.c b/sound/hda/codecs/via.c
index fc30d1e8aa76..6becea9bb810 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/hda/codecs/via.c
@@ -1,24 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
+ * HD audio codec driver for VIA VT17xx/VT18xx/VT20xx codec
*
* (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 * * * * * * * * * * * * * * * * */
@@ -52,11 +39,11 @@
#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"
#include "hda_auto_parser.h"
#include "hda_jack.h"
-#include "hda_generic.h"
+#include "generic.h"
/* Pin Widget NID */
#define VT1708_HP_PIN_NID 0x20
@@ -65,8 +52,10 @@
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
+ VT1709,
VT1709_10CH,
VT1709_6CH,
+ VT1708B,
VT1708B_8CH,
VT1708B_4CH,
VT1708S,
@@ -79,6 +68,7 @@ enum VIA_HDA_CODEC {
VT1802,
VT1705CF,
VT1808,
+ VT3476,
CODEC_TYPES,
};
@@ -90,13 +80,6 @@ enum VIA_HDA_CODEC {
struct via_spec {
struct hda_gen_spec gen;
- /* codec parameterization */
- const struct snd_kcontrol_new *mixers[6];
- unsigned int num_mixers;
-
- const struct hda_verb *init_verbs[5];
- unsigned int num_iverbs;
-
/* HP mode source */
unsigned int dmic_enabled;
enum VIA_HDA_CODEC codec_type;
@@ -107,8 +90,6 @@ struct via_spec {
/* work to check hp jack state */
int hp_work_active;
int vt1708_jack_detect;
-
- unsigned int beep_amp;
};
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
@@ -117,8 +98,6 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
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;
@@ -135,11 +114,11 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
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;
}
@@ -262,69 +241,51 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
- {
+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,
- },
- {} /* terminator */
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid,
- int idx, int dir)
-{
- spec->gen.beep_nid = nid;
- spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
-}
-
/* additional beep mixers; the actual parameters are overwritten at build */
-static const struct snd_kcontrol_new cxt_beep_mixer[] = {
+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),
- { } /* end */
};
-/* create beep controls if needed */
-static int add_beep_ctls(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;
- int err;
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
- if (spec->beep_amp) {
- const 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(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;
}
return 0;
}
-static void auto_parse_beep(struct hda_codec *codec)
+static int auto_parse_beep(struct hda_codec *codec)
{
struct via_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) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
- break;
- }
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
}
#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define add_beep_ctls(codec) 0
-#define auto_parse_beep(codec)
+#define auto_parse_beep(codec) 0
#endif
/* check AA path's mute status */
@@ -403,30 +364,6 @@ static void analog_low_current_mode(struct hda_codec *codec)
return __analog_low_current_mode(codec, false);
}
-static int via_build_controls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err, i;
-
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
-
- err = add_beep_ctls(codec);
- if (err < 0)
- return err;
-
- spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream,
@@ -436,13 +373,12 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
vt1708_update_hp_work(codec);
}
-static void via_free(struct hda_codec *codec)
+static void via_remove(struct hda_codec *codec)
{
vt1708_stop_hp_work(codec);
- snd_hda_gen_free(codec);
+ snd_hda_gen_remove(codec);
}
-#ifdef CONFIG_PM
static int via_suspend(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -459,13 +395,11 @@ static int via_resume(struct hda_codec *codec)
{
/* some delay here to make jack detection working (bko#98921) */
msleep(10);
- codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
return 0;
}
-#endif
-#ifdef CONFIG_PM
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
@@ -473,27 +407,10 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
vt1708_update_hp_work(codec);
return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
}
-#endif
/*
*/
-static int via_init(struct hda_codec *codec);
-
-static const struct hda_codec_ops via_patch_ops = {
- .build_controls = via_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = via_init,
- .free = via_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .suspend = via_suspend,
- .resume = via_resume,
- .check_power_status = via_check_power_status,
-#endif
-};
-
-
static const struct hda_verb vt1708_init_verbs[] = {
/* power down jack detect function */
{0x1, 0xf81, 0x1},
@@ -512,8 +429,6 @@ 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_detect_get(struct snd_kcontrol *kcontrol,
@@ -545,16 +460,13 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
- {
+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,
- },
- {} /* terminator */
};
static const struct badness_table via_main_out_badness = {
@@ -586,12 +498,17 @@ static int via_parse_auto_config(struct hda_codec *codec)
if (err < 0)
return err;
- auto_parse_beep(codec);
+ err = auto_parse_beep(codec);
+ if (err < 0)
+ return err;
err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
+ return -ENOMEM;
+
/* disable widget PM at start for compatibility */
codec->power_save_node = 0;
spec->gen.power_down_unused = 0;
@@ -600,12 +517,6 @@ static int via_parse_auto_config(struct hda_codec *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]);
-
/* init power states */
__analog_low_current_mode(codec, true);
@@ -616,19 +527,21 @@ static int via_init(struct hda_codec *codec)
return 0;
}
-static int vt1708_build_controls(struct hda_codec *codec)
+static int via_build_controls(struct hda_codec *codec)
{
/* In order not to create "Phantom Jack" controls,
temporary enable jackpoll */
int err;
int old_interval = codec->jackpoll_interval;
- codec->jackpoll_interval = msecs_to_jiffies(100);
- err = via_build_controls(codec);
- codec->jackpoll_interval = old_interval;
+ if (old_interval)
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ err = snd_hda_gen_build_controls(codec);
+ if (old_interval)
+ codec->jackpoll_interval = old_interval;
return err;
}
-static int vt1708_build_pcms(struct hda_codec *codec)
+static int via_build_pcms(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int i, err;
@@ -655,19 +568,11 @@ static int vt1708_build_pcms(struct hda_codec *codec)
return 0;
}
-static int patch_vt1708(struct hda_codec *codec)
+static int probe_vt1708(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* 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 */
@@ -684,17 +589,18 @@ static int patch_vt1708(struct hda_codec *codec)
vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+ err = snd_hda_add_verbs(codec, vt1708_init_verbs);
+ if (err < 0)
+ return err;
+
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ if (err < 0)
return err;
- }
/* add jack detect on/off control */
- spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
-
- spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl))
+ return -ENOMEM;
/* clear jackpoll_interval again; it's set dynamically */
codec->jackpoll_interval = 0;
@@ -702,54 +608,30 @@ static int patch_vt1708(struct hda_codec *codec)
return 0;
}
-static int patch_vt1709(struct hda_codec *codec)
+static int probe_vt1709(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;
+ struct via_spec *spec = codec->spec;
spec->gen.mixer_nid = 0x18;
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- }
-
- return 0;
+ return via_parse_auto_config(codec);
}
-static int patch_vt1708S(struct hda_codec *codec);
-static int patch_vt1708B(struct hda_codec *codec)
+static int probe_vt1708S(struct hda_codec *codec);
+static int probe_vt1708B(struct hda_codec *codec)
{
- struct via_spec *spec;
- int err;
+ struct via_spec *spec = codec->spec;
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;
+ return probe_vt1708S(codec);
spec->gen.mixer_nid = 0x16;
/* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- }
-
- return 0;
+ return via_parse_auto_config(codec);
}
-/* Patch for VT1708S */
+/* Support for VT1708S */
static const struct hda_verb vt1708S_init_verbs[] = {
/* Enable Mic Boost Volume backdoor */
{0x1, 0xf98, 0x1},
@@ -770,16 +652,11 @@ static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
(0 << AC_AMPCAP_MUTE_SHIFT));
}
-static int patch_vt1708S(struct hda_codec *codec)
+static int probe_vt1708S(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
spec->gen.mixer_nid = 0x16;
override_mic_boost(codec, 0x1a, 0, 3, 40);
override_mic_boost(codec, 0x1e, 0, 3, 40);
@@ -791,19 +668,14 @@ static int patch_vt1708S(struct hda_codec *codec)
if (codec->core.vendor_id == 0x11064397)
snd_hda_codec_set_name(codec, "VT1705");
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
+ if (err < 0)
return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
- return 0;
+ return via_parse_auto_config(codec);
}
-/* Patch for VT1702 */
+/* Support for VT1702 */
static const struct hda_verb vt1702_init_verbs[] = {
/* mixer enable */
@@ -813,16 +685,11 @@ static const struct hda_verb vt1702_init_verbs[] = {
{ }
};
-static int patch_vt1702(struct hda_codec *codec)
+static int probe_vt1702(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
spec->gen.mixer_nid = 0x1a;
/* limit AA path volume to 0 dB */
@@ -832,19 +699,15 @@ static int patch_vt1702(struct hda_codec *codec)
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ err = snd_hda_add_verbs(codec, vt1702_init_verbs);
+ if (err < 0)
return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
- return 0;
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
}
-/* Patch for VT1718S */
+/* Support for VT1718S */
static const struct hda_verb vt1718S_init_verbs[] = {
/* Enable MW0 adjust Gain 5 */
@@ -869,6 +732,9 @@ static int add_secret_dac_path(struct hda_codec *codec)
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;
@@ -889,34 +755,25 @@ static int add_secret_dac_path(struct hda_codec *codec)
}
-static int patch_vt1718S(struct hda_codec *codec)
+static int probe_vt1718S(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* 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);
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
+ if (err < 0)
return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
- return 0;
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
}
-/* Patch for VT1716S */
+/* Support for VT1716S */
static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -955,9 +812,9 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const 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,
@@ -965,16 +822,12 @@ static const 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 const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
- HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
+static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
+ HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
static const struct hda_verb vt1716S_init_verbs[] = {
/* Enable Boost Volume backdoor */
@@ -986,31 +839,28 @@ static const struct hda_verb vt1716S_init_verbs[] = {
{ }
};
-static int patch_vt1716S(struct hda_codec *codec)
+static int probe_vt1716S(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
spec->gen.mixer_nid = 0x16;
override_mic_boost(codec, 0x1a, 0, 3, 40);
override_mic_boost(codec, 0x1e, 0, 3, 40);
+ err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
+ if (err < 0)
+ return err;
+
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ if (err < 0)
return err;
- }
- spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
-
- spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
- spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
+ 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))
+ return -ENOMEM;
return 0;
}
@@ -1043,6 +893,7 @@ static const struct hda_verb vt1802_init_verbs[] = {
enum {
VIA_FIXUP_INTMIC_BOOST,
VIA_FIXUP_ASUS_G75,
+ VIA_FIXUP_POWER_SAVE,
};
static void via_fixup_intmic_boost(struct hda_codec *codec,
@@ -1052,6 +903,13 @@ static void via_fixup_intmic_boost(struct hda_codec *codec,
override_mic_boost(codec, 0x30, 0, 2, 40);
}
+static void via_fixup_power_save(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->power_save_node = 0;
+}
+
static const struct hda_fixup via_fixups[] = {
[VIA_FIXUP_INTMIC_BOOST] = {
.type = HDA_FIXUP_FUNC,
@@ -1066,11 +924,17 @@ static const struct hda_fixup via_fixups[] = {
{ }
}
},
+ [VIA_FIXUP_POWER_SAVE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_power_save,
+ },
};
-static const struct snd_pci_quirk vt2002p_fixups[] = {
+static const struct hda_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),
{}
};
@@ -1079,24 +943,19 @@ static const struct snd_pci_quirk vt2002p_fixups[] = {
*/
static void fix_vt1802_connections(struct hda_codec *codec)
{
- static hda_nid_t conn_24[] = { 0x14, 0x1c };
- static hda_nid_t conn_33[] = { 0x1c };
+ static const hda_nid_t conn_24[] = { 0x14, 0x1c };
+ static const hda_nid_t conn_33[] = { 0x1c };
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);
}
-/* patch for vt2002P */
-static int patch_vt2002P(struct hda_codec *codec)
+/* Support for vt2002P */
+static int probe_vt2002P(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* 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);
@@ -1107,19 +966,15 @@ static int patch_vt2002P(struct hda_codec *codec)
snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- }
-
if (spec->codec_type == VT1802)
- spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
+ err = snd_hda_add_verbs(codec, vt1802_init_verbs);
else
- spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
+ err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
+ if (err < 0)
+ return err;
- return 0;
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
}
/* for vt1812 */
@@ -1132,35 +987,25 @@ static const struct hda_verb vt1812_init_verbs[] = {
{ }
};
-/* patch for vt1812 */
-static int patch_vt1812(struct hda_codec *codec)
+static int probe_vt1812(struct hda_codec *codec)
{
- struct via_spec *spec;
+ struct via_spec *spec = codec->spec;
int err;
- /* 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);
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
+ err = snd_hda_add_verbs(codec, vt1812_init_verbs);
+ if (err < 0)
return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
- return 0;
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
}
-/* patch for vt3476 */
+/* Support for vt3476 */
static const struct hda_verb vt3476_init_verbs[] = {
/* Enable DMic 8/16/32K */
@@ -1172,92 +1017,155 @@ static const struct hda_verb vt3476_init_verbs[] = {
{ }
};
-static int patch_vt3476(struct hda_codec *codec)
+static int probe_vt3476(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x3f;
+ add_secret_dac_path(codec);
+
+ err = snd_hda_add_verbs(codec, vt3476_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+
+}
+
+/*
+ * common driver probe
+ */
+static int via_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
struct via_spec *spec;
int err;
/* create a codec specific record */
spec = via_new_spec(codec);
- if (spec == NULL)
+ if (!spec)
return -ENOMEM;
- spec->gen.mixer_nid = 0x3f;
- add_secret_dac_path(codec);
+ switch (id->driver_data) {
+ case VT1708:
+ err = probe_vt1708(codec);
+ break;
+ case VT1709:
+ err = probe_vt1709(codec);
+ break;
+ case VT1708B:
+ err = probe_vt1708B(codec);
+ break;
+ case VT1708S:
+ err = probe_vt1708S(codec);
+ break;
+ case VT1702:
+ err = probe_vt1702(codec);
+ break;
+ case VT1718S:
+ err = probe_vt1718S(codec);
+ break;
+ case VT1716S:
+ err = probe_vt1716S(codec);
+ break;
+ case VT2002P:
+ err = probe_vt2002P(codec);
+ break;
+ case VT1812:
+ err = probe_vt1812(codec);
+ break;
+ case VT3476:
+ err = probe_vt3476(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
if (err < 0) {
- via_free(codec);
+ via_remove(codec);
return err;
}
- spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
-
return 0;
}
+static const struct hda_codec_ops via_codec_ops = {
+ .probe = via_probe,
+ .remove = via_remove,
+ .build_controls = via_build_controls,
+ .build_pcms = via_build_pcms,
+ .init = via_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = via_suspend,
+ .resume = via_resume,
+ .check_power_status = via_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
/*
- * patch entries
+ * driver entries
*/
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),
+ HDA_CODEC_ID_MODEL(0x11061708, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x11061709, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106170a, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106170b, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106e710, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e711, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e712, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e713, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e714, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e715, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e716, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e717, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e720, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e721, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e722, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e723, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e724, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e725, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e726, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e727, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x11060397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11061397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11062397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11063397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11064397, "VT1705", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11065397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11066397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11067397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11060398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11061398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11062398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11063398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11064398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11065398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11066398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11067398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11060428, "VT1718S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11064428, "VT1718S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11060441, "VT2020", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11064441, "VT1828S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11060433, "VT1716S", VT1716S),
+ HDA_CODEC_ID_MODEL(0x1106a721, "VT1716S", VT1716S),
+ HDA_CODEC_ID_MODEL(0x11060438, "VT2002P", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11064438, "VT2002P", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11060448, "VT1812", VT1812),
+ HDA_CODEC_ID_MODEL(0x11060440, "VT1818S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11060446, "VT1802", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11068446, "VT1802", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11064760, "VT1705CF", VT3476),
+ HDA_CODEC_ID_MODEL(0x11064761, "VT1708SCE", VT3476),
+ HDA_CODEC_ID_MODEL(0x11064762, "VT1808", VT3476),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
static struct hda_codec_driver via_driver = {
.id = snd_hda_id_via,
+ .ops = &via_codec_ops,
};
MODULE_LICENSE("GPL");
diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig
new file mode 100644
index 000000000000..f38e1947fb3e
--- /dev/null
+++ b/sound/hda/common/Kconfig
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SND_HDA
+ tristate
+ select SND_PCM
+ select SND_VMASTER
+ select SND_JACK
+ select SND_HDA_CORE
+
+if SND_HDA
+
+config SND_HDA_HWDEP
+ bool "Build hwdep interface for HD-audio driver"
+ select SND_HWDEP
+ help
+ Say Y here to build a hwdep interface for HD-audio driver.
+ This interface can be used for out-of-band communication
+ with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+ bool "Allow dynamic codec reconfiguration"
+ help
+ Say Y here to enable the HD-audio codec re-configuration feature.
+ 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
+ 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)"
+ depends on SND_HDA_INPUT_BEEP=y
+ default "1"
+ 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.
+
+config SND_HDA_PATCH_LOADER
+ bool "Support initialization patch loading for HD-audio"
+ select FW_LOADER
+ select SND_HDA_RECONFIG
+ help
+ Say Y here to allow the HD-audio driver to load a pseudo
+ firmware file ("patch") for overriding the BIOS setup at
+ start up. The "patch" file can be specified via patch module
+ option, such as patch=hda-init.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+ int "Default time-out for HD-audio power-save mode"
+ 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_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.
+
+config SND_HDA_PREALLOC_SIZE
+ int "Pre-allocated buffer size for HD-audio driver"
+ 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 S/G allocation works
+ and no preallocation is needed in most cases.
+
+ Note that the pre-allocation size can be changed dynamically
+ via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+
+endif
diff --git a/sound/hda/common/Makefile b/sound/hda/common/Makefile
new file mode 100644
index 000000000000..3344fa0efe75
--- /dev/null
+++ b/sound/hda/common/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-codec-y := bind.o codec.o jack.o auto_parser.o sysfs.o
+snd-hda-codec-y += controller.o
+snd-hda-codec-$(CONFIG_SND_PROC_FS) += proc.o
+
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += beep.o
+
+# for trace-points
+CFLAGS_controller.o := -I$(src)
+
+# common driver
+obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/hda/common/auto_parser.c
index d3ea73171a3d..8923813ce424 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/hda/common/auto_parser.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BIOS auto-parser helper functions for HD-audio
*
* Copyright (c) 2012 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.
*/
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/sort.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -76,9 +72,19 @@ static int compare_input_type(const void *ap, const void *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);
+ if (a->has_boost_on_pin != b->has_boost_on_pin)
+ return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
+
+ /* Keep the original order */
+ return a->order - b->order;
}
/* Reorder the surround channels
@@ -90,14 +96,10 @@ static int compare_input_type(const void *ap, const void *bp)
*/
static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
{
- hda_nid_t nid;
-
switch (nums) {
case 3:
case 4:
- nid = pins[1];
- pins[1] = pins[2];
- pins[2] = nid;
+ swap(pins[1], pins[2]);
break;
}
}
@@ -348,7 +350,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
*/
if (!cfg->line_outs && cfg->hp_outs > 1 &&
!(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
- int i = 0;
+ i = 0;
while (i < cfg->hp_outs) {
/* The real HPs should have the sequence 0x0f */
if ((hp_out[i].seq & 0x0f) == 0x0f) {
@@ -402,6 +404,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
/* sort inputs in the order of AUTO_PIN_* type */
+ for (i = 0; i < cfg->num_inputs; i++)
+ cfg->inputs[i].order = i;
sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
compare_input_type, NULL);
@@ -762,7 +766,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
}
if (!name)
return 0;
- strlcpy(label, name, maxlen);
+ strscpy(label, name, maxlen);
return 1;
}
EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
@@ -793,11 +797,11 @@ EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
*/
void snd_hda_apply_verbs(struct hda_codec *codec)
{
+ const struct hda_verb **v;
int i;
- for (i = 0; i < codec->verbs.used; i++) {
- struct hda_verb **v = snd_array_elem(&codec->verbs, i);
+
+ snd_array_for_each(&codec->verbs, i, v)
snd_hda_sequence_write(codec, *v);
- }
}
EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
@@ -821,15 +825,17 @@ static void set_pin_targets(struct hda_codec *codec,
snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
}
-static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
+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)
- apply_fixup(codec, fix->chain_id, action, depth + 1);
+ __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1);
switch (fix->type) {
case HDA_FIXUP_PINS:
@@ -867,11 +873,10 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
}
if (!fix->chained || fix->chained_before)
break;
- if (++depth > 10)
- break;
id = fix->chain_id;
}
}
+EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup);
/**
* snd_hda_apply_fixup - Apply the fixup chain with the given action
@@ -881,19 +886,20 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
void snd_hda_apply_fixup(struct hda_codec *codec, int action)
{
if (codec->fixup_list)
- apply_fixup(codec, codec->fixup_id, action, 0);
+ __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)
+ const struct hda_pintbl *pins,
+ bool match_all_pins)
{
+ const struct hda_pincfg *pin;
int i;
- 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) {
hda_nid_t nid = pin->nid;
u32 cfg = pin->cfg;
const struct hda_pintbl *t_pins;
@@ -912,7 +918,8 @@ static bool pin_config_match(struct hda_codec *codec,
return false;
}
}
- if (!found && (cfg & 0xf0000000) != 0x40000000)
+ if (match_all_pins &&
+ !found && (cfg & 0xf0000000) != 0x40000000)
return false;
}
@@ -924,12 +931,15 @@ static bool pin_config_match(struct hda_codec *codec,
* @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)
+ const struct hda_fixup *fixlist,
+ bool match_all_pins)
{
const struct snd_hda_pin_quirk *pq;
+ const char *name = NULL;
if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
return;
@@ -939,13 +949,14 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
continue;
if (codec->core.vendor_id != pq->codec)
continue;
- if (pin_config_match(codec, pq->pins)) {
+ 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);
+ name = pq->name;
#endif
+ codec_info(codec, "%s: picked fixup %s (pin match)\n",
+ codec->core.chip_name, name ? name : "");
codec->fixup_list = fixlist;
return;
}
@@ -953,6 +964,28 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+/* check whether the given quirk entry matches with vendor/device pair */
+static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q)
+{
+ if (q->subvendor != vendor)
+ return false;
+ return !q->subdevice ||
+ (device & q->subdevice_mask) == q->subdevice;
+}
+
+/* look through the quirk list and return the matching entry */
+static const struct hda_quirk *
+hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list)
+{
+ const struct hda_quirk *q;
+
+ for (q = list; q->subvendor || q->subdevice; q++) {
+ if (hda_quirk_match(vendor, device, q))
+ return q;
+ }
+ return NULL;
+}
+
/**
* snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string
* @codec: the HDA codec
@@ -965,76 +998,107 @@ EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
* 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_quirk *quirk,
const struct hda_fixup *fixlist)
{
- const struct snd_pci_quirk *q;
+ const struct hda_quirk *q;
int id = HDA_FIXUP_ID_NOT_SET;
const char *name = NULL;
+ const char *type = NULL;
+ unsigned int vendor, device;
+ u16 pci_vendor, pci_device;
+ u16 codec_vendor, codec_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")) {
- codec->fixup_list = NULL;
- codec->fixup_name = NULL;
- codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP;
- codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n",
- codec->core.chip_name);
- return;
+ id = HDA_FIXUP_ID_NO_FIXUP;
+ fixlist = NULL;
+ codec_info(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)) {
- codec->fixup_id = models->id;
- codec->fixup_name = models->name;
- codec->fixup_list = fixlist;
- codec_dbg(codec, "%s: picked fixup %s (model specified)\n",
- codec->core.chip_name, codec->fixup_name);
- return;
+ id = models->id;
+ name = models->name;
+ codec_info(codec, "%s: picked fixup %s (model specified)\n",
+ codec->core.chip_name, name);
+ goto found;
}
models++;
}
}
- if (quirk) {
- q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+
+ if (!quirk)
+ return;
+
+ if (codec->bus->pci) {
+ pci_vendor = codec->bus->pci->subsystem_vendor;
+ pci_device = codec->bus->pci->subsystem_device;
+ }
+
+ codec_vendor = codec->core.subsystem_id >> 16;
+ codec_device = codec->core.subsystem_id & 0xffff;
+
+ /* match with the SSID alias given by the model string "XXXX:YYYY" */
+ if (codec->modelname &&
+ sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) {
+ q = hda_quirk_lookup_id(vendor, device, quirk);
if (q) {
- id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = q->name;
- codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n",
- codec->core.chip_name, name, q->subdevice_mask ? "" : " - vendor generic");
-#endif
+ type = "alias SSID";
+ goto found_device;
}
}
- if (id < 0 && quirk) {
- for (q = quirk; q->subvendor || q->subdevice; q++) {
- unsigned int vendorid =
- q->subdevice | (q->subvendor << 16);
- unsigned int mask = 0xffff0000 | q->subdevice_mask;
- if ((codec->core.subsystem_id & mask) == (vendorid & mask)) {
- id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = q->name;
- codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n",
- codec->core.chip_name, name);
-#endif
- break;
+
+ /* match primarily with the PCI SSID */
+ for (q = quirk; q->subvendor || q->subdevice; q++) {
+ /* if the entry is specific to codec SSID, check with it */
+ if (!codec->bus->pci || q->match_codec_ssid) {
+ if (hda_quirk_match(codec_vendor, codec_device, q)) {
+ type = "codec SSID";
+ goto found_device;
+ }
+ } else {
+ if (hda_quirk_match(pci_vendor, pci_device, q)) {
+ type = "PCI SSID";
+ goto found_device;
}
}
}
- codec->fixup_id = id;
- if (id >= 0) {
- codec->fixup_list = fixlist;
- codec->fixup_name = name;
+ /* match with the codec SSID */
+ q = hda_quirk_lookup_id(codec_vendor, codec_device, 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_info(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_beep.c b/sound/hda/common/beep.c
index c397e7da0eac..13a7d92e8d8d 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/hda/common/beep.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matt Ranostay <mranostay@gmail.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>
@@ -44,8 +31,9 @@ static void generate_tone(struct hda_beep *beep, int tone)
beep->power_hook(beep, true);
beep->playing = 1;
}
- snd_hda_codec_write(codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, tone);
+ if (!codec->beep_just_power_on)
+ 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)
@@ -115,7 +103,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (hz)
hz = 1000;
- /* fallthru */
+ fallthrough;
case SND_TONE:
if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz);
@@ -131,6 +119,12 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
+static void turn_on_beep(struct hda_beep *beep)
+{
+ if (beep->keep_power_at_enable)
+ snd_hda_power_up_pm(beep->codec);
+}
+
static void turn_off_beep(struct hda_beep *beep)
{
cancel_work_sync(&beep->beep_work);
@@ -138,44 +132,8 @@ static void turn_off_beep(struct hda_beep *beep)
/* turn off beep */
generate_tone(beep, 0);
}
-}
-
-static void snd_hda_do_detach(struct hda_beep *beep)
-{
- if (beep->registered)
- input_unregister_device(beep->dev);
- else
- input_free_device(beep->dev);
- beep->dev = NULL;
- turn_off_beep(beep);
-}
-
-static int snd_hda_do_attach(struct hda_beep *beep)
-{
- struct input_dev *input_dev;
- struct hda_codec *codec = beep->codec;
-
- input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
-
- /* 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;
- return 0;
+ if (beep->keep_power_at_enable)
+ snd_hda_power_down_pm(beep->codec);
}
/**
@@ -191,7 +149,9 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
enable = !!enable;
if (beep->enabled != enable) {
beep->enabled = enable;
- if (!enable)
+ if (enable)
+ turn_on_beep(beep);
+ else
turn_off_beep(beep);
return 1;
}
@@ -199,6 +159,39 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
}
EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
+static int beep_dev_register(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+ int err;
+
+ err = input_register_device(beep->dev);
+ if (!err)
+ beep->registered = true;
+ return err;
+}
+
+static int beep_dev_disconnect(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+
+ if (beep->registered)
+ input_unregister_device(beep->dev);
+ else
+ input_free_device(beep->dev);
+ if (beep->enabled)
+ turn_off_beep(beep);
+ return 0;
+}
+
+static int beep_dev_free(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+
+ beep->codec->beep = NULL;
+ kfree(beep);
+ return 0;
+}
+
/**
* snd_hda_attach_beep_device - Attach a beep input device
* @codec: the HDA codec
@@ -207,21 +200,25 @@ EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
* 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.
*
- * The attached beep device has to be registered via
- * snd_hda_register_beep_device() and released via snd_hda_detach_beep_device()
- * appropriately.
- *
* 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 */
- if (codec->beep_mode == HDA_BEEP_MODE_OFF)
- return 0; /* disabled by module option */
+ if (!codec->beep_just_power_on) {
+ if (!snd_hda_get_bool_hint(codec, "beep"))
+ return 0; /* disabled explicitly by hints */
+ if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+ return 0; /* disabled by module option */
+ }
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
if (beep == NULL)
@@ -237,16 +234,42 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
codec->beep = beep;
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
- mutex_init(&beep->mutex);
- 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_GPL(snd_hda_attach_beep_device);
@@ -256,41 +279,11 @@ EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
*/
void snd_hda_detach_beep_device(struct hda_codec *codec)
{
- struct hda_beep *beep = codec->beep;
- if (beep) {
- if (beep->dev)
- snd_hda_do_detach(beep);
- codec->beep = NULL;
- kfree(beep);
- }
+ if (!codec->bus->shutdown && codec->beep)
+ snd_device_free(codec->card, codec->beep);
}
EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
-/**
- * snd_hda_register_beep_device - Register the beep device
- * @codec: the HDA codec
- */
-int snd_hda_register_beep_device(struct hda_codec *codec)
-{
- struct hda_beep *beep = codec->beep;
- int err;
-
- if (!beep || !beep->dev)
- return 0;
-
- err = input_register_device(beep->dev);
- if (err < 0) {
- codec_err(codec, "hda_beep: unable to register input device\n");
- input_free_device(beep->dev);
- codec->beep = NULL;
- kfree(beep);
- return err;
- }
- beep->registered = true;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_register_beep_device);
-
static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -310,8 +303,12 @@ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
{
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))) {
- ucontrol->value.integer.value[0] =
+ if (chs & 1)
+ ucontrol->value.integer.value[0] = beep->enabled;
+ if (chs & 2)
ucontrol->value.integer.value[1] = beep->enabled;
return 0;
}
diff --git a/sound/pci/hda/hda_bind.c b/sound/hda/common/bind.c
index d361bb77ca00..bb1090b65699 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/hda/common/bind.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HD-audio codec driver binding
* Copyright (c) Takashi Iwai <tiwai@suse.de>
@@ -9,18 +10,18 @@
#include <linux/module.h>
#include <linux/export.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
-#include "hda_codec.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)
+static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
- struct hda_codec_driver *driver =
+ const 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 */
@@ -41,9 +42,18 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
- if (codec->patch_ops.unsol_event)
- codec->patch_ops.unsol_event(codec, ev);
+ /* ignore unsol events during shutdown */
+ if (codec->card->shutdown || codec->bus->shutdown)
+ return;
+
+ /* ignore unsol events during system suspend/resume */
+ if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
+ return;
+
+ if (driver->ops->unsol_event)
+ driver->ops->unsol_event(codec, ev);
}
/**
@@ -78,9 +88,15 @@ 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;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
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;
@@ -96,20 +112,22 @@ static int hda_codec_driver_probe(struct device *dev)
goto error;
}
- patch = (hda_codec_patch_t)codec->preset->driver_data;
- if (patch) {
- err = patch(codec);
- if (err < 0)
- goto error_module_put;
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ err = -EINVAL;
+ goto error_module_put;
}
+ err = driver->ops->probe(codec, codec->preset);
+ 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;
- if (codec->card->registered) {
+ /* 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;
@@ -120,33 +138,45 @@ static int hda_codec_driver_probe(struct device *dev)
return 0;
error_module:
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
+ if (driver->ops->remove)
+ driver->ops->remove(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);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ 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);
+ if (driver->ops->remove)
+ driver->ops->remove(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)
{
- struct hda_codec *codec = dev_to_hda_codec(dev);
-
- if (!pm_runtime_suspended(dev) && codec->patch_ops.reboot_notify)
- codec->patch_ops.reboot_notify(codec);
+ snd_hda_codec_shutdown(dev_to_hda_codec(dev));
}
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -158,7 +188,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
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.driver.pm = pm_ptr(&hda_codec_driver_pm);
drv->core.type = HDA_DEV_LEGACY;
drv->core.match = hda_codec_match;
drv->core.unsol_event = hda_codec_unsol_event;
@@ -222,6 +252,13 @@ 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)) {
@@ -271,7 +308,7 @@ static int codec_bind_generic(struct hda_codec *codec)
* @codec: the HDA codec
*
* Start parsing of the given codec tree and (re-)initialize the whole
- * patch instance.
+ * codec driver binding.
*
* Returns 0 if successful or a negative error code.
*/
@@ -279,29 +316,31 @@ 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;
- err = snd_hdac_device_register(&codec->core);
- if (err < 0)
- return err;
+ 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_err(codec, "Unable to bind the codec\n");
- goto error;
+ codec_dbg(codec, "Unable to bind the codec\n");
+ return err;
}
}
+ codec->configured = 1;
return 0;
-
- error:
- snd_hdac_device_unregister(&codec->core);
- return err;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
diff --git a/sound/pci/hda/hda_codec.c b/sound/hda/common/codec.c
index e018ecbf78a8..c6d44168c7f9 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/hda/common/codec.c
@@ -1,33 +1,20 @@
+// 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/minmax.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>
@@ -36,21 +23,31 @@
#include "hda_beep.h"
#include "hda_jack.h"
#include <sound/hda_hwdep.h>
+#include <sound/hda_component.h>
-#ifdef CONFIG_PM
-#define codec_in_pm(codec) atomic_read(&(codec)->core.in_pm)
-#define hda_codec_is_power_on(codec) \
- (!pm_runtime_suspended(hda_codec_dev(codec)))
-#else
-#define codec_in_pm(codec) 0
-#define hda_codec_is_power_on(codec) 1
-#endif
-
+#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)
+static int call_exec_verb(struct hda_bus *bus, struct hda_codec *codec,
+ unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ int err;
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ guard(mutex)(&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;
+ return err;
+}
+
/*
* Send and receive a verb - passed to exec_verb override for hdac_device
*/
@@ -65,15 +62,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
return -1;
again:
- 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);
+ err = call_exec_verb(bus, codec, cmd, flags, res);
if (!codec_in_pm(codec) && res && err == -EAGAIN) {
if (bus->response_reset) {
codec_dbg(codec,
@@ -108,7 +97,7 @@ struct hda_conn_list {
struct list_head list;
int len;
hda_nid_t nid;
- hda_nid_t conns[0];
+ hda_nid_t conns[] __counted_by(len);
};
/* look up the cached results */
@@ -128,7 +117,7 @@ static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
{
struct hda_conn_list *p;
- p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
+ p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
if (!p)
return -ENOMEM;
p->len = len;
@@ -158,7 +147,7 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
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(sizeof(hda_nid_t) * len, GFP_KERNEL);
+ result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL);
if (!result)
return -ENOMEM;
len = snd_hda_get_raw_connections(codec, nid, result, len);
@@ -320,7 +309,7 @@ EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int parm;
+ int parm;
if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
get_wcaps_type(wcaps) != AC_WID_PIN)
@@ -343,18 +332,16 @@ EXPORT_SYMBOL_GPL(snd_hda_get_num_devices);
* Copy the device list. This info is dynamic and so not cached.
* Currently called only from hda_proc.c, so not exported.
*/
-int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
- u8 *dev_list, int max_devices)
+unsigned int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, unsigned int max_devices)
{
- unsigned int parm;
- int i, dev_len, devices;
+ unsigned int parm, i, dev_len, devices;
parm = snd_hda_get_num_devices(codec, nid);
if (!parm) /* not multi-stream capable */
return 0;
- dev_len = parm + 1;
- dev_len = dev_len < max_devices ? dev_len : max_devices;
+ dev_len = min(parm + 1, max_devices);
devices = 0;
while (devices < dev_len) {
@@ -438,7 +425,7 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
int i;
hda_nid_t nid;
- codec->wcaps = kmalloc(codec->core.num_nodes * 4, GFP_KERNEL);
+ codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL);
if (!codec->wcaps)
return -ENOMEM;
nid = codec->core.start_nid;
@@ -481,9 +468,10 @@ 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;
}
@@ -498,15 +486,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
{
struct hda_pincfg *pin;
- /* 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;
- */
-
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
pin = snd_array_new(list);
@@ -551,11 +530,11 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
#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);
+ scoped_guard(mutex, &codec->user_mutex) {
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ cfg = pin->cfg;
+ }
if (cfg)
return cfg;
}
@@ -618,14 +597,15 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
*/
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);
@@ -634,38 +614,38 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
}
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 hda_jackpoll_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, jackpoll_work.work);
- snd_hda_jack_set_dirty_all(codec);
- snd_hda_jack_poll_all(codec);
-
if (!codec->jackpoll_interval)
return;
- schedule_delayed_work(&codec->jackpoll_work,
- codec->jackpoll_interval);
+ /* the power-up/down sequence triggers the runtime resume */
+ CLASS(snd_hda_power, pm)(codec);
+ /* update jacks manually if polling is required, too */
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+ schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval);
}
/* release all pincfg lists */
@@ -697,8 +677,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;
}
@@ -711,20 +690,10 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
/*
* PCM device
*/
-static void release_pcm(struct kref *kref)
-{
- struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
-
- 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);
-}
-
void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
{
- kref_put(&pcm->kref, release_pcm);
+ if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+ wake_up(&pcm->codec->remove_sleep);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
@@ -739,7 +708,6 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
return NULL;
pcm->codec = codec;
- kref_init(&pcm->kref);
va_start(args, fmt);
pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
va_end(args);
@@ -749,6 +717,7 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
}
list_add_tail(&pcm->list, &codec->pcm_list_head);
+ refcount_inc(&codec->pcm_ref);
return pcm;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
@@ -756,33 +725,53 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
/*
* codec destructor
*/
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+ 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_init(&pcm->list);
+ list_del(&pcm->list);
if (pcm->pcm)
- snd_device_disconnect(codec->card, pcm->pcm);
- snd_hda_codec_pcm_put(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->registered) {
+ 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->registered = 0;
+ 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;
@@ -792,32 +781,46 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->spdif_out);
snd_array_free(&codec->verbs);
- codec->preset = NULL;
- codec->slave_dig_outs = NULL;
+ codec->follower_dig_outs = NULL;
codec->spdif_status_reset = 0;
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
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);
-/* also called from hda_bind.c */
+/* 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->registered)
+ if (codec->core.registered)
return;
if (device_is_registered(hda_codec_dev(codec))) {
- snd_hda_register_beep_device(codec);
- snd_hdac_link_power(&codec->core, true);
+ 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->registered = 1;
+ codec->core.registered = 1;
}
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_register);
static int snd_hda_codec_dev_register(struct snd_device *device)
{
@@ -825,22 +828,34 @@ static int snd_hda_codec_dev_register(struct snd_device *device)
return 0;
}
-static int snd_hda_codec_dev_disconnect(struct snd_device *device)
+/**
+ * snd_hda_codec_unregister - Unregister specified codec device
+ * @codec: codec device to unregister
+ */
+void snd_hda_codec_unregister(struct hda_codec *codec)
{
- struct hda_codec *codec = device->device_data;
+ 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);
- snd_hda_detach_beep_device(codec);
- return 0;
+ /*
+ * 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)
{
- struct hda_codec *codec = device->device_data;
-
- codec->in_freeing = 1;
- snd_hdac_device_unregister(&codec->core);
- snd_hdac_link_power(&codec->core, false);
- put_device(hda_codec_dev(codec));
+ snd_hda_codec_unregister(device->device_data);
return 0;
}
@@ -856,51 +871,50 @@ static void snd_hda_codec_dev_release(struct device *dev)
kfree(codec);
}
+#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 snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
- 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];
- hda_nid_t fg;
int err;
- static struct snd_device_ops dev_ops = {
- .dev_register = snd_hda_codec_dev_register,
- .dev_disconnect = snd_hda_codec_dev_disconnect,
- .dev_free = snd_hda_codec_dev_free,
- };
if (snd_BUG_ON(!bus))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
codec = kzalloc(sizeof(*codec), GFP_KERNEL);
if (!codec)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ va_start(vargs, fmt);
+ vsprintf(name, fmt, vargs);
+ va_end(vargs);
- sprintf(component, "hdaudioC%dD%d", card->number, codec_addr);
- err = snd_hdac_device_init(&codec->core, &bus->core, component,
- codec_addr);
+ err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
if (err < 0) {
kfree(codec);
- return err;
+ return ERR_PTR(err);
}
+ codec->bus = bus;
+ 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;
- codec->core.exec_verb = codec_exec_verb;
- codec->bus = bus;
- codec->card = card;
- codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -913,35 +927,87 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
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);
- codec->depop_delay = -1;
- codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ refcount_set(&codec->pcm_ref, 1);
+ init_waitqueue_head(&codec->remove_sleep);
+
+ 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
snd_hda_sysfs_init(codec);
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- err = -ENOMEM;
- goto error;
- }
+ if (!codec->modelname)
+ return -ENOMEM;
}
fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
err = read_widget_caps(codec, fg);
if (err < 0)
- goto error;
+ return err;
err = read_pin_defaults(codec);
if (err < 0)
- goto error;
+ return err;
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
+ codec->core.dev.power.power_state = PMSG_ON;
snd_hda_codec_proc_new(codec);
@@ -951,19 +1017,25 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
codec->core.subsystem_id, codec->core.revision_id);
snd_component_add(card, component);
- err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
- if (err < 0)
- goto error;
+ if (snddev_managed) {
+ /* ASoC features component management instead */
+ err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
+ if (err < 0)
+ return err;
+ }
- if (codecp)
- *codecp = codec;
- return 0;
+#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
- error:
- put_device(hda_codec_dev(codec));
- return err;
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
/**
* snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
@@ -977,7 +1049,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec)
hda_nid_t fg;
int err;
- err = snd_hdac_refresh_widgets(&codec->core, true);
+ err = snd_hdac_refresh_widgets(&codec->core);
if (err < 0)
return err;
@@ -1047,6 +1119,7 @@ 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_driver *driver = hda_codec_to_driver(codec);
struct hda_codec *c;
struct hda_cvt_setup *p;
int type;
@@ -1062,8 +1135,8 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
if (!p)
return;
- if (codec->patch_ops.stream_pm)
- codec->patch_ops.stream_pm(codec, nid, true);
+ if (driver->ops->stream_pm)
+ driver->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);
@@ -1076,8 +1149,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
/* make other inactive cvts with the same stream-tag dirty */
type = get_wcaps_type(get_wcaps(codec, nid));
list_for_each_codec(c, codec->bus) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- p = snd_array_elem(&c->cvt_setups, i);
+ snd_array_for_each(&c->cvt_setups, i, p) {
if (!p->active && p->stream_tag == stream_tag &&
get_wcaps_type(get_wcaps(c, p->nid)) == type)
p->dirty = 1;
@@ -1124,7 +1196,9 @@ EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
hda_nid_t nid = q->nid;
+
if (q->stream_tag || q->channel_id)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
if (q->format_id)
@@ -1132,39 +1206,36 @@ static void really_cleanup_stream(struct hda_codec *codec,
);
memset(q, 0, sizeof(*q));
q->nid = nid;
- if (codec->patch_ops.stream_pm)
- codec->patch_ops.stream_pm(codec, nid, false);
+ if (driver->ops->stream_pm)
+ driver->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_codec(c, codec->bus) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- struct hda_cvt_setup *p;
- p = snd_array_elem(&c->cvt_setups, i);
+ 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
@@ -1238,6 +1309,18 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
}
EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
+static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+ /* 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;
+}
+
/**
* snd_hda_codec_amp_update - update the AMP mono value
* @codec: HD-audio codec
@@ -1253,12 +1336,8 @@ EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
int ch, int dir, int idx, int mask, int val)
{
- unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
- /* 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 snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
@@ -1306,16 +1385,11 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
int dir, int idx, int mask, int val)
{
- int orig;
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
if (!codec->core.regmap)
return -EINVAL;
- regcache_cache_only(codec->core.regmap, true);
- orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
- regcache_cache_only(codec->core.regmap, false);
- if (orig >= 0)
- return 0;
- return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val);
+ return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
@@ -1413,7 +1487,7 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
/* ofs = 0: raw max value */
maxval = get_amp_max_value(codec, nid, dir, 0);
if (val > maxval)
- val = maxval;
+ return -EINVAL;
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
HDA_AMP_VOLMASK, val);
}
@@ -1464,13 +1538,21 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value;
int change = 0;
+ int err;
if (chs & 1) {
- change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+ err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
valp++;
}
- if (chs & 2)
- change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+ if (chs & 2) {
+ err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
+ }
return change;
}
EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
@@ -1493,10 +1575,10 @@ static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
val1 = ((int)val1) * ((int)val2);
if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
val2 |= TLV_DB_SCALE_MUTE;
- tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
- tlv[1] = 2 * sizeof(unsigned int);
- tlv[2] = val1;
- tlv[3] = val2;
+ 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;
}
/**
@@ -1544,10 +1626,10 @@ 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_GPL(snd_hda_set_vmaster_tlv);
@@ -1562,7 +1644,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
- strcpy(id.name, name);
+ strscpy(id.name, name);
return snd_ctl_find_id(codec->card, &id);
}
@@ -1641,37 +1723,6 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
/**
- * snd_hda_add_nid - Assign a NID to a control element
- * @codec: HD-audio codec
- * @nid: corresponding NID (optional)
- * @kctl: the control element to assign
- * @index: index to kctl
- *
- * Add the given control element to an array inside the codec instance.
- * This function is used when #snd_hda_ctl_add cannot be used for 1:1
- * NID:KCTL mapping - for example "Capture Source" selector.
- */
-int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t nid)
-{
- struct hda_nid_item *item;
-
- if (nid > 0) {
- item = snd_array_new(&codec->nids);
- if (!item)
- return -ENOMEM;
- item->kctl = kctl;
- item->index = index;
- item->nid = nid;
- return 0;
- }
- codec_err(codec, "no NID for mapping control %s:%d:%d\n",
- kctl->id.name, kctl->id.index, index);
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_nid);
-
-/**
* snd_hda_ctls_clear - Clear all controls assigned to the given codec
* @codec: HD-audio codec
*/
@@ -1679,6 +1730,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
{
int i;
struct hda_nid_item *items = codec->mixers.list;
+
for (i = 0; i < codec->mixers.used; i++)
snd_ctl_remove(codec->card, items[i].kctl);
snd_array_free(&codec->mixers);
@@ -1696,9 +1748,9 @@ int snd_hda_lock_devices(struct hda_bus *bus)
struct snd_card *card = bus->card;
struct hda_codec *codec;
- spin_lock(&card->files_lock);
+ guard(spinlock)(&card->files_lock);
if (card->shutdown)
- goto err_unlock;
+ return -EINVAL;
card->shutdown = 1;
if (!list_empty(&card->ctl_files))
goto err_clear;
@@ -1713,13 +1765,10 @@ int snd_hda_lock_devices(struct hda_bus *bus)
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);
@@ -1732,9 +1781,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus)
{
struct snd_card *card = bus->card;
- spin_lock(&card->files_lock);
+ guard(spinlock)(&card->files_lock);
card->shutdown = 0;
- spin_unlock(&card->files_lock);
}
EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
@@ -1756,18 +1804,18 @@ int snd_hda_codec_reset(struct hda_codec *codec)
return -EBUSY;
/* OK, let it free */
- snd_hdac_device_unregister(&codec->core);
+ device_release_driver(hda_codec_dev(codec));
/* allow device access again */
snd_hda_unlock_devices(bus);
return 0;
}
-typedef int (*map_slave_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
+typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
-/* apply the function to all matching slave ctls in the mixer list */
-static int map_slaves(struct hda_codec *codec, const char * const *slaves,
- const char *suffix, map_slave_func_t func, void *data)
+/* 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;
@@ -1778,7 +1826,7 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
struct snd_kcontrol *sctl = items[i].kctl;
if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
continue;
- for (s = slaves; *s; s++) {
+ for (s = followers; *s; s++) {
char tmpname[sizeof(sctl->id.name)];
const char *name = *s;
if (suffix) {
@@ -1797,8 +1845,8 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
return 0;
}
-static int check_slave_present(struct hda_codec *codec,
- void *data, struct snd_kcontrol *sctl)
+static int check_follower_present(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *sctl)
{
return 1;
}
@@ -1806,28 +1854,28 @@ static int check_slave_present(struct hda_codec *codec,
/* 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;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+
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 slave_init_arg {
+struct follower_init_arg {
struct hda_codec *codec;
int step;
};
-/* initialize the slave volume with 0dB via snd_ctl_apply_vmaster_slaves() */
-static int init_slave_0dB(struct snd_kcontrol *slave,
- struct snd_kcontrol *kctl,
- void *_arg)
+/* 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 slave_init_arg *arg = _arg;
+ struct follower_init_arg *arg = _arg;
int _tlv[4];
const int *tlv = NULL;
int step;
@@ -1836,7 +1884,7 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
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 slave %s:%d\n",
+ "Unexpected TLV callback for follower %s:%d\n",
kctl->id.name, kctl->id.index);
return 0; /* ignore */
}
@@ -1845,67 +1893,68 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
} else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
tlv = kctl->tlv.p;
- if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE)
+ if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
return 0;
- step = tlv[3];
+ 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 slave (%d!=%d)\n",
+ "Mismatching dB step for vmaster follower (%d!=%d)\n",
arg->step, step);
return 0;
}
arg->step = step;
- val = -tlv[2] / step;
+ val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
if (val > 0) {
- put_kctl_with_value(slave, val);
+ put_kctl_with_value(follower, val);
return val;
}
return 0;
}
-/* unmute the slave via snd_ctl_apply_vmaster_slaves() */
-static int init_slave_unmute(struct snd_kcontrol *slave,
- struct snd_kcontrol *kctl,
- void *_arg)
+/* 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(slave, 1);
+ return put_kctl_with_value(follower, 1);
}
-static int add_slave(struct hda_codec *codec,
- void *data, struct snd_kcontrol *slave)
+static int add_follower(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *follower)
{
- return snd_ctl_add_slave(data, slave);
+ 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)
- * @suffix: suffix string to each slave name (optional)
- * @init_slave_vol: initialize slaves to unmute/0dB
+ * @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 * const *slaves,
- const char *suffix, bool init_slave_vol,
- struct snd_kcontrol **ctl_ret)
+ 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;
int err;
@@ -1913,32 +1962,33 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
if (ctl_ret)
*ctl_ret = NULL;
- err = map_slaves(codec, slaves, suffix, check_slave_present, NULL);
+ err = map_followers(codec, followers, suffix, check_follower_present, NULL);
if (err != 1) {
- codec_dbg(codec, "No slave found for %s\n", name);
+ 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;
- err = map_slaves(codec, slaves, suffix, add_slave, kctl);
+ 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_slave_vol) {
- struct slave_init_arg arg = {
+ if (init_follower_vol) {
+ struct follower_init_arg arg = {
.codec = codec,
.step = 0,
};
- snd_ctl_apply_vmaster_slaves(kctl,
- tlv ? init_slave_0dB : init_slave_unmute,
- &arg);
+ snd_ctl_apply_vmaster_followers(kctl,
+ tlv ? init_follower_0dB : init_follower_unmute,
+ &arg);
}
if (ctl_ret)
@@ -1947,87 +1997,29 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
-/*
- * mute-LED control using vmaster
- */
-static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = {
- "On", "Off", "Follow Master"
- };
-
- return snd_ctl_enum_info(uinfo, 1, 3, texts);
-}
-
-static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hook->mute_mode;
- return 0;
-}
-
-static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
- unsigned int old_mode = hook->mute_mode;
-
- hook->mute_mode = ucontrol->value.enumerated.item[0];
- if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
- hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
- if (old_mode == hook->mute_mode)
- return 0;
- snd_hda_sync_vmaster_hook(hook);
- return 1;
-}
-
-static const struct snd_kcontrol_new vmaster_mute_mode = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mute-LED Mode",
- .info = vmaster_mute_mode_info,
- .get = vmaster_mute_mode_get,
- .put = vmaster_mute_mode_put,
-};
-
/* 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;
- if (hook->mute_mode != HDA_VMUTE_FOLLOW_MASTER)
- enabled = hook->mute_mode;
hook->hook(hook->codec, enabled);
}
/**
- * snd_hda_add_vmaster_hook - Add a vmaster hook for mute-LED
+ * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
* @codec: the HDA codec
* @hook: the vmaster hook object
- * @expose_enum_ctl: flag to create an enum ctl
*
- * Add a mute-LED hook with the given vmaster switch kctl.
- * When @expose_enum_ctl is set, "Mute-LED Mode" control is automatically
- * created and associated with the given hook.
+ * 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,
- bool expose_enum_ctl)
+ struct hda_vmaster_mute_hook *hook)
{
- struct snd_kcontrol *kctl;
-
if (!hook->hook || !hook->sw_kctl)
return 0;
hook->codec = codec;
- hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
- if (!expose_enum_ctl)
- return 0;
- kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
- if (!kctl)
- return -ENOMEM;
- return snd_hda_ctl_add(codec, 0, kctl);
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
@@ -2121,15 +2113,20 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
int change = 0;
if (chs & 1) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
valp++;
}
- if (chs & 2)
+ if (chs & 2) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
+ }
hda_call_check_power_status(codec, nid);
return change;
}
@@ -2175,13 +2172,14 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
int idx = kcontrol->private_value;
struct hda_spdif_out *spdif;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&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;
}
@@ -2239,7 +2237,7 @@ 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 mask, int val)
{
@@ -2247,7 +2245,7 @@ static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
mask, val);
- d = codec->slave_dig_outs;
+ d = codec->follower_dig_outs;
if (!d)
return;
for (; *d; d++)
@@ -2282,7 +2280,9 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned short val;
int change;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
nid = spdif->nid;
spdif->status = ucontrol->value.iec958.status[0] |
@@ -2295,7 +2295,6 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
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;
}
@@ -2308,10 +2307,11 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
int idx = kcontrol->private_value;
struct hda_spdif_out *spdif;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&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;
}
@@ -2336,7 +2336,9 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
unsigned short val;
int change;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
nid = spdif->nid;
val = spdif->ctls & ~AC_DIG1_ENABLE;
@@ -2346,11 +2348,10 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
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,
@@ -2389,7 +2390,7 @@ static struct snd_kcontrol_new dig_mixes[] = {
* @cvt_nid: converter NID
* @type: HDA_PCM_TYPE_*
* Creates controls related with the digital output.
- * Called from each patch supporting the digital out.
+ * Called from each codec driver supporting the digital out.
*
* Returns 0 if successful, or a negative error code.
*/
@@ -2400,7 +2401,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
{
int err;
struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *dig_mix;
+ const struct snd_kcontrol_new *dig_mix;
int idx = 0;
int val = 0;
const int spdif_index = 16;
@@ -2414,10 +2415,16 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
type == HDA_PCM_TYPE_HDMI) {
/* suppose a single SPDIF device */
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ struct snd_ctl_elem_id id;
+
kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
if (!kctl)
break;
- kctl->id.index = spdif_index;
+ id = kctl->id;
+ id.index = spdif_index;
+ err = snd_ctl_rename_id(codec->card, &kctl->id, &id);
+ if (err < 0)
+ return err;
}
bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
}
@@ -2461,10 +2468,10 @@ EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
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;
- for (i = 0; i < codec->spdif_out.used; i++) {
- struct hda_spdif_out *spdif =
- snd_array_elem(&codec->spdif_out, i);
+
+ snd_array_for_each(&codec->spdif_out, i, spdif) {
if (spdif->nid == nid)
return spdif;
}
@@ -2483,10 +2490,11 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
{
struct hda_spdif_out *spdif;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ guard(mutex)(&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);
@@ -2503,14 +2511,15 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
struct hda_spdif_out *spdif;
unsigned short val;
- mutex_lock(&codec->spdif_mutex);
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ guard(mutex)(&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);
@@ -2585,14 +2594,13 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
unsigned int val = !!ucontrol->value.integer.value[0];
int change;
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
change = codec->spdif_in_enable != val;
if (change) {
codec->spdif_in_enable = val;
snd_hdac_regmap_write(&codec->core, nid,
AC_VERB_SET_DIGI_CONVERT_1, val);
}
- mutex_unlock(&codec->spdif_mutex);
return change;
}
@@ -2614,7 +2622,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),
@@ -2638,7 +2646,7 @@ static struct snd_kcontrol_new dig_in_ctls[] = {
* @nid: audio in widget NID
*
* Creates controls related with the SPDIF input.
- * Called from each patch supporting the SPDIF in.
+ * Called from each codec driver supporting the SPDIF in.
*
* Returns 0 if successful, or a negative error code.
*/
@@ -2646,7 +2654,7 @@ 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;
idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
@@ -2702,32 +2710,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
}
EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
-/*
- * wait until the state is reached, returns the current state
- */
-static unsigned int hda_sync_power_state(struct hda_codec *codec,
- hda_nid_t fg,
- unsigned int power_state)
-{
- unsigned long end_time = jiffies + msecs_to_jiffies(500);
- unsigned int state, actual_state;
-
- for (;;) {
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state & AC_PWRST_ERROR)
- 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;
-}
-
/**
* snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
* @codec: the HDA codec
@@ -2761,6 +2743,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
int count;
unsigned int state;
@@ -2777,9 +2760,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
/* 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);
+ /* might be called before binding to driver, too */
+ if (driver && driver->ops && driver->ops->set_power_state)
+ driver->ops->set_power_state(codec, fg, power_state);
else {
state = power_state;
if (codec->power_filter)
@@ -2790,7 +2773,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
state);
snd_hda_codec_set_power_to_all(codec, fg, power_state);
}
- state = hda_sync_power_state(codec, fg, power_state);
+ state = snd_hda_sync_power_state(codec, fg, power_state);
if (!(state & AC_PWRST_ERROR))
break;
}
@@ -2834,7 +2817,6 @@ static void hda_exec_init_verbs(struct hda_codec *codec)
static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif
-#ifdef CONFIG_PM
/* update the power on/off account with the current jiffies */
static void update_power_acct(struct hda_codec *codec, bool on)
{
@@ -2858,16 +2840,17 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
*/
static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
unsigned int state;
- atomic_inc(&codec->core.in_pm);
-
- if (codec->patch_ops.suspend)
- codec->patch_ops.suspend(codec);
- hda_cleanup_all_streams(codec);
+ snd_hdac_enter_pm(&codec->core);
+ if (driver->ops->suspend)
+ driver->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);
- atomic_dec(&codec->core.in_pm);
+ snd_hdac_leave_pm(&codec->core);
return state;
}
@@ -2876,8 +2859,9 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
- atomic_inc(&codec->core.in_pm);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ snd_hdac_enter_pm(&codec->core);
if (codec->core.regmap)
regcache_mark_dirty(codec->core.regmap);
@@ -2887,36 +2871,37 @@ static void hda_call_codec_resume(struct hda_codec *codec)
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);
+ if (driver->ops->resume)
+ driver->ops->resume(codec);
else {
- if (codec->patch_ops.init)
- codec->patch_ops.init(codec);
- if (codec->core.regmap)
- regcache_sync(codec->core.regmap);
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
}
+ snd_hda_jack_report_sync(codec);
+ codec->core.dev.power.power_state = PMSG_ON;
+ snd_hdac_leave_pm(&codec->core);
if (codec->jackpoll_interval)
- hda_jackpoll_work(&codec->jackpoll_work.work);
- else
- snd_hda_jack_report_sync(codec);
- atomic_dec(&codec->core.in_pm);
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
}
static int hda_codec_runtime_suspend(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
- struct hda_pcm *pcm;
unsigned int state;
- cancel_delayed_work_sync(&codec->jackpoll_work);
- list_for_each_entry(pcm, &codec->pcm_list_head, list)
- snd_pcm_suspend_all(pcm->pcm);
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
state = hda_call_codec_suspend(codec);
- if (codec_has_clkstop(codec) && codec_has_epss(codec) &&
- (state & AC_PWRST_CLK_STOP_OK))
+ 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_hdac_link_power(&codec->core, false);
+ snd_hda_codec_display_power(codec, false);
+
return 0;
}
@@ -2924,22 +2909,113 @@ static int hda_codec_runtime_resume(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
- snd_hdac_link_power(&codec->core, true);
+ /* 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;
}
-#endif /* CONFIG_PM */
+
+static int hda_codec_runtime_idle(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend)
+ return -EBUSY;
+ return 0;
+}
+
+static int hda_codec_pm_prepare(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_suspended(dev);
+}
+
+static void hda_codec_pm_complete(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ /* 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;
+
+ if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
+ hda_codec_need_resume(codec) || codec->forced_resume))
+ pm_request_resume(dev);
+}
+
+static int hda_codec_pm_suspend(struct device *dev)
+{
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_force_suspend(dev);
+}
+
+static int hda_codec_pm_resume(struct device *dev)
+{
+ dev->power.power_state = PMSG_RESUME;
+ return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_freeze(struct device *dev)
+{
+ 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 int hda_codec_pm_thaw(struct device *dev)
+{
+ dev->power.power_state = PMSG_THAW;
+ return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_restore(struct device *dev)
+{
+ dev->power.power_state = PMSG_RESTORE;
+ return pm_runtime_force_resume(dev);
+}
/* referred in hda_bind.c */
const struct dev_pm_ops hda_codec_driver_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
- NULL)
+ .prepare = pm_sleep_ptr(hda_codec_pm_prepare),
+ .complete = pm_sleep_ptr(hda_codec_pm_complete),
+ .suspend = pm_sleep_ptr(hda_codec_pm_suspend),
+ .resume = pm_sleep_ptr(hda_codec_pm_resume),
+ .freeze = pm_sleep_ptr(hda_codec_pm_freeze),
+ .thaw = pm_sleep_ptr(hda_codec_pm_thaw),
+ .poweroff = pm_sleep_ptr(hda_codec_pm_suspend),
+ .restore = pm_sleep_ptr(hda_codec_pm_restore),
+ RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
+ hda_codec_runtime_idle)
};
+/* suspend the codec at shutdown; called from driver's shutdown callback */
+void snd_hda_codec_shutdown(struct hda_codec *codec)
+{
+ struct hda_pcm *cpcm;
+
+ /* Skip the shutdown if codec is not registered */
+ if (!codec->core.registered)
+ return;
+
+ codec->jackpoll_interval = 0; /* don't poll any longer */
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list)
+ snd_pcm_suspend_all(cpcm->pcm);
+
+ pm_runtime_force_suspend(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+}
+
/*
* add standard channel maps if not specified
*/
@@ -2983,28 +3059,35 @@ EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
int snd_hda_codec_build_controls(struct hda_codec *codec)
{
- int err = 0;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ int err;
+
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);
+ err = snd_hda_codec_init(codec);
if (err < 0)
return err;
+ if (driver->ops->build_controls) {
+ err = driver->ops->build_controls(codec);
+ if (err < 0)
+ return err;
+ }
+
/* we create chmaps here instead of build_pcms */
err = add_std_chmaps(codec);
if (err < 0)
return err;
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
if (codec->jackpoll_interval)
- hda_jackpoll_work(&codec->jackpoll_work.work);
- else
- snd_hda_jack_report_sync(codec); /* call at the last init point */
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
+
sync_power_up_states(codec);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls);
/*
* PCM stuff
@@ -3044,6 +3127,7 @@ static int set_pcm_default_values(struct hda_codec *codec,
err = snd_hda_query_supported_pcm(codec, info->nid,
info->rates ? NULL : &info->rates,
info->formats ? NULL : &info->formats,
+ info->subformats ? NULL : &info->subformats,
info->maxbps ? NULL : &info->maxbps);
if (err < 0)
return err;
@@ -3086,7 +3170,8 @@ int snd_hda_codec_prepare(struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
int ret;
- mutex_lock(&codec->bus->prepare_mutex);
+
+ guard(mutex)(&codec->bus->prepare_mutex);
if (hinfo->ops.prepare)
ret = hinfo->ops.prepare(hinfo, codec, stream, format,
substream);
@@ -3094,13 +3179,12 @@ int snd_hda_codec_prepare(struct hda_codec *codec,
ret = -ENODEV;
if (ret >= 0)
purify_inactive_streams(codec);
- mutex_unlock(&codec->bus->prepare_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
/**
- * snd_hda_codec_cleanup - Prepare a stream
+ * snd_hda_codec_cleanup - Clean up stream resources
* @codec: the HDA codec
* @hinfo: PCM information
* @substream: PCM substream
@@ -3111,10 +3195,9 @@ 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);
+ guard(mutex)(&codec->bus->prepare_mutex);
if (hinfo->ops.cleanup)
hinfo->ops.cleanup(hinfo, codec, substream);
- mutex_unlock(&codec->bus->prepare_mutex);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
@@ -3132,7 +3215,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
/* assigned to static slots up to dev#10; if more needed, assign
* the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
*/
- static int audio_idx[HDA_PCM_NTYPES][5] = {
+ 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 },
@@ -3174,16 +3257,17 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
/* 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_codec_driver *driver = hda_codec_to_driver(codec);
struct hda_pcm *cpcm;
int err;
if (!list_empty(&codec->pcm_list_head))
return 0; /* already parsed */
- if (!codec->patch_ops.build_pcms)
+ if (!driver->ops->build_pcms)
return 0;
- err = codec->patch_ops.build_pcms(codec);
+ err = driver->ops->build_pcms(codec);
if (err < 0) {
codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
codec->core.addr, err);
@@ -3193,7 +3277,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
int stream;
- for (stream = 0; stream < 2; stream++) {
+ for_each_pcm_streams(stream) {
struct hda_pcm_stream *info = &cpcm->stream[stream];
if (!info->substreams)
@@ -3210,6 +3294,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms);
/* assign all PCMs of the given codec */
int snd_hda_codec_build_pcms(struct hda_codec *codec)
@@ -3265,13 +3350,18 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
for (; knew->name; knew++) {
struct snd_kcontrol *kctl;
int addr = 0, idx = 0;
- if (knew->iface == -1) /* skip this codec private value */
- continue;
+ 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;
- if (addr > 0)
+ /* 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;
@@ -3282,9 +3372,11 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
* the codec addr; if it still fails (or it's the
* primary codec), then try another control index
*/
- if (!addr && codec->core.addr)
+ if (!addr && codec->core.addr) {
addr = codec->core.addr;
- else if (!idx && !knew->index) {
+ 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)
@@ -3297,8 +3389,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
-#ifdef CONFIG_PM
-static void codec_set_power_save(struct hda_codec *codec, int delay)
+/**
+ * snd_hda_codec_set_power_save - Configure codec's runtime PM
+ * @codec: codec device to configure
+ * @delay: autosuspend delay
+ */
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
{
struct device *dev = hda_codec_dev(codec);
@@ -3316,6 +3412,7 @@ static void codec_set_power_save(struct hda_codec *codec, int delay)
pm_runtime_forbid(dev);
}
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
/**
* snd_hda_set_power_save - reprogram autosuspend for the given delay
@@ -3329,7 +3426,7 @@ void snd_hda_set_power_save(struct hda_bus *bus, int delay)
struct hda_codec *c;
list_for_each_codec(c, bus)
- codec_set_power_save(c, delay);
+ snd_hda_codec_set_power_save(c, delay);
}
EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
@@ -3340,7 +3437,7 @@ EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
* @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
@@ -3382,14 +3479,13 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
-#endif
/*
* 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
*/
@@ -3406,13 +3502,13 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
index = uinfo->value.enumerated.item;
if (index >= imux->num_items)
index = imux->num_items - 1;
- strcpy(uinfo->value.enumerated.name, imux->items[index].label);
+ strscpy(uinfo->value.enumerated.name, imux->items[index].label);
return 0;
}
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
@@ -3501,9 +3597,9 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
-1);
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- if (codec->slave_dig_outs) {
+ if (codec->follower_dig_outs) {
const hda_nid_t *d;
- for (d = codec->slave_dig_outs; *d; d++)
+ for (d = codec->follower_dig_outs; *d; d++)
snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
format);
}
@@ -3516,9 +3612,9 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
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) {
+ if (codec->follower_dig_outs) {
const hda_nid_t *d;
- for (d = codec->slave_dig_outs; *d; d++)
+ for (d = codec->follower_dig_outs; *d; d++)
snd_hda_codec_cleanup_stream(codec, *d);
}
}
@@ -3531,12 +3627,11 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout)
{
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
/* already opened as analog dup; reset it once */
cleanup_dig_out_stream(codec, mout->dig_out_nid);
mout->dig_out_used = HDA_DIG_EXCLUSIVE;
- mutex_unlock(&codec->spdif_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
@@ -3555,9 +3650,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
unsigned int format,
struct snd_pcm_substream *substream)
{
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
- mutex_unlock(&codec->spdif_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
@@ -3570,9 +3664,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
cleanup_dig_out_stream(codec, mout->dig_out_nid);
- mutex_unlock(&codec->spdif_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
@@ -3585,9 +3678,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
struct hda_multi_out *mout)
{
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
mout->dig_out_used = 0;
- mutex_unlock(&codec->spdif_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
@@ -3600,7 +3692,7 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
* @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,
@@ -3624,9 +3716,10 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
&mout->spdif_rates,
&mout->spdif_formats,
+ NULL,
&mout->spdif_maxbps);
}
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
if (mout->share_spdif) {
if ((runtime->hw.rates & mout->spdif_rates) &&
(runtime->hw.formats & mout->spdif_formats)) {
@@ -3639,7 +3732,6 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
/* FIXME: need notify? */
}
}
- mutex_unlock(&codec->spdif_mutex);
}
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
@@ -3668,23 +3760,23 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
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 && spdif != NULL &&
- snd_hda_is_supported_format(codec, mout->dig_out_nid,
- format) &&
- !(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);
- } else {
- mout->dig_out_used = 0;
- cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ scoped_guard(mutex, &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 && spdif != NULL &&
+ snd_hda_is_supported_format(codec, mout->dig_out_nid,
+ format) &&
+ !(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);
+ } else {
+ mout->dig_out_used = 0;
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ }
}
}
- mutex_unlock(&codec->spdif_mutex);
/* front */
snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
@@ -3751,12 +3843,11 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
if (mout->extra_out_nid[i])
snd_hda_codec_cleanup_stream(codec,
mout->extra_out_nid[i]);
- mutex_lock(&codec->spdif_mutex);
+ guard(mutex)(&codec->spdif_mutex);
if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
cleanup_dig_out_stream(codec, mout->dig_out_nid);
mout->dig_out_used = 0;
}
- mutex_unlock(&codec->spdif_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
@@ -3799,7 +3890,7 @@ EXPORT_SYMBOL_GPL(snd_hda_get_default_vref);
unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
hda_nid_t pin, unsigned int val)
{
- static unsigned int cap_lists[][2] = {
+ 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 },
@@ -3847,7 +3938,7 @@ unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
/**
- * _snd_hda_pin_ctl - Helper to set pin ctl value
+ * _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
@@ -3856,7 +3947,7 @@ EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
* 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_update_cache() or
+ * 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,
@@ -3865,7 +3956,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
val = snd_hda_correct_pin_ctl(codec, pin, val);
snd_hda_codec_set_pin_target(codec, pin, val);
if (cached)
- return snd_hda_codec_update_cache(codec, pin, 0,
+ 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,
@@ -3905,7 +3996,7 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
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++;
@@ -3923,13 +4014,12 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus)
list_for_each_codec(codec, bus) {
/* FIXME: maybe a better way needed for forced reset */
- cancel_delayed_work_sync(&codec->jackpoll_work);
-#ifdef CONFIG_PM
+ if (current_work() != &codec->jackpoll_work.work)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
if (hda_codec_is_power_on(codec)) {
hda_call_codec_suspend(codec);
hda_call_codec_resume(codec);
}
-#endif
}
}
@@ -3943,12 +4033,12 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus)
*/
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 */
}
diff --git a/sound/pci/hda/hda_controller.c b/sound/hda/common/controller.c
index d1eb14842340..b1cfd9bd4dcb 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/hda/common/controller.c
@@ -1,23 +1,12 @@
+// 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 Intel Corporation
*
* 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.
- *
- *
*/
#include <linux/clocksource.h>
@@ -35,14 +24,19 @@
#include <sound/core.h>
#include <sound/initval.h>
+#include <sound/pcm_params.h>
#include "hda_controller.h"
+#include "hda_local.h"
#define CREATE_TRACE_POINTS
-#include "hda_controller_trace.h"
+#include "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))
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+#define guard_dsp_lock(dev) guard(snd_hdac_dsp_lock)(azx_stream(dev))
+#else
+#define guard_dsp_lock(dev) do {} while (0)
+#endif
#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
/* assign a stream for the PCM */
@@ -102,12 +96,12 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
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);
+ scoped_guard(mutex, &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);
+ }
snd_hda_codec_pcm_put(apcm->info);
return 0;
}
@@ -118,44 +112,41 @@ static int azx_pcm_hw_params(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);
- int ret;
+ struct hdac_stream *hdas = azx_stream(azx_dev);
trace_azx_pcm_hw_params(chip, azx_dev);
- dsp_lock(azx_dev);
- if (dsp_is_locked(azx_dev)) {
- ret = -EBUSY;
- goto unlock;
- }
+ guard_dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev))
+ return -EBUSY;
+
+ /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */
+ hdas->bufsize = params_buffer_bytes(hw_params);
+ hdas->period_bytes = params_period_bytes(hw_params);
+ hdas->format_val = 0;
+ hdas->no_period_wakeup =
+ (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+ (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
+ if (snd_hdac_stream_setup_periods(hdas) < 0)
+ return -ENOMEM;
- azx_dev->core.bufsize = 0;
- azx_dev->core.period_bytes = 0;
- azx_dev->core.format_val = 0;
- ret = chip->ops->substream_alloc_pages(chip, substream,
- params_buffer_bytes(hw_params));
-unlock:
- dsp_unlock(azx_dev);
- return ret;
+ return 0;
}
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 azx *chip = apcm->chip;
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
- int err;
/* reset BDL address */
- dsp_lock(azx_dev);
+ guard_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);
- err = chip->ops->substream_free_pages(chip, substream);
azx_stream(azx_dev)->prepared = 0;
- dsp_unlock(azx_dev);
- return err;
+ return 0;
}
static int azx_pcm_prepare(struct snd_pcm_substream *substream)
@@ -165,38 +156,33 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
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;
+ unsigned int format_val, stream_tag, bits;
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;
- }
+ guard_dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev))
+ return -EBUSY;
snd_hdac_stream_reset(azx_stream(azx_dev));
- format_val = snd_hdac_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps,
- ctls);
+ bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
+
+ format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, 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;
+ return -EINVAL;
}
err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
if (err < 0)
- goto unlock;
+ return err;
- snd_hdac_stream_setup(azx_stream(azx_dev));
+ snd_hdac_stream_setup(azx_stream(azx_dev), false);
stream_tag = azx_dev->core.stream_tag;
/* CA-IBG chips need the playback stream starting from 1 */
@@ -205,12 +191,11 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
stream_tag -= chip->capture_streams;
err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
azx_dev->core.format_val, substream);
+ if (err < 0)
+ return err;
- unlock:
- if (!err)
- azx_stream(azx_dev)->prepared = 1;
- dsp_unlock(azx_dev);
- return err;
+ azx_stream(azx_dev)->prepared = 1;
+ return 0;
}
static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -260,32 +245,29 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
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);
+ scoped_guard(spinlock, &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), true);
- } else {
- snd_hdac_stream_stop(azx_stream(azx_dev));
+ 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);
+ guard(spinlock)(&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);
+ snd_hdac_stream_timecounter_init(hstr, sbits, start);
return 0;
}
@@ -388,7 +370,7 @@ static int azx_get_sync_time(ktime_t *device,
u32 wallclk_ctr, wallclk_cycles;
bool direction;
u32 dma_select;
- u32 timeout = 200;
+ u32 timeout;
u32 retry_count = 0;
runtime = substream->runtime;
@@ -471,7 +453,8 @@ static int azx_get_sync_time(ktime_t *device,
*device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
- *system = convert_art_to_tsc(tsc_counter);
+ system->cycles = tsc_counter;
+ system->cs_id = CSID_X86_ART;
return 0;
}
@@ -502,7 +485,7 @@ static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
}
static int azx_get_time_info(struct snd_pcm_substream *substream,
- struct timespec *system_ts, struct timespec *audio_ts,
+ 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)
{
@@ -518,11 +501,10 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
snd_pcm_gettime(substream->runtime, system_ts);
nsec = timecounter_read(&azx_dev->core.tc);
- nsec = div_u64(nsec, 3); /* can be optimized */
if (audio_tstamp_config->report_delay)
nsec = azx_adjust_codec_delay(substream, nsec);
- *audio_ts = ns_to_timespec(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 */
@@ -539,16 +521,16 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
return -EINVAL;
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
- *system_ts = ktime_to_timespec(xtstamp.sys_monoraw);
+ *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
break;
default:
- *system_ts = ktime_to_timespec(xtstamp.sys_realtime);
+ *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
break;
}
- *audio_ts = ktime_to_timespec(xtstamp.device);
+ *audio_ts = ktime_to_timespec64(xtstamp.device);
audio_tstamp_report->actual_type =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
@@ -563,7 +545,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_pcm_hardware azx_pcm_hw = {
+static const struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -609,11 +591,9 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
}
runtime->private_data = azx_dev;
- if (chip->gts_present)
- azx_pcm_hw.info = azx_pcm_hw.info |
- SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
-
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;
@@ -686,28 +666,15 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
return err;
}
-static int azx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- if (chip->ops->pcm_mmap_prepare)
- chip->ops->pcm_mmap_prepare(substream, area);
- return snd_pcm_lib_default_mmap(substream, area);
-}
-
static const 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,
.get_time_info = azx_get_time_info,
- .mmap = azx_pcm_mmap,
- .page = snd_pcm_sgbuf_ops_page,
};
static void azx_pcm_free(struct snd_pcm *pcm)
@@ -732,6 +699,7 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
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) {
@@ -746,10 +714,12 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
&pcm);
if (err < 0)
return err;
- strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
+ strscpy(pcm->name, cpcm->name, sizeof(pcm->name));
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (apcm == NULL)
+ if (apcm == NULL) {
+ snd_device_free(chip->card, pcm);
return -ENOMEM;
+ }
apcm->chip = chip;
apcm->pcm = pcm;
apcm->codec = codec;
@@ -768,9 +738,10 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
if (size > MAX_PREALLOC_SIZE)
size = MAX_PREALLOC_SIZE;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- chip->card->dev,
- 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;
}
@@ -792,54 +763,21 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
{
struct azx *chip = bus_to_azx(bus);
struct hda_bus *hbus = &chip->bus;
- unsigned long timeout;
- unsigned long loopcounter;
- int do_poll = 0;
+ int err;
again:
- timeout = jiffies + msecs_to_jiffies(1000);
-
- for (loopcounter = 0;; loopcounter++) {
- spin_lock_irq(&bus->reg_lock);
- if (chip->polling_mode || do_poll)
- snd_hdac_bus_update_rirb(bus);
- if (!bus->rirb.cmds[addr]) {
- if (!do_poll)
- chip->poll_count = 0;
- if (res)
- *res = bus->rirb.res[addr]; /* the last value */
- spin_unlock_irq(&bus->reg_lock);
- return 0;
- }
- spin_unlock_irq(&bus->reg_lock);
- if (time_after(jiffies, timeout))
- break;
- if (hbus->needs_damn_long_delay || loopcounter > 3000)
- msleep(2); /* temporary workaround */
- else {
- udelay(10);
- cond_resched();
- }
- }
+ err = snd_hdac_bus_get_response(bus, addr, res);
+ if (!err)
+ return 0;
if (hbus->no_response_fallback)
return -EIO;
- if (!chip->polling_mode && chip->poll_count < 2) {
- dev_dbg(chip->card->dev,
- "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
- bus->last_cmd[addr]);
- do_poll = 1;
- chip->poll_count++;
- goto again;
- }
-
-
- if (!chip->polling_mode) {
+ 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]);
- chip->polling_mode = 1;
+ bus->polling_mode = 1;
goto again;
}
@@ -870,6 +808,9 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
*/
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 */
}
@@ -964,7 +905,7 @@ static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
if (chip->disabled)
return 0;
- if (chip->single_cmd)
+ if (chip->single_cmd || bus->use_pio_for_commands)
return azx_single_send_cmd(bus, val);
else
return snd_hdac_bus_send_cmd(bus, val);
@@ -978,26 +919,15 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
if (chip->disabled)
return 0;
- if (chip->single_cmd)
+ if (chip->single_cmd || bus->use_pio_for_commands)
return azx_single_get_response(bus, addr, res);
else
return azx_rirb_get_response(bus, addr, res);
}
-static int azx_link_power(struct hdac_bus *bus, bool enable)
-{
- struct azx *chip = bus_to_azx(bus);
-
- if (chip->ops->link_power)
- return chip->ops->link_power(chip, enable);
- else
- return -EINVAL;
-}
-
static const struct hdac_bus_ops bus_core_ops = {
.command = azx_send_cmd,
.get_response = azx_get_response,
- .link_power = azx_link_power,
};
#ifdef CONFIG_SND_HDA_DSP_LOADER
@@ -1032,19 +962,18 @@ int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
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;
+ scoped_guard(spinlock_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);
+ guard(spinlock_irq)(&bus->reg_lock);
if (saved)
*azx_dev = chip->saved_azx_dev;
- spin_unlock_irq(&bus->reg_lock);
return err;
}
@@ -1075,11 +1004,10 @@ void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
return;
snd_hdac_dsp_cleanup(hstr, dmab);
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_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 */
@@ -1100,10 +1028,8 @@ EXPORT_SYMBOL_GPL(azx_init_chip);
void azx_stop_all_streams(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
- struct hdac_stream *s;
- list_for_each_entry(s, &bus->stream_list, list)
- snd_hdac_stream_stop(s);
+ snd_hdac_stop_streams(bus);
}
EXPORT_SYMBOL_GPL(azx_stop_all_streams);
@@ -1138,16 +1064,14 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
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);
+ guard(spinlock)(&bus->reg_lock);
if (chip->disabled)
- goto unlock;
+ return IRQ_NONE;
do {
status = azx_readl(chip, INTSTS);
@@ -1159,22 +1083,26 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
active = true;
- /* clear rirb int */
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);
}
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
} while (active && ++repeat < 10);
- unlock:
- spin_unlock(&bus->reg_lock);
-
return IRQ_RETVAL(handled);
}
EXPORT_SYMBOL_GPL(azx_interrupt);
@@ -1194,12 +1122,12 @@ static int probe_codec(struct azx *chip, int addr)
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);
+ scoped_guard(mutex, &bus->cmd_mutex) {
+ chip->probing = 1;
+ azx_send_cmd(bus, cmd);
+ err = azx_get_response(bus, addr, &res);
+ chip->probing = 0;
+ }
if (err < 0 || res == -1)
return -EIO;
dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
@@ -1218,36 +1146,13 @@ void snd_hda_bus_reset(struct hda_bus *bus)
bus->in_reset = 0;
}
-static int get_jackpoll_interval(struct azx *chip)
-{
- int i;
- unsigned int j;
-
- if (!chip->jackpoll_ms)
- return 0;
-
- i = chip->jackpoll_ms[chip->dev_index];
- if (i == 0)
- return 0;
- if (i < 50 || i > 60000)
- j = 0;
- else
- j = msecs_to_jiffies(i);
- if (j == 0)
- dev_warn(chip->card->dev,
- "jackpoll_ms value out of range: %d\n", i);
- return j;
-}
-
/* HD-audio bus initialization */
-int azx_bus_init(struct azx *chip, const char *model,
- const struct hdac_io_ops *io_ops)
+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,
- io_ops);
+ err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
if (err < 0)
return err;
@@ -1267,15 +1172,11 @@ int azx_bus_init(struct azx *chip, const char *model,
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
bus->core.align_bdle_4k = true;
- /* AMD chipsets often cause the communication stalls upon certain
- * sequence like the pin-detection. It seems that forcing the synced
- * access works around the stall. Grrr...
- */
- if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
- dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
- bus->core.sync_write = 1;
- bus->allow_bus_reset = 1;
- }
+ if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS)
+ bus->core.use_pio_for_commands = true;
+
+ /* enable sync_write flag for stable communication as default */
+ bus->core.sync_write = 1;
return 0;
}
@@ -1301,6 +1202,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
dev_warn(chip->card->dev,
"Codec #%d probe error; disabling it...\n", c);
bus->codec_mask &= ~(1 << c);
+ /* no codecs */
+ if (bus->codec_mask == 0)
+ break;
/* More badly, accessing to a non-existing
* codec often screws up the controller chip,
* and disturbs the further communications.
@@ -1321,8 +1225,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
if (err < 0)
continue;
- codec->jackpoll_interval = get_jackpoll_interval(chip);
+ codec->jackpoll_interval = chip->jackpoll_interval;
codec->beep_mode = chip->beep_mode;
+ codec->ctl_dev_id = chip->ctl_dev_id;
codecs++;
}
}
@@ -1338,17 +1243,24 @@ EXPORT_SYMBOL_GPL(azx_probe_codecs);
int azx_codec_configure(struct azx *chip)
{
struct hda_codec *codec, *next;
+ int success = 0;
- /* use _safe version here since snd_hda_codec_configure() deregisters
- * the device upon error and deletes itself from the bus list.
- */
- list_for_each_codec_safe(codec, next, &chip->bus) {
- snd_hda_codec_configure(codec);
+ list_for_each_codec(codec, &chip->bus) {
+ if (!snd_hda_codec_configure(codec))
+ success++;
}
- if (!azx_bus(chip)->num_codecs)
- return -ENODEV;
- return 0;
+ 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);
diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/hda/common/controller_trace.h
index bf48304e230a..7f5841f8919e 100644
--- a/sound/pci/hda/hda_controller_trace.h
+++ b/sound/hda/common/controller_trace.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hda_controller
-#define TRACE_INCLUDE_FILE hda_controller_trace
+#define TRACE_INCLUDE_FILE controller_trace
#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HDA_CONTROLLER_H
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h
index 2b8e29fd73e7..87af3d8c02f7 100644
--- a/sound/pci/hda/hda_auto_parser.h
+++ b/sound/hda/common/hda_auto_parser.h
@@ -1,17 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* BIOS auto-parser helper functions for HD-audio
*
* Copyright (c) 2012 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.
*/
#ifndef __SOUND_HDA_AUTO_PARSER_H
#define __SOUND_HDA_AUTO_PARSER_H
+#include "hda_local.h"
+
/*
* Helper for automatic pin configuration
*/
@@ -31,7 +29,7 @@ enum {
};
#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
-#define AUTO_CFG_MAX_INS 8
+#define AUTO_CFG_MAX_INS 18
struct auto_pin_cfg_item {
hda_nid_t pin;
@@ -39,6 +37,7 @@ struct auto_pin_cfg_item {
unsigned int is_headset_mic:1;
unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
unsigned int has_boost_on_pin:1;
+ int order;
};
struct auto_pin_cfg;
diff --git a/sound/pci/hda/hda_beep.h b/sound/hda/common/hda_beep.h
index 1052ad380e97..923ea862446a 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/hda/common/hda_beep.h
@@ -1,28 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matt Ranostay <mranostay@gmail.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
@@ -38,8 +25,8 @@ struct hda_beep {
unsigned int enabled:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
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);
};
@@ -47,7 +34,6 @@ struct hda_beep {
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
-int snd_hda_register_beep_device(struct hda_codec *codec);
#else
static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
@@ -56,9 +42,5 @@ static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
{
}
-static inline int snd_hda_register_beep_device(struct hda_codec *codec)
-{
- return 0;
-}
#endif
#endif
diff --git a/sound/pci/hda/hda_controller.h b/sound/hda/common/hda_controller.h
index a68e75b00ea3..c2d0109866e6 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/hda/common/hda_controller.h
@@ -1,15 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Common functionality for the alsa driver code base for HD Audio.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * 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 __SOUND_HDA_CONTROLLER_H
@@ -20,7 +11,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include <sound/hda_register.h>
#define AZX_MAX_CODECS HDA_MAX_CODECS
@@ -40,9 +31,9 @@
/* 14 unused */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
-/* 17 unused */
+#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
-#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
+/* 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 */
@@ -50,14 +41,11 @@
/* 24 unused */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
-#ifdef CONFIG_SND_HDA_I915
-#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
-#else
-#define AZX_DCAPS_I915_POWERWELL 0 /* NOP */
-#endif
+#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 */
+#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */
enum {
AZX_SNOOP_TYPE_NONE,
@@ -76,7 +64,6 @@ struct azx_dev {
* when link position is not greater than FIFO size
*/
unsigned int insufficient:1;
- unsigned int wc_marked:1;
};
#define azx_stream(dev) (&(dev)->core)
@@ -88,13 +75,6 @@ struct azx;
struct hda_controller_ops {
/* Disable msi if supported, PCI only */
int (*disable_msi_reset_irq)(struct azx *);
- int (*substream_alloc_pages)(struct azx *chip,
- struct snd_pcm_substream *substream,
- size_t size);
- int (*substream_free_pages)(struct azx *chip,
- struct snd_pcm_substream *substream);
- void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream,
- struct vm_area_struct *area);
/* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
/* enable/disable the link power */
@@ -127,7 +107,7 @@ struct azx {
int capture_streams;
int capture_index_offset;
int num_streams;
- const int *jackpoll_ms; /* per-card jack poll interval */
+ int jackpoll_interval; /* jack poll interval in jiffies */
/* Register interaction. */
const struct hda_controller_ops *ops;
@@ -145,6 +125,7 @@ struct azx {
/* 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;
@@ -152,17 +133,16 @@ struct azx {
/* flags */
int bdl_pos_adj;
- int poll_count;
unsigned int running:1;
unsigned int fallback_to_single_cmd:1;
unsigned int single_cmd:1;
- unsigned int polling_mode: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 region_requested:1;
unsigned int disabled:1; /* disabled by vga_switcheroo */
+ unsigned int pm_prepared:1;
/* GTS present */
unsigned int gts_present:1;
@@ -175,11 +155,10 @@ struct azx {
#define azx_bus(chip) (&(chip)->bus.core)
#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
-#ifdef CONFIG_X86
-#define azx_snoop(chip) ((chip)->snoop)
-#else
-#define azx_snoop(chip) true
-#endif
+static inline bool azx_snoop(struct azx *chip)
+{
+ return !IS_ENABLED(CONFIG_X86) || chip->snoop;
+}
/*
* macros for easy use
@@ -227,8 +206,7 @@ void azx_stop_chip(struct azx *chip);
irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */
-int azx_bus_init(struct azx *chip, const char *model,
- const struct hdac_io_ops *io_ops);
+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);
diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h
new file mode 100644
index 000000000000..ff7d289c034b
--- /dev/null
+++ b/sound/hda/common/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/hda/common/hda_local.h
index 5b5c324c99b9..a7e53277a0fe 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/hda/common/hda_local.h
@@ -1,28 +1,17 @@
+/* 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
#define __SOUND_HDA_LOCAL_H
+#include <sound/pcm_drm_eld.h>
+
/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
* the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
* snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
@@ -113,7 +102,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,
@@ -132,7 +121,7 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
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);
+ 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,
@@ -142,20 +131,15 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
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 * const *slaves,
- const char *suffix, bool init_slave_vol,
- struct snd_kcontrol **ctl_ret);
-#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
- __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
+ 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_register(struct hda_codec *codec);
-void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
-enum {
- HDA_VMUTE_OFF,
- HDA_VMUTE_ON,
- HDA_VMUTE_FOLLOW_MASTER,
-};
+#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
@@ -164,13 +148,11 @@ struct hda_vmaster_mute_hook {
struct snd_kcontrol *sw_kctl;
void (*hook)(void *, int);
/* below are initialized automatically */
- unsigned int mute_mode; /* HDA_VMUTE_XXX */
struct hda_codec *codec;
};
int snd_hda_add_vmaster_hook(struct hda_codec *codec,
- struct hda_vmaster_mute_hook *hook,
- bool expose_enum_ctl);
+ struct hda_vmaster_mute_hook *hook);
void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
/* amp value bits */
@@ -191,7 +173,7 @@ 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;
@@ -209,7 +191,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
unsigned int *cur_val);
int snd_hda_add_imux_item(struct hda_codec *codec,
struct hda_input_mux *imux, const char *label,
- int index, int *type_index_ret);
+ int index, int *type_idx);
/*
* Multi-channel / digital-out PCM helper
@@ -227,7 +209,7 @@ struct hda_multi_out {
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 */
- const 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 */
@@ -312,6 +294,32 @@ struct hda_fixup {
} v;
};
+/*
+ * extended form of snd_pci_quirk:
+ * for PCI SSID matching, use SND_PCI_QUIRK() like before;
+ * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead
+ */
+struct hda_quirk {
+ unsigned short subvendor; /* PCI subvendor ID */
+ unsigned short subdevice; /* PCI subdevice ID */
+ unsigned short subdevice_mask; /* bitmask to match */
+ bool match_codec_ssid; /* match only with codec SSID */
+ int value; /* value */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *name; /* name of the device (optional) */
+#endif
+};
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+ { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\
+ .match_codec_ssid = true }
+#else
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+ { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \
+ .match_codec_ssid = true }
+#endif
+
struct snd_hda_pin_quirk {
unsigned int codec; /* Codec vendor/device ID */
unsigned short subvendor; /* PCI subvendor ID */
@@ -368,13 +376,15 @@ 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_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);
+ const struct hda_fixup *fixlist,
+ bool match_all_pins);
/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
@@ -456,6 +466,15 @@ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
#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
*/
@@ -552,8 +571,6 @@ struct hda_nid_item {
int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
struct snd_kcontrol *kctl);
-int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t nid);
void snd_hda_ctls_clear(struct hda_codec *codec);
/*
@@ -623,10 +640,27 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
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);
+
+static inline int snd_hda_codec_init(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ if (driver->ops->init)
+ return driver->ops->init(codec);
+ return 0;
+}
+
/*
* AMP control callbacks
*/
@@ -646,74 +680,28 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
*/
int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo,
- int num_entries, const char * const *texts);
+ 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 {
- int channels;
- int format; /* (format == 0) indicates invalid SAD */
- int rates;
- int sample_bits; /* for LPCM */
- int max_bitrate; /* for AC3...ATRAC */
- int profile; /* for WMAPRO */
-};
-
-#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 parsed_hdmi_eld {
- /*
- * all fields will be cleared before updating ELD
- */
- int baseline_len;
- int eld_ver;
- int cea_edid_ver;
- char monitor_name[ELD_MAX_MNL + 1];
- int manufacture_id;
- int product_id;
- u64 port_id;
- int support_hdcp;
- int support_ai;
- int conn_type;
- int aud_synch_delay;
- int spk_alloc;
- int sad_count;
- struct cea_sad sad[ELD_MAX_SAD];
-};
-
struct hdmi_eld {
bool monitor_present;
bool eld_valid;
int eld_size;
char eld_buffer[ELD_MAX_SIZE];
- struct parsed_hdmi_eld info;
+ struct snd_parsed_hdmi_eld info;
};
int snd_hdmi_get_eld_size(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 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,
+void snd_hdmi_eld_update_pcm_info(struct snd_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);
+ 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
@@ -721,6 +709,8 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
#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...) \
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/hda/common/hwdep.c
index 57df06e76968..9325e5c3cbe6 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/hda/common/hwdep.c
@@ -1,28 +1,16 @@
+// 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/compat.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>
@@ -51,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;
@@ -87,10 +84,8 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
{
-#ifndef CONFIG_SND_DEBUG_VERBOSE
if (!capable(CAP_SYS_RAWIO))
return -EACCES;
-#endif
return 0;
}
@@ -117,8 +112,8 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
#endif
/* for sysfs */
- hwdep->dev.groups = snd_hda_dev_attr_groups;
- dev_set_drvdata(&hwdep->dev, codec);
+ hwdep->dev->groups = snd_hda_dev_attr_groups;
+ dev_set_drvdata(hwdep->dev, codec);
return 0;
}
diff --git a/sound/pci/hda/hda_jack.c b/sound/hda/common/jack.c
index a33234e04d4f..7d7786df60ea 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/hda/common/jack.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Jack-detection handling for HD-audio
*
* Copyright (c) 2011 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.
*/
#include <linux/init.h>
@@ -15,7 +11,7 @@
#include <sound/core.h>
#include <sound/control.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_jack.h"
@@ -47,7 +43,7 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
EXPORT_SYMBOL_GPL(is_jack_detectable);
/* execute pin sense measurement */
-static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
u32 pincap;
u32 val;
@@ -59,19 +55,20 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
AC_VERB_SET_PIN_SENSE, 0);
}
val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
+ AC_VERB_GET_PIN_SENSE, dev_id);
if (codec->inv_jack_detect)
val ^= AC_PINSENSE_PRESENCE;
return val;
}
/**
- * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
+ * 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(struct hda_codec *codec, hda_nid_t nid)
+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;
@@ -79,19 +76,21 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
if (!nid || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
- if (jack->nid == nid)
+ if (jack->nid == nid && jack->dev_id == dev_id)
return jack;
return NULL;
}
-EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get);
+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)
+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;
@@ -99,32 +98,77 @@ snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
if (!tag || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
- if (jack->tag == tag)
+ 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)
+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(codec, nid);
+ 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;
- jack->tag = codec->jacktbl.used;
+ 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;
@@ -157,10 +201,12 @@ static void jack_detect_update(struct hda_codec *codec,
if (jack->phantom_jack)
jack->pin_sense = AC_PINSENSE_PRESENCE;
else
- jack->pin_sense = read_pin_sense(codec, jack->nid);
+ 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(codec, jack->gating_jack))
+ 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;
@@ -168,7 +214,8 @@ static void jack_detect_update(struct hda_codec *codec,
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
- snd_hda_jack_tbl_get(codec, jack->gated_jack);
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
if (gated) {
gated->jack_dirty = 1;
jack_detect_update(codec, gated);
@@ -177,7 +224,7 @@ static void jack_detect_update(struct hda_codec *codec,
}
/**
- * snd_hda_set_dirty_all - Mark all the cached as dirty
+ * 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.
@@ -195,71 +242,99 @@ void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
/**
- * snd_hda_pin_sense - execute pin sense measurement
+ * 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_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+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(codec, nid);
+ 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);
+ return read_pin_sense(codec, nid, dev_id);
}
-EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
+EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
/**
- * snd_hda_jack_detect_state - query pin Presence Detect status
+ * 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(struct hda_codec *codec, hda_nid_t nid)
+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(codec, nid);
+ 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_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
+ 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);
+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 - enable the jack-detection
+ * 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(struct hda_codec *codec, hda_nid_t nid,
- hda_jack_callback_fn func)
+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);
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
if (!jack)
return ERR_PTR(-ENOMEM);
- if (func) {
+
+ 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;
}
@@ -276,19 +351,24 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
return ERR_PTR(err);
return callback;
}
-EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_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 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(codec, nid, NULL));
+ 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);
@@ -303,8 +383,11 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
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);
- struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, 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;
@@ -317,6 +400,69 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
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
*/
@@ -339,9 +485,15 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
if (jack->nid) {
if (!jack->jack || jack->block_report)
continue;
- state = get_jack_plug_state(jack->pin_sense);
- snd_jack_report(jack->jack,
- state ? jack->type : 0);
+ 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);
@@ -374,43 +526,62 @@ static void hda_free_jack_priv(struct snd_jack *jack)
}
/**
- * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ * 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(struct hda_codec *codec, hda_nid_t nid,
- const char *name, bool phantom_jack)
+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;
- int err, state, type;
+ const struct hda_jack_keymap *map;
+ int err, state, buttons;
- jack = snd_hda_jack_tbl_new(codec, nid);
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
if (!jack)
return 0;
if (jack->jack)
return 0; /* already created */
- type = get_input_jack_type(codec, nid);
- err = snd_jack_new(codec->card, name, type,
+ 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;
- state = snd_hda_jack_detect(codec, nid);
+ 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);
+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,
@@ -421,6 +592,8 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
int err;
bool phantom_jack;
+ WARN_ON(codec->dp_mst);
+
if (!nid)
return 0;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
@@ -431,18 +604,18 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
!is_jack_detectable(codec, nid);
if (base_name)
- strlcpy(name, base_name, sizeof(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);
+ 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);
+ return snd_hda_jack_detect_enable(codec, nid, 0);
return 0;
}
@@ -508,19 +681,26 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
-static void call_jack_callback(struct hda_codec *codec,
+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)
+ 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(codec, jack->gated_jack);
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
if (gated) {
- for (cb = gated->callback; cb; cb = cb->next)
+ for (cb = gated->callback; cb; cb = cb->next) {
+ cb->jack = gated;
+ cb->unsol_res = res;
cb->func(codec, cb);
+ }
}
}
}
@@ -533,14 +713,29 @@ static void call_jack_callback(struct hda_codec *codec,
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_SHIFT) & 0x7f;
+ 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);
+ 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;
- event->jack_dirty = 1;
- call_jack_callback(codec, event);
+ 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);
@@ -566,7 +761,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec)
if (old_sense == get_jack_plug_state(jack->pin_sense))
continue;
changes = 1;
- call_jack_callback(codec, jack);
+ call_jack_callback(codec, 0, jack);
}
if (changes)
snd_hda_jack_report_sync(codec);
diff --git a/sound/pci/hda/hda_proc.c b/sound/hda/common/proc.c
index 033aa84365b9..5f3f61519ba6 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/hda/common/proc.c
@@ -1,31 +1,17 @@
+// 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 <linux/module.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
static int dump_coef = -1;
@@ -174,7 +160,7 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
- static unsigned int rates[] = {
+ static const unsigned int rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
96000, 176400, 192000, 384000
};
@@ -693,21 +679,52 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids);
}
+static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
+ hda_nid_t nid, int dev_num)
+{
+ 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;
+ unsigned 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)
+ snd_iprintf(buffer, " Devices: %u\n", devlist_len);
+ if (devlist_len == 0)
return;
- curr = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DEVICE_SEL, 0);
+ curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
for (i = 0; i < devlist_len; i++) {
if (i == curr)
@@ -716,10 +733,14 @@ static void print_device_list(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " ");
snd_iprintf(buffer,
- "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i,
+ "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");
}
}
@@ -760,7 +781,7 @@ static void print_codec_info(struct snd_info_entry *entry,
fg = codec->core.afg;
if (!fg)
return;
- snd_hda_power_up(codec);
+ CLASS(snd_hda_power, pm)(codec);
snd_iprintf(buffer, "Default PCM:\n");
print_pcm_caps(buffer, codec, fg);
snd_iprintf(buffer, "Default Amp-In caps: ");
@@ -773,7 +794,6 @@ static void print_codec_info(struct snd_info_entry *entry,
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;
}
@@ -825,8 +845,9 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_caps & AC_WCAP_CONN_LIST) {
conn_len = snd_hda_get_num_raw_conns(codec, nid);
if (conn_len > 0) {
- conn = kmalloc(sizeof(hda_nid_t) * conn_len,
- GFP_KERNEL);
+ conn = kmalloc_array(conn_len,
+ sizeof(hda_nid_t),
+ GFP_KERNEL);
if (!conn)
return;
if (snd_hda_get_raw_connections(codec, nid, conn,
@@ -909,7 +930,6 @@ static void print_codec_info(struct snd_info_entry *entry,
kfree(conn);
}
- snd_hda_power_down(codec);
}
/*
@@ -918,15 +938,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->core.addr);
- err = snd_card_proc_new(codec->card, name, &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, codec, print_codec_info);
- return 0;
+ return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
}
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/hda/common/sysfs.c
index 9b7efece4484..f8c8483fd5e5 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/hda/common/sysfs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sysfs interface for HD-audio codec
*
@@ -14,7 +15,7 @@
#include <linux/string.h>
#include <linux/export.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>
@@ -25,14 +26,13 @@ struct hda_hint {
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 sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
}
static ssize_t power_off_acct_show(struct device *dev,
@@ -41,12 +41,11 @@ static ssize_t power_off_acct_show(struct device *dev,
{
struct hda_codec *codec = dev_get_drvdata(dev);
snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+ 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, \
@@ -54,7 +53,7 @@ static ssize_t type##_show(struct device *dev, \
char *buf) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
- return sprintf(buf, "0x%x\n", codec->field); \
+ return sysfs_emit(buf, "0x%x\n", codec->field); \
}
#define CODEC_INFO_STR_SHOW(type, field) \
@@ -63,8 +62,8 @@ static ssize_t type##_show(struct device *dev, \
char *buf) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
- return sprintf(buf, "%s\n", \
- codec->field ? codec->field : ""); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->field ? codec->field : ""); \
}
CODEC_INFO_SHOW(vendor_id, core.vendor_id);
@@ -80,14 +79,14 @@ 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);
- 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);
+
+ guard(mutex)(&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;
}
@@ -130,21 +129,18 @@ static int reconfig_codec(struct hda_codec *codec)
{
int err;
- snd_hda_power_up(codec);
+ CLASS(snd_hda_power, pm)(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;
+ return err;
}
- err = snd_hda_codec_configure(codec);
+ 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;
+ return err;
+ return snd_card_register(codec->card);
}
/*
@@ -217,15 +213,14 @@ static ssize_t init_verbs_show(struct device *dev,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_verb *v;
int i, len = 0;
- mutex_lock(&codec->user_mutex);
- 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);
+
+ guard(mutex)(&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;
}
@@ -238,16 +233,13 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf)
return -EINVAL;
if (!nid || !verb)
return -EINVAL;
- mutex_lock(&codec->user_mutex);
+ guard(mutex)(&codec->user_mutex);
v = snd_array_new(&codec->init_verbs);
- if (!v) {
- mutex_unlock(&codec->user_mutex);
+ if (!v)
return -ENOMEM;
- }
v->nid = nid;
v->verb = verb;
v->param = param;
- mutex_unlock(&codec->user_mutex);
return 0;
}
@@ -267,23 +259,23 @@ static ssize_t hints_show(struct device *dev,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_hint *hint;
int i, len = 0;
- mutex_lock(&codec->user_mutex);
- 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);
+
+ guard(mutex)(&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;
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ snd_array_for_each(&codec->hints, i, hint) {
if (!strcmp(hint->key, key))
return hint;
}
@@ -307,9 +299,9 @@ static void remove_trail_spaces(char *str)
static int parse_hints(struct hda_codec *codec, const char *buf)
{
- char *key, *val;
+ char *key __free(kfree) = NULL;
+ char *val;
struct hda_hint *hint;
- int err = 0;
buf = skip_spaces(buf);
if (!*buf || *buf == '#' || *buf == '\n')
@@ -321,39 +313,29 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
return -ENOMEM;
/* extract key and val */
val = strchr(key, '=');
- if (!val) {
- kfree(key);
+ if (!val)
return -EINVAL;
- }
*val++ = 0;
val = skip_spaces(val);
remove_trail_spaces(key);
remove_trail_spaces(val);
- mutex_lock(&codec->user_mutex);
+ guard(mutex)(&codec->user_mutex);
hint = get_hint(codec, key);
if (hint) {
/* replace */
kfree(hint->key);
- hint->key = key;
- hint->val = val;
- goto unlock;
+ goto replace;
}
/* 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;
+ return -ENOMEM;
+ hint = snd_array_new(&codec->hints);
+ if (!hint)
+ return -ENOMEM;
+ replace:
+ hint->key = no_free_ptr(key);
+ hint->val = val;
+ return 0;
}
static ssize_t hints_store(struct device *dev,
@@ -375,20 +357,16 @@ static ssize_t user_pin_configs_show(struct device *dev,
return pin_configs_show(codec, &codec->user_pins, buf);
}
-#define MAX_PIN_CONFIGS 32
-
static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
{
- int nid, cfg, err;
+ int nid, cfg;
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;
+ guard(mutex)(&codec->user_mutex);
+ return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
}
static ssize_t user_pin_configs_store(struct device *dev,
@@ -436,26 +414,19 @@ EXPORT_SYMBOL_GPL(snd_hda_get_hint);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
const char *p;
- int ret;
- mutex_lock(&codec->user_mutex);
+ guard(mutex)(&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;
- }
+ return -ENOENT;
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ return 1;
+ default:
+ return 0;
}
- mutex_unlock(&codec->user_mutex);
- return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
@@ -473,20 +444,17 @@ 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);
+ guard(mutex)(&codec->user_mutex);
p = snd_hda_get_hint(codec, key);
if (!p)
- ret = -ENOENT;
+ return -ENOENT;
else if (kstrtoul(p, 0, &val))
- ret = -EINVAL;
+ return -EINVAL;
else {
*valp = val;
- ret = 0;
+ return 0;
}
- mutex_unlock(&codec->user_mutex);
- return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
#endif /* CONFIG_SND_HDA_RECONFIG */
@@ -610,7 +578,7 @@ struct hda_patch_item {
void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
};
-static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
[LINE_MODE_CODEC] = {
.tag = "[codec]",
.parser = parse_codec_mode,
@@ -652,7 +620,7 @@ static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
},
};
-/* check the line starting with '[' -- change the parser mode accodingly */
+/* check the line starting with '[' -- change the parser mode accordingly */
static int parse_line_mode(char *buf, struct hda_bus *bus)
{
int i;
@@ -747,10 +715,8 @@ static struct attribute *hda_dev_attrs[] = {
&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,
@@ -783,13 +749,13 @@ void snd_hda_sysfs_init(struct hda_codec *codec)
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 */
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ snd_array_for_each(&codec->hints, i, hint) {
kfree(hint->key); /* we don't need to free hint->val */
}
snd_array_free(&codec->hints);
diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig
new file mode 100644
index 000000000000..34721f50b055
--- /dev/null
+++ b/sound/hda/controllers/Kconfig
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only
+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.
+
+ This option enables the HD-audio controller. Don't forget
+ to choose the appropriate HD-audio codec options.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 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.
+
+config SND_HDA_ACPI
+ tristate "HD Audio ACPI"
+ depends on ACPI
+ select SND_HDA
+ help
+ Say Y here to include support for Azalia-compatible HDA controllers
+ which are advertised via ACPI objects.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-acpi.
diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile
new file mode 100644
index 000000000000..a4bcd055e9ae
--- /dev/null
+++ b/sound/hda/controllers/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-intel-y := intel.o
+snd-hda-tegra-y := tegra.o
+snd-hda-acpi-y := acpi.o
+
+subdir-ccflags-y += -I$(src)/../common
+
+# for trace-points
+CFLAGS_intel.o := -I$(src)
+
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
+obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o
diff --git a/sound/hda/controllers/acpi.c b/sound/hda/controllers/acpi.c
new file mode 100644
index 000000000000..505cc97e0ee9
--- /dev/null
+++ b/sound/hda/controllers/acpi.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA driver for ACPI-based HDA Controllers.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include <sound/hda_codec.h>
+
+#include "hda_controller.h"
+
+struct hda_acpi {
+ struct azx azx;
+ struct snd_card *card;
+ struct platform_device *pdev;
+ void __iomem *regs;
+ struct work_struct probe_work;
+ const struct hda_data *data;
+};
+
+/**
+ * struct hda_data - Optional device-specific data
+ * @short_name: Used for the ALSA card name; defaults to KBUILD_MODNAME
+ * @long_name: Used for longer description; defaults to short_name
+ * @flags: Passed to &azx->driver_caps
+ *
+ * A pointer to a record of this type may be stored in the
+ * &acpi_device_id->driver_data field of an ACPI match table entry in order to
+ * customize the naming and behavior of a particular device. All fields are
+ * optional and sensible defaults will be selected in their absence.
+ */
+struct hda_data {
+ const char *short_name;
+ const char *long_name;
+ unsigned long flags;
+};
+
+static int hda_acpi_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
+static int hda_acpi_dev_free(struct snd_device *device)
+{
+ struct azx *azx = device->device_data;
+ struct hda_acpi *hda = container_of(azx, struct hda_acpi, azx);
+
+ cancel_work_sync(&hda->probe_work);
+ if (azx_bus(azx)->chip_init) {
+ azx_stop_all_streams(azx);
+ azx_stop_chip(azx);
+ }
+
+ azx_free_stream_pages(azx);
+ azx_free_streams(azx);
+ snd_hdac_bus_exit(azx_bus(azx));
+
+ return 0;
+}
+
+static int hda_acpi_init(struct hda_acpi *hda)
+{
+ struct hdac_bus *bus = azx_bus(&hda->azx);
+ struct snd_card *card = hda->azx.card;
+ struct device *dev = &hda->pdev->dev;
+ struct azx *azx = &hda->azx;
+ struct resource *res;
+ unsigned short gcap;
+ const char *sname, *lname;
+ int err, irq;
+
+ /* The base address for the HDA registers and the interrupt are wrapped
+ * in an ACPI _CRS object which can be parsed by platform_get_irq() and
+ * devm_platform_get_and_ioremap_resource()
+ */
+
+ irq = platform_get_irq(hda->pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ hda->regs = devm_platform_get_and_ioremap_resource(hda->pdev, 0, &res);
+ if (IS_ERR(hda->regs))
+ return PTR_ERR(hda->regs);
+
+ bus->remap_addr = hda->regs;
+ bus->addr = res->start;
+
+ err = devm_request_irq(dev, irq, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, azx);
+ if (err) {
+ dev_err(dev, "unable to request IRQ %d, disabling device\n",
+ irq);
+ return err;
+ }
+ bus->irq = irq;
+ bus->dma_stop_delay = 100;
+ card->sync_irq = bus->irq;
+
+ gcap = azx_readw(azx, GCAP);
+ dev_dbg(dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ azx->align_buffer_size = 1;
+
+ azx->capture_streams = (gcap >> 8) & 0x0f;
+ azx->playback_streams = (gcap >> 12) & 0x0f;
+
+ azx->capture_index_offset = 0;
+ azx->playback_index_offset = azx->capture_streams;
+ azx->num_streams = azx->playback_streams + azx->capture_streams;
+
+ err = azx_init_streams(azx);
+ if (err < 0) {
+ dev_err(dev, "failed to initialize streams: %d\n", err);
+ return err;
+ }
+
+ err = azx_alloc_stream_pages(azx);
+ if (err < 0) {
+ dev_err(dev, "failed to allocate stream pages: %d\n", err);
+ return err;
+ }
+
+ azx_init_chip(azx, 1);
+
+ if (!bus->codec_mask) {
+ dev_err(dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ strscpy(card->driver, "hda-acpi");
+
+ sname = hda->data->short_name ? hda->data->short_name : KBUILD_MODNAME;
+
+ if (strlen(sname) > sizeof(card->shortname))
+ dev_info(dev, "truncating shortname for card %s\n", sname);
+ strscpy(card->shortname, sname);
+
+ lname = hda->data->long_name ? hda->data->long_name : sname;
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i", lname, bus->addr, bus->irq);
+
+ return 0;
+}
+
+static void hda_acpi_probe_work(struct work_struct *work)
+{
+ struct hda_acpi *hda = container_of(work, struct hda_acpi, probe_work);
+ struct azx *chip = &hda->azx;
+ int err;
+
+ err = hda_acpi_init(hda);
+ if (err < 0)
+ return;
+
+ err = azx_probe_codecs(chip, 8);
+ if (err < 0)
+ return;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ return;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ return;
+
+ chip->running = 1;
+}
+
+static int hda_acpi_create(struct hda_acpi *hda)
+{
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = hda_acpi_dev_disconnect,
+ .dev_free = hda_acpi_dev_free,
+ };
+ static const struct hda_controller_ops null_ops;
+ struct azx *azx = &hda->azx;
+ int err;
+
+ mutex_init(&azx->open_mutex);
+ azx->card = hda->card;
+ INIT_LIST_HEAD(&azx->pcm_list);
+
+ azx->ops = &null_ops;
+ azx->driver_caps = hda->data->flags;
+ azx->driver_type = hda->data->flags & 0xff;
+ azx->codec_probe_mask = -1;
+
+ err = azx_bus_init(azx, NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_device_new(hda->card, SNDRV_DEV_LOWLEVEL, &hda->azx, &ops);
+ if (err < 0) {
+ dev_err(&hda->pdev->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int hda_acpi_probe(struct platform_device *pdev)
+{
+ struct hda_acpi *hda;
+ int err;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+
+ hda->pdev = pdev;
+ hda->data = acpi_device_get_match_data(&pdev->dev);
+
+ /* Fall back to defaults if the table didn't have a *struct hda_data */
+ if (!hda->data)
+ hda->data = devm_kzalloc(&pdev->dev, sizeof(*hda->data),
+ GFP_KERNEL);
+ if (!hda->data)
+ return -ENOMEM;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &hda->card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ INIT_WORK(&hda->probe_work, hda_acpi_probe_work);
+
+ err = hda_acpi_create(hda);
+ if (err < 0)
+ goto out_free;
+ hda->card->private_data = &hda->azx;
+
+ dev_set_drvdata(&pdev->dev, hda->card);
+
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(hda->card);
+ return err;
+}
+
+static void hda_acpi_remove(struct platform_device *pdev)
+{
+ snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static void hda_acpi_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 int hda_acpi_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 hda_acpi_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 const struct dev_pm_ops hda_acpi_pm = {
+ SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume)
+};
+
+static const struct hda_data nvidia_hda_data = {
+ .short_name = "NVIDIA",
+ .long_name = "NVIDIA HDA Controller",
+ .flags = AZX_DCAPS_CORBRP_SELF_CLEAR,
+};
+
+static const struct acpi_device_id hda_acpi_match[] = {
+ { .id = "NVDA2014", .driver_data = (uintptr_t) &nvidia_hda_data },
+ { .id = "NVDA2015", .driver_data = (uintptr_t) &nvidia_hda_data },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, hda_acpi_match);
+
+static struct platform_driver hda_acpi_platform_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &hda_acpi_pm,
+ .acpi_match_table = hda_acpi_match,
+ },
+ .probe = hda_acpi_probe,
+ .remove = hda_acpi_remove,
+ .shutdown = hda_acpi_shutdown,
+};
+module_platform_driver(hda_acpi_platform_driver);
+
+MODULE_DESCRIPTION("Driver for ACPI-based HDA Controllers");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_intel.c b/sound/hda/controllers/intel.c
index c71dcacea807..48c52a207024 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/hda/controllers/intel.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* hda_intel.c - Implementation of primary alsa driver code base
* for Intel HD Audio.
*
- * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ * Copyright(c) 2004 Intel Corporation
*
* 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,7 +18,6 @@
* CHANGES:
*
* 2004.12.01 Major rewrite by tiwai, merged the work of pshou
- *
*/
#include <linux/delay.h>
@@ -49,10 +35,12 @@
#include <linux/clocksource.h>
#include <linux/time.h>
#include <linux/completion.h>
+#include <linux/acpi.h>
+#include <linux/pgtable.h>
+#include <linux/dmi.h>
#ifdef CONFIG_X86
/* for snoop control */
-#include <asm/pgtable.h>
#include <asm/set_memory.h>
#include <asm/cpufeature.h>
#endif
@@ -60,15 +48,16 @@
#include <sound/initval.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 "hda_codec.h"
-#include "hda_controller.h"
-#include "hda_intel.h"
+#include <sound/hda_codec.h>
+#include "intel.h"
#define CREATE_TRACE_POINTS
-#include "hda_intel_trace.h"
+#include "intel_trace.h"
/* position fix mode */
enum {
@@ -78,6 +67,7 @@ enum {
POS_FIX_VIACOMBO,
POS_FIX_COMBO,
POS_FIX_SKL,
+ POS_FIX_FIFO,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -97,11 +87,6 @@ enum {
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
-/* Define IN stream 0 FIFO size offset in VIA controller */
-#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
-/* Define VIA HD Audio Device ID*/
-#define VIA_HDAC_DEVICE_ID 0x3288
-
/* max number of SDs */
/* ICH, ATI and VIA have 4 playback and 4 capture */
#define ICH6_NUM_CAPTURE 4
@@ -115,10 +100,6 @@ enum {
#define ATIHDMI_NUM_CAPTURE 0
#define ATIHDMI_NUM_PLAYBACK 8
-/* TERA has 4 playback and 3 capture */
-#define TERA_NUM_CAPTURE 3
-#define TERA_NUM_PLAYBACK 4
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -138,6 +119,8 @@ static char *patch[SNDRV_CARDS];
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.");
@@ -149,7 +132,7 @@ 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."
- "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+).");
+ "(-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);
@@ -172,6 +155,12 @@ module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
"(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);
@@ -186,6 +175,10 @@ module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
+static int pm_blacklist = -1;
+module_param(pm_blacklist, bint, 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.
@@ -193,8 +186,10 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
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.");
-#else
+#else /* CONFIG_PM */
#define power_save 0
+#define pm_blacklist 0
+#define power_save_controller false
#endif /* CONFIG_PM */
static int align_buffer_size = -1;
@@ -212,40 +207,6 @@ MODULE_PARM_DESC(snoop, "Enable/disable snooping");
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, PPT},"
- "{Intel, LPT},"
- "{Intel, LPT_LP},"
- "{Intel, WPT_LP},"
- "{Intel, SPT},"
- "{Intel, SPT_LP},"
- "{Intel, HPT},"
- "{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");
#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO)
@@ -268,6 +229,7 @@ enum {
AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI,
AZX_DRIVER_ATIHDMI_NS,
+ AZX_DRIVER_GFHDMI,
AZX_DRIVER_VIA,
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
@@ -276,6 +238,9 @@ enum {
AZX_DRIVER_CTX,
AZX_DRIVER_CTHDA,
AZX_DRIVER_CMEDIA,
+ AZX_DRIVER_ZHAOXIN,
+ AZX_DRIVER_ZHAOXINHDMI,
+ AZX_DRIVER_LOONGSON,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -306,46 +271,50 @@ enum {
#define AZX_DCAPS_INTEL_HASWELL \
(/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\
AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
- AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH))
+ 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_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH))
+ AZX_DCAPS_SNOOP_TYPE(SCH))
#define AZX_DCAPS_INTEL_BAYTRAIL \
- (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT |\
- AZX_DCAPS_I915_POWERWELL)
+ (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 | AZX_DCAPS_I915_POWERWELL)
+ 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 |\
- AZX_DCAPS_I915_POWERWELL)
+ AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT)
-#define AZX_DCAPS_INTEL_BROXTON \
- (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
- AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\
- AZX_DCAPS_I915_POWERWELL)
+#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE
+
+#define AZX_DCAPS_INTEL_LNL \
+ (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS)
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
- (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\
+ (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_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\
+ (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 |\
@@ -361,18 +330,13 @@ enum {
*/
#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)->device == 0x0a0c) || \
- ((pci)->device == 0x0c0c) || \
- ((pci)->device == 0x0d0c) || \
- ((pci)->device == 0x160c))
-
-#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
-
-static char *driver_short_names[] = {
+static const char * const driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
[AZX_DRIVER_SCH] = "HDA Intel MID",
@@ -381,6 +345,7 @@ static char *driver_short_names[] = {
[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",
@@ -389,65 +354,14 @@ static char *driver_short_names[] = {
[AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_CMEDIA] = "HDA C-Media",
+ [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
+ [AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI",
+ [AZX_DRIVER_LOONGSON] = "HDA Loongson",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
-#ifdef CONFIG_X86
-static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
-{
- int pages;
-
- if (azx_snoop(chip))
- return;
- if (!dmab || !dmab->area || !dmab->bytes)
- return;
-
-#ifdef CONFIG_SND_DMA_SGBUF
- if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
- struct snd_sg_buf *sgbuf = dmab->private_data;
- if (chip->driver_type == AZX_DRIVER_CMEDIA)
- return; /* deal with only CORB/RIRB buffers */
- if (on)
- set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
- else
- set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
- return;
- }
-#endif
-
- pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
- if (on)
- set_memory_wc((unsigned long)dmab->area, pages);
- else
- set_memory_wb((unsigned long)dmab->area, pages);
-}
-
-static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
- bool on)
-{
- __mark_pages_wc(chip, buf, on);
-}
-static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
- struct snd_pcm_substream *substream, bool on)
-{
- if (azx_dev->wc_marked != on) {
- __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
- azx_dev->wc_marked = on;
- }
-}
-#else
-/* NOP for other archs */
-static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
- bool on)
-{
-}
-static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
- struct snd_pcm_substream *substream, bool on)
-{
-}
-#endif
-
static int azx_acquire_irq(struct azx *chip, int do_disconnect);
+static void set_default_power_save(struct azx *chip);
/*
* initialize the PCI registers
@@ -552,7 +466,7 @@ static void bxt_reduce_dma_latency(struct azx *chip)
static int intel_get_lctl_scf(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
- static int preferred_bits[] = { 2, 3, 1, 4, 5 };
+ static const int preferred_bits[] = { 2, 3, 1, 4, 5 };
u32 val, t;
int i;
@@ -575,18 +489,18 @@ static int intel_ml_lctl_set_power(struct azx *chip, int state)
int timeout;
/*
- * the codecs are sharing the first link setting by default
- * If other links are enabled for stream, they need similar fix
+ * 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_MLCTL_SPA;
- val |= state << AZX_MLCTL_SPA_SHIFT;
+ 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_MLCTL_CPA) == (state << AZX_MLCTL_CPA_SHIFT))
+ AZX_ML_LCTL_CPA) == (state << AZX_ML_LCTL_CPA_SHIFT))
return 0;
timeout--;
udelay(10);
@@ -603,16 +517,16 @@ static void intel_init_lctl(struct azx *chip)
/* 0. check lctl register value is correct or not */
val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
- /* if SCF is already set, let's use it */
- if ((val & ML_LCTL_SCF_MASK) != 0)
+ /* only perform additional configurations if the SCF is initially based on 6MHz */
+ if ((val & AZX_ML_LCTL_SCF) != 0)
return;
/*
* Before operating on SPA, CPA must match SPA.
* Any deviation may result in undefined behavior.
*/
- if (((val & AZX_MLCTL_SPA) >> AZX_MLCTL_SPA_SHIFT) !=
- ((val & AZX_MLCTL_CPA) >> AZX_MLCTL_CPA_SHIFT))
+ if (((val & AZX_ML_LCTL_SPA) >> AZX_ML_LCTL_SPA_SHIFT) !=
+ ((val & AZX_ML_LCTL_CPA) >> AZX_ML_LCTL_CPA_SHIFT))
return;
/* 1. turn link down: set SPA to 0 and wait CPA to 0 */
@@ -621,8 +535,8 @@ static void intel_init_lctl(struct azx *chip)
if (ret)
goto set_spa;
- /* 2. update SCF to select a properly audio clock*/
- val &= ~ML_LCTL_SCF_MASK;
+ /* 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);
@@ -638,8 +552,7 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)
struct pci_dev *pci = chip->pci;
u32 val;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- snd_hdac_set_codec_wakeup(bus, true);
+ 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;
@@ -651,11 +564,11 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)
val = val | INTEL_HDA_CGCTL_MISCBDCGE;
pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
}
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- snd_hdac_set_codec_wakeup(bus, false);
+
+ snd_hdac_set_codec_wakeup(bus, false);
/* reduce dma latency to avoid noise */
- if (IS_BXT(pci))
+ if (HDA_CONTROLLER_IS_APL(pci))
bxt_reduce_dma_latency(chip);
if (bus->mlcap != NULL)
@@ -714,13 +627,8 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
-/* Enable/disable i915 display power for the link */
-static int azx_intel_link_power(struct azx *chip, bool enable)
-{
- struct hdac_bus *bus = azx_bus(chip);
-
- return snd_hdac_display_power(bus, enable);
-}
+#define display_power(chip, enable) \
+ snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable)
/*
* Check whether the current DMA position is acceptable for updating
@@ -730,13 +638,24 @@ static int azx_intel_link_power(struct azx *chip, bool enable)
* the update-IRQ timing. The IRQ is issued before actually the
* data is processed. So, we need to process it afterwords in a
* workqueue.
+ *
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
*/
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
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;
+
+ /*
+ * The value of the WALLCLK register is always 0
+ * on the Loongson controller, so we return directly.
+ */
+ if (chip->driver_type == AZX_DRIVER_LOONGSON)
+ return 1;
wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
@@ -773,6 +692,24 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
/* 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;
+ }
+
return 1; /* OK, it's fine */
}
@@ -827,17 +764,24 @@ static void azx_clear_irq_pending(struct azx *chip)
struct hdac_bus *bus = azx_bus(chip);
struct hdac_stream *s;
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_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);
}
static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
struct hdac_bus *bus = azx_bus(chip);
+ int ret;
+
+ if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) {
+ ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX);
+ if (ret < 0)
+ return ret;
+ chip->msi = 0;
+ }
if (request_irq(chip->pci->irq, azx_interrupt,
chip->msi ? 0 : IRQF_SHARED,
@@ -850,7 +794,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
return -1;
}
bus->irq = chip->pci->irq;
- pci_intx(chip->pci, !chip->msi);
+ chip->card->sync_irq = bus->irq;
return 0;
}
@@ -875,11 +819,7 @@ static unsigned int azx_via_get_position(struct azx *chip,
mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
mod_dma_pos %= azx_dev->core.period_bytes;
- /* azx_dev->fifo_size can't get FIFO size of in stream.
- * Get from base address + offset.
- */
- fifo_size = readw(azx_bus(chip)->remap_addr +
- VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
+ fifo_size = azx_stream(azx_dev)->fifo_size;
if (azx_dev->insufficient) {
/* Link position never gather than FIFO size */
@@ -911,53 +851,84 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos;
}
-static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
- struct azx_dev *azx_dev)
+#define AMD_FIFO_SIZE 32
+
+/* get the current DMA position with FIFO size correction */
+static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev)
{
- return _snd_hdac_chip_readl(azx_bus(chip),
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- azx_dev->core.index));
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int pos, delay;
+
+ pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (!runtime)
+ return pos;
+
+ 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;
+ }
+ }
+
+ /* correct the DMA position for capture stream */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (pos < delay)
+ pos += azx_dev->core.bufsize;
+ pos -= delay;
+ }
+
+ return pos;
}
-/* get the current DMA position with correction on SKL+ chips */
-static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
+static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
+ unsigned int pos)
{
- /* DPIB register gives a more accurate position for playback */
- if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return azx_skl_get_dpib_pos(chip, azx_dev);
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
- /* For capture, we need to read posbuf, but it requires a delay
- * for the possible boundary overlap; the read of DPIB fetches the
- * actual posbuf
- */
- udelay(20);
- azx_skl_get_dpib_pos(chip, azx_dev);
- return azx_get_pos_posbuf(chip, azx_dev);
+ /* just read back the calculated value in the above */
+ return substream->runtime->delay;
+}
+
+static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
+{
+ azx_stop_chip(chip);
+ if (!skip_link_reset)
+ azx_enter_link_reset(chip);
+ azx_clear_irq_pending(chip);
+ display_power(chip, false);
}
-#ifdef CONFIG_PM
static DEFINE_MUTEX(card_list_lock);
static LIST_HEAD(card_list);
+static void azx_shutdown_chip(struct azx *chip)
+{
+ __azx_shutdown_chip(chip, false);
+}
+
static void azx_add_card_list(struct azx *chip)
{
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
- mutex_lock(&card_list_lock);
+
+ guard(mutex)(&card_list_lock);
list_add(&hda->list, &card_list);
- mutex_unlock(&card_list_lock);
}
static void azx_del_card_list(struct azx *chip)
{
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
- mutex_lock(&card_list_lock);
+
+ guard(mutex)(&card_list_lock);
list_del_init(&hda->list);
- mutex_unlock(&card_list_lock);
}
/* trigger power-save check at writing parameter */
-static int param_set_xint(const char *val, const struct kernel_param *kp)
+static int __maybe_unused param_set_xint(const char *val, const struct kernel_param *kp)
{
struct hda_intel *hda;
struct azx *chip;
@@ -967,105 +938,134 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
if (ret || prev == power_save)
return ret;
- mutex_lock(&card_list_lock);
+ if (pm_blacklist > 0)
+ return 0;
+
+ guard(mutex)(&card_list_lock);
list_for_each_entry(hda, &card_list, list) {
chip = &hda->chip;
- if (!hda->probe_continued || chip->disabled)
+ if (!hda->probe_continued || chip->disabled ||
+ hda->runtime_pm_disabled)
continue;
snd_hda_set_power_save(&chip->bus, power_save * 1000);
}
- mutex_unlock(&card_list_lock);
return 0;
}
-#else
-#define azx_add_card_list(chip) /* NOP */
-#define azx_del_card_list(chip) /* NOP */
-#endif /* CONFIG_PM */
-#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
/*
* power management
*/
-static int azx_suspend(struct device *dev)
+static bool azx_is_pm_ready(struct snd_card *card)
{
- struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
- struct hdac_bus *bus;
if (!card)
- return 0;
-
+ 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_runtime_resume(struct azx *chip)
+{
+ 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);
+
+ azx_init_pci(chip);
+ hda_intel_init_chip(chip, true);
+
+ /* 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;
+
+ if (codec->forced_resume || (status & (1 << codec->addr)))
+ pm_request_resume(hda_codec_dev(codec));
+ }
+ }
+
+ /* power down again for link-controlled chips */
+ if (!hda->need_i915_power)
+ display_power(chip, false);
+}
+
+static int azx_prepare(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
return 0;
- bus = azx_bus(chip);
+ chip = card->private_data;
+ chip->pm_prepared = 1;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- azx_clear_irq_pending(chip);
- azx_stop_chip(chip);
- azx_enter_link_reset(chip);
- if (bus->irq >= 0) {
- free_irq(bus->irq, chip);
- bus->irq = -1;
- }
- if (chip->msi)
- pci_disable_msi(chip->pci);
- if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- && hda->need_i915_power)
- snd_hdac_display_power(bus, false);
+ flush_work(&azx_bus(chip)->unsol_work);
- trace_azx_suspend(chip);
+ /* HDA controller always requires different WAKEEN for runtime suspend
+ * and system suspend, so don't use direct-complete here.
+ */
return 0;
}
-static int azx_resume(struct device *dev)
+static void azx_complete(struct device *dev)
{
- struct pci_dev *pci = to_pci_dev(dev);
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
- struct hda_intel *hda;
- struct hdac_bus *bus;
- if (!card)
- return 0;
+ if (!azx_is_pm_ready(card))
+ return;
chip = card->private_data;
- hda = container_of(chip, struct hda_intel, chip);
- bus = azx_bus(chip);
- if (chip->disabled || hda->init_failed || !chip->running)
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ chip->pm_prepared = 0;
+}
+
+static int azx_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- snd_hdac_display_power(bus, true);
- if (hda->need_i915_power)
- snd_hdac_i915_set_bclk(bus);
- }
+ chip = card->private_data;
+ azx_shutdown_chip(chip);
- 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);
+ trace_azx_suspend(chip);
+ return 0;
+}
- hda_intel_init_chip(chip, true);
+static int azx_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
- /* power down again for link-controlled chips */
- if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) &&
- !hda->need_i915_power)
- snd_hdac_display_power(bus, false);
+ if (!azx_is_pm_ready(card))
+ return 0;
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ chip = card->private_data;
+
+ __azx_runtime_resume(chip);
trace_azx_resume(chip);
return 0;
}
-#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
-#ifdef CONFIG_PM_SLEEP
/* put codec down to D3 at hibernation for Intel SKL+;
* otherwise BIOS may still access the codec and screw up the driver
*/
@@ -1075,6 +1075,8 @@ static int azx_freeze_noirq(struct device *dev)
struct azx *chip = card->private_data;
struct pci_dev *pci = to_pci_dev(dev);
+ if (!azx_is_pm_ready(card))
+ return 0;
if (chip->driver_type == AZX_DRIVER_SKL)
pci_set_power_state(pci, PCI_D3hot);
@@ -1087,42 +1089,27 @@ static int azx_thaw_noirq(struct device *dev)
struct azx *chip = card->private_data;
struct pci_dev *pci = to_pci_dev(dev);
+ if (!azx_is_pm_ready(card))
+ return 0;
if (chip->driver_type == AZX_DRIVER_SKL)
pci_set_power_state(pci, PCI_D0);
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int azx_runtime_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
- struct hda_intel *hda;
- if (!card)
+ if (!azx_is_pm_ready(card))
return 0;
-
chip = card->private_data;
- hda = container_of(chip, struct hda_intel, chip);
- if (chip->disabled || hda->init_failed)
- return 0;
-
- if (!azx_has_pm_runtime(chip))
- return 0;
/* 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);
- azx_clear_irq_pending(chip);
- if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- && hda->need_i915_power)
- snd_hdac_display_power(azx_bus(chip), false);
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK);
+ azx_shutdown_chip(chip);
trace_azx_runtime_suspend(chip);
return 0;
}
@@ -1131,50 +1118,14 @@ static int azx_runtime_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
- struct hda_intel *hda;
- struct hdac_bus *bus;
- struct hda_codec *codec;
- int status;
- if (!card)
+ if (!azx_is_pm_ready(card))
return 0;
-
chip = card->private_data;
- hda = container_of(chip, struct hda_intel, chip);
- bus = azx_bus(chip);
- if (chip->disabled || hda->init_failed)
- return 0;
-
- if (!azx_has_pm_runtime(chip))
- return 0;
-
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- snd_hdac_display_power(bus, true);
- if (hda->need_i915_power)
- snd_hdac_i915_set_bclk(bus);
- }
-
- /* Read STATESTS before controller reset */
- status = azx_readw(chip, STATESTS);
-
- azx_init_pci(chip);
- hda_intel_init_chip(chip, true);
-
- if (status) {
- list_for_each_codec(codec, &chip->bus)
- if (status & (1 << codec->addr))
- schedule_delayed_work(&codec->jackpoll_work,
- codec->jackpoll_interval);
- }
+ __azx_runtime_resume(chip);
/* disable controller Wake Up event*/
- azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
- ~STATESTS_INT_MASK);
-
- /* power down again for link-controlled chips */
- if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) &&
- !hda->need_i915_power)
- snd_hdac_display_power(bus, false);
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);
trace_azx_runtime_resume(chip);
return 0;
@@ -1198,23 +1149,22 @@ static int azx_runtime_idle(struct device *dev)
azx_bus(chip)->codec_powered || !chip->running)
return -EBUSY;
+ /* ELD notification gets broken when HD-audio bus is off */
+ if (needs_eld_notify_link(chip))
+ return -EBUSY;
+
return 0;
}
static const struct dev_pm_ops azx_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
-#ifdef CONFIG_PM_SLEEP
- .freeze_noirq = azx_freeze_noirq,
- .thaw_noirq = azx_thaw_noirq,
-#endif
- SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
+ SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+ .prepare = pm_sleep_ptr(azx_prepare),
+ .complete = pm_sleep_ptr(azx_complete),
+ .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq),
+ .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq),
+ RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
};
-#define AZX_PM_OPS &azx_pm
-#else
-#define AZX_PM_OPS NULL
-#endif /* CONFIG_PM */
-
static int azx_probe_continue(struct azx *chip);
@@ -1227,6 +1177,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
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;
wait_for_completion(&hda->probe_wait);
@@ -1242,17 +1193,19 @@ static void azx_vs_set_state(struct pci_dev *pci,
if (!disabled) {
dev_info(chip->card->dev,
"Start delayed initialization\n");
- if (azx_probe_continue(chip) < 0) {
+ if (azx_probe_continue(chip) < 0)
dev_err(chip->card->dev, "initialization error\n");
- hda->init_failed = true;
- }
}
} else {
dev_info(chip->card->dev, "%s via vga_switcheroo\n",
disabled ? "Disabling" : "Enabling");
if (disabled) {
- pm_runtime_put_sync_suspend(card->dev);
- azx_suspend(card->dev);
+ 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 */
@@ -1263,9 +1216,12 @@ static void azx_vs_set_state(struct pci_dev *pci,
"Cannot lock devices!\n");
} else {
snd_hda_unlock_devices(&chip->bus);
- pm_runtime_get_noresume(card->dev);
chip->disabled = false;
- azx_resume(card->dev);
+ 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));
+ }
}
}
}
@@ -1287,14 +1243,51 @@ static bool azx_vs_can_switch(struct pci_dev *pci)
return true;
}
+/*
+ * The discrete GPU cannot power down unless the HDA controller runtime
+ * suspends, so activate runtime PM on codecs even if power_save == 0.
+ */
+static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hda_codec *codec;
+
+ 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);
+ }
+}
+
+static void azx_vs_gpu_bound(struct pci_dev *pci,
+ enum vga_switcheroo_client_id client_id)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+
+ if (client_id == VGA_SWITCHEROO_DIS)
+ chip->bus.keep_power = 0;
+ setup_vga_switcheroo_runtime_pm(chip);
+}
+
static void init_vga_switcheroo(struct azx *chip)
{
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;
+
+ /* 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);
}
}
@@ -1302,46 +1295,54 @@ static void init_vga_switcheroo(struct azx *chip)
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 int register_vga_switcheroo(struct azx *chip)
{
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct pci_dev *p;
int err;
if (!hda->use_vga_switcheroo)
return 0;
- /* FIXME: currently only handling DIS controller
- * is there any machine with two switchable HDMI audio controllers?
- */
- err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops,
- VGA_SWITCHEROO_DIS);
+
+ p = get_bound_vga(chip->pci);
+ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p);
+ pci_dev_put(p);
+
if (err < 0)
return err;
hda->vga_switcheroo_registered = 1;
- /* register as an optimus hdmi audio power domain */
- vga_switcheroo_init_domain_pm_optimus_hdmi_audio(chip->card->dev,
- &hda->hdmi_pm_domain);
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 */
/*
* destructor
*/
-static int azx_free(struct azx *chip)
+static void azx_free(struct azx *chip)
{
struct pci_dev *pci = chip->pci;
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
struct hdac_bus *bus = azx_bus(chip);
- if (azx_has_pm_runtime(chip) && chip->running)
+ if (hda->freed)
+ return;
+
+ 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);
+ }
+
+ chip->running = 0;
azx_del_card_list(chip);
@@ -1353,7 +1354,18 @@ static int azx_free(struct azx *chip)
snd_hda_unlock_devices(&chip->bus);
if (hda->vga_switcheroo_registered) {
vga_switcheroo_unregister_client(chip->pci);
- vga_switcheroo_fini_domain_pm_ops(chip->card->dev);
+
+ /* Some GPUs don't have sound, and azx_first_init fails,
+ * leaving the device probed but non-functional. As long
+ * as it's probed, the PCI subsystem keeps its runtime
+ * PM status as active. Force it to suspended (as we
+ * actually stop the chip) to allow GPU to suspend via
+ * vga_switcheroo, and print a warning.
+ */
+ dev_warn(&pci->dev, "GPU sound probed, but not operational: please add a quirk to driver_denylist\n");
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_set_suspended(&pci->dev);
+ pm_runtime_enable(&pci->dev);
}
}
@@ -1365,47 +1377,71 @@ static int azx_free(struct azx *chip)
if (bus->irq >= 0)
free_irq(bus->irq, (void*)chip);
- if (chip->msi)
- pci_disable_msi(chip->pci);
- iounmap(bus->remap_addr);
azx_free_stream_pages(chip);
azx_free_streams(chip);
snd_hdac_bus_exit(bus);
- if (chip->region_requested)
- pci_release_regions(chip->pci);
-
- pci_disable_device(chip->pci);
#ifdef CONFIG_SND_HDA_PATCH_LOADER
release_firmware(chip->fw);
#endif
+ display_power(chip, false);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- if (hda->need_i915_power)
- snd_hdac_display_power(bus, false);
- }
if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT)
snd_hdac_i915_exit(bus);
- kfree(hda);
- return 0;
+ hda->freed = 1;
}
static int azx_dev_disconnect(struct snd_device *device)
{
struct azx *chip = device->device_data;
+ struct hdac_bus *bus = azx_bus(chip);
chip->bus.shutdown = 1;
+ cancel_work_sync(&bus->unsol_work);
+
return 0;
}
static int azx_dev_free(struct snd_device *device)
{
- return azx_free(device->device_data);
+ azx_free(device->device_data);
+ return 0;
}
#ifdef SUPPORT_VGA_SWITCHEROO
+#ifdef CONFIG_ACPI
+/* ATPX is in the integrated GPU's namespace */
+static bool atpx_present(void)
+{
+ struct pci_dev *pdev = NULL;
+ acpi_handle dhandle, atpx_handle;
+ acpi_status status;
+
+ while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
+ if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
+ (pdev->class != PCI_CLASS_DISPLAY_OTHER << 8))
+ continue;
+
+ 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;
+}
+#else
+static bool atpx_present(void)
+{
+ return false;
+}
+#endif
+
/*
* Check of disabled HDMI controller by vga_switcheroo
*/
@@ -1417,12 +1453,28 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci)
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 (pci_is_display(p) &&
+ (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 >> 8) == PCI_CLASS_DISPLAY_VGA)
+ if (pci_is_display(p))
return p;
pci_dev_put(p);
}
@@ -1447,9 +1499,9 @@ static bool check_hdmi_disabled(struct pci_dev *pci)
#endif /* SUPPORT_VGA_SWITCHEROO */
/*
- * white/black-listing for position_fix
+ * allow/deny-listing for position_fix
*/
-static struct snd_pci_quirk position_fix_list[] = {
+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(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
@@ -1478,6 +1530,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_VIACOMBO:
case POS_FIX_COMBO:
case POS_FIX_SKL:
+ case POS_FIX_FIFO:
return fix;
}
@@ -1494,6 +1547,10 @@ static int check_position_fix(struct azx *chip, int fix)
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;
@@ -1507,13 +1564,14 @@ static int check_position_fix(struct azx *chip, int fix)
static void assign_position_fix(struct azx *chip, int fix)
{
- static azx_get_pos_callback_t callbacks[] = {
+ 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_skl,
+ [POS_FIX_SKL] = azx_get_pos_posbuf,
+ [POS_FIX_FIFO] = azx_get_pos_fifo,
};
chip->get_position[0] = chip->get_position[1] = callbacks[fix];
@@ -1528,12 +1586,15 @@ static void assign_position_fix(struct azx *chip, int fix)
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[] = {
+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.
*/
@@ -1547,6 +1608,7 @@ static struct snd_pci_quirk probe_mask_list[] = {
/* 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),
{}
@@ -1579,9 +1641,9 @@ static void check_probe_mask(struct azx *chip, int dev)
}
/*
- * white/black-list for enable_msi
+ * allow/deny-list for enable_msi
*/
-static struct snd_pci_quirk msi_black_list[] = {
+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 */
@@ -1604,7 +1666,7 @@ static void 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) {
dev_info(chip->card->dev,
"msi for device %04x:%04x set to %d\n",
@@ -1629,6 +1691,7 @@ static void azx_check_snoop_available(struct azx *chip)
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;
}
@@ -1640,7 +1703,8 @@ static void azx_check_snoop_available(struct azx *chip)
*/
u8 val;
pci_read_config_byte(chip->pci, 0x42, &val);
- if (!(val & 0x80) && chip->pci->revision == 0x30)
+ if (!(val & 0x80) && (chip->pci->revision == 0x30 ||
+ chip->pci->revision == 0x20))
snoop = false;
}
@@ -1648,13 +1712,17 @@ static void azx_check_snoop_available(struct azx *chip)
snoop = false;
chip->snoop = snoop;
- if (!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);
+ struct hda_intel *hda = container_of(work, struct hda_intel, probe_work.work);
azx_probe_continue(&hda->chip);
}
@@ -1663,16 +1731,26 @@ 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 */
+ case PCI_DEVICE_ID_INTEL_HDA_BYT:
+ case PCI_DEVICE_ID_INTEL_HDA_BSW:
return 32;
+ case PCI_DEVICE_ID_INTEL_HDA_APL:
+ return 64;
}
}
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;
+ case AZX_DRIVER_ZHAOXINHDMI:
+ return 128;
default:
return 32;
}
@@ -1681,14 +1759,13 @@ static int default_bdl_pos_adj(struct azx *chip)
/*
* constructor
*/
-static const struct hdac_io_ops pci_hda_io_ops;
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)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_disconnect = azx_dev_disconnect,
.dev_free = azx_dev_free,
};
@@ -1698,15 +1775,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
*rchip = NULL;
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- hda = kzalloc(sizeof(*hda), GFP_KERNEL);
- if (!hda) {
- pci_disable_device(pci);
+ hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
return -ENOMEM;
- }
chip = &hda->chip;
mutex_init(&chip->open_mutex);
@@ -1717,7 +1792,8 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
chip->driver_type = driver_caps & 0xff;
check_msi(chip);
chip->dev_index = dev;
- chip->jackpoll_ms = jackpoll_ms;
+ 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);
@@ -1726,8 +1802,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
assign_position_fix(chip, check_position_fix(chip, position_fix[dev]));
- check_probe_mask(chip, dev);
-
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 */
@@ -1740,18 +1814,21 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
else
chip->bdl_pos_adj = bdl_pos_adj[dev];
- err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
- if (err < 0) {
- kfree(hda);
- pci_disable_device(pci);
+ 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;
if (chip->driver_type == AZX_DRIVER_NVIDIA) {
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
- chip->bus.needs_damn_long_delay = 1;
+ 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");
@@ -1760,7 +1837,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
}
/* continue probing in work context as may trigger request module */
- INIT_WORK(&hda->probe_work, azx_probe_work);
+ INIT_DELAYED_WORK(&hda->probe_work, azx_probe_work);
*rchip = chip;
@@ -1786,18 +1863,29 @@ static int azx_first_init(struct azx *chip)
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)
- return err;
- chip->region_requested = 1;
+ if (chip->driver_type == AZX_DRIVER_LOONGSON) {
+ bus->polling_mode = 1;
+ bus->not_use_interrupts = 1;
+ bus->access_sdnctl_in_dword = 1;
+ if (!chip->jackpoll_interval)
+ chip->jackpoll_interval = msecs_to_jiffies(1500);
+ }
+
+ if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI)
+ bus->polling_mode = 1;
+
+ bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");
+ if (IS_ERR(bus->remap_addr))
+ return PTR_ERR(bus->remap_addr);
bus->addr = pci_resource_start(pci, 0);
- bus->remap_addr = pci_ioremap_bar(pci, 0);
- if (bus->remap_addr == NULL) {
- dev_err(card->dev, "ioremap error\n");
- return -ENXIO;
- }
if (chip->driver_type == AZX_DRIVER_SKL)
snd_hdac_bus_parse_capabilities(bus);
@@ -1815,20 +1903,12 @@ static int azx_first_init(struct azx *chip)
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 (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+ dev_dbg(card->dev, "Disabling 64bit MSI\n");
+ pci->no_64bit_msi = true;
}
- if (azx_acquire_irq(chip, 0) < 0)
- return -EBUSY;
-
pci_set_master(pci);
- synchronize_irq(bus->irq);
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
@@ -1874,12 +1954,9 @@ static int azx_first_init(struct azx *chip)
/* allow 64bit DMA address if supported by H/W */
if (!(gcap & AZX_GCAP_64OK))
dma_bits = 32;
- if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
- } else {
- dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(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
@@ -1899,6 +1976,8 @@ static int azx_first_init(struct azx *chip)
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
break;
+ case AZX_DRIVER_GFHDMI:
+ case AZX_DRIVER_ZHAOXINHDMI:
case AZX_DRIVER_GENERIC:
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
@@ -1930,19 +2009,21 @@ static int azx_first_init(struct azx *chip)
/* initialize chip */
azx_init_pci(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- snd_hdac_i915_set_bclk(bus);
+ snd_hdac_i915_set_bclk(bus);
hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */
if (!azx_bus(chip)->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
- return -ENODEV;
+ /* keep running the rest for the runtime PM */
}
- strcpy(card->driver, "HDA-Intel");
- strlcpy(card->shortname, driver_short_names[chip->driver_type],
+ if (azx_acquire_irq(chip, 0) < 0)
+ return -EBUSY;
+
+ strscpy(card->driver, "HDA-Intel");
+ strscpy(card->shortname, driver_short_names[chip->driver_type],
sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
@@ -1957,62 +2038,18 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
{
struct snd_card *card = context;
struct azx *chip = card->private_data;
- struct pci_dev *pci = chip->pci;
-
- if (!fw) {
- dev_err(card->dev, "Cannot load firmware, aborting\n");
- goto error;
- }
- chip->fw = fw;
+ if (fw)
+ chip->fw = fw;
+ else
+ dev_err(card->dev, "Cannot load firmware, continue without patching\n");
if (!chip->disabled) {
/* continue probing */
- if (azx_probe_continue(chip))
- goto error;
+ azx_probe_continue(chip);
}
- return; /* OK */
-
- error:
- snd_card_free(card);
- pci_set_drvdata(pci, NULL);
}
#endif
-/*
- * HDA controller ops.
- */
-
-/* PCI register access. */
-static void pci_azx_writel(u32 value, u32 __iomem *addr)
-{
- writel(value, addr);
-}
-
-static u32 pci_azx_readl(u32 __iomem *addr)
-{
- return readl(addr);
-}
-
-static void pci_azx_writew(u16 value, u16 __iomem *addr)
-{
- writew(value, addr);
-}
-
-static u16 pci_azx_readw(u16 __iomem *addr)
-{
- return readw(addr);
-}
-
-static void pci_azx_writeb(u8 value, u8 __iomem *addr)
-{
- writeb(value, addr);
-}
-
-static u8 pci_azx_readb(u8 __iomem *addr)
-{
- return readb(addr);
-}
-
static int disable_msi_reset_irq(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
@@ -2020,7 +2057,8 @@ static int disable_msi_reset_irq(struct azx *chip)
free_irq(bus->irq, chip);
bus->irq = -1;
- pci_disable_msi(chip->pci);
+ chip->card->sync_irq = -1;
+ pci_free_irq_vectors(chip->pci);
chip->msi = 0;
err = azx_acquire_irq(chip, 1);
if (err < 0)
@@ -2029,103 +2067,88 @@ static int disable_msi_reset_irq(struct azx *chip)
return 0;
}
-/* DMA page allocation helpers. */
-static int dma_alloc_pages(struct hdac_bus *bus,
- int type,
- size_t size,
- struct snd_dma_buffer *buf)
-{
- struct azx *chip = bus_to_azx(bus);
- int err;
-
- err = snd_dma_alloc_pages(type,
- bus->dev,
- size, buf);
- if (err < 0)
- return err;
- mark_pages_wc(chip, buf, true);
- return 0;
-}
-
-static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
-{
- struct azx *chip = bus_to_azx(bus);
-
- mark_pages_wc(chip, buf, false);
- snd_dma_free_pages(buf);
-}
-
-static int substream_alloc_pages(struct azx *chip,
- struct snd_pcm_substream *substream,
- size_t size)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
- int ret;
-
- mark_runtime_wc(chip, azx_dev, substream, false);
- ret = snd_pcm_lib_malloc_pages(substream, size);
- if (ret < 0)
- return ret;
- mark_runtime_wc(chip, azx_dev, substream, true);
- return 0;
-}
-
-static int substream_free_pages(struct azx *chip,
- struct snd_pcm_substream *substream)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
- mark_runtime_wc(chip, azx_dev, substream, false);
- return snd_pcm_lib_free_pages(substream);
-}
+/* 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 void pcm_mmap_prepare(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
-{
-#ifdef CONFIG_X86
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- if (!azx_snoop(chip) && chip->driver_type != AZX_DRIVER_CMEDIA)
- area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
-#endif
-}
+static struct pci_device_id driver_denylist_ideapad_z570[] = {
+ { PCI_DEVICE_SUB(0x10de, 0x0bea, 0x0000, 0x0000) }, /* NVIDIA GF108 HDA */
+ {}
+};
-static const struct hdac_io_ops pci_hda_io_ops = {
- .reg_writel = pci_azx_writel,
- .reg_readl = pci_azx_readl,
- .reg_writew = pci_azx_writew,
- .reg_readw = pci_azx_readw,
- .reg_writeb = pci_azx_writeb,
- .reg_readb = pci_azx_readb,
- .dma_alloc_pages = dma_alloc_pages,
- .dma_free_pages = dma_free_pages,
+/* DMI-based denylist, to be used when:
+ * - PCI subsystem IDs are zero, impossible to distinguish from valid sound cards.
+ * - Different modifications of the same laptop use different GPU models.
+ */
+static const struct dmi_system_id driver_denylist_dmi[] = {
+ {
+ /* No HDA in NVIDIA DGPU. BIOS disables it, but quirk_nvidia_hda() reenables. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
+ },
+ .driver_data = &driver_denylist_ideapad_z570,
+ },
+ {}
};
static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq,
- .substream_alloc_pages = substream_alloc_pages,
- .substream_free_pages = substream_free_pages,
- .pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check,
- .link_power = azx_intel_link_power,
};
+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;
+ const struct dmi_system_id *dmi;
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;
+ }
+
+ dmi = dmi_first_match(driver_denylist_dmi);
+ if (dmi && pci_match_id(dmi->driver_data, pci)) {
+ dev_info(&pci->dev, "Skipping the device on the DMI 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;
}
+ /*
+ * 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) {
@@ -2141,6 +2164,39 @@ static int azx_probe(struct pci_dev *pci,
pci_set_drvdata(pci, card);
+#ifdef CONFIG_SND_HDA_I915
+ /* bind with i915 if needed */
+ if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) {
+ err = snd_hdac_i915_init(azx_bus(chip));
+ if (err < 0) {
+ if (err == -EPROBE_DEFER)
+ goto out_free;
+
+ /* 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 (HDA_CONTROLLER_IN_GPU(pci)) {
+ dev_err_probe(card->dev, err,
+ "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 (HDA_CONTROLLER_IN_GPU(pci))
+ hda->need_i915_power = true;
+ }
+#else
+ if (HDA_CONTROLLER_IN_GPU(pci))
+ dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n");
+#endif
+
err = register_vga_switcheroo(chip);
if (err < 0) {
dev_err(card->dev, "Error registering vga_switcheroo client\n");
@@ -2168,26 +2224,90 @@ static int azx_probe(struct pci_dev *pci,
}
#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_work(&hda->probe_work);
+ schedule_delayed_work(&hda->probe_work, 0);
- dev++;
+ set_bit(dev, probed_devs);
if (chip->disabled)
complete_all(&hda->probe_wait);
return 0;
out_free:
+ pci_set_drvdata(pci, NULL);
snd_card_free(card);
return err;
}
+/* 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),
+ SND_PCI_QUIRK(0x17aa, 0x316e, "Lenovo ThinkCentre M70q", 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),
+ /* KONTRON SinglePC may cause a stall at runtime resume */
+ SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0),
+ /* Dell ALC3271 */
+ SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */
+ SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0),
+ {}
+};
+
+static void set_default_power_save(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ int val = power_save;
+
+ if (pm_blacklist < 0) {
+ 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;
+ hda->runtime_pm_disabled = 1;
+ }
+ } else if (pm_blacklist > 0) {
+ dev_info(chip->card->dev, "Forcing power_save to 0 via option\n");
+ val = 0;
+ }
+ snd_hda_set_power_save(&chip->bus, val * 1000);
+}
+
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
-static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
+static const unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
[AZX_DRIVER_NVIDIA] = 8,
[AZX_DRIVER_TERA] = 1,
};
@@ -2200,46 +2320,20 @@ static int azx_probe_continue(struct azx *chip)
int dev = chip->dev_index;
int err;
- hda->probe_continued = 1;
+ if (chip->disabled || hda->init_failed)
+ return -EIO;
+ if (hda->probe_retry)
+ goto probe_retry;
- /* 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 | AZX_DCAPS_I915_POWERWELL);
- }
- }
- }
+ to_hda_bus(bus)->bus_probing = 1;
+ hda->probe_continued = 1;
/* 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.
*/
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- /* HSW/BDW controllers need this power */
- if (CONTROLLER_IN_GPU(pci))
- hda->need_i915_power = 1;
-
- err = snd_hdac_display_power(bus, true);
- if (err < 0) {
- dev_err(chip->card->dev,
- "Cannot turn on display power on i915\n");
- goto i915_power_fail;
- }
- }
+ display_power(chip, true);
err = azx_first_init(chip);
if (err < 0)
@@ -2249,10 +2343,14 @@ static int azx_probe_continue(struct azx *chip)
chip->beep_mode = beep_mode[dev];
#endif
+ chip->ctl_dev_id = ctl_dev_id;
+
/* create codec instances */
- err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
- 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 (chip->fw) {
@@ -2260,38 +2358,54 @@ static int azx_probe_continue(struct azx *chip)
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;
+ }
}
err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
+ setup_vga_switcheroo_runtime_pm(chip);
+
chip->running = 1;
azx_add_card_list(chip);
- snd_hda_set_power_save(&chip->bus, power_save * 1000);
- if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
+
+ 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);
+ }
out_free:
- if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- && !hda->need_i915_power)
- snd_hdac_display_power(bus, false);
+ if (err < 0) {
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(chip->card);
+ return err;
+ }
-i915_power_fail:
- if (err < 0)
- hda->init_failed = 1;
+ if (!hda->need_i915_power)
+ display_power(chip, false);
complete_all(&hda->probe_wait);
- return err;
+ to_hda_bus(bus)->bus_probing = 0;
+ hda->probe_retry = 0;
+ return 0;
}
static void azx_remove(struct pci_dev *pci)
@@ -2316,9 +2430,11 @@ static void azx_remove(struct pci_dev *pci)
* device during cancel_work_sync() call.
*/
device_unlock(&pci->dev);
- cancel_work_sync(&hda->probe_work);
+ 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);
}
}
@@ -2332,225 +2448,299 @@ static void azx_shutdown(struct pci_dev *pci)
return;
chip = card->private_data;
if (chip && chip->running)
- azx_stop_chip(chip);
+ __azx_shutdown_chip(chip, true);
}
/* PCI IDs */
static const struct pci_device_id azx_ids[] = {
/* CPT */
- { PCI_DEVICE(0x8086, 0x1c20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ { PCI_DEVICE_DATA(INTEL, HDA_CPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
/* PBG */
- { PCI_DEVICE(0x8086, 0x1d20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ { PCI_DEVICE_DATA(INTEL, HDA_PBG, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
/* Panther Point */
- { PCI_DEVICE(0x8086, 0x1e20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ { PCI_DEVICE_DATA(INTEL, HDA_PPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
/* Lynx Point */
- { PCI_DEVICE(0x8086, 0x8c20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
/* 9 Series */
- { PCI_DEVICE(0x8086, 0x8ca0),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE_DATA(INTEL, HDA_9_SERIES, 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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_WBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ { PCI_DEVICE_DATA(INTEL, HDA_WBG_1, 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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_LBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_LBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
/* Lynx Point-LP */
- { PCI_DEVICE(0x8086, 0x9c20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
/* Lynx Point-LP */
- { PCI_DEVICE(0x8086, 0x9c21),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_1, 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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_WPT_LP, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Skylake (Sunrise Point) */
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Skylake-LP (Sunrise Point-LP) */
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Kabylake */
- { PCI_DEVICE(0x8086, 0xa171),
- .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Kabylake-LP */
- { PCI_DEVICE(0x8086, 0x9d71),
- .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Kabylake-H */
- { PCI_DEVICE(0x8086, 0xa2f0),
- .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Coffelake */
- { PCI_DEVICE(0x8086, 0xa348),
- .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Cannonlake */
- { PCI_DEVICE(0x8086, 0x9dc8),
- .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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-R */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_R, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Icelake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Icelake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Jasperlake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Tigerlake */
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Tigerlake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* DG1 */
+ { PCI_DEVICE_DATA(INTEL, HDA_DG1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* DG2 */
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_2, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-P */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-M */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-N */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Elkhart Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Raptor Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Battlemage */
+ { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Lunarlake-P */
+ { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Arrow Lake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Arrow Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Panther Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Panther Lake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Wildcat Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Apollolake (Broxton-P) */
+ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Gemini-Lake */
- { PCI_DEVICE(0x8086, 0x3198),
- .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
+ { PCI_DEVICE_DATA(INTEL, HDA_GML, 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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_3, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
/* Broadwell */
- { PCI_DEVICE(0x8086, 0x160c),
- .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL },
+ { PCI_DEVICE_DATA(INTEL, HDA_BDW, 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_DATA(INTEL, HDA_5_3400_SERIES_0, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_1, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
/* Poulsbo */
- { PCI_DEVICE(0x8086, 0x811b),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE },
+ { PCI_DEVICE_DATA(INTEL, HDA_POULSBO, 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 },
+ { PCI_DEVICE_DATA(INTEL, HDA_OAKTRAIL, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE) },
/* BayTrail */
- { PCI_DEVICE(0x8086, 0x0f04),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL },
+ { PCI_DEVICE_DATA(INTEL, HDA_BYT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL) },
/* Braswell */
- { PCI_DEVICE(0x8086, 0x2284),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL },
+ { PCI_DEVICE_DATA(INTEL, HDA_BSW, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL) },
/* ICH6 */
- { PCI_DEVICE(0x8086, 0x2668),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH6, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH7 */
- { PCI_DEVICE(0x8086, 0x27d8),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH7, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ESB2 */
- { PCI_DEVICE(0x8086, 0x269a),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ESB2, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH8 */
- { PCI_DEVICE(0x8086, 0x284b),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH8, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH9 */
- { PCI_DEVICE(0x8086, 0x293e),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH9_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH9 */
- { PCI_DEVICE(0x8086, 0x293f),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH9_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH10 */
- { PCI_DEVICE(0x8086, 0x3a3e),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH10_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
/* ICH10 */
- { PCI_DEVICE(0x8086, 0x3a6e),
- .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH10_1, 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 | AZX_DCAPS_NO_ALIGN_BUFSIZE },
/* ATI SB 450/600/700/800/900 */
- { PCI_DEVICE(0x1002, 0x437b),
+ { PCI_VDEVICE(ATI, 0x437b),
.driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
- { PCI_DEVICE(0x1002, 0x4383),
+ { PCI_VDEVICE(ATI, 0x4383),
.driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
/* AMD Hudson */
- { PCI_DEVICE(0x1022, 0x780d),
+ { PCI_VDEVICE(AMD, 0x780d),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD, X370 & co */
+ { PCI_VDEVICE(AMD, 0x1457),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD, X570 & co */
+ { PCI_VDEVICE(AMD, 0x1487),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD Stoney */
+ { PCI_VDEVICE(AMD, 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_ATI_SB },
+ { PCI_VDEVICE(AMD, 0x15e3),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
/* ATI HDMI */
- { PCI_DEVICE(0x1002, 0x0002),
- .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0x1308),
+ { PCI_VDEVICE(ATI, 0x0002),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0x1308),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0x157a),
+ { PCI_VDEVICE(ATI, 0x157a),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0x15b3),
+ { PCI_VDEVICE(ATI, 0x15b3),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0x793b),
+ { PCI_VDEVICE(ATI, 0x793b),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0x7919),
+ { PCI_VDEVICE(ATI, 0x7919),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0x960f),
+ { PCI_VDEVICE(ATI, 0x960f),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0x970f),
+ { PCI_VDEVICE(ATI, 0x970f),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0x9840),
+ { PCI_VDEVICE(ATI, 0x9840),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaa00),
+ { PCI_VDEVICE(ATI, 0xaa00),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa08),
+ { PCI_VDEVICE(ATI, 0xaa08),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa10),
+ { PCI_VDEVICE(ATI, 0xaa10),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa18),
+ { PCI_VDEVICE(ATI, 0xaa18),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa20),
+ { PCI_VDEVICE(ATI, 0xaa20),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa28),
+ { PCI_VDEVICE(ATI, 0xaa28),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa30),
+ { PCI_VDEVICE(ATI, 0xaa30),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa38),
+ { PCI_VDEVICE(ATI, 0xaa38),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa40),
+ { PCI_VDEVICE(ATI, 0xaa40),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa48),
+ { PCI_VDEVICE(ATI, 0xaa48),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa50),
+ { PCI_VDEVICE(ATI, 0xaa50),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa58),
+ { PCI_VDEVICE(ATI, 0xaa58),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa60),
+ { PCI_VDEVICE(ATI, 0xaa60),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa68),
+ { PCI_VDEVICE(ATI, 0xaa68),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa80),
+ { PCI_VDEVICE(ATI, 0xaa80),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa88),
+ { PCI_VDEVICE(ATI, 0xaa88),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa90),
+ { PCI_VDEVICE(ATI, 0xaa90),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
- { PCI_DEVICE(0x1002, 0xaa98),
+ { PCI_VDEVICE(ATI, 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),
+ { PCI_VDEVICE(ATI, 0x9902),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaaa8),
+ { PCI_VDEVICE(ATI, 0xaaa0),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaab0),
+ { PCI_VDEVICE(ATI, 0xaaa8),
.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 },
- { PCI_DEVICE(0x1002, 0xaac8),
- .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaad8),
- .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaae8),
- .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaae0),
- .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
- { PCI_DEVICE(0x1002, 0xaaf0),
+ { PCI_VDEVICE(ATI, 0xaab0),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaac0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaac8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaad8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaae0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaae8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaaf0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaaf8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab00),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab08),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab10),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab18),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab20),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab28),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab30),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab38),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab40),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ /* GLENFLY */
+ { PCI_DEVICE(PCI_VENDOR_ID_GLENFLY, 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 },
+ { PCI_VDEVICE(VIA, 0x3288), .driver_data = AZX_DRIVER_VIA },
/* VIA GFX VT7122/VX900 */
- { PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
+ { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
/* VIA GFX VT6122/VX11 */
- { PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
+ { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
/* SIS966 */
- { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
+ { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS },
/* ULI M5461 */
- { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
+ { PCI_VDEVICE(AL, 0x5461), .driver_data = AZX_DRIVER_ULI },
/* NVIDIA MCP */
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
@@ -2563,9 +2753,9 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
/* Creative X-Fi (CA0110-IBG) */
/* CTHDA chips */
- { PCI_DEVICE(0x1102, 0x0010),
+ { PCI_VDEVICE(CREATIVE, 0x0010),
.driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
- { PCI_DEVICE(0x1102, 0x0012),
+ { PCI_VDEVICE(CREATIVE, 0x0012),
.driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
#if !IS_ENABLED(CONFIG_SND_CTXFI)
/* the following entry conflicts with snd-ctxfi driver,
@@ -2579,18 +2769,18 @@ static const struct pci_device_id azx_ids[] = {
AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009),
+ { PCI_VDEVICE(CREATIVE, 0x0009),
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#endif
/* CM8888 */
- { PCI_DEVICE(0x13f6, 0x5011),
+ { PCI_VDEVICE(CMEDIA, 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 },
+ { PCI_VDEVICE(RDC, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
/* VMware HDAudio */
- { PCI_DEVICE(0x15ad, 0x1977), .driver_data = AZX_DRIVER_GENERIC },
+ { PCI_VDEVICE(VMWARE, 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,
@@ -2600,6 +2790,28 @@ static const struct pci_device_id azx_ids[] = {
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
+ /* Zhaoxin */
+ { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
+ { PCI_VDEVICE(ZHAOXIN, 0x9141),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9142),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9144),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9145),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9146),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ /* Loongson HDAudio*/
+ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA),
+ .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
+ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
+ .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
@@ -2612,7 +2824,7 @@ static struct pci_driver azx_driver = {
.remove = azx_remove,
.shutdown = azx_shutdown,
.driver = {
- .pm = AZX_PM_OPS,
+ .pm = pm_ptr(&azx_pm),
},
};
diff --git a/sound/hda/controllers/intel.h b/sound/hda/controllers/intel.h
new file mode 100644
index 000000000000..2d1725f86ef1
--- /dev/null
+++ b/sound/hda/controllers/intel.h
@@ -0,0 +1,38 @@
+/* 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;
+ unsigned int runtime_pm_disabled: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/hda/controllers/intel_trace.h
index 73a7adfa192d..fb10ab9e7e55 100644
--- a/sound/pci/hda/hda_intel_trace.h
+++ b/sound/hda/controllers/intel_trace.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hda_intel
-#define TRACE_INCLUDE_FILE hda_intel_trace
+#define TRACE_INCLUDE_FILE intel_trace
#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HDA_INTEL_H
@@ -34,7 +34,6 @@ DEFINE_EVENT(hda_pm, azx_resume,
TP_ARGS(chip)
);
-#ifdef CONFIG_PM
DEFINE_EVENT(hda_pm, azx_runtime_suspend,
TP_PROTO(struct azx *chip),
TP_ARGS(chip)
@@ -44,7 +43,6 @@ DEFINE_EVENT(hda_pm, azx_runtime_resume,
TP_PROTO(struct azx *chip),
TP_ARGS(chip)
);
-#endif
#endif /* _TRACE_HDA_INTEL_H */
diff --git a/sound/pci/hda/hda_tegra.c b/sound/hda/controllers/tegra.c
index 0621920f7617..6ab338f37db5 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/hda/controllers/tegra.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/clk.h>
@@ -28,14 +16,18 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_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 "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_controller.h"
/* Defines for Nvidia Tegra HDA support */
@@ -62,18 +54,40 @@
#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;
+ bool has_hda2codec_2x;
+ bool input_stream;
+ bool always_on;
+ bool requires_init;
+};
+
struct hda_tegra {
struct azx chip;
struct device *dev;
- struct clk *hda_clk;
- struct clk *hda2codec_2x_clk;
- struct clk *hda2hdmi_clk;
+ 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
@@ -85,105 +99,7 @@ MODULE_PARM_DESC(power_save,
#define power_save 0
#endif
-/*
- * DMA page allocation ops.
- */
-static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
- struct snd_dma_buffer *buf)
-{
- return snd_dma_alloc_pages(type, bus->dev, size, buf);
-}
-
-static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
-{
- snd_dma_free_pages(buf);
-}
-
-static int substream_alloc_pages(struct azx *chip,
- struct snd_pcm_substream *substream,
- size_t size)
-{
- return snd_pcm_lib_malloc_pages(substream, size);
-}
-
-static int substream_free_pages(struct azx *chip,
- struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/*
- * Register access ops. Tegra HDA register access is DWORD only.
- */
-static void hda_tegra_writel(u32 value, u32 __iomem *addr)
-{
- writel(value, addr);
-}
-
-static u32 hda_tegra_readl(u32 __iomem *addr)
-{
- return readl(addr);
-}
-
-static void hda_tegra_writew(u16 value, u16 __iomem *addr)
-{
- unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
- u32 v;
-
- v = readl(dword_addr);
- v &= ~(0xffff << shift);
- v |= value << shift;
- writel(v, dword_addr);
-}
-
-static u16 hda_tegra_readw(u16 __iomem *addr)
-{
- unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
- u32 v;
-
- v = readl(dword_addr);
- return (v >> shift) & 0xffff;
-}
-
-static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
-{
- unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
- u32 v;
-
- v = readl(dword_addr);
- v &= ~(0xff << shift);
- v |= value << shift;
- writel(v, dword_addr);
-}
-
-static u8 hda_tegra_readb(u8 __iomem *addr)
-{
- unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
- u32 v;
-
- v = readl(dword_addr);
- return (v >> shift) & 0xff;
-}
-
-static const struct hdac_io_ops hda_tegra_io_ops = {
- .reg_writel = hda_tegra_writel,
- .reg_readl = hda_tegra_readl,
- .reg_writew = hda_tegra_writew,
- .reg_readw = hda_tegra_readw,
- .reg_writeb = hda_tegra_writeb,
- .reg_readb = hda_tegra_readb,
- .dma_alloc_pages = dma_alloc_pages,
- .dma_free_pages = dma_free_pages,
-};
-
-static const struct hda_controller_ops hda_tegra_ops = {
- .substream_alloc_pages = substream_alloc_pages,
- .substream_free_pages = substream_free_pages,
-};
+static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
static void hda_tegra_init(struct hda_tegra *hda)
{
@@ -210,75 +126,92 @@ static void hda_tegra_init(struct hda_tegra *hda)
writel(v, hda->regs + HDA_IPFS_INTR_MASK);
}
-static int hda_tegra_enable_clocks(struct hda_tegra *data)
+/*
+ * power management
+ */
+static int hda_tegra_suspend(struct device *dev)
{
+ struct snd_card *card = dev_get_drvdata(dev);
int rc;
- rc = clk_prepare_enable(data->hda_clk);
- if (rc)
+ rc = pm_runtime_force_suspend(dev);
+ if (rc < 0)
return rc;
- rc = clk_prepare_enable(data->hda2codec_2x_clk);
- if (rc)
- goto disable_hda;
- rc = clk_prepare_enable(data->hda2hdmi_clk);
- if (rc)
- goto disable_codec_2x;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
return 0;
-
-disable_codec_2x:
- clk_disable_unprepare(data->hda2codec_2x_clk);
-disable_hda:
- clk_disable_unprepare(data->hda_clk);
- return rc;
}
-#ifdef CONFIG_PM_SLEEP
-static void hda_tegra_disable_clocks(struct hda_tegra *data)
+static int hda_tegra_resume(struct device *dev)
{
- clk_disable_unprepare(data->hda2hdmi_clk);
- clk_disable_unprepare(data->hda2codec_2x_clk);
- clk_disable_unprepare(data->hda_clk);
+ 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;
}
-/*
- * power management
- */
-static int hda_tegra_suspend(struct device *dev)
+static int 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);
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ 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);
- hda_tegra_disable_clocks(hda);
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ }
+ clk_bulk_disable_unprepare(hda->nclocks, hda->clocks);
return 0;
}
-static int hda_tegra_resume(struct device *dev)
+static int 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;
- hda_tegra_enable_clocks(hda);
-
- hda_tegra_init(hda);
-
- azx_init_chip(chip, 1);
+ if (!chip->running) {
+ rc = reset_control_bulk_assert(hda->nresets, hda->resets);
+ if (rc)
+ return rc;
+ }
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks);
+ if (rc != 0)
+ return rc;
+ if (chip->running) {
+ if (hda->soc->requires_init)
+ 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;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops hda_tegra_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+ SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+ RUNTIME_PM_OPS(hda_tegra_runtime_suspend, hda_tegra_runtime_resume, NULL)
};
static int hda_tegra_dev_disconnect(struct snd_device *device)
@@ -314,52 +247,34 @@ 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 device *dev = hda->dev;
struct resource *res;
- int err;
-
- hda->hda_clk = devm_clk_get(dev, "hda");
- if (IS_ERR(hda->hda_clk)) {
- dev_err(dev, "failed to get hda clock\n");
- return PTR_ERR(hda->hda_clk);
- }
- hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
- if (IS_ERR(hda->hda2codec_2x_clk)) {
- dev_err(dev, "failed to get hda2codec_2x clock\n");
- return PTR_ERR(hda->hda2codec_2x_clk);
- }
- hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
- if (IS_ERR(hda->hda2hdmi_clk)) {
- dev_err(dev, "failed to get hda2hdmi clock\n");
- return PTR_ERR(hda->hda2hdmi_clk);
- }
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hda->regs = devm_ioremap_resource(dev, 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;
- err = hda_tegra_enable_clocks(hda);
- if (err) {
- dev_err(dev, "failed to get enable clocks\n");
- return err;
- }
-
- hda_tegra_init(hda);
+ if (hda->soc->requires_init)
+ 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)
@@ -374,16 +289,50 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
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;
- synchronize_irq(bus->irq);
+ 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 (!hda->soc->input_stream)
+ 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 */
@@ -411,14 +360,40 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
/* 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;
}
- strcpy(card->driver, "tegra-hda");
- strcpy(card->shortname, "tegra-hda");
+ /* driver name */
+ strscpy(card->driver, drv_name);
+ /* 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");
+ strscpy(card->shortname, sname);
+
+ /* longname for card */
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, bus->addr, bus->irq);
@@ -436,7 +411,7 @@ static int hda_tegra_create(struct snd_card *card,
unsigned int driver_caps,
struct hda_tegra *hda)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_disconnect = hda_tegra_dev_disconnect,
.dev_free = hda_tegra_dev_free,
};
@@ -460,11 +435,23 @@ static int hda_tegra_create(struct snd_card *card,
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
- err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
+ err = azx_bus_init(chip, NULL);
if (err < 0)
return err;
- chip->bus.needs_damn_long_delay = 1;
+ chip->bus.core.sync_write = 0;
+ chip->bus.core.needs_damn_long_delay = 1;
+ chip->bus.core.aligned_mmio = 1;
+
+ /*
+ * HDA power domain and clocks are always on for Tegra264 and
+ * the jack detection logic would work always, so no need of
+ * jack polling mechanism running.
+ */
+ if (!hda->soc->always_on) {
+ chip->jackpoll_interval = msecs_to_jiffies(5000);
+ chip->bus.jackpoll_in_suspend = 1;
+ }
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
@@ -475,15 +462,56 @@ static int hda_tegra_create(struct snd_card *card,
return 0;
}
+static const struct hda_tegra_soc tegra30_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = true,
+ .has_hda2codec_2x = true,
+ .input_stream = true,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra194_data = {
+ .has_hda2codec_2x_reset = false,
+ .has_hda2hdmi = true,
+ .has_hda2codec_2x = true,
+ .input_stream = true,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra234_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = false,
+ .has_hda2codec_2x = true,
+ .input_stream = false,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra264_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = false,
+ .has_hda2codec_2x = false,
+ .input_stream = false,
+ .always_on = true,
+ .requires_init = false,
+};
+
static const struct of_device_id hda_tegra_match[] = {
- { .compatible = "nvidia,tegra30-hda" },
+ { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
+ { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
+ { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data },
+ { .compatible = "nvidia,tegra264-hda", .data = &tegra264_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;
+ 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;
@@ -495,6 +523,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
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) {
@@ -502,12 +532,50 @@ static int hda_tegra_probe(struct platform_device *pdev)
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";
+
+ if (hda->soc->has_hda2codec_2x)
+ 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;
@@ -524,12 +592,13 @@ static void hda_tegra_probe_work(struct work_struct *work)
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, 0);
+ err = azx_probe_codecs(chip, 8);
if (err < 0)
goto out_free;
@@ -545,12 +614,14 @@ static void hda_tegra_probe_work(struct work_struct *work)
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 int hda_tegra_remove(struct platform_device *pdev)
+static void hda_tegra_remove(struct platform_device *pdev)
{
- return snd_card_free(dev_get_drvdata(&pdev->dev));
+ snd_card_free(dev_get_drvdata(&pdev->dev));
+ pm_runtime_disable(&pdev->dev);
}
static void hda_tegra_shutdown(struct platform_device *pdev)
@@ -568,7 +639,7 @@ static void hda_tegra_shutdown(struct platform_device *pdev)
static struct platform_driver tegra_platform_hda = {
.driver = {
.name = "tegra-hda",
- .pm = &hda_tegra_pm,
+ .pm = pm_ptr(&hda_tegra_pm),
.of_match_table = hda_tegra_match,
},
.probe = hda_tegra_probe,
diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig
new file mode 100644
index 000000000000..bfdcf6384c52
--- /dev/null
+++ b/sound/hda/core/Kconfig
@@ -0,0 +1,51 @@
+# 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_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 ACPI_NHLT if ACPI
+ 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/core/Makefile b/sound/hda/core/Makefile
new file mode 100644
index 000000000000..89cb46143050
--- /dev/null
+++ b/sound/hda/core/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-core-y := hda_bus_type.o bus.o device.o sysfs.o \
+ regmap.o controller.o stream.o array.o hdmi_chmap.o
+
+snd-hda-core-y += trace.o
+CFLAGS_trace.o := -I$(src)
+
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += component.o
+snd-hda-core-$(CONFIG_SND_HDA_I915) += i915.o
+
+obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
+
+#extended hda
+obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+
+snd-intel-dspcfg-y := 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-y := 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/core/array.c
index 5dfa610e4471..a204dcee0034 100644
--- a/sound/hda/array.c
+++ b/sound/hda/core/array.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* generic arrays
*/
diff --git a/sound/hda/hdac_bus.c b/sound/hda/core/bus.c
index 714a51721a31..9b196c915f37 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/core/bus.c
@@ -1,32 +1,35 @@
+// 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 process_unsol_events(struct work_struct *work);
+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
- * @io_ops: lowlevel I/O 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,
- const struct hdac_io_ops *io_ops)
+ const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
@@ -34,13 +37,28 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
bus->ops = ops;
else
bus->ops = &default_ops;
- bus->io_ops = io_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, process_unsol_events);
+ 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);
@@ -60,6 +78,7 @@ 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
*
@@ -68,18 +87,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
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;
+ guard(mutex)(&bus->cmd_mutex);
+ return snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
/**
* 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
*
@@ -143,18 +158,18 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
schedule_work(&bus->unsol_work);
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
/*
* process queued unsolicited events
*/
-static void process_unsol_events(struct work_struct *work)
+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;
@@ -164,12 +179,15 @@ static void process_unsol_events(struct work_struct *work)
if (!(caddr & (1 << 4))) /* no unsolicited event? */
continue;
codec = bus->caddr_tbl[caddr & 0x0f];
- if (!codec || !codec->dev.driver)
+ 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);
}
/**
@@ -195,7 +213,6 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
bus->num_codecs++;
return 0;
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
/**
* snd_hdac_bus_remove_device - Remove a codec from bus
@@ -214,4 +231,55 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
bus->num_codecs--;
flush_work(&bus->unsol_work);
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
+
+#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/core/component.c b/sound/hda/core/component.c
new file mode 100644
index 000000000000..04755903880e
--- /dev/null
+++ b/sound/hda/core/component.c
@@ -0,0 +1,351 @@
+// 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 <linux/string_choices.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", str_enable_disable(enable));
+
+ 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", str_enable_disable(enable));
+
+ guard(mutex)(&bus->lock);
+ if (enable)
+ set_bit(idx, &bus->display_power_status);
+ else
+ clear_bit(idx, &bus->display_power_status);
+
+ if (!acomp || !acomp->ops)
+ return;
+
+ 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;
+ }
+ }
+}
+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/core/controller.c
index 560ec0986e1a..a7c00ad80117 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/core/controller.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HD-audio controller helpers
*/
@@ -8,6 +9,7 @@
#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)
@@ -40,7 +42,9 @@ static void azx_clear_corbrp(struct hdac_bus *bus)
*/
void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
{
- spin_lock_irq(&bus->reg_lock);
+ WARN_ON_ONCE(!bus->rb.area);
+
+ guard(spinlock_irq)(&bus->reg_lock);
/* CORB set up */
bus->corb.addr = bus->rb.addr;
bus->corb.buf = (__le32 *)bus->rb.area;
@@ -58,7 +62,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
azx_clear_corbrp(bus);
/* enable corb dma */
- snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+ if (!bus->use_pio_for_commands)
+ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
/* RIRB set up */
bus->rirb.addr = bus->rb.addr + 2048;
@@ -75,8 +80,12 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
/* 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);
- spin_unlock_irq(&bus->reg_lock);
+ if (bus->not_use_interrupts)
+ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
+ else
+ 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);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
@@ -102,18 +111,17 @@ static void hdac_wait_for_cmd_dmas(struct hdac_bus *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);
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ /* disable ringbuffer DMAs */
+ snd_hdac_chip_writeb(bus, RIRBCTL, 0);
+ snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ }
hdac_wait_for_cmd_dmas(bus);
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_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);
@@ -126,19 +134,94 @@ static unsigned int azx_command_addr(u32 cmd)
return addr;
}
+/* receive an Immediate Response with PIO */
+static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus,
+ unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV bit */
+ if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n",
+ snd_hdac_chip_readw(bus, IRS));
+
+ bus->rirb.res[addr] = -1;
+
+ return -EIO;
+}
+
/**
- * snd_hdac_bus_send_cmd - send a command verb via CORB
+ * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command
* @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)
+static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+
+ while (timeout--) {
+ /* check ICB bit */
+ if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) {
+ /* Clear IRV bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID);
+ snd_hdac_chip_writel(bus, IC, val);
+ /* Set ICB bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY);
+
+ return snd_hdac_bus_wait_for_pio_response(bus, addr);
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n",
+ snd_hdac_chip_readw(bus, IRS), val);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_bus_get_response_pio - receive a response via Immediate Response
+ * @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.
+ */
+static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus,
+ unsigned int addr, unsigned int *res)
+{
+ if (res)
+ *res = bus->rirb.res[addr];
+
+ return 0;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_corb - 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.
+ */
+static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val)
{
unsigned int addr = azx_command_addr(val);
unsigned int wp, rp;
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
bus->last_cmd[azx_command_addr(val)] = val;
@@ -146,7 +229,6 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
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++;
@@ -155,7 +237,6 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
rp = snd_hdac_chip_readw(bus, CORBRP);
if (wp == rp) {
/* oops, it's full */
- spin_unlock_irq(&bus->reg_lock);
return -EAGAIN;
}
@@ -163,11 +244,8 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
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)
@@ -176,6 +254,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
* @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)
{
@@ -211,6 +290,9 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
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",
@@ -221,42 +303,97 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
/**
- * snd_hdac_bus_get_response - receive a response via RIRB
+ * snd_hdac_bus_get_response_rirb - 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)
+static int snd_hdac_bus_get_response_rirb(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->rirb.cmds[addr]) {
- if (res)
- *res = bus->rirb.res[addr]; /* the last value */
- spin_unlock_irq(&bus->reg_lock);
- return 0;
+ scoped_guard(spinlock_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);
+ return 0;
+ }
}
- spin_unlock_irq(&bus->reg_lock);
if (time_after(jiffies, timeout))
break;
- if (loopcounter > 3000)
+#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 {
+ } else {
udelay(10);
cond_resched();
}
}
+ if (!bus->polling_mode)
+ finish_wait(&bus->rirb_wq, &wait);
+
return -EIO;
}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO
+ * @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)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_send_cmd_pio(bus, val);
+
+ return snd_hdac_bus_send_cmd_corb(bus, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB or PIO
+ * @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)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_get_response_pio(bus, addr, res);
+
+ return snd_hdac_bus_get_response_rirb(bus, addr, res);
+}
EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
#define HDAC_MAX_CAPS 10
@@ -374,7 +511,7 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
{
unsigned long timeout;
- snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET);
+ 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))
@@ -383,13 +520,14 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
/* reset codec link */
-static int azx_reset(struct hdac_bus *bus, bool full_reset)
+int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
{
if (!full_reset)
goto skip_reset;
- /* clear STATESTS */
- snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+ /* 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);
@@ -408,13 +546,10 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset)
skip_reset:
/* check to see if controller is ready */
if (!snd_hdac_chip_readb(bus, GCTL)) {
- dev_dbg(bus->dev, "azx_reset: controller not ready!\n");
+ dev_dbg(bus->dev, "controller not ready!\n");
return -EBUSY;
}
- /* Accept unsolicited responses */
- snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL);
-
/* detect codecs */
if (!bus->codec_mask) {
bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
@@ -423,12 +558,15 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset)
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, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+ snd_hdac_chip_updatel(bus, INTCTL,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
}
/* disable interrupts */
@@ -440,11 +578,8 @@ static void azx_int_disable(struct hdac_bus *bus)
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 */
- snd_hdac_chip_writeb(bus, INTCTL, 0);
-
- /* disable controller CIE and GIE */
- snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, 0);
+ /* disable SIE for all streams & disable controller CIE and GIE */
+ snd_hdac_chip_writel(bus, INTCTL, 0);
}
/* clear interrupts */
@@ -477,15 +612,17 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
return false;
/* reset controller */
- azx_reset(bus, full_reset);
+ snd_hdac_bus_reset_link(bus, full_reset);
- /* initialize interrupts */
+ /* clear interrupts */
azx_int_clear(bus);
- azx_int_enable(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);
@@ -493,6 +630,7 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
}
bus->chip_init = true;
+
return true;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
@@ -527,7 +665,7 @@ 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
- * @ask: callback to be called for woken streams
+ * @ack: callback to be called for woken streams
*
* Returns the bits of handled streams, or zero if no stream is handled.
*/
@@ -544,8 +682,8 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
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->running ||
- !(sd_status & SD_INT_COMPLETE))
+ if ((!azx_dev->substream && !azx_dev->cstream) ||
+ !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
continue;
if (ack)
ack(bus, azx_dev);
@@ -566,12 +704,13 @@ 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 = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- BDL_SIZE, &s->bdl);
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ BDL_SIZE, &s->bdl);
num_streams++;
if (err < 0)
return -ENOMEM;
@@ -580,16 +719,15 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
if (WARN_ON(!num_streams))
return -EINVAL;
/* allocate memory for the position buffer */
- err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- num_streams * 8, &bus->posbuf);
+ 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 bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- PAGE_SIZE, &bus->rb);
+ return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
@@ -603,12 +741,26 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
list_for_each_entry(s, &bus->stream_list, list) {
if (s->bdl.area)
- bus->io_ops->dma_free_pages(bus, &s->bdl);
+ snd_dma_free_pages(&s->bdl);
}
if (bus->rb.area)
- bus->io_ops->dma_free_pages(bus, &bus->rb);
+ snd_dma_free_pages(&bus->rb);
if (bus->posbuf.area)
- bus->io_ops->dma_free_pages(bus, &bus->posbuf);
+ 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/core/device.c
index 06f845e293cb..160c8d0453b0 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/core/device.c
@@ -1,8 +1,10 @@
+// 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>
@@ -11,6 +13,7 @@
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include "local.h"
static void setup_fg_nodes(struct hdac_device *codec);
@@ -18,7 +21,7 @@ static int get_codec_vendor_name(struct hdac_device *codec);
static void default_release(struct device *dev)
{
- snd_hdac_device_exit(container_of(dev, struct hdac_device, dev));
+ snd_hdac_device_exit(dev_to_hdac_dev(dev));
}
/**
@@ -54,6 +57,8 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
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);
@@ -87,7 +92,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
fg = codec->afg ? codec->afg : codec->mfg;
- err = snd_hdac_refresh_widgets(codec, false);
+ err = snd_hdac_refresh_widgets(codec);
if (err < 0)
goto error;
@@ -123,6 +128,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_init);
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);
@@ -131,7 +138,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
/**
* snd_hdac_device_register - register the hd-audio codec base device
- * codec: the device to register
+ * @codec: the device to register
*/
int snd_hdac_device_register(struct hdac_device *codec)
{
@@ -140,7 +147,9 @@ int snd_hdac_device_register(struct hdac_device *codec)
err = device_add(&codec->dev);
if (err < 0)
return err;
- err = hda_widget_sysfs_init(codec);
+ scoped_guard(mutex, &codec->widget_lock) {
+ err = hda_widget_sysfs_init(codec);
+ }
if (err < 0) {
device_del(&codec->dev);
return err;
@@ -152,12 +161,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_register);
/**
* snd_hdac_device_unregister - unregister the hd-audio codec base device
- * codec: the device to unregister
+ * @codec: the device to unregister
*/
void snd_hdac_device_unregister(struct hdac_device *codec)
{
if (device_is_registered(&codec->dev)) {
- hda_widget_sysfs_exit(codec);
+ scoped_guard(mutex, &codec->widget_lock) {
+ hda_widget_sysfs_exit(codec);
+ }
device_del(&codec->dev);
snd_hdac_bus_remove_device(codec->bus, codec);
}
@@ -194,9 +205,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
*
* Returns the size of string, like snprintf(), or a negative error code.
*/
-int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+int snd_hdac_codec_modalias(const struct hdac_device *codec, char *buf, size_t size)
{
- return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ 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);
@@ -211,8 +222,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
*
* Return an encoded command verb or -1 for error.
*/
-unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
- unsigned int verb, unsigned int parm)
+static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm)
{
u32 val, addr;
@@ -230,7 +241,6 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
val |= parm;
return val;
}
-EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
/**
* snd_hdac_exec_verb - execute an encoded verb
@@ -251,7 +261,6 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
return codec->exec_verb(codec, cmd, flags, res);
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
}
-EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
/**
@@ -275,6 +284,10 @@ 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().
*/
@@ -388,13 +401,17 @@ static void setup_fg_nodes(struct hdac_device *codec)
/**
* snd_hdac_refresh_widgets - Reset the widget start/end nodes
* @codec: the codec object
- * @sysfs: re-initialize sysfs tree, too
*/
-int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
+int snd_hdac_refresh_widgets(struct hdac_device *codec)
{
hda_nid_t start_nid;
- int nums, err;
+ int nums, err = 0;
+ /*
+ * Serialize against multiple threads trying to update the sysfs
+ * widgets array.
+ */
+ guard(mutex)(&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",
@@ -402,11 +419,9 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
return -EINVAL;
}
- if (sysfs) {
- err = hda_widget_sysfs_reinit(codec, start_nid, nums);
- if (err < 0)
- return err;
- }
+ err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+ if (err < 0)
+ return err;
codec->num_nodes = nums;
codec->start_nid = start_nid;
@@ -418,11 +433,11 @@ 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 wcaps = snd_hdac_get_wcaps(codec, nid);
unsigned int parm;
if (!(wcaps & AC_WCAP_CONN_LIST) &&
- get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+ snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
return 0;
parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
@@ -563,7 +578,6 @@ 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);
@@ -594,7 +608,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
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);
+ int ret = pm_runtime_get_if_active(&codec->dev);
if (!ret)
return -1;
if (ret < 0)
@@ -621,30 +635,14 @@ int snd_hdac_power_down_pm(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
-/**
- * snd_hdac_link_power - Enable/disable the link power for a codec
- * @codec: the codec object
- * @bool: enable or disable the link power
- */
-int snd_hdac_link_power(struct hdac_device *codec, bool enable)
-{
- if (!codec->link_power_control)
- return 0;
-
- if (codec->bus->ops->link_power)
- return codec->bus->ops->link_power(codec->bus, enable);
- else
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_link_power);
-
/* codec vendor labels */
struct hda_vendor_id {
unsigned int id;
const char *name;
};
-static struct hda_vendor_id hda_vendor_ids[] = {
+static const struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x0014, "Loongson" },
{ 0x1002, "ATI" },
{ 0x1013, "Cirrus Logic" },
{ 0x1057, "Motorola" },
@@ -660,8 +658,10 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x14f1, "Conexant" },
{ 0x17e8, "Chrontel" },
{ 0x1854, "LG" },
+ { 0x19e5, "Huawei" },
{ 0x1aec, "Wolfson Microelectronics" },
{ 0x1af4, "QEMU" },
+ { 0x1fa8, "Senarytech" },
{ 0x434d, "C-Media" },
{ 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
@@ -699,7 +699,7 @@ struct hda_rate_tbl {
(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
(((div) - 1) << AC_FMT_DIV_SHIFT))
-static struct hda_rate_tbl rate_bits[] = {
+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 */
@@ -723,40 +723,85 @@ static struct hda_rate_tbl rate_bits[] = {
{ 0 } /* terminator */
};
+static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S20_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+
+ case SNDRV_PCM_FORMAT_U20_LE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ return SNDRV_PCM_FORMAT_U32_LE;
+
+ case SNDRV_PCM_FORMAT_S20_BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ return SNDRV_PCM_FORMAT_S32_BE;
+
+ case SNDRV_PCM_FORMAT_U20_BE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ return SNDRV_PCM_FORMAT_U32_BE;
+
+ default:
+ return format;
+ }
+}
+
/**
- * 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)
+ * snd_hdac_stream_format_bits - obtain bits per sample value.
+ * @format: the PCM format.
+ * @subformat: the PCM subformat.
+ * @maxbits: the maximum bits per sample.
*
- * Calculate the format bitset from the given rate, channels and th PCM format.
+ * Return: The number of bits per sample.
+ */
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits)
+{
+ struct snd_pcm_hw_params params;
+ unsigned int bits;
+
+ memset(&params, 0, sizeof(params));
+
+ params_set_format(&params, snd_hdac_format_normalize(format));
+ snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
+ (__force unsigned int)subformat);
+
+ bits = snd_pcm_hw_params_bits(&params);
+ if (maxbits)
+ return min(bits, maxbits);
+ return bits;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
+
+/**
+ * snd_hdac_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
*
- * Return zero if invalid.
+ * Return: The format bitset or zero if invalid.
*/
-unsigned int snd_hdac_calc_stream_format(unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls)
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate)
{
- int i;
unsigned int val = 0;
+ int i;
- for (i = 0; rate_bits[i].hz; i++)
+ 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)
+ if (channels == 0 || channels > 16)
return 0;
val |= channels - 1;
- switch (snd_pcm_format_width(format)) {
+ switch (bits) {
case 8:
val |= AC_FMT_BITS_8;
break;
@@ -764,32 +809,49 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate,
val |= AC_FMT_BITS_16;
break;
case 20:
+ val |= AC_FMT_BITS_20;
+ break;
case 24:
+ val |= AC_FMT_BITS_24;
+ break;
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;
+ val |= AC_FMT_BITS_32;
break;
default:
return 0;
}
- if (spdif_ctls & AC_DIG1_NONAUDIO)
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format);
+
+/**
+ * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant).
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ unsigned int rate, unsigned short spdif_ctls)
+{
+ unsigned int val = snd_hdac_stream_format(channels, bits, rate);
+
+ if (val && spdif_ctls & AC_DIG1_NONAUDIO)
val |= AC_FMT_TYPE_NON_PCM;
return val;
}
-EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
+EXPORT_SYMBOL_GPL(snd_hdac_spdif_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))
+ (snd_hdac_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);
@@ -815,19 +877,21 @@ static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
* @nid: NID to query
* @ratesp: the pointer to store the detected rate bitflags
* @formatsp: the pointer to store the detected formats
+ * @subformatsp: the pointer to store the detected subformats for S32_LE format
* @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.
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp,
+ * @subformatsp or @bpsp 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)
+ u32 *ratesp, u64 *formatsp, u32 *subformatsp,
+ unsigned int *bpsp)
{
unsigned int i, val, wcaps;
- wcaps = get_wcaps(codec, nid);
+ wcaps = snd_hdac_get_wcaps(codec, nid);
val = query_pcm_param(codec, nid);
if (ratesp) {
@@ -846,9 +910,10 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
*ratesp = rates;
}
- if (formatsp || bpsp) {
- u64 formats = 0;
+ if (formatsp || subformatsp || bpsp) {
unsigned int streams, bps;
+ u32 subformats = 0;
+ u64 formats = 0;
streams = query_stream_param(codec, nid);
if (!streams)
@@ -864,24 +929,24 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
formats |= SNDRV_PCM_FMTBIT_S16_LE;
bps = 16;
}
- if (wcaps & AC_WCAP_DIGITAL) {
- if (val & AC_SUPPCM_BITS_32)
+ if (val & AC_SUPPCM_BITS_20) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20;
+ bps = 20;
+ }
+ if (val & AC_SUPPCM_BITS_24) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24;
+ bps = 24;
+ }
+ if (val & AC_SUPPCM_BITS_32) {
+ if (wcaps & AC_WCAP_DIGITAL) {
formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
- if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+ } else {
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)
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
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 */
@@ -909,6 +974,8 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
}
if (formatsp)
*formatsp = formats;
+ if (subformatsp)
+ *subformatsp = subformats;
if (bpsp)
*bpsp = bps;
}
@@ -1064,3 +1131,37 @@ bool snd_hdac_check_power_state(struct hdac_device *hdac,
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/core/ext/Makefile b/sound/hda/core/ext/Makefile
new file mode 100644
index 000000000000..85190a7eb5de
--- /dev/null
+++ b/sound/hda/core/ext/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-hda-ext-core-y := bus.o controller.o stream.o
+
+obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/core/ext/bus.c b/sound/hda/core/ext/bus.c
new file mode 100644
index 000000000000..6004ea1c373e
--- /dev/null
+++ b/sound/hda/core/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/core/ext/controller.c b/sound/hda/core/ext/controller.c
new file mode 100644
index 000000000000..9eea3ea2dae0
--- /dev/null
+++ b/sound/hda/core/ext/controller.c
@@ -0,0 +1,410 @@
+// 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/bitfield.h>
+#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;
+ u32 leptr;
+
+ 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);
+ hlink->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+
+ if (hdac_ext_link_alt(hlink)) {
+ leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+ hlink->id = FIELD_GET(AZX_REG_ML_LEPTR_ID, leptr);
+ }
+
+ /* 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);
+
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_id(struct hdac_bus *bus, u32 id)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ if (hdac_ext_link_alt(hlink) && hlink->id == id)
+ return hlink;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_id);
+
+/**
+ * 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;
+
+ guard(mutex)(&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;
+ }
+
+ 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;
+
+ guard(mutex)(&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;
+ }
+ }
+
+ 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/core/ext/stream.c b/sound/hda/core/ext/stream.c
new file mode 100644
index 000000000000..b4759198e51d
--- /dev/null
+++ b/sound/hda/core/ext/stream.c
@@ -0,0 +1,449 @@
+// 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/pci.h>
+#include <linux/pci_ids.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_host_stream_setup - Setup a HOST stream.
+ * @hext_stream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+int snd_hdac_ext_host_stream_setup(struct hdac_ext_stream *hext_stream, bool code_loading)
+{
+ return hext_stream->host_setup(hdac_stream(hext_stream), code_loading);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_host_stream_setup);
+
+/**
+ * snd_hdac_apl_host_stream_setup - Setup a HOST stream following procedure
+ * recommended for ApolloLake devices.
+ * @hstream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+static int snd_hdac_apl_host_stream_setup(struct hdac_stream *hstream, bool code_loading)
+{
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ int ret;
+
+ snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, false);
+ ret = snd_hdac_stream_setup(hstream, code_loading);
+ snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, true);
+
+ return ret;
+}
+
+/**
+ * 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)
+{
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int (*setup_op)(struct hdac_stream *, bool);
+ int stream_tag = 0;
+ int i, tag, idx = start_idx;
+
+ if (pci->device == PCI_DEVICE_ID_INTEL_HDA_APL)
+ setup_op = snd_hdac_apl_host_stream_setup;
+ else
+ setup_op = snd_hdac_stream_setup;
+
+ 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++;
+ hext_stream->host_setup = setup_op;
+ }
+
+ 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)
+{
+ guard(spinlock_irq)(&bus->reg_lock);
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
+}
+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;
+ }
+
+ guard(spinlock_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;
+ }
+ 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;
+ }
+
+ guard(spinlock_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;
+ }
+
+ 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:
+ scoped_guard(spinlock_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);
+ }
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ scoped_guard(spinlock_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;
+ }
+ 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;
+
+ guard(spinlock_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;
+ }
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/core/hda_bus_type.c
index 3060e2aee36f..eb72a7af2e56 100644
--- a/sound/hda/hda_bus_type.c
+++ b/sound/hda/core/hda_bus_type.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HD-audio bus
*/
@@ -20,7 +21,7 @@ MODULE_LICENSE("GPL");
* 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)
+hdac_get_device_id(struct hdac_device *hdev, const struct hdac_driver *drv)
{
if (drv->id_table) {
const struct hda_device_id *id = drv->id_table;
@@ -37,7 +38,7 @@ hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv)
}
EXPORT_SYMBOL_GPL(hdac_get_device_id);
-static int hdac_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
+static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
{
if (hdac_get_device_id(dev, drv))
return 1;
@@ -45,10 +46,10 @@ static int hdac_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
return 0;
}
-static int hda_bus_match(struct device *dev, struct device_driver *drv)
+static int hda_bus_match(struct device *dev, const struct device_driver *drv)
{
struct hdac_device *hdev = dev_to_hdac_dev(dev);
- struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
+ const struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
if (hdev->type != hdrv->type)
return 0;
@@ -64,7 +65,7 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
return 1;
}
-static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
char modalias[32];
@@ -75,7 +76,7 @@ static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-struct bus_type snd_hda_bus_type = {
+const struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
.uevent = hda_uevent,
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c
index f21633cd9b38..7b276047f85a 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/sound/hda/core/hdmi_chmap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HDMI Channel map support helpers
*/
@@ -58,7 +59,7 @@ static const char * const cea_speaker_allocation_names[] = {
/*
* ELD SA bits in the CEA Speaker Allocation data block
*/
-static int eld_speaker_allocation_bits[] = {
+static const int eld_speaker_allocation_bits[] = {
[0] = FL | FR,
[1] = LFE,
[2] = FC,
@@ -249,7 +250,7 @@ void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
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",
+ j += scnprintf(buf + j, buflen - j, " %s",
cea_speaker_allocation_names[i]);
}
buf[j] = '\0'; /* necessary when j == 0 */
@@ -752,6 +753,20 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
+/* a simple sanity check for input values to chmap kcontrol */
+static int chmap_value_check(struct hdac_chmap *hchmap,
+ const struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+
+ for (i = 0; i < hchmap->channels_max; i++) {
+ if (ucontrol->value.integer.value[i] < 0 ||
+ ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -763,6 +778,10 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
unsigned char chmap[8], per_pin_chmap[8];
int i, err, ca, prepared = 0;
+ err = chmap_value_check(hchmap, ucontrol);
+ if (err < 0)
+ return err;
+
/* No monitor is connected in dyn_pcm_assign.
* It's invalid to setup the chmap
*/
@@ -773,7 +792,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
substream = snd_pcm_chmap_substream(info, ctl_idx);
if (!substream || !substream->runtime)
return 0; /* just for avoiding error from alsactl restore */
- switch (substream->runtime->status->state) {
+ switch (substream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
break;
diff --git a/sound/hda/core/i915.c b/sound/hda/core/i915.c
new file mode 100644
index 000000000000..44438c799f95
--- /dev/null
+++ b/sound/hda/core/i915.c
@@ -0,0 +1,208 @@
+// 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>
+#include <video/nomodeset.h>
+
+static int gpu_bind = -1;
+module_param(gpu_bind, int, 0644);
+MODULE_PARM_DESC(gpu_bind, "Whether to bind sound component to GPU "
+ "(1=always, 0=never, -1=on nomodeset(default))");
+
+/**
+ * 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 (!HDA_CONTROLLER_IS_HSW(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;
+
+ bus_a = bus_a->parent;
+ bus_b = bus_b->parent;
+
+ /* connected via parent bus (may be NULL!) */
+ if (bus_a == bus_b)
+ return true;
+
+ if (!bus_a || !bus_b)
+ return false;
+
+ /*
+ * 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_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") ||
+ !strcmp(dev->driver->name, "xe")) &&
+ 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)
+{
+ /* List of known platforms with no i915 support. */
+ static const struct pci_device_id denylist[] = {
+ /* CNL */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 },
+ /* LKF */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 },
+ {}
+ };
+ struct pci_dev *display_dev = NULL;
+
+ if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only()))
+ return false;
+
+ for_each_pci_dev(display_dev) {
+ if (display_dev->vendor != PCI_VENDOR_ID_INTEL ||
+ !pci_is_display(display_dev))
+ continue;
+
+ if (pci_match_id(denylist, display_dev))
+ continue;
+
+ if (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) {
+ snd_hdac_acomp_exit(bus);
+ return dev_err_probe(bus->dev, -EPROBE_DEFER,
+ "couldn't bind with audio component\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c
new file mode 100644
index 000000000000..2a9e35cddcf7
--- /dev/null
+++ b/sound/hda/core/intel-dsp-config.c
@@ -0,0 +1,843 @@
+// 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>
+
+#include <acpi/nhlt.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, 4=AVS)");
+
+#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 = PCI_DEVICE_ID_INTEL_SST_TNG,
+ },
+#endif
+/*
+ * Skylake, Kabylake, Apollolake
+ * 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_INTEL_AVS)
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_FCL,
+ },
+#else /* AVS disabled; force to legacy as SOF doesn't work for SKL or KBL */
+ {
+ .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ },
+ {
+ .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .codec_hid = &essx_83x6,
+ },
+#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 = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .codec_hid = &essx_83x6,
+ },
+#endif
+
+/*
+ * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake,
+ * RaptorLake, MeteorLake 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 = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ },
+#endif
+
+/* Coffelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+ },
+#endif
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
+/* Cometlake-LP */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ },
+/* Cometlake-H */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ },
+#endif
+
+/* Icelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ },
+#endif
+
+/* Jasper Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "Google firmware",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VERSION, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ },
+#endif
+
+/* Tigerlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_H,
+ },
+#endif
+
+/* Elkhart Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_EHL_0,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_EHL_3,
+ },
+#endif
+
+/* Alder Lake / Raptor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_S,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PX,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_M,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_PX,
+ },
+#endif
+
+/* Meteor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
+ /* Meteorlake-P */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_MTL,
+ .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 = PCI_DEVICE_ID_INTEL_HDA_MTL,
+ },
+ /* ArrowLake-S */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
+ },
+ /* ArrowLake */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
+ },
+#endif
+
+/* Lunar Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
+ /* Lunarlake-P */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
+ },
+#endif
+
+ /* Panther Lake, Wildcat Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_PANTHERLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_PTL,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_PTL_H,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_WCL,
+ },
+
+#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++) {
+ struct nhlt_acpi_table *nhlt;
+ bool ssp_found = false;
+
+ if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+ continue;
+
+ nhlt = intel_nhlt_init(&pci->dev);
+ if (!nhlt) {
+ dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n",
+ __func__, table->codec_hid->codecs[i]);
+ continue;
+ }
+
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) &&
+ intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S))
+ ssp_found = true;
+
+ intel_nhlt_free(nhlt);
+
+ if (ssp_found)
+ break;
+
+ dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n",
+ __func__, table->codec_hid->codecs[i]);
+ }
+ if (i == table->codec_hid->num_codecs)
+ continue;
+ }
+ return table;
+ }
+ return NULL;
+}
+
+static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
+{
+ int ret = 0;
+
+ acpi_nhlt_get_gbl_table();
+
+ if (acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1))
+ ret = 1;
+
+ acpi_nhlt_put_gbl_table();
+
+ 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);
+ if (!handle)
+ return -ENODEV;
+
+ 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 != PCI_VENDOR_ID_INTEL)
+ 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 PCI_DEVICE_ID_INTEL_HDA_BDW:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_0:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_2:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_3:
+ case PCI_DEVICE_ID_INTEL_HDA_BYT:
+ case PCI_DEVICE_ID_INTEL_HDA_BSW:
+ 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_dbg(&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_once(&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_once(&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_once(&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 = "LPE0F28",
+ },
+ {
+ .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/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c
new file mode 100644
index 000000000000..6d72a871bda0
--- /dev/null
+++ b/sound/hda/core/intel-nhlt.c
@@ -0,0 +1,388 @@
+// 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, bool ignore_vbps)
+{
+ 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);
+
+ /*
+ * When looking for exact match of configuration ignore the vbps
+ * from NHLT table when ignore_vbps is true
+ */
+ if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+ (ignore_vbps || 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;
+ bool ignore_vbps = false;
+ 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);
+ if (link_type == NHLT_LINK_DMIC && bps == 32 && (vbps == 24 || vbps == 32)) {
+ /*
+ * The DMIC hardware supports only one type of 32 bits sample
+ * size, which is 24 bit sampling on the MSB side and bits[1:0]
+ * are used for indicating the channel number.
+ * It has been observed that some NHLT tables have the vbps
+ * specified as 32 while some uses 24.
+ * The format these variations describe are identical, the
+ * hardware is configured and behaves the same way.
+ * Note: when the samples assumed to be vbps=32 then the 'noise'
+ * introduced by the lower two bits (channel number) have no
+ * real life implication on audio quality.
+ */
+ dev_dbg(dev,
+ " ch=%d fmt=%d rate=%d (vbps is ignored for DMIC 32bit format)\n",
+ num_ch, bps, rate);
+ ignore_vbps = true;
+ } else {
+ 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, ignore_vbps);
+ if (cfg)
+ return cfg;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
+
+int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
+ u8 virtual_bus_id)
+{
+ struct nhlt_endpoint *epnt;
+ int i;
+
+ if (!nhlt) {
+ dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n",
+ __func__, virtual_bus_id);
+ return -EINVAL;
+ }
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ /* for SSP link the virtual bus id is the SSP port number */
+ if (epnt->linktype == NHLT_LINK_SSP &&
+ epnt->virtual_bus_id == virtual_bus_id) {
+ dev_dbg(dev, "SSP%d: dev_type=%d\n", virtual_bus_id,
+ epnt->device_type);
+ return epnt->device_type;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
+ virtual_bus_id);
+
+ dev_dbg(dev, "Available endpoints:\n");
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ dev_dbg(dev,
+ "%d: link_type: %d, vbus_id: %d, dir: %d, dev_type: %d\n",
+ i, epnt->linktype, epnt->virtual_bus_id,
+ epnt->direction, epnt->device_type);
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_device_type);
diff --git a/sound/hda/core/intel-sdw-acpi.c b/sound/hda/core/intel-sdw-acpi.c
new file mode 100644
index 000000000000..d3511135f7d3
--- /dev/null
+++ b/sound/hda/core/intel-sdw-acpi.c
@@ -0,0 +1,204 @@
+// 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/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <linux/string.h>
+
+#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
+
+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 ulong ctrl_addr = 0x40000000;
+module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444);
+MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR");
+
+static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
+{
+ struct fwnode_handle *link;
+ char name[32];
+ u32 quirk_mask = 0;
+
+ /* Find master handle */
+ snprintf(name, sizeof(name),
+ "mipi-sdw-link-%hhu-subproperties", idx);
+
+ link = fwnode_get_named_child_node(fw_node, name);
+ if (!link)
+ return false;
+
+ fwnode_property_read_u32(link,
+ "intel-quirk-mask",
+ &quirk_mask);
+
+ fwnode_handle_put(link);
+
+ 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);
+ struct fwnode_handle *fwnode;
+ unsigned long list;
+ unsigned int i;
+ u32 count;
+ u32 tmp;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ fwnode = acpi_fwnode_handle(adev);
+
+ /*
+ * Found controller, find links supported
+ *
+ * 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
+ */
+ ret = fwnode_property_read_u32(fwnode, "mipi-sdw-manager-list", &tmp);
+ if (ret) {
+ ret = fwnode_property_read_u32(fwnode, "mipi-sdw-master-count", &count);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-master-count: %d\n",
+ ret);
+ return ret;
+ }
+ list = GENMASK(count - 1, 0);
+ } else {
+ list = tmp;
+ count = hweight32(list);
+ }
+
+ /* Check count is within bounds */
+ if (count > SDW_INTEL_MAX_LINKS) {
+ dev_err(&adev->dev, "Link count %d exceeds max %d\n",
+ count, SDW_INTEL_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_each_set_bit(i, &list, SDW_INTEL_MAX_LINKS) {
+ 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(fwnode, 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;
+ u64 adr;
+ int ret;
+
+ ret = acpi_get_local_u64_address(handle, &adr);
+ if (ret < 0)
+ 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 */
+
+ if (adr != ctrl_addr)
+ 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/core/local.h b/sound/hda/core/local.h
new file mode 100644
index 000000000000..5f03b203c416
--- /dev/null
+++ b/sound/hda/core/local.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Local helper macros and functions for HD-audio core drivers
+ */
+
+#ifndef __HDAC_LOCAL_H
+#define __HDAC_LOCAL_H
+
+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/hdac_regmap.c b/sound/hda/core/regmap.c
index 47a358fab132..e7b866fc52c1 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/core/regmap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Regmap support for HD-audio verbs
*
@@ -16,10 +17,10 @@
#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)
{
@@ -65,10 +66,10 @@ 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;
- for (i = 0; i < codec->vendor_verbs.used; i++) {
- unsigned int *v = snd_array_elem(&codec->vendor_verbs, i);
+ snd_array_for_each(&codec->vendor_verbs, i, v) {
if (verb == *v)
return true;
}
@@ -356,10 +357,12 @@ static const struct regmap_config hda_regmap_cfg = {
.writeable_reg = hda_writeable_reg,
.readable_reg = hda_readable_reg,
.volatile_reg = hda_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_read = hda_reg_read,
.reg_write = hda_reg_write,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
+ .disable_locking = true,
};
/**
@@ -382,7 +385,7 @@ int snd_hdac_regmap_init(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
/**
- * snd_hdac_regmap_init - Release the regmap from HDA codec
+ * snd_hdac_regmap_exit - Release the regmap from HDA codec
* @codec: the codec object
*/
void snd_hdac_regmap_exit(struct hdac_device *codec)
@@ -422,12 +425,25 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
+ guard(mutex)(&codec->regmap_lock);
if (!codec->regmap)
return hda_reg_write(codec, reg, val);
else
return regmap_write(codec->regmap, reg, val);
}
+/* 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
@@ -439,22 +455,14 @@ static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
- int err;
-
- err = reg_raw_write(codec, reg, val);
- if (err == -EAGAIN) {
- err = snd_hdac_power_up_pm(codec);
- if (err >= 0)
- err = reg_raw_write(codec, reg, val);
- snd_hdac_power_down_pm(codec);
- }
- return err;
+ 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)
{
+ guard(mutex)(&codec->regmap_lock);
if (uncached || !codec->regmap)
return hda_reg_read(codec, reg, val);
else
@@ -465,16 +473,7 @@ static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
unsigned int reg, unsigned int *val,
bool uncached)
{
- int err;
-
- err = reg_raw_read(codec, reg, val, uncached);
- if (err == -EAGAIN) {
- err = snd_hdac_power_up_pm(codec);
- if (err >= 0)
- err = reg_raw_read(codec, reg, val, uncached);
- snd_hdac_power_down_pm(codec);
- }
- return err;
+ return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
}
/**
@@ -501,11 +500,39 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
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;
+
+ guard(mutex)(&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;
+ }
+ }
+ }
+ 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 udpate
+ * @mask: bit mask to update
* @val: value to update
*
* Returns zero if successful or a negative error code.
@@ -513,19 +540,49 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
unsigned int mask, unsigned int val)
{
- unsigned int orig;
- int err;
-
- val &= mask;
- err = snd_hdac_regmap_read_raw(codec, reg, &orig);
- if (err < 0)
- return err;
- val |= orig & ~mask;
- if (val == orig)
- return 0;
- err = snd_hdac_regmap_write_raw(codec, reg, val);
- if (err < 0)
- return err;
- return 1;
+ 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)
+{
+ if (!codec->regmap)
+ return reg_raw_update(codec, reg, mask, val);
+
+ guard(mutex)(&codec->regmap_lock);
+ /* Discard any updates to already initialised registers. */
+ if (!regcache_reg_cached(codec->regmap, reg))
+ return regmap_update_bits(codec->regmap, reg, mask, val);
+ return 0;
+}
+
+/**
+ * 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)
+{
+ guard(mutex)(&codec->regmap_lock);
+ if (codec->regmap)
+ regcache_sync(codec->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/core/stream.c
index e1472c7ab6c1..579ec544ef4a 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/core/stream.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HD-audio stream operations
*/
@@ -6,12 +7,80 @@
#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
@@ -35,47 +104,75 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
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
- * @fresh_start: false = wallclock timestamp relative to period wallclock
*
* Start a stream, set start_wallclk and set the running flag.
*/
-void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
+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);
- if (!fresh_start)
- azx_dev->start_wallclk -= azx_dev->period_wallclk;
/* enable SIE */
- snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index);
+ 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,
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL,
+ 0, SD_CTL_DMA_START | SD_INT_MASK);
+ else
+ 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 - stop a stream DMA
+ * snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
* @azx_dev: HD-audio core stream to stop
*/
-void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
+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;
}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
/**
* snd_hdac_stream_stop - stop a stream
@@ -94,37 +191,57 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
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 timeout;
+ 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);
- udelay(3);
- timeout = 300;
- do {
- val = snd_hdac_stream_readb(azx_dev, SD_CTL) &
- SD_CTL_STREAM_RESET;
- if (val)
- break;
- } while (--timeout);
- val &= ~SD_CTL_STREAM_RESET;
- snd_hdac_stream_writeb(azx_dev, SD_CTL, val);
- udelay(3);
-
- timeout = 300;
- /* waiting for hardware to report that the stream is out of reset */
- do {
- val = snd_hdac_stream_readb(azx_dev, SD_CTL) &
- SD_CTL_STREAM_RESET;
- if (!val)
- break;
- } while (--timeout);
+
+ /* 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)
@@ -135,12 +252,15 @@ 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
+ * @code_loading: Whether the stream is for PCM or code-loading.
*/
-int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading)
{
struct hdac_bus *bus = azx_dev->bus;
struct snd_pcm_runtime *runtime;
unsigned int val;
+ u16 reg;
+ int ret;
if (azx_dev->substream)
runtime = azx_dev->substream->runtime;
@@ -183,11 +303,15 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
/* set the interrupt enable bits in the descriptor control register */
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
- if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size =
- snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
+ if (!code_loading) {
+ /* Once SDxFMT is set, the controller programs SDxFIFOS to non-zero value. */
+ ret = snd_hdac_stream_readw_poll(azx_dev, SD_FIFOSIZE, reg,
+ reg & AZX_SD_FIFOSIZE_MASK, 3, 300);
+ if (ret)
+ dev_dbg(bus->dev, "polling SD_FIFOSIZE 0x%04x failed: %d\n",
+ AZX_REG_SD_FIFOSIZE, ret);
+ azx_dev->fifo_size = reg;
+ }
/* when LPIB delay correction gives a small negative value,
* we ignore it; currently set the threshold statically to
@@ -241,9 +365,12 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
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);
+ int key = (substream->number << 2) | (substream->stream + 1);
+ if (substream->pcm)
+ key |= (substream->pcm->device << 16);
+
+ guard(spinlock_irq)(&bus->reg_lock);
list_for_each_entry(azx_dev, &bus->stream_list, list) {
if (azx_dev->direction != substream->stream)
continue;
@@ -257,18 +384,31 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
res = azx_dev;
}
if (res) {
- spin_lock_irq(&bus->reg_lock);
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
*
@@ -278,11 +418,8 @@ void snd_hdac_stream_release(struct hdac_stream *azx_dev)
{
struct hdac_bus *bus = azx_dev->bus;
- spin_lock_irq(&bus->reg_lock);
- azx_dev->opened = 0;
- azx_dev->running = 0;
- azx_dev->substream = NULL;
- spin_unlock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
+ snd_hdac_stream_release_locked(azx_dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
@@ -353,20 +490,21 @@ static int setup_bdle(struct hdac_bus *bus,
}
/**
- * snd_hdac_stream_setup_periods - set up BDL entries
+ * snd_hdac_stream_setup_bdle - set up BDL entries
* @azx_dev: HD-audio core stream to set up
+ * @dmab: allocated DMA buffer
+ * @runtime: substream runtime, optional
*
* 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)
+static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab,
+ struct snd_pcm_runtime *runtime)
{
struct hdac_bus *bus = azx_dev->bus;
- struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- __le32 *bdl;
int i, ofs, periods, period_bytes;
int pos_adj, pos_align;
+ __le32 *bdl;
/* reset BDL address */
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
@@ -381,22 +519,20 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
azx_dev->frags = 0;
pos_adj = bus->bdl_pos_adj;
- if (!azx_dev->no_period_wakeup && pos_adj > 0) {
+ if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
pos_align = pos_adj;
- pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+ pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
if (!pos_adj)
pos_adj = pos_align;
else
- pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
- pos_align;
+ 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, snd_pcm_get_dma_buf(substream),
- azx_dev,
+ ofs = setup_bdle(bus, dmab, azx_dev,
&bdl, ofs, pos_adj, true);
if (ofs < 0)
goto error;
@@ -406,13 +542,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes - pos_adj, 0);
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes - pos_adj, 0);
else
- ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes,
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes,
!azx_dev->no_period_wakeup);
if (ofs < 0)
goto error;
@@ -420,10 +554,37 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
return 0;
error:
- dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
azx_dev->bufsize, period_bytes);
return -EINVAL;
}
+
+/**
+ * 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 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;
+
+ 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;
+ }
+
+ return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime);
+}
EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
/**
@@ -437,26 +598,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val)
{
-
- unsigned int bufsize, period_bytes;
struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ unsigned int bufsize, period_bytes;
+ unsigned int no_period_wakeup;
int err;
- if (!substream)
+ 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;
- runtime = substream->runtime;
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
+ }
if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes ||
format_val != azx_dev->format_val ||
- runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
+ 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 = runtime->no_period_wakeup;
+ azx_dev->no_period_wakeup = no_period_wakeup;
err = snd_hdac_stream_setup_periods(azx_dev);
if (err < 0)
return err;
@@ -465,7 +632,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
-static u64 azx_cc_read(const struct cyclecounter *cc)
+static u64 azx_cc_read(struct cyclecounter *cc)
{
struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
@@ -483,17 +650,11 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev,
cc->mask = CLOCKSOURCE_MASK(32);
/*
- * Converting from 24 MHz to ns means applying a 125/3 factor.
- * To avoid any saturation issues in intermediate operations,
- * the 125 factor is applied first. The division is applied
- * last after reading the timecounter value.
- * Applying the 1/3 factor as part of the multiplication
- * requires at least 20 bits for a decent precision, however
- * overflows occur after about 4 hours or less, not a option.
+ * Calculate the optimal mult/shift values. The counter wraps
+ * around after ~178.9 seconds.
*/
-
- cc->mult = 125; /* saturation after 195 years */
- cc->shift = 0;
+ 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);
@@ -510,6 +671,7 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev,
* snd_hdac_stream_timecounter_init - initialize time counter
* @azx_dev: HD-audio core stream (master stream)
* @streams: bit flags of streams to set up
+ * @start: true for PCM trigger start, false for other cases
*
* Initializes the time counter of streams marked by the bit flags (each
* bit corresponds to the stream index).
@@ -517,26 +679,28 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev,
* updated accordingly, too.
*/
void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
- unsigned int streams)
+ unsigned int streams, bool start)
{
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;
+
+ if (!start)
+ goto skip;
list_for_each_entry(s, &bus->stream_list, list) {
- if (streams & (1 << i)) {
+ if ((streams & (1 << s->index))) {
azx_timecounter_init(s, inited, cycle_last);
if (!inited) {
inited = true;
cycle_last = s->tc.cycle_last;
}
}
- i++;
}
+skip:
snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
runtime->trigger_tstamp_latched = true;
}
@@ -545,7 +709,9 @@ 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)
@@ -565,7 +731,7 @@ void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
/**
- * snd_hdac_stream_sync - sync with start/strop trigger operation
+ * 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
@@ -577,27 +743,33 @@ 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;
+ int 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)) {
- 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++;
+ if (!(streams & (1 << s->index)))
+ 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);
}
}
- i++;
}
if (!nwait)
break;
@@ -606,6 +778,131 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
}
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_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
@@ -621,53 +918,42 @@ 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;
- u32 *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;
+ guard(snd_hdac_dsp_lock)(azx_dev);
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ if (azx_dev->running || azx_dev->locked)
+ return -EBUSY;
+ azx_dev->locked = true;
}
- azx_dev->locked = true;
- spin_unlock_irq(&bus->reg_lock);
- err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
- byte_size, bufp);
+ 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;
+ /* It is recommended to transfer the firmware in two or more chunks. */
+ azx_dev->period_bytes = byte_size / 2;
azx_dev->format_val = format;
+ azx_dev->no_period_wakeup = 1;
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 = (u32 *)azx_dev->bdl.area;
- err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0);
+ err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL);
if (err < 0)
goto error;
- snd_hdac_stream_setup(azx_dev);
- snd_hdac_dsp_unlock(azx_dev);
+ snd_hdac_stream_setup(azx_dev, true);
return azx_dev->stream_tag;
error:
- bus->io_ops->dma_free_pages(bus, bufp);
+ 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);
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ azx_dev->locked = false;
+ }
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
@@ -680,7 +966,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
{
if (start)
- snd_hdac_stream_start(azx_dev, true);
+ snd_hdac_stream_start(azx_dev);
else
snd_hdac_stream_stop(azx_dev);
}
@@ -699,7 +985,7 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
if (!dmab->area || !azx_dev->locked)
return;
- snd_hdac_dsp_lock(azx_dev);
+ guard(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);
@@ -708,13 +994,11 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
- bus->io_ops->dma_free_pages(bus, dmab);
+ snd_dma_free_pages(dmab);
dmab->area = NULL;
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_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/core/sysfs.c
index fb2aa344981e..bffe52859dba 100644
--- a/sound/hda/hdac_sysfs.c
+++ b/sound/hda/core/sysfs.c
@@ -22,7 +22,7 @@ static ssize_t type##_show(struct device *dev, \
char *buf) \
{ \
struct hdac_device *codec = dev_to_hdac_dev(dev); \
- return sprintf(buf, "0x%x\n", codec->type); \
+ return sysfs_emit(buf, "0x%x\n", codec->type); \
} \
static DEVICE_ATTR_RO(type)
@@ -32,8 +32,8 @@ static ssize_t type##_show(struct device *dev, \
char *buf) \
{ \
struct hdac_device *codec = dev_to_hdac_dev(dev); \
- return sprintf(buf, "%s\n", \
- codec->type ? codec->type : ""); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
} \
static DEVICE_ATTR_RO(type)
@@ -66,7 +66,7 @@ static struct attribute *hdac_dev_attrs[] = {
NULL
};
-static struct attribute_group hdac_dev_attr_group = {
+static const struct attribute_group hdac_dev_attr_group = {
.attrs = hdac_dev_attrs,
};
@@ -148,7 +148,7 @@ static void widget_release(struct kobject *kobj)
kfree(kobj);
}
-static struct kobj_type widget_ktype = {
+static const struct kobj_type widget_ktype = {
.release = widget_release,
.sysfs_ops = &widget_sysfs_ops,
};
@@ -161,16 +161,16 @@ static struct kobj_type widget_ktype = {
static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
- return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid));
+ return sysfs_emit(buf, "0x%08x\n", snd_hdac_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)
+ if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
+ 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,
@@ -178,18 +178,18 @@ static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
{
unsigned int val;
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ if (snd_hdac_get_wcaps_type(snd_hdac_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 sprintf(buf, "0x%08x\n", val);
+ 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))) {
+ switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) {
case AC_WID_AUD_OUT:
case AC_WID_AUD_IN:
return true;
@@ -203,8 +203,8 @@ static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
{
if (!has_pcm_cap(codec, nid))
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
+ 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,
@@ -212,42 +212,42 @@ static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
{
if (!has_pcm_cap(codec, nid))
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
+ 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))
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
+ 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))
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
+ 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))
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER))
return 0;
- return sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
+ 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 sprintf(buf, "0x%08x\n",
- snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
+ 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,
@@ -261,8 +261,8 @@ static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
if (nconns <= 0)
return nconns;
for (i = 0; i < nconns; i++)
- ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]);
- ret += sprintf(buf + ret, "\n");
+ ret += sysfs_emit_at(buf, ret, "%s0x%02x", i ? " " : "", list[i]);
+ ret += sysfs_emit_at(buf, ret, "\n");
return ret;
}
@@ -346,8 +346,10 @@ static int add_widget_node(struct kobject *parent, hda_nid_t nid,
return -ENOMEM;
kobject_init(kobj, &widget_ktype);
err = kobject_add(kobj, parent, "%02x", nid);
- if (err < 0)
+ if (err < 0) {
+ kobject_put(kobj);
return err;
+ }
err = sysfs_create_group(kobj, group);
if (err < 0) {
kobject_put(kobj);
@@ -395,6 +397,7 @@ static int widget_tree_create(struct hdac_device *codec)
return 0;
}
+/* call with codec->widget_lock held */
int hda_widget_sysfs_init(struct hdac_device *codec)
{
int err;
@@ -411,11 +414,13 @@ int hda_widget_sysfs_init(struct hdac_device *codec)
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)
{
@@ -425,7 +430,7 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec,
int i;
if (!codec->widgets)
- return hda_widget_sysfs_init(codec);
+ return 0;
tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
if (!tree)
diff --git a/sound/hda/trace.c b/sound/hda/core/trace.c
index ca2d6bd94518..ca2d6bd94518 100644
--- a/sound/hda/trace.c
+++ b/sound/hda/core/trace.c
diff --git a/sound/hda/trace.h b/sound/hda/core/trace.h
index 70af6c815089..280c42f3eb75 100644
--- a/sound/hda/trace.h
+++ b/sound/hda/core/trace.h
@@ -19,37 +19,48 @@ struct hdac_codec;
TRACE_EVENT(hda_send_cmd,
TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
TP_ARGS(bus, cmd),
- TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, cmd)
+ ),
TP_fast_assign(
- snprintf(__get_str(msg), HDAC_MSG_MAX,
- "[%s:%d] val=0x%08x",
- dev_name((bus)->dev), (cmd) >> 28, cmd);
+ __assign_str(name);
+ __entry->cmd = cmd;
),
- TP_printk("%s", __get_str(msg))
+ 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(__dynamic_array(char, msg, HDAC_MSG_MAX)),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, addr)
+ __field(u32, res)
+ ),
TP_fast_assign(
- snprintf(__get_str(msg), HDAC_MSG_MAX,
- "[%s:%d] val=0x%08x",
- dev_name((bus)->dev), addr, res);
+ __assign_str(name);
+ __entry->addr = addr;
+ __entry->res = res;
),
- TP_printk("%s", __get_str(msg))
+ 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(__dynamic_array(char, msg, HDAC_MSG_MAX)),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, res)
+ __field(u32, res_ex)
+ ),
TP_fast_assign(
- snprintf(__get_str(msg), HDAC_MSG_MAX,
- "[%s:%d] res=0x%08x, res_ex=0x%08x",
- dev_name((bus)->dev), res_ex & 0x0f, res, res_ex);
+ __assign_str(name);
+ __entry->res = res;
+ __entry->res_ex = res_ex;
),
- TP_printk("%s", __get_str(msg))
+ 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,
diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile
deleted file mode 100644
index 9b6f641c7777..000000000000
--- a/sound/hda/ext/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index 0daf31383084..000000000000
--- a/sound/hda/ext/hdac_ext_bus.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * hdac-ext-bus.c - HD-audio extended core bus functions.
- *
- * Copyright (C) 2014-2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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; 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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#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");
-
-static void hdac_ext_writel(u32 value, u32 __iomem *addr)
-{
- writel(value, addr);
-}
-
-static u32 hdac_ext_readl(u32 __iomem *addr)
-{
- return readl(addr);
-}
-
-static void hdac_ext_writew(u16 value, u16 __iomem *addr)
-{
- writew(value, addr);
-}
-
-static u16 hdac_ext_readw(u16 __iomem *addr)
-{
- return readw(addr);
-}
-
-static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
-{
- writeb(value, addr);
-}
-
-static u8 hdac_ext_readb(u8 __iomem *addr)
-{
- return readb(addr);
-}
-
-static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
- size_t size, struct snd_dma_buffer *buf)
-{
- return snd_dma_alloc_pages(type, bus->dev, size, buf);
-}
-
-static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
-{
- snd_dma_free_pages(buf);
-}
-
-static const struct hdac_io_ops hdac_ext_default_io = {
- .reg_writel = hdac_ext_writel,
- .reg_readl = hdac_ext_readl,
- .reg_writew = hdac_ext_writew,
- .reg_readw = hdac_ext_readw,
- .reg_writeb = hdac_ext_writeb,
- .reg_readb = hdac_ext_readb,
- .dma_alloc_pages = hdac_ext_dma_alloc_pages,
- .dma_free_pages = hdac_ext_dma_free_pages,
-};
-
-/**
- * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
- * @ebus: the pointer to extended bus object
- * @dev: device pointer
- * @ops: bus verb operators
- * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
- * default ops
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
- const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops)
-{
- int ret;
- static int idx;
-
- /* check if io ops are provided, if not load the defaults */
- if (io_ops == NULL)
- io_ops = &hdac_ext_default_io;
-
- ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops);
- if (ret < 0)
- return ret;
-
- INIT_LIST_HEAD(&ebus->hlink_list);
- ebus->idx = idx++;
-
- mutex_init(&ebus->lock);
- ebus->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
- * @ebus: the pointer to extended bus object
- */
-void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus)
-{
- snd_hdac_bus_exit(&ebus->bus);
- WARN_ON(!list_empty(&ebus->hlink_list));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
-
-static void default_release(struct device *dev)
-{
- snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev));
-}
-
-/**
- * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device
- * @ebus: hdac extended bus to attach to
- * @addr: codec address
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
-{
- struct hdac_ext_device *edev;
- struct hdac_device *hdev = NULL;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- char name[15];
- int ret;
-
- edev = kzalloc(sizeof(*edev), GFP_KERNEL);
- if (!edev)
- return -ENOMEM;
- hdev = &edev->hdev;
- edev->ebus = ebus;
-
- snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
-
- ret = snd_hdac_device_init(hdev, bus, name, addr);
- if (ret < 0) {
- dev_err(bus->dev, "device init failed for hdac device\n");
- return ret;
- }
- hdev->type = HDA_DEV_ASOC;
- hdev->dev.release = default_release;
-
- ret = snd_hdac_device_register(hdev);
- if (ret) {
- dev_err(bus->dev, "failed to register hdac device\n");
- snd_hdac_ext_bus_device_exit(hdev);
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init);
-
-/**
- * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device
- * @hdev: hdac device to clean up
- */
-void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev)
-{
- struct hdac_ext_device *edev = to_ehdac_device(hdev);
-
- snd_hdac_device_exit(hdev);
- kfree(edev);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit);
-
-/**
- * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
- *
- * @ebus: HD-audio extended bus
- */
-void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus)
-{
- 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, &ebus->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_ext_driver *get_edrv(struct device *dev)
-{
- struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
- struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv);
-
- return edrv;
-}
-
-static inline struct hdac_ext_device *get_edev(struct device *dev)
-{
- struct hdac_device *hdev = dev_to_hdac_dev(dev);
- struct hdac_ext_device *edev = to_ehdac_device(hdev);
-
- return edev;
-}
-
-static int hda_ext_drv_probe(struct device *dev)
-{
- return (get_edrv(dev))->probe(get_edev(dev));
-}
-
-static int hdac_ext_drv_remove(struct device *dev)
-{
- return (get_edrv(dev))->remove(get_edev(dev));
-}
-
-static void hdac_ext_drv_shutdown(struct device *dev)
-{
- return (get_edrv(dev))->shutdown(get_edev(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_ext_driver *drv)
-{
- drv->hdac.type = HDA_DEV_ASOC;
- drv->hdac.driver.bus = &snd_hda_bus_type;
- /* we use default match */
-
- if (drv->probe)
- drv->hdac.driver.probe = hda_ext_drv_probe;
- if (drv->remove)
- drv->hdac.driver.remove = hdac_ext_drv_remove;
- if (drv->shutdown)
- drv->hdac.driver.shutdown = hdac_ext_drv_shutdown;
-
- return driver_register(&drv->hdac.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_ext_driver *drv)
-{
- driver_unregister(&drv->hdac.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
deleted file mode 100644
index 84f3b8168716..000000000000
--- a/sound/hda/ext/hdac_ext_controller.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * hdac-ext-controller.c - HD-audio extended controller functions.
- *
- * Copyright (C) 2014-2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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; 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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/hda_register.h>
-#include <sound/hdaudio_ext.h>
-
-/*
- * maximum HDAC capablities we should parse to avoid endless looping:
- * currently we have 4 extended caps, so this is future proof for now.
- * extend when this limit is seen meeting in real HW
- */
-#define HDAC_MAX_CAPS 10
-
-/*
- * 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
- * @ebus: HD-audio extended core bus
- * @enable: flag to turn on/off the capability
- */
-void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- 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, 0, 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
- * @ebus: HD-audio extended core bus
- * @enable: flag to enable/disable interrupt
- */
-void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- 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, 0, 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
- * @ebus: HD-audio extended core bus
- *
- * 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_ext_bus *ebus)
-{
- int idx;
- u32 link_count;
- struct hdac_ext_link *hlink;
- struct hdac_bus *bus = &ebus->bus;
-
- 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, &ebus->hlink_list);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
-
-/**
- * snd_hdac_link_free_all- free hdac extended link objects
- *
- * @ebus: HD-audio ext core bus
- */
-
-void snd_hdac_link_free_all(struct hdac_ext_bus *ebus)
-{
- struct hdac_ext_link *l;
-
- while (!list_empty(&ebus->hlink_list)) {
- l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list);
- list_del(&l->list);
- kfree(l);
- }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
-
-/**
- * snd_hdac_ext_bus_get_link_index - get link based on codec name
- * @ebus: HD-audio extended core bus
- * @codec_name: codec name
- */
-struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus,
- const char *codec_name)
-{
- int i;
- struct hdac_ext_link *hlink = NULL;
- int bus_idx, addr;
-
- if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
- return NULL;
- if (ebus->idx != bus_idx)
- return NULL;
-
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
- for (i = 0; i < HDA_MAX_CODECS; i++) {
- if (hlink->lsdiid & (0x1 << addr))
- return hlink;
- }
- }
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link);
-
-static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
-{
- int timeout;
- u32 val;
- int mask = (1 << AZX_MLCTL_CPA_SHIFT);
-
- udelay(3);
- timeout = 150;
-
- do {
- val = readl(link->ml_addr + AZX_REG_ML_LCTL);
- if (enable) {
- if (((val & mask) >> AZX_MLCTL_CPA_SHIFT))
- return 0;
- } else {
- if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT))
- return 0;
- }
- udelay(3);
- } while (--timeout);
-
- return -EIO;
-}
-
-/**
- * snd_hdac_ext_bus_link_power_up -power up hda link
- * @link: HD-audio extended link
- */
-int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link)
-{
- snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
-
- return check_hdac_link_power_active(link, true);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
-
-/**
- * snd_hdac_ext_bus_link_power_down -power down hda link
- * @link: HD-audio extended link
- */
-int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link)
-{
- snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
-
- return check_hdac_link_power_active(link, false);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
-
-/**
- * snd_hdac_ext_bus_link_power_up_all -power up all hda link
- * @ebus: HD-audio extended bus
- */
-int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus)
-{
- struct hdac_ext_link *hlink = NULL;
- int ret;
-
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
- snd_hdac_updatel(hlink->ml_addr,
- AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
- ret = check_hdac_link_power_active(hlink, true);
- 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
- * @ebus: HD-audio extended bus
- */
-int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
-{
- struct hdac_ext_link *hlink = NULL;
- int ret;
-
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
- snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
- ret = check_hdac_link_power_active(hlink, false);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
-
-int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
- struct hdac_ext_link *link)
-{
- int ret = 0;
-
- mutex_lock(&ebus->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 (++link->ref_count == 1) {
- if (!ebus->cmd_dma_state) {
- snd_hdac_bus_init_cmd_io(&ebus->bus);
- ebus->cmd_dma_state = true;
- }
-
- ret = snd_hdac_ext_bus_link_power_up(link);
- }
-
- mutex_unlock(&ebus->lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
-
-int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
- struct hdac_ext_link *link)
-{
- int ret = 0;
- struct hdac_ext_link *hlink;
- bool link_up = false;
-
- mutex_lock(&ebus->lock);
-
- /*
- * if we move from 1 to 0, count will be 0
- * so power down this link as well
- */
- if (--link->ref_count == 0) {
- ret = snd_hdac_ext_bus_link_power_down(link);
-
- /*
- * now check if all links are off, if so turn off
- * cmd dma as well
- */
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
- if (hlink->ref_count) {
- link_up = true;
- break;
- }
- }
-
- if (!link_up) {
- snd_hdac_bus_stop_cmd_io(&ebus->bus);
- ebus->cmd_dma_state = false;
- }
- }
-
- mutex_unlock(&ebus->lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
deleted file mode 100644
index c96d7a7a36af..000000000000
--- a/sound/hda/ext/hdac_ext_stream.c
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * hdac-ext-stream.c - HD-audio extended stream operations.
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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; 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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/hda_register.h>
-#include <sound/hdaudio_ext.h>
-
-/**
- * snd_hdac_ext_stream_init - initialize each stream (aka device)
- * @ebus: HD-audio ext core bus
- * @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
- */
-void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *stream,
- int idx, int direction, int tag)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- if (bus->ppcap) {
- stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
- AZX_PPHC_INTERVAL * idx;
-
- stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
- AZX_PPLC_MULTI * ebus->num_streams +
- AZX_PPLC_INTERVAL * idx;
- }
-
- if (bus->spbcap) {
- stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
- AZX_SPB_INTERVAL * idx +
- AZX_SPB_SPIB;
-
- stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
- AZX_SPB_INTERVAL * idx +
- AZX_SPB_MAXFIFO;
- }
-
- if (bus->drsmcap)
- stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
- AZX_DRSM_INTERVAL * idx;
-
- stream->decoupled = false;
- snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
-
-/**
- * snd_hdac_ext_stream_init_all - create and initialize the stream objects
- * for an extended hda bus
- * @ebus: HD-audio ext 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_ext_bus *ebus, 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 *stream =
- kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
- tag = ++stream_tag;
- snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag);
- idx++;
- }
-
- return 0;
-
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
-
-/**
- * snd_hdac_stream_free_all - free hdac extended stream objects
- *
- * @ebus: HD-audio ext core bus
- */
-void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
-{
- struct hdac_stream *s, *_s;
- struct hdac_ext_stream *stream;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
- stream = stream_to_hdac_ext_stream(s);
- snd_hdac_ext_stream_decouple(ebus, stream, false);
- list_del(&s->list);
- kfree(stream);
- }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
-
-/**
- * snd_hdac_ext_stream_decouple - decouple the hdac stream
- * @ebus: HD-audio ext core bus
- * @stream: HD-audio ext core stream object to initialize
- * @decouple: flag to decouple
- */
-void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *stream, bool decouple)
-{
- struct hdac_stream *hstream = &stream->hstream;
- struct hdac_bus *bus = &ebus->bus;
- u32 val;
- int mask = AZX_PPCTL_PROCEN(hstream->index);
-
- spin_lock_irq(&bus->reg_lock);
- 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);
-
- stream->decoupled = decouple;
- spin_unlock_irq(&bus->reg_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
-
-/**
- * snd_hdac_ext_linkstream_start - start a stream
- * @stream: HD-audio ext core stream to start
- */
-void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream)
-{
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
-
-/**
- * snd_hdac_ext_link_stream_clear - stop a stream DMA
- * @stream: HD-audio ext core stream to stop
- */
-void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream)
-{
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
-
-/**
- * snd_hdac_ext_link_stream_reset - reset a stream
- * @stream: HD-audio ext core stream to reset
- */
-void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream)
-{
- unsigned char val;
- int timeout;
-
- snd_hdac_ext_link_stream_clear(stream);
-
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_STRST);
- udelay(3);
- timeout = 50;
- do {
- val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) &
- AZX_PPLCCTL_STRST;
- if (val)
- break;
- udelay(3);
- } while (--timeout);
- val &= ~AZX_PPLCCTL_STRST;
- writel(val, 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(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
- if (!val)
- break;
- udelay(3);
- } while (--timeout);
-
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
-
-/**
- * snd_hdac_ext_link_stream_setup - set up the SD for streaming
- * @stream: HD-audio ext core stream to set up
- * @fmt: stream format
- */
-int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt)
-{
- struct hdac_stream *hstream = &stream->hstream;
- unsigned int val;
-
- /* make sure the run bit is zero for SD */
- snd_hdac_ext_link_stream_clear(stream);
- /* program the stream_tag */
- val = readl(stream->pplc_addr + AZX_REG_PPLCCTL);
- val = (val & ~AZX_PPLCCTL_STRM_MASK) |
- (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
- writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
-
- /* program the stream format */
- writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
-
-/**
- * snd_hdac_ext_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_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_link_set_stream_id);
-
-/**
- * snd_hdac_ext_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_link_clear_stream_id(struct hdac_ext_link *link,
- int stream)
-{
- snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, 0, (1 << stream));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
-
-static struct hdac_ext_stream *
-hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
- struct snd_pcm_substream *substream)
-{
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
-
- if (!hbus->ppcap) {
- dev_err(hbus->dev, "stream type not supported\n");
- return NULL;
- }
-
- list_for_each_entry(stream, &hbus->stream_list, list) {
- struct hdac_ext_stream *hstream = container_of(stream,
- struct hdac_ext_stream,
- hstream);
- if (stream->direction != substream->stream)
- continue;
-
- /* check if decoupled stream and not in use is available */
- if (hstream->decoupled && !hstream->link_locked) {
- res = hstream;
- break;
- }
-
- if (!hstream->link_locked) {
- snd_hdac_ext_stream_decouple(ebus, hstream, true);
- res = hstream;
- break;
- }
- }
- if (res) {
- spin_lock_irq(&hbus->reg_lock);
- res->link_locked = 1;
- res->link_substream = substream;
- spin_unlock_irq(&hbus->reg_lock);
- }
- return res;
-}
-
-static struct hdac_ext_stream *
-hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
- struct snd_pcm_substream *substream)
-{
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
-
- if (!hbus->ppcap) {
- dev_err(hbus->dev, "stream type not supported\n");
- return NULL;
- }
-
- list_for_each_entry(stream, &hbus->stream_list, list) {
- struct hdac_ext_stream *hstream = container_of(stream,
- struct hdac_ext_stream,
- hstream);
- if (stream->direction != substream->stream)
- continue;
-
- if (!stream->opened) {
- if (!hstream->decoupled)
- snd_hdac_ext_stream_decouple(ebus, hstream, true);
- res = hstream;
- break;
- }
- }
- if (res) {
- spin_lock_irq(&hbus->reg_lock);
- res->hstream.opened = 1;
- res->hstream.running = 0;
- res->hstream.substream = substream;
- spin_unlock_irq(&hbus->reg_lock);
- }
-
- return res;
-}
-
-/**
- * snd_hdac_ext_stream_assign - assign a stream for the PCM
- * @ebus: HD-audio ext 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_ext_bus *ebus,
- struct snd_pcm_substream *substream,
- int type)
-{
- struct hdac_ext_stream *hstream = NULL;
- struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
-
- switch (type) {
- case HDAC_EXT_STREAM_TYPE_COUPLED:
- stream = snd_hdac_stream_assign(hbus, substream);
- if (stream)
- hstream = container_of(stream,
- struct hdac_ext_stream, hstream);
- return hstream;
-
- case HDAC_EXT_STREAM_TYPE_HOST:
- return hdac_ext_host_stream_assign(ebus, substream);
-
- case HDAC_EXT_STREAM_TYPE_LINK:
- return hdac_ext_link_stream_assign(ebus, substream);
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
-
-/**
- * snd_hdac_ext_stream_release - release the assigned stream
- * @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 *stream, int type)
-{
- struct hdac_bus *bus = stream->hstream.bus;
- struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
-
- switch (type) {
- case HDAC_EXT_STREAM_TYPE_COUPLED:
- snd_hdac_stream_release(&stream->hstream);
- break;
-
- case HDAC_EXT_STREAM_TYPE_HOST:
- if (stream->decoupled && !stream->link_locked)
- snd_hdac_ext_stream_decouple(ebus, stream, false);
- snd_hdac_stream_release(&stream->hstream);
- break;
-
- case HDAC_EXT_STREAM_TYPE_LINK:
- if (stream->decoupled && !stream->hstream.opened)
- snd_hdac_ext_stream_decouple(ebus, stream, false);
- spin_lock_irq(&bus->reg_lock);
- stream->link_locked = 0;
- 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_stream_spbcap_enable - enable SPIB for a stream
- * @ebus: HD-audio ext core bus
- * @enable: flag to enable/disable SPIB
- * @index: stream index for which SPIB need to be enabled
- */
-void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
- bool enable, int index)
-{
- u32 mask = 0;
- u32 register_mask = 0;
- struct hdac_bus *bus = &ebus->bus;
-
- if (!bus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL\n");
- return;
- }
-
- mask |= (1 << index);
-
- register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
-
- mask |= register_mask;
-
- if (enable)
- snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
- else
- snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
-
-/**
- * snd_hdac_ext_stream_set_spib - sets the spib value of a stream
- * @ebus: HD-audio ext core bus
- * @stream: hdac_ext_stream
- * @value: spib value to set
- */
-int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *stream, u32 value)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- if (!bus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL\n");
- return -EINVAL;
- }
-
- writel(value, stream->spib_addr);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
-
-/**
- * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
- * @ebus: HD-audio ext core bus
- * @stream: hdac_ext_stream
- *
- * Return maxfifo for the stream
- */
-int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *stream)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- if (!bus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL\n");
- return -EINVAL;
- }
-
- return readl(stream->fifo_addr);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
-
-
-/**
- * snd_hdac_ext_stop_streams - stop all stream if running
- * @ebus: HD-audio ext core bus
- */
-void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
-{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct hdac_stream *stream;
-
- if (bus->chip_init) {
- list_for_each_entry(stream, &bus->stream_list, list)
- snd_hdac_stream_stop(stream);
- snd_hdac_bus_stop_chip(bus);
- }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
-
-/**
- * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
- * @ebus: HD-audio ext core bus
- * @enable: flag to enable/disable DRSM
- * @index: stream index for which DRSM need to be enabled
- */
-void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
- bool enable, int index)
-{
- u32 mask = 0;
- u32 register_mask = 0;
- struct hdac_bus *bus = &ebus->bus;
-
- if (!bus->drsmcap) {
- dev_err(bus->dev, "Address of DRSM capability is NULL\n");
- return;
- }
-
- mask |= (1 << index);
-
- register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
-
- mask |= register_mask;
-
- if (enable)
- snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
- else
- snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
-
-/**
- * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
- * @ebus: HD-audio ext core bus
- * @stream: hdac_ext_stream
- * @value: dpib value to set
- */
-int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *stream, u32 value)
-{
- struct hdac_bus *bus = &ebus->bus;
-
- if (!bus->drsmcap) {
- dev_err(bus->dev, "Address of DRSM capability is NULL\n");
- return -EINVAL;
- }
-
- writel(value, stream->dpibr_addr);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
-
-/**
- * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
- * @ebus: HD-audio ext core bus
- * @stream: hdac_ext_stream
- * @value: lpib value to set
- */
-int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value)
-{
- snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
deleted file mode 100644
index cbe818eda336..000000000000
--- a/sound/hda/hdac_i915.c
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * hdac_i915.c - routines for sync between HD-A core and i915 display driver
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/component.h>
-#include <drm/i915_component.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_i915.h>
-#include <sound/hda_register.h>
-
-static struct i915_audio_component *hdac_acomp;
-
-/**
- * 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 i915 graphics.
- *
- * 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 i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp || !acomp->ops)
- return -ENODEV;
-
- if (!acomp->ops->codec_wake_override) {
- dev_warn(bus->dev,
- "Invalid codec wake callback\n");
- 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
- * @enable: power up or down
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function manages a refcount and calls the i915 get_power() and
- * put_power() ops accordingly, toggling the codec wakeup, too.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
-{
- struct i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp || !acomp->ops)
- return -ENODEV;
-
- dev_dbg(bus->dev, "display power %s\n",
- enable ? "enable" : "disable");
-
- if (enable) {
- if (!bus->i915_power_refcount++) {
- acomp->ops->get_power(acomp->dev);
- snd_hdac_set_codec_wakeup(bus, true);
- snd_hdac_set_codec_wakeup(bus, false);
- }
- } else {
- WARN_ON(!bus->i915_power_refcount);
- if (!--bus->i915_power_refcount)
- acomp->ops->put_power(acomp->dev);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_display_power);
-
-#define CONTROLLER_IN_GPU(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 i915_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 (!CONTROLLER_IN_GPU(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);
-
-/* 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 pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
-{
- int base_nid;
-
- switch (codec->vendor_id) {
- case 0x80860054: /* ILK */
- case 0x80862804: /* ILK */
- case 0x80862882: /* VLV */
- base_nid = 3;
- break;
- default:
- base_nid = 4;
- break;
- }
-
- if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
- return -1;
- return pin_nid - base_nid;
-}
-
-/**
- * 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 i915 graphics.
- *
- * 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 i915_audio_component *acomp = bus->audio_component;
- int port, pipe;
-
- if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
- return -ENODEV;
- port = 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 i915 graphics.
- *
- * 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 i915_audio_component *acomp = bus->audio_component;
- int port, pipe;
-
- if (!acomp || !acomp->ops || !acomp->ops->get_eld)
- return -ENODEV;
-
- port = 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 i915_audio_component *acomp = hdac_acomp;
- int ret;
-
- ret = component_bind_all(dev, acomp);
- if (ret < 0)
- return ret;
-
- if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
- acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
- ret = -EINVAL;
- goto out_unbind;
- }
-
- /*
- * Atm, we don't support dynamic unbinding initiated by the child
- * component, so pin its containing module until we unbind.
- */
- if (!try_module_get(acomp->ops->owner)) {
- ret = -ENODEV;
- goto out_unbind;
- }
-
- return 0;
-
-out_unbind:
- component_unbind_all(dev, acomp);
-
- return ret;
-}
-
-static void hdac_component_master_unbind(struct device *dev)
-{
- struct i915_audio_component *acomp = hdac_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,
-};
-
-static int hdac_component_master_match(struct device *dev, void *data)
-{
- /* i915 is the only supported component */
- return !strcmp(dev->driver->name, "i915");
-}
-
-/**
- * snd_hdac_i915_register_notifier - Register i915 audio component ops
- * @aops: i915 audio component ops
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function sets the given ops to be called by the i915 graphics driver.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
-{
- if (!hdac_acomp)
- return -ENODEV;
-
- hdac_acomp->audio_ops = aops;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
-
-/* check whether intel graphics is present */
-static bool i915_gfx_present(void)
-{
- static const struct pci_device_id ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
- .class = PCI_BASE_CLASS_DISPLAY << 16,
- .class_mask = 0xff << 16 },
- {}
- };
- return pci_dev_present(ids);
-}
-
-/**
- * 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 component_match *match = NULL;
- struct device *dev = bus->dev;
- struct i915_audio_component *acomp;
- int ret;
-
- if (WARN_ON(hdac_acomp))
- return -EBUSY;
-
- if (!i915_gfx_present())
- return -ENODEV;
-
- acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
- if (!acomp)
- return -ENOMEM;
- bus->audio_component = acomp;
- hdac_acomp = acomp;
-
- component_match_add(dev, &match, hdac_component_master_match, bus);
- ret = component_master_add_with_match(dev, &hdac_component_master_ops,
- match);
- if (ret < 0)
- goto out_err;
-
- /*
- * Atm, we don't support deferring the component binding, so make sure
- * i915 is loaded and that the binding successfully completes.
- */
- request_module("i915");
-
- if (!acomp->ops) {
- ret = -ENODEV;
- goto out_master_del;
- }
- dev_dbg(dev, "bound to i915 component master\n");
-
- return 0;
-out_master_del:
- component_master_del(dev, &hdac_component_master_ops);
-out_err:
- kfree(acomp);
- bus->audio_component = NULL;
- hdac_acomp = NULL;
- dev_info(dev, "failed to add i915 component master (%d)\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
-
-/**
- * snd_hdac_i915_exit - Finalize 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 releases the i915 audio component that has been used.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_i915_exit(struct hdac_bus *bus)
-{
- struct device *dev = bus->dev;
- struct i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp)
- return 0;
-
- WARN_ON(bus->i915_power_refcount);
- if (bus->i915_power_refcount > 0 && acomp->ops)
- acomp->ops->put_power(acomp->dev);
-
- component_master_del(dev, &hdac_component_master_ops);
-
- kfree(acomp);
- bus->audio_component = NULL;
- hdac_acomp = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_exit);
diff --git a/sound/hda/local.h b/sound/hda/local.h
deleted file mode 100644
index 877631e39373..000000000000
--- a/sound/hda/local.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* 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);
-
-#endif /* __HDAC_LOCAL_H */
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 09978855e08e..c827f9f70a33 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-i2c-objs := i2c.o
-snd-cs8427-objs := cs8427.o
-snd-tea6330t-objs := tea6330t.o
+snd-i2c-y := i2c.o
+snd-cs8427-y := cs8427.o
+snd-tea6330t-y := tea6330t.o
obj-$(CONFIG_SND) += other/
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 7e21621e492a..46f081268348 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -1,23 +1,8 @@
+// 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>
@@ -25,7 +10,7 @@
#include <linux/init.h>
#include <linux/bitrev.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -65,9 +50,11 @@ 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) {
- snd_printk(KERN_ERR "unable to send bytes 0x%02x:0x%02x "
- "to CS8427 (%i)\n", buf[0], buf[1], err);
+ err = snd_i2c_sendbytes(device, buf, 2);
+ if (err != 2) {
+ dev_err(device->bus->card->dev,
+ "unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n",
+ buf[0], buf[1], err);
return err < 0 ? err : -EIO;
}
return 0;
@@ -80,14 +67,16 @@ 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) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte "
- "to CS8427\n", reg);
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "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) {
- snd_printk(KERN_ERR "unable to read register 0x%x byte "
- "from CS8427\n", reg);
+ err = snd_i2c_readbytes(device, &buf, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "unable to read register 0x%x byte from CS8427\n", reg);
return err < 0 ? err : -EIO;
}
return buf;
@@ -118,12 +107,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) {
@@ -206,16 +196,18 @@ int snd_cs8427_init(struct snd_i2c_bus *bus,
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
if (err != CS8427_VER8427A) {
/* give second chance */
- snd_printk(KERN_WARNING "invalid CS8427 signature 0x%x: "
- "let me try again...\n", err);
+ dev_warn(device->bus->card->dev,
+ "invalid CS8427 signature 0x%x: let me try again...\n",
+ err);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
}
if (err != CS8427_VER8427A) {
snd_i2c_unlock(bus);
- snd_printk(KERN_ERR "unable to find CS8427 signature "
- "(expected 0x%x, read 0x%x),\n",
- CS8427_VER8427A, err);
- snd_printk(KERN_ERR " initialization is not completed\n");
+ dev_err(device->bus->card->dev,
+ "unable to find CS8427 signature (expected 0x%x, read 0x%x),\n",
+ CS8427_VER8427A, err);
+ dev_err(device->bus->card->dev,
+ " initialization is not completed\n");
return -EFAULT;
}
/* turn off run bit while making changes to configuration */
@@ -224,7 +216,8 @@ int snd_cs8427_init(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;
}
@@ -232,11 +225,13 @@ int snd_cs8427_init(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;
}
@@ -297,7 +292,7 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
snd_i2c_sendbytes(device, buf, 1);
snd_i2c_readbytes(device, buf, 127);
for (xx = 0; xx < 127; xx++)
- printk(KERN_DEBUG "reg[0x%x] = 0x%x\n", xx+1, buf[xx]);
+ dev_dbg(device->bus->card->dev, "reg[0x%x] = 0x%x\n", xx+1, buf[xx]);
}
#endif
@@ -398,16 +393,17 @@ 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) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte "
- "to CS8427\n", reg);
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
err = snd_i2c_readbytes(device, ucontrol->value.bytes.data, 10);
if (err != 10) {
- snd_printk(KERN_ERR "unable to read Q-subcode bytes "
- "from CS8427\n");
+ dev_err(device->bus->card->dev,
+ "unable to read Q-subcode bytes from CS8427\n");
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
@@ -474,7 +470,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,
@@ -568,10 +564,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);
@@ -621,15 +620,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 ef2a9afe9e19..847e3b6ca601 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -1,23 +1,9 @@
+// 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>
@@ -81,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,
};
@@ -98,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);
@@ -122,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;
@@ -338,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 1a4ce1236146..0a2c0d147ab8 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -4,11 +4,11 @@
# Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ak4114-objs := ak4114.o
-snd-ak4117-objs := ak4117.o
-snd-ak4113-objs := ak4113.o
-snd-ak4xxx-adda-objs := ak4xxx-adda.o
-snd-pt2258-objs := pt2258.o
+snd-ak4114-y := ak4114.o
+snd-ak4117-y := ak4117.o
+snd-ak4113-y := ak4113.o
+snd-ak4xxx-adda-y := ak4xxx-adda.o
+snd-pt2258-y := pt2258.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
index 4099e6062d3c..70b3f7e17f9e 100644
--- a/sound/i2c/other/ak4113.c
+++ b/sound/i2c/other/ak4113.c
@@ -1,24 +1,9 @@
+// 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>
@@ -75,7 +60,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
struct ak4113 *chip;
int err;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4113_dev_free,
};
@@ -142,9 +127,9 @@ void snd_ak4113_reinit(struct ak4113 *chip)
{
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);
+ scoped_guard(mutex, &chip->reinit_mutex) {
+ ak4113_init_regs(chip);
+ }
/* bring up statistics / event queing */
if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
@@ -200,11 +185,10 @@ static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
{
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
ucontrol->value.integer.value[0] =
chip->errors[kcontrol->private_value];
chip->errors[kcontrol->private_value] = 0;
- spin_unlock_irq(&chip->lock);
return 0;
}
@@ -250,14 +234,13 @@ static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
int change;
u8 old_val;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
old_val = chip->regmap[AK4113_REG_IO1];
change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
if (change)
reg_write(chip, AK4113_REG_IO1,
(old_val & (~AK4113_IPS(0xff))) |
(AK4113_IPS(ucontrol->value.integer.value[0])));
- spin_unlock_irq(&chip->lock);
return change;
}
@@ -364,7 +347,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",
@@ -492,9 +475,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,
@@ -548,27 +530,27 @@ int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
goto __rate;
rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
- spin_lock_irqsave(&ak4113->lock, _flags);
- if (rcs0 & AK4113_PAR)
- ak4113->errors[AK4113_PARITY_ERRORS]++;
- if (rcs0 & AK4113_V)
- ak4113->errors[AK4113_V_BIT_ERRORS]++;
- if (rcs2 & AK4113_CCRC)
- ak4113->errors[AK4113_CCRC_ERRORS]++;
- if (rcs2 & AK4113_QCRC)
- 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 |
- AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
- c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
- AK4113_DAT | 0xf0)) ^
- (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
- AK4113_DAT | 0xf0));
- ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
- ak4113->rcs1 = rcs1;
- ak4113->rcs2 = rcs2;
- spin_unlock_irqrestore(&ak4113->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4113->lock) {
+ if (rcs0 & AK4113_PAR)
+ ak4113->errors[AK4113_PARITY_ERRORS]++;
+ if (rcs0 & AK4113_V)
+ ak4113->errors[AK4113_V_BIT_ERRORS]++;
+ if (rcs2 & AK4113_CCRC)
+ ak4113->errors[AK4113_CCRC_ERRORS]++;
+ if (rcs2 & AK4113_QCRC)
+ 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 |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+ c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0)) ^
+ (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0));
+ ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+ ak4113->rcs1 = rcs1;
+ ak4113->rcs2 = rcs2;
+ }
if (rcs0 & AK4113_PAR)
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
@@ -615,8 +597,6 @@ __rate:
(runtime->rate != res)) {
snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
if (snd_pcm_running(ak4113->substream)) {
- /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
- * runtime->rate, res); */
snd_pcm_stop(ak4113->substream,
SNDRV_PCM_STATE_DRAINING);
wake_up(&runtime->sleep);
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 7fb1aeb46915..0e3a272c1490 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -1,23 +1,8 @@
+// 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>
@@ -53,17 +38,6 @@ static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg)
return ak4114->read(ak4114->private_data, reg);
}
-#if 0
-static void reg_dump(struct ak4114 *ak4114)
-{
- int i;
-
- 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 < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0);
-}
-#endif
-
static void snd_ak4114_free(struct ak4114 *chip)
{
atomic_inc(&chip->wq_processing); /* don't schedule new work */
@@ -86,7 +60,7 @@ int snd_ak4114_create(struct snd_card *card,
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,
};
@@ -112,7 +86,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_CODEC, chip, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
goto __fail;
if (r_ak4114)
@@ -157,9 +132,9 @@ void snd_ak4114_reinit(struct ak4114 *chip)
{
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);
+ scoped_guard(mutex, &chip->reinit_mutex) {
+ ak4114_init_regs(chip);
+ }
/* bring up statistics / event queing */
if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
@@ -195,11 +170,10 @@ static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol,
{
struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
ucontrol->value.integer.value[0] =
chip->errors[kcontrol->private_value];
chip->errors[kcontrol->private_value] = 0;
- spin_unlock_irq(&chip->lock);
return 0;
}
@@ -333,7 +307,7 @@ 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",
@@ -465,9 +439,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,
@@ -578,21 +551,21 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
if (flags & AK4114_CHECK_NO_STAT)
goto __rate;
rcs0 = reg_read(ak4114, AK4114_REG_RCS0);
- spin_lock_irqsave(&ak4114->lock, _flags);
- if (rcs0 & AK4114_PAR)
- ak4114->errors[AK4114_PARITY_ERRORS]++;
- if (rcs1 & AK4114_V)
- ak4114->errors[AK4114_V_BIT_ERRORS]++;
- if (rcs1 & AK4114_CCRC)
- ak4114->errors[AK4114_CCRC_ERRORS]++;
- if (rcs1 & AK4114_QCRC)
- 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);
- ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT);
- ak4114->rcs1 = rcs1;
- spin_unlock_irqrestore(&ak4114->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4114->lock) {
+ if (rcs0 & AK4114_PAR)
+ ak4114->errors[AK4114_PARITY_ERRORS]++;
+ if (rcs1 & AK4114_V)
+ ak4114->errors[AK4114_V_BIT_ERRORS]++;
+ if (rcs1 & AK4114_CCRC)
+ ak4114->errors[AK4114_CCRC_ERRORS]++;
+ if (rcs1 & AK4114_QCRC)
+ 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);
+ ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT);
+ ak4114->rcs1 = rcs1;
+ }
ak4114_notify(ak4114, rcs0, rcs1, c0, c1);
if (ak4114->change_callback && (c0 | c1) != 0)
@@ -604,7 +577,6 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) {
snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags);
if (snd_pcm_running(ak4114->capture_substream)) {
- // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING);
res = 1;
}
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index b923342cadf4..d2ec20f885f0 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -1,23 +1,8 @@
+// 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>
@@ -49,20 +34,9 @@ static inline unsigned char reg_read(struct ak4117 *ak4117, unsigned char reg)
return ak4117->read(ak4117->private_data, reg);
}
-#if 0
-static void reg_dump(struct ak4117 *ak4117)
-{
- int i;
-
- printk(KERN_DEBUG "AK4117 REG DUMP:\n");
- for (i = 0; i < 0x1b; i++)
- printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0);
-}
-#endif
-
static void snd_ak4117_free(struct ak4117 *chip)
{
- del_timer_sync(&chip->timer);
+ timer_shutdown_sync(&chip->timer);
kfree(chip);
}
@@ -79,7 +53,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,
};
@@ -101,7 +75,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)
@@ -124,7 +99,7 @@ void snd_ak4117_reinit(struct ak4117 *chip)
{
unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
- del_timer(&chip->timer);
+ timer_delete(&chip->timer);
chip->init = 1;
/* bring the chip to reset state and powerdown state */
reg_write(chip, AK4117_REG_PWRDN, 0);
@@ -169,11 +144,10 @@ static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
{
struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
ucontrol->value.integer.value[0] =
chip->errors[kcontrol->private_value];
chip->errors[kcontrol->private_value] = 0;
- spin_unlock_irq(&chip->lock);
return 0;
}
@@ -217,12 +191,11 @@ static int snd_ak4117_rx_put(struct snd_kcontrol *kcontrol,
int change;
u8 old_val;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
old_val = chip->regmap[AK4117_REG_IO];
change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
if (change)
reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
- spin_unlock_irq(&chip->lock);
return change;
}
@@ -320,7 +293,7 @@ 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",
@@ -466,24 +439,23 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
goto __rate;
rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
- // 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->errors[AK4117_PARITY_ERRORS]++;
- if (rcs0 & AK4117_V)
- ak4117->errors[AK4117_V_BIT_ERRORS]++;
- if (rcs2 & AK4117_CCRC)
- ak4117->errors[AK4117_CCRC_ERRORS]++;
- if (rcs2 & AK4117_QCRC)
- 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)) ^
- (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
- ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
- ak4117->rcs1 = rcs1;
- ak4117->rcs2 = rcs2;
- spin_unlock_irqrestore(&ak4117->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4117->lock) {
+ if (rcs0 & AK4117_PAR)
+ ak4117->errors[AK4117_PARITY_ERRORS]++;
+ if (rcs0 & AK4117_V)
+ ak4117->errors[AK4117_V_BIT_ERRORS]++;
+ if (rcs2 & AK4117_CCRC)
+ ak4117->errors[AK4117_CCRC_ERRORS]++;
+ if (rcs2 & AK4117_QCRC)
+ 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)) ^
+ (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
+ ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
+ ak4117->rcs1 = rcs1;
+ ak4117->rcs2 = rcs2;
+ }
if (rcs0 & AK4117_PAR)
snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id);
@@ -519,7 +491,6 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
if (snd_pcm_running(ak4117->substream)) {
- // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING);
wake_up(&runtime->sleep);
res = 1;
@@ -531,7 +502,7 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
static void snd_ak4117_timer(struct timer_list *t)
{
- struct ak4117 *chip = from_timer(chip, t, timer);
+ struct ak4117 *chip = timer_container_of(chip, t, timer);
if (chip->init)
return;
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index bf377dc192aa..b24c80410d45 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -1,24 +1,10 @@
+// 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 <linux/io.h>
@@ -405,8 +391,6 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
nval = mask - nval;
if (AK_GET_NEEDSMSB(kcontrol->private_value))
nval |= 0x80;
- /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x,
- nval %x\n", chip, addr, nval); */
snd_akm4xxx_write(ak, chip, addr, nval);
return 1;
}
@@ -789,11 +773,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;
@@ -875,13 +860,7 @@ 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);
}
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
@@ -911,15 +890,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 9fa390ba1718..0fbac827124b 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>
@@ -77,7 +63,7 @@ int snd_pt2258_reset(struct snd_pt2258 *pt)
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 reset failed\n");
+ dev_err(pt->card->dev, "PT2258 reset failed\n");
return -EIO;
}
@@ -94,7 +80,7 @@ static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
int base = kcontrol->private_value;
/* chip does not support register reads */
@@ -106,7 +92,7 @@ static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
int base = kcontrol->private_value;
unsigned char bytes[2];
int val0, val1;
@@ -138,7 +124,7 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 access failed\n");
+ dev_err(pt->card->dev, "PT2258 access failed\n");
return -EIO;
}
@@ -147,7 +133,7 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = !pt->mute;
return 0;
@@ -156,7 +142,7 @@ static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
unsigned char bytes[2];
int val;
@@ -175,7 +161,7 @@ static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 access failed 2\n");
+ dev_err(pt->card->dev, "PT2258 access failed 2\n");
return -EIO;
}
diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
index 2d22310dce05..676d58054944 100644
--- a/sound/i2c/tea6330t.c
+++ b/sound/i2c/tea6330t.c
@@ -1,23 +1,8 @@
+// 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>
@@ -71,9 +56,6 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer)
static void snd_tea6330t_set(struct tea6330t *tea,
unsigned char addr, unsigned char value)
{
-#if 0
- printk(KERN_DEBUG "set - 0x%x/0x%x\n", addr, value);
-#endif
snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1);
}
#endif
@@ -130,7 +112,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);
@@ -175,7 +158,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;
@@ -222,7 +206,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;
@@ -269,13 +254,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),
@@ -293,16 +279,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;
}
@@ -342,18 +329,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;
}
@@ -368,19 +358,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 cb54d9c0a77f..f8159179e38d 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -1,26 +1,29 @@
+# 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
+ depends on HAS_IOPORT
default y
help
Support for sound devices connected via the ISA bus.
@@ -458,7 +461,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 5eaddbf4a712..2135d68a15ac 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -4,15 +4,15 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-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
-snd-sc6000-objs := sc6000.o
-snd-sscape-objs := sscape.o
+snd-adlib-y := adlib.o
+snd-als100-y := als100.o
+snd-azt2320-y := azt2320.o
+snd-cmi8328-y := cmi8328.o
+snd-cmi8330-y := cmi8330.o
+snd-es18xx-y := es18xx.o
+snd-opl3sa2-y := opl3sa2.o
+snd-sc6000-y := sc6000.o
+snd-sscape-y := sscape.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile
index 487ab23860e3..573325228534 100644
--- a/sound/isa/ad1816a/Makefile
+++ b/sound/isa/ad1816a/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1816a-objs := ad1816a.o ad1816a_lib.o
+snd-ad1816a-y := ad1816a.o ad1816a_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 4be6c1245820..8e84d4091f1e 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -1,21 +1,9 @@
+// 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>
@@ -29,18 +17,9 @@
#include <sound/mpu401.h>
#include <sound/opl3.h>
-#define PFX "ad1816a: "
-
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 */
@@ -66,7 +45,7 @@ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).")
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" } } },
@@ -106,7 +85,7 @@ static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
err = pnp_activate_dev(pdev);
if (err < 0) {
- printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
+ dev_err(&pdev->dev, "AUDIO PnP configure failure\n");
return -EBUSY;
}
@@ -119,13 +98,13 @@ static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
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");
+ pr_warn("MPU401 device busy, skipping.\n");
return 0;
}
err = pnp_activate_dev(pdev);
if (err < 0) {
- printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
+ dev_err(&pdev->dev, "MPU401 PnP configure failure\n");
mpu_port[dev] = -1;
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
@@ -143,75 +122,68 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
struct snd_ad1816a *chip;
struct snd_opl3 *opl3;
- error = snd_card_new(&pcard->card->dev,
- index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_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;
chip = card->private_data;
- if ((error = snd_card_ad1816a_pnp(dev, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_ad1816a_pnp(dev, pcard, pid);
+ if (error)
return error;
- }
- 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];
- strcpy(card->driver, "AD1816A");
- strcpy(card->shortname, "ADI SoundPort AD1816A");
+ strscpy(card->driver, "AD1816A");
+ strscpy(card->shortname, "ADI SoundPort AD1816A");
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)) < 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);
- if (error < 0) {
- snd_card_free(card);
+ 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],
NULL) < 0)
- printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
+ dev_err(card->dev, "no MPU-401 device at 0x%lx.\n",
+ mpu_port[dev]);
}
if (fm_port[dev] > 0) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "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;
}
@@ -237,12 +209,6 @@ static int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void snd_ad1816a_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_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
pm_message_t state)
@@ -269,7 +235,6 @@ static struct pnp_card_driver ad1816a_pnpc_driver = {
.name = "ad1816a",
.id_table = snd_ad1816a_pnpids,
.probe = snd_ad1816a_pnp_detect,
- .remove = snd_ad1816a_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_ad1816a_pnp_suspend,
.resume = snd_ad1816a_pnp_resume,
@@ -287,7 +252,7 @@ static int __init alsa_card_ad1816a_init(void)
if (!ad1816a_devices) {
pnp_unregister_card_driver(&ad1816a_pnpc_driver);
#ifdef MODULE
- printk(KERN_ERR "no AD1816A based soundcards found.\n");
+ pr_err("no AD1816A based soundcards found.\n");
#endif /* MODULE */
return -ENODEV;
}
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 923201414469..50f62304de61 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>
@@ -37,7 +25,7 @@ static inline int snd_ad1816a_busy_wait(struct snd_ad1816a *chip)
if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)
return 0;
- snd_printk(KERN_WARNING "chip busy.\n");
+ dev_warn(chip->card->dev, "chip busy.\n");
return -EBUSY;
}
@@ -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;
@@ -107,14 +96,10 @@ static unsigned char snd_ad1816a_get_format(struct snd_ad1816a *chip,
static int snd_ad1816a_open(struct snd_ad1816a *chip, unsigned int mode)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
- if (chip->mode & mode) {
- spin_unlock_irqrestore(&chip->lock, flags);
+ if (chip->mode & mode)
return -EAGAIN;
- }
switch ((mode &= AD1816A_MODE_OPEN)) {
case AD1816A_MODE_PLAYBACK:
@@ -137,15 +122,12 @@ static int snd_ad1816a_open(struct snd_ad1816a *chip, unsigned int mode)
}
chip->mode |= mode;
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static void snd_ad1816a_close(struct snd_ad1816a *chip, unsigned int mode)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
switch ((mode &= AD1816A_MODE_OPEN)) {
case AD1816A_MODE_PLAYBACK:
@@ -166,10 +148,9 @@ 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);
}
@@ -181,22 +162,22 @@ static int snd_ad1816a_trigger(struct snd_ad1816a *chip, unsigned char what,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
- spin_lock(&chip->lock);
- cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
- /* if (what & AD1816A_PLAYBACK_ENABLE) */
- /* That is not valid, because playback and capture enable
- * are the same bit pattern, just to different addresses
- */
- if (! iscapture)
- snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
- AD1816A_PLAYBACK_ENABLE, cmd);
- else
- snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
- AD1816A_CAPTURE_ENABLE, cmd);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
+ /* if (what & AD1816A_PLAYBACK_ENABLE) */
+ /* That is not valid, because playback and capture enable
+ * are the same bit pattern, just to different addresses
+ */
+ if (!iscapture)
+ snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+ AD1816A_PLAYBACK_ENABLE, cmd);
+ else
+ snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+ AD1816A_CAPTURE_ENABLE, cmd);
+ }
break;
default:
- snd_printk(KERN_WARNING "invalid trigger mode 0x%x.\n", what);
+ dev_warn(chip->card->dev, "invalid trigger mode 0x%x.\n", what);
error = -EINVAL;
}
@@ -217,25 +198,13 @@ 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);
- unsigned long flags;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size, rate;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
@@ -255,19 +224,16 @@ static int snd_ad1816a_playback_prepare(struct snd_pcm_substream *substream)
snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT,
snd_pcm_lib_period_bytes(substream) / 4 - 1);
-
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int snd_ad1816a_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_ad1816a *chip = snd_pcm_substream_chip(substream);
- unsigned long flags;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size, rate;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
@@ -287,8 +253,6 @@ static int snd_ad1816a_capture_prepare(struct snd_pcm_substream *substream)
snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT,
snd_pcm_lib_period_bytes(substream) / 4 - 1);
-
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
@@ -319,9 +283,9 @@ static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id)
struct snd_ad1816a *chip = dev_id;
unsigned char status;
- spin_lock(&chip->lock);
- status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
+ }
if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream)
snd_pcm_period_elapsed(chip->playback_substream);
@@ -332,9 +296,9 @@ static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id)
if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
- spin_lock(&chip->lock);
- snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+ }
return IRQ_HANDLED;
}
@@ -402,9 +366,9 @@ static unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer)
static int snd_ad1816a_timer_start(struct snd_timer *timer)
{
unsigned short bits;
- unsigned long flags;
struct snd_ad1816a *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->lock, flags);
+
+ guard(spinlock_irqsave)(&chip->lock);
bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE);
if (!(bits & AD1816A_TIMER_ENABLE)) {
@@ -414,24 +378,20 @@ static int snd_ad1816a_timer_start(struct snd_timer *timer)
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_ENABLE, 0xffff);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int snd_ad1816a_timer_stop(struct snd_timer *timer)
{
- unsigned long flags;
struct snd_ad1816a *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_ENABLE, 0x0000);
-
- spin_unlock_irqrestore(&chip->lock, flags);
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 +408,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 +424,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);
@@ -493,9 +455,7 @@ static int snd_ad1816a_capture_close(struct snd_pcm_substream *substream)
static void snd_ad1816a_init(struct snd_ad1816a *chip)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
@@ -507,41 +467,32 @@ static void snd_ad1816a_init(struct snd_ad1816a *chip)
AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff);
snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000);
snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000);
-
- spin_unlock_irqrestore(&chip->lock, flags);
}
#ifdef CONFIG_PM
void snd_ad1816a_suspend(struct snd_ad1816a *chip)
{
int reg;
- unsigned long flags;
- snd_pcm_suspend_all(chip->pcm);
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
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);
+ guard(spinlock_irqsave)(&chip->lock);
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;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) {
case 0:
@@ -556,33 +507,9 @@ static int snd_ad1816a_probe(struct snd_ad1816a *chip)
default:
chip->hardware = AD1816A_HW_AUTO;
}
-
- spin_unlock_irqrestore(&chip->lock, flags);
- 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);
- }
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 *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
{
switch (chip->hardware) {
@@ -590,8 +517,8 @@ static const char *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
case AD1816A_HW_AD1815: return "AD1815";
case AD1816A_HW_AD18MAX10: return "AD18max10";
default:
- snd_printk(KERN_WARNING "Unknown chip version %d:%d.\n",
- chip->version, chip->hardware);
+ dev_warn(chip->card->dev, "Unknown chip version %d:%d.\n",
+ chip->version, chip->hardware);
return "AD1816A - unknown";
}
}
@@ -600,35 +527,31 @@ 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;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
- if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) {
- snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port);
- snd_ad1816a_free(chip);
+ chip->res_port = devm_request_region(card->dev, port, 16, "AD1816A");
+ if (!chip->res_port) {
+ dev_err(card->dev, "ad1816a: can't grab port 0x%lx\n", port);
return -EBUSY;
}
- if (request_irq(irq, snd_ad1816a_interrupt, 0, "AD1816A", (void *) chip)) {
- snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq);
- snd_ad1816a_free(chip);
+ if (devm_request_irq(card->dev, irq, snd_ad1816a_interrupt, 0,
+ "AD1816A", (void *) chip)) {
+ dev_err(card->dev, "ad1816a: can't grab IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
- if (request_dma(dma1, "AD1816A - 1")) {
- snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1);
- snd_ad1816a_free(chip);
+ card->sync_irq = chip->irq;
+ if (snd_devm_request_dma(card->dev, dma1, "AD1816A - 1")) {
+ dev_err(card->dev, "ad1816a: can't grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (request_dma(dma2, "AD1816A - 2")) {
- snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2);
- snd_ad1816a_free(chip);
+ if (snd_devm_request_dma(card->dev, dma2, "AD1816A - 2")) {
+ dev_err(card->dev, "ad1816a: can't grab DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
@@ -637,28 +560,18 @@ int 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;
- }
-
return 0;
}
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,
@@ -667,9 +580,6 @@ static const struct snd_pcm_ops snd_ad1816a_playback_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,
@@ -680,7 +590,8 @@ 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);
@@ -689,12 +600,11 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device)
pcm->private_data = chip;
pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0;
- strcpy(pcm->name, snd_ad1816a_chip_id(chip));
+ strscpy(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;
return 0;
@@ -711,9 +621,10 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device)
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));
+ strscpy(timer->name, snd_ad1816a_chip_id(chip));
timer->private_data = chip;
chip->timer = timer;
timer->hw = snd_ad1816a_timer_table;
@@ -737,12 +648,10 @@ static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short val;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL);
- spin_unlock_irqrestore(&chip->lock, flags);
ucontrol->value.enumerated.item[0] = (val >> 12) & 7;
ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
return 0;
@@ -751,7 +660,6 @@ static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short val;
int change;
@@ -760,10 +668,9 @@ static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return -EINVAL;
val = (ucontrol->value.enumerated.item[0] << 12) |
(ucontrol->value.enumerated.item[1] << 4);
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val;
snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -793,15 +700,13 @@ static int snd_ad1816a_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_ad1816a_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -810,7 +715,6 @@ static int snd_ad1816a_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -822,12 +726,11 @@ static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
old_val = snd_ad1816a_read(chip, reg);
val = (old_val & ~(mask << shift)) | val;
change = val != old_val;
snd_ad1816a_write(chip, reg, val);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -858,7 +761,6 @@ static int snd_ad1816a_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift_left = (kcontrol->private_value >> 8) & 0x0f;
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
@@ -866,11 +768,10 @@ static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int invert = (kcontrol->private_value >> 24) & 0xff;
unsigned short val;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
val = snd_ad1816a_read(chip, reg);
ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -881,7 +782,6 @@ static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift_left = (kcontrol->private_value >> 8) & 0x0f;
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
@@ -898,12 +798,11 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
old_val = snd_ad1816a_read(chip, reg);
val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
change = val1 != old_val;
snd_ad1816a_write(chip, reg, val1);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -913,7 +812,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[] = {
+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),
@@ -970,10 +869,11 @@ int snd_ad1816a_mixer(struct snd_ad1816a *chip)
card = chip->card;
- strcpy(card->mixername, snd_ad1816a_chip_id(chip));
+ strscpy(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..5fdfc1c9f059 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1848-objs := ad1848.o
+snd-ad1848-y := ad1848.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 7c8e92f62f3b..401d8df28d87 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>
@@ -37,9 +22,6 @@
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 */
@@ -90,7 +72,7 @@ static int snd_ad1848_probe(struct device *dev, unsigned int n)
struct snd_wss *chip;
int error;
- error = snd_card_new(dev, 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;
@@ -98,45 +80,36 @@ static int 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);
if (error < 0)
- goto out;
+ return error;
error = snd_wss_mixer(chip);
if (error < 0)
- goto out;
+ return error;
- strlcpy(card->driver, "AD1848", sizeof(card->driver));
- strlcpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ strscpy(card->driver, "AD1848", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
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]);
+ scnprintf(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]);
+ scnprintf(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 snd_ad1848_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- 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 = snd_ad1848_remove,
#ifdef CONFIG_PM
.suspend = snd_ad1848_suspend,
.resume = snd_ad1848_resume,
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 5fb619eca5c8..03fb2bce9255 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.
*/
@@ -42,70 +43,53 @@ static int 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 snd_adlib_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_opl3 *opl3;
int error;
- error = snd_card_new(dev, 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);
+ strscpy(card->driver, DEV_NAME);
+ strscpy(card->shortname, CRD_NAME);
sprintf(card->longname, CRD_NAME " at %#lx", port[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;
}
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 snd_adlib_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- return 0;
}
static struct isa_driver snd_adlib_driver = {
.match = snd_adlib_match,
.probe = snd_adlib_probe,
- .remove = snd_adlib_remove,
.driver = {
.name = DEV_NAME
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index f63142ec287e..cfc241bd252e 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.
@@ -9,19 +10,6 @@
Generalised for soundcards based on DT-0196 and ALS-007 chips
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>
@@ -35,20 +23,7 @@
#include <sound/opl3.h>
#include <sound/sb.h>
-#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");
@@ -135,7 +110,7 @@ static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -158,7 +133,7 @@ static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
__mpu_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");
}
acard->devmpu = NULL;
mpu_port[dev] = -1;
@@ -174,7 +149,7 @@ static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
__fm_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "OPL3 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "OPL3 pnp configure failure, skipping\n");
}
acard->devopl = NULL;
fm_port[dev] = -1;
@@ -193,17 +168,16 @@ static int snd_card_als100_probe(int dev,
struct snd_card_als100 *acard;
struct snd_opl3 *opl3;
- error = snd_card_new(&pcard->card->dev,
- 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;
- }
if (pid->driver_data == SB_HW_DT019X)
dma16[dev] = -1;
@@ -213,36 +187,32 @@ static int 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");
+ strscpy(card->driver, "DT-019X");
+ strscpy(card->shortname, "Diamond Tech. DT-019X");
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");
+ strscpy(card->driver, "ALS100");
+ strscpy(card->shortname, "Avance Logic ALS100");
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)) < 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;
@@ -258,31 +228,28 @@ static int snd_card_als100_probe(int dev,
mpu_port[dev], 0,
mpu_irq[dev],
NULL) < 0)
- snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
+ dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "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;
}
@@ -308,12 +275,6 @@ static int snd_als100_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void 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 +283,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 +305,6 @@ static struct pnp_card_driver als100_pnpc_driver = {
.name = "als100",
.id_table = snd_als100_pnpids,
.probe = snd_als100_pnp_detect,
- .remove = snd_als100_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_als100_pnp_suspend,
.resume = snd_als100_pnp_resume,
@@ -363,7 +322,7 @@ static int __init alsa_card_als100_init(void)
if (!als100_devices) {
pnp_unregister_card_driver(&als100_pnpc_driver);
#ifdef MODULE
- snd_printk(KERN_ERR "no Avance Logic based soundcards found\n");
+ pr_err("no Avance Logic based soundcards found\n");
#endif
return -ENODEV;
}
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index 4e6fad4f94d9..588b9f0831d3 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
*/
/*
@@ -42,16 +30,9 @@
#include <sound/mpu401.h>
#include <sound/opl3.h>
-#define PFX "azt2320: "
-
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 */
@@ -116,7 +97,7 @@ static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -137,7 +118,7 @@ static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
__mpu_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");
}
acard->devmpu = NULL;
mpu_port[dev] = -1;
@@ -165,9 +146,11 @@ 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);
@@ -184,82 +167,69 @@ static int snd_card_azt2320_probe(int dev,
struct snd_wss *chip;
struct snd_opl3 *opl3;
- error = snd_card_new(&pcard->card->dev,
- 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;
- }
- 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");
+ strscpy(card->driver, "AZT2320");
+ strscpy(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);
- if (error < 0) {
- snd_card_free(card);
+ 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);
- if (error < 0) {
- snd_card_free(card);
+ 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], NULL) < 0)
- snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
+ dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "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;
}
@@ -285,12 +255,6 @@ static int snd_azt2320_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void 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)
{
@@ -320,7 +284,6 @@ static struct pnp_card_driver azt2320_pnpc_driver = {
.name = "azt2320",
.id_table = snd_azt2320_pnpids,
.probe = snd_azt2320_pnp_detect,
- .remove = snd_azt2320_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_azt2320_pnp_suspend,
.resume = snd_azt2320_pnp_resume,
@@ -338,7 +301,7 @@ static int __init alsa_card_azt2320_init(void)
if (!azt2320_devices) {
pnp_unregister_card_driver(&azt2320_pnpc_driver);
#ifdef MODULE
- snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
+ pr_err("no AZT2320 based soundcards found\n");
#endif
return -ENODEV;
}
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
index d09e456107ad..4e6d823af103 100644
--- a/sound/isa/cmi8328.c
+++ b/sound/isa/cmi8328.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for C-Media CMI8328-based soundcards, such as AudioExcel AV500
* Copyright (c) 2012 Ondrej Zary
@@ -31,7 +32,7 @@ MODULE_LICENSE("GPL");
#endif
/* I/O port is configured by jumpers on the card to one of these */
-static int cmi8328_ports[] = { 0x530, 0xe80, 0xf40, 0x604 };
+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};
@@ -154,37 +155,37 @@ static int snd_cmi8328_mixer(struct snd_wss *chip)
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");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "error renaming control\n");
+ dev_err(card->dev, "error renaming control\n");
return err;
}
/* rename AUX0 volume to CD */
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "CD Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "error renaming control\n");
+ dev_err(card->dev, "error renaming control\n");
return err;
}
/* rename AUX1 switch to Synth */
- strcpy(id1.name, "Aux Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch");
id1.index = 1;
- strcpy(id2.name, "Synth Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "error renaming control\n");
+ dev_err(card->dev, "error renaming control\n");
return err;
}
/* rename AUX1 volume to Synth */
- strcpy(id1.name, "Aux Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
id1.index = 1;
- strcpy(id2.name, "Synth Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "error renaming control\n");
+ dev_err(card->dev, "error renaming control\n");
return err;
}
@@ -192,7 +193,7 @@ static int snd_cmi8328_mixer(struct snd_wss *chip)
}
/* find index of an item in "-1"-ended array */
-int array_find(int array[], int item)
+static int array_find(const int array[], int item)
{
int i;
@@ -203,7 +204,7 @@ int array_find(int array[], int item)
return -1;
}
/* the same for long */
-int array_find_l(long array[], long item)
+static int array_find_l(const long array[], long item)
{
int i;
@@ -223,16 +224,16 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
struct resource *res;
#endif
int err, pos;
- static long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334,
+ static const long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334,
0x336, -1 };
- static u8 mpu_port_bits[] = { 3, 0, 1, 2, 4, 5, 6 };
- static int mpu_irqs[] = { 9, 7, 5, 3, -1 };
- static u8 mpu_irq_bits[] = { 3, 2, 1, 0 };
- static int irqs[] = { 9, 10, 11, 7, -1 };
- static u8 irq_bits[] = { 2, 3, 4, 1 };
- static int dma1s[] = { 3, 1, 0, -1 };
- static u8 dma_bits[] = { 3, 2, 1 };
- static int dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, {0, -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;
@@ -250,35 +251,35 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
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");
+ dev_err(pdev, "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");
+ dev_err(pdev, "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");
+ dev_warn(pdev, "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]);
+ dev_err(pdev, "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]);
+ dev_err(pdev, "invalid DMA1 %d\n", dma1[ndev]);
return -EINVAL;
}
val |= dma_bits[pos];
@@ -286,15 +287,15 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
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]);
+ dev_err(pdev, "invalid DMA2 %d\n", dma2[ndev]);
return -EINVAL;
}
val |= 0x04; /* enable separate capture DMA */
}
outb(val, port);
- err = snd_card_new(pdev, index[ndev], id[ndev], THIS_MODULE,
- sizeof(struct snd_cmi8328), &card);
+ 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;
@@ -305,64 +306,64 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
err = snd_wss_create(card, port + 4, -1, irq[ndev], dma1[ndev],
dma2[ndev], WSS_HW_DETECT, 0, &cmi->wss);
if (err < 0)
- goto error;
+ return err;
err = snd_wss_pcm(cmi->wss, 0);
if (err < 0)
- goto error;
+ return err;
err = snd_wss_mixer(cmi->wss);
if (err < 0)
- goto error;
+ return err;
err = snd_cmi8328_mixer(cmi->wss);
if (err < 0)
- goto error;
+ return err;
if (snd_wss_timer(cmi->wss, 0) < 0)
- snd_printk(KERN_WARNING "error initializing WSS timer\n");
+ dev_warn(pdev, "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");
+ dev_err(pdev, "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");
+ dev_err(pdev, "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]);
+ dev_warn(pdev, "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]);
+ dev_warn(pdev, "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");
+ dev_err(pdev, "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");
+ dev_err(pdev, "error initializing OPL3\n");
else
if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0)
- snd_printk(KERN_WARNING "error initializing OPL3 hwdep\n");
+ dev_warn(pdev, "error initializing OPL3 hwdep\n");
- strcpy(card->driver, "CMI8328");
- strcpy(card->shortname, "C-Media CMI8328");
+ strscpy(card->driver, "CMI8328");
+ strscpy(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]);
@@ -370,24 +371,21 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
dev_set_drvdata(pdev, card);
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
#ifdef SUPPORT_JOYSTICK
if (!gameport[ndev])
return 0;
/* gameport is hardwired to 0x200 */
- res = request_region(0x200, 8, "CMI8328 gameport");
+ res = devm_request_region(pdev, 0x200, 8, "CMI8328 gameport");
if (!res)
- snd_printk(KERN_WARNING "unable to allocate gameport I/O port\n");
+ dev_warn(pdev, "unable to allocate gameport I/O port\n");
else {
struct gameport *gp = cmi->gameport = gameport_allocate_port();
- if (!cmi->gameport)
- release_and_free_resource(res);
- else {
+ 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;
- gameport_set_port_data(gp, res);
/* Enable gameport */
snd_cmi8328_cfg_write(port, CFG1,
CFG1_SB_DISABLE | CFG1_GAMEPORT);
@@ -396,30 +394,21 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
}
#endif
return 0;
-error:
- snd_card_free(card);
-
- return err;
}
-static int snd_cmi8328_remove(struct device *pdev, unsigned int dev)
+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) {
- struct resource *res = gameport_get_port_data(cmi->gameport);
+ if (cmi->gameport)
gameport_unregister_port(cmi->gameport);
- release_and_free_resource(res);
- }
#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);
- snd_card_free(card);
- return 0;
}
#ifdef CONFIG_PM
@@ -434,7 +423,6 @@ static int snd_cmi8328_suspend(struct device *pdev, unsigned int n,
cmi = card->private_data;
snd_cmi8328_cfg_save(cmi->port, cmi->cfg);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(cmi->wss->pcm);
cmi->wss->suspend(cmi->wss);
return 0;
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 6b8c46942efb..3d1f19321b9e 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
- *
*/
/*
@@ -65,7 +51,6 @@
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;
@@ -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
@@ -193,7 +178,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids);
#endif
-static struct snd_kcontrol_new snd_cmi8330_controls[] = {
+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[] = {
+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] = {
+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 },
@@ -284,22 +269,22 @@ static unsigned char cmi8330_sb_init_values[][2] = {
static int cmi8330_add_sb_mixers(struct snd_sb *chip)
{
int idx, err;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */
+ }
/* mute and zero volume channels */
for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) {
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0],
- cmi8330_sb_init_values[idx][1]);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0],
+ cmi8330_sb_init_values[idx][1]);
+ }
}
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;
@@ -311,7 +296,7 @@ static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
unsigned int idx;
int err;
- strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+ strscpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
err = snd_ctl_add(card,
@@ -322,7 +307,8 @@ static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
}
#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;
@@ -355,7 +341,7 @@ static int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AD1848 PnP configure failure\n");
+ dev_err(&pdev->dev, "AD1848 PnP configure failure\n");
return -EBUSY;
}
wssport[dev] = pnp_port_start(pdev, 0);
@@ -369,7 +355,7 @@ static int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "SB16 PnP configure failure\n");
+ dev_err(&pdev->dev, "SB16 PnP configure failure\n");
return -EBUSY;
}
sbport[dev] = pnp_port_start(pdev, 0);
@@ -389,7 +375,7 @@ static int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0)
- snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n");
+ dev_err(&pdev->dev, "MPU-401 PnP configure failure: will be disabled\n");
else {
mpuport[dev] = pnp_port_start(pdev, 0);
mpuirq[dev] = pnp_irq(pdev, 0);
@@ -442,14 +428,15 @@ 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");
+ strscpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
pcm->private_data = chip;
/* SB16 */
@@ -469,9 +456,8 @@ static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip)
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 +470,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;
@@ -512,8 +497,6 @@ static int snd_cmi8330_resume(struct snd_card *card)
#define is_isapnp_selected(dev) 0
#endif
-#define PFX "cmi8330: "
-
static int snd_cmi8330_card_new(struct device *pdev, int dev,
struct snd_card **cardp)
{
@@ -521,10 +504,10 @@ static int snd_cmi8330_card_new(struct device *pdev, int dev,
struct snd_cmi8330 *acard;
int err;
- err = snd_card_new(pdev, 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");
+ dev_err(pdev, "could not get a new card\n");
return err;
}
acard = card->private_data;
@@ -545,26 +528,27 @@ static int snd_cmi8330_probe(struct snd_card *card, int dev)
wssdma[dev], -1,
WSS_HW_DETECT, 0, &acard->wss);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AD1848 device busy??\n");
+ dev_err(card->dev, "AD1848 device busy??\n");
return err;
}
if (acard->wss->hardware != WSS_HW_CMI8330) {
- snd_printk(KERN_ERR PFX "AD1848 not found during probe\n");
+ dev_err(card->dev, "AD1848 not found during probe\n");
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) {
- snd_printk(KERN_ERR PFX "SB16 device busy??\n");
+ err = snd_sbdsp_create(card, sbport[dev],
+ sbirq[dev],
+ snd_sb16dsp_interrupt,
+ sbdma8[dev],
+ sbdma16[dev],
+ SB_HW_AUTO, &acard->sb);
+ if (err < 0) {
+ dev_err(card->dev, "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;
+ dev_err(card->dev, "SB16 not found during probe\n");
+ return -ENODEV;
}
snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */
@@ -572,22 +556,24 @@ static int 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) {
- snd_printk(KERN_ERR PFX "failed to create mixers\n");
+ err = snd_cmi8330_mixer(card, acard);
+ if (err < 0) {
+ dev_err(card->dev, "failed to create mixers\n");
return err;
}
- if ((err = snd_cmi8330_pcm(card, acard)) < 0) {
- snd_printk(KERN_ERR PFX "failed to create pcms\n");
+ err = snd_cmi8330_pcm(card, acard);
+ if (err < 0) {
+ dev_err(card->dev, "failed to create pcms\n");
return err;
}
if (fmport[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fmport[dev], fmport[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX
- "no OPL device at 0x%lx-0x%lx ?\n",
- fmport[dev], fmport[dev] + 2);
+ dev_err(card->dev,
+ "no OPL device at 0x%lx-0x%lx ?\n",
+ fmport[dev], fmport[dev] + 2);
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
@@ -599,12 +585,12 @@ static int snd_cmi8330_probe(struct snd_card *card, int dev)
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpuport[dev], 0, mpuirq[dev],
NULL) < 0)
- printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n",
+ dev_err(card->dev, "no MPU-401 device at 0x%lx.\n",
mpuport[dev]);
}
- strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
- strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
+ strscpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+ strscpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
card->shortname,
acard->wss->port,
@@ -620,11 +606,11 @@ static int snd_cmi8330_isa_match(struct device *pdev,
if (!enable[dev] || is_isapnp_selected(dev))
return 0;
if (wssport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify wssport\n");
+ dev_err(pdev, "specify wssport\n");
return 0;
}
if (sbport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify sbport\n");
+ dev_err(pdev, "specify sbport\n");
return 0;
}
return 1;
@@ -639,21 +625,13 @@ static int snd_cmi8330_isa_probe(struct device *pdev,
err = snd_cmi8330_card_new(pdev, dev, &card);
if (err < 0)
return err;
- 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 snd_cmi8330_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cmi8330_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -672,7 +650,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 = snd_cmi8330_isa_remove,
#ifdef CONFIG_PM
.suspend = snd_cmi8330_isa_suspend,
.resume = snd_cmi8330_isa_resume,
@@ -701,26 +678,19 @@ static int snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
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) {
- snd_printk(KERN_ERR PFX "PnP detection failed\n");
- snd_card_free(card);
+ res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0) {
+ dev_err(card->dev, "PnP detection failed\n");
return res;
}
- 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 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)
{
@@ -738,7 +708,6 @@ static struct pnp_card_driver cmi8330_pnpc_driver = {
.name = "cmi8330",
.id_table = snd_cmi8330_pnpids,
.probe = snd_cmi8330_pnp_detect,
- .remove = 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..013a777d23fa 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/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-cs4231-objs := cs4231.o
-snd-cs4236-objs := cs4236.o cs4236_lib.o
+snd-cs4231-y := cs4231.o
+snd-cs4236-y := cs4236.o cs4236_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index d90ab9558f7f..c87be4be6df1 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>
@@ -37,7 +23,6 @@
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 */
@@ -94,40 +79,40 @@ static int snd_cs4231_probe(struct device *dev, unsigned int n)
struct snd_wss *chip;
int error;
- error = snd_card_new(dev, 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);
if (error < 0)
- goto out;
+ return error;
- strlcpy(card->driver, "CS4231", sizeof(card->driver));
- strlcpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ strscpy(card->driver, "CS4231", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
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]);
+ scnprintf(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]);
+ scnprintf(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);
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)
@@ -140,19 +125,10 @@ static int snd_cs4231_probe(struct device *dev, unsigned int 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 snd_cs4231_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- return 0;
}
#ifdef CONFIG_PM
@@ -180,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 = snd_cs4231_remove,
#ifdef CONFIG_PM
.suspend = snd_cs4231_suspend,
.resume = snd_cs4231_resume,
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 70559e59d18f..e36cc147651a 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -1,22 +1,7 @@
+// 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>
@@ -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+"
@@ -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;
@@ -254,7 +204,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
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");
+ dev_err(&pdev->dev, IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
return -EBUSY;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -264,10 +214,12 @@ static int snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
irq[dev] = pnp_irq(pdev, 0);
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1) == 4 ? -1 : (int)pnp_dma(pdev, 1);
- snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
- port[dev], fm_port[dev], sb_port[dev]);
- snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
- irq[dev], dma1[dev], dma2[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
+ port[dev], fm_port[dev], sb_port[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
+ irq[dev], dma1[dev], dma2[dev]);
return 0;
}
@@ -275,11 +227,11 @@ static int snd_cs423x_pnp_init_wss(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");
+ dev_err(&pdev->dev, IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
return -EBUSY;
}
cport[dev] = pnp_port_start(pdev, 0);
- snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]);
+ dev_dbg(&pdev->dev, "isapnp CTRL: control port=0x%lx\n", cport[dev]);
return 0;
}
@@ -287,19 +239,20 @@ static int snd_cs423x_pnp_init_ctrl(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");
+ dev_err(&pdev->dev, IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
mpu_port[dev] = SNDRV_AUTO_PORT;
mpu_irq[dev] = SNDRV_AUTO_IRQ;
} 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 */
}
}
- snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]);
+ dev_dbg(&pdev->dev, "isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]);
return 0;
}
@@ -357,24 +310,16 @@ static int 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(struct device *pdev, int dev,
struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_new(pdev, 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;
}
@@ -387,11 +332,14 @@ static int snd_cs423x_probe(struct snd_card *card, int dev)
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) {
- printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]);
+ if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) {
+ if (!devm_request_region(card->dev, sb_port[dev], 16,
+ IDENT " SB")) {
+ dev_err(card->dev, 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],
@@ -419,17 +367,17 @@ static int snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
}
- strlcpy(card->driver, chip->pcm->name, sizeof(card->driver));
- strlcpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ 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]);
+ scnprintf(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]);
+ scnprintf(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)
@@ -439,9 +387,10 @@ static int snd_cs423x_probe(struct snd_card *card, int dev)
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3_CS, 0, &opl3) < 0) {
- printk(KERN_WARNING IDENT ": OPL3 not detected\n");
+ dev_warn(card->dev, 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;
}
}
@@ -452,7 +401,7 @@ static int snd_cs423x_probe(struct snd_card *card, int dev)
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[dev], 0,
mpu_irq[dev], NULL) < 0)
- printk(KERN_WARNING IDENT ": MPU401 not detected\n");
+ dev_warn(card->dev, IDENT ": MPU401 not detected\n");
}
return snd_card_register(card);
@@ -492,22 +441,13 @@ static int snd_cs423x_isa_probe(struct device *pdev,
err = snd_cs423x_card_new(pdev, dev, &card);
if (err < 0)
return err;
- 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 snd_cs423x_isa_remove(struct device *pdev,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_suspend(struct snd_card *card)
{
@@ -540,7 +480,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 = snd_cs423x_isa_remove,
#ifdef CONFIG_PM
.suspend = snd_cs423x_isa_suspend,
.resume = snd_cs423x_isa_resume,
@@ -558,7 +497,7 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
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))
@@ -571,36 +510,31 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
return -ENODEV;
/* prepare second id */
- strcpy(cid, pdev->id[0].id);
+ strscpy(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(&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);
+ dev_err(card->dev, "PnP BIOS detection failed for " IDENT "\n");
return err;
}
- 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 snd_cs423x_pnp_remove(struct pnp_dev *pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -617,7 +551,6 @@ static struct pnp_driver cs423x_pnp_driver = {
.name = "cs423x-pnpbios",
.id_table = snd_cs423x_pnpbiosids,
.probe = snd_cs423x_pnpbios_detect,
- .remove = snd_cs423x_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_cs423x_pnp_suspend,
.resume = snd_cs423x_pnp_resume,
@@ -641,27 +574,20 @@ static int snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
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) {
- printk(KERN_ERR "isapnp detection failed and probing for " IDENT
+ res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid);
+ if (res < 0) {
+ dev_err(card->dev, "isapnp detection failed and probing for " IDENT
" is not supported\n");
- snd_card_free(card);
return res;
}
- 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 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)
{
@@ -679,7 +605,6 @@ static struct pnp_card_driver cs423x_pnpc_driver = {
.name = CS423X_ISAPNP_DRIVER,
.id_table = snd_cs423x_pnpids,
.probe = snd_cs423x_pnpc_detect,
- .remove = 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 2012936f6756..e2c29e831020 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
@@ -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,
@@ -183,10 +169,9 @@ static void snd_cs4236_playback_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char pdfr)
{
- unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set fast playback format change and clean playback FIFO */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x10);
@@ -194,17 +179,15 @@ static void snd_cs4236_playback_format(struct snd_wss *chip,
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4236_capture_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char cdfr)
{
- unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set fast capture format change and clean capture FIFO */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x20);
@@ -212,7 +195,6 @@ static void snd_cs4236_capture_format(struct snd_wss *chip,
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
#ifdef CONFIG_PM
@@ -220,48 +202,45 @@ static void snd_cs4236_capture_format(struct snd_wss *chip,
static void snd_cs4236_suspend(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
for (reg = 0; reg < 32; reg++)
chip->image[reg] = snd_wss_in(chip, reg);
for (reg = 0; reg < 18; reg++)
chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
for (reg = 2; reg < 9; reg++)
chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4236_resume(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++) {
- switch (reg) {
- case CS4236_EXT_REG:
- case CS4231_VERSION:
- case 27: /* why? CS4235 - master left */
- case 29: /* why? CS4235 - master right */
- break;
- default:
- snd_wss_out(chip, reg, chip->image[reg]);
- break;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++) {
+ switch (reg) {
+ case CS4236_EXT_REG:
+ case CS4231_VERSION:
+ case 27: /* why? CS4235 - master left */
+ case 29: /* why? CS4235 - master right */
+ break;
+ default:
+ snd_wss_out(chip, reg, chip->image[reg]);
+ break;
+ }
}
- }
- for (reg = 0; reg < 18; reg++)
- snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
- for (reg = 2; reg < 9; reg++) {
- switch (reg) {
- case 7:
- break;
- default:
- snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+ for (reg = 0; reg < 18; reg++)
+ snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
+ for (reg = 2; reg < 9; reg++) {
+ switch (reg) {
+ case 7:
+ break;
+ default:
+ snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+ }
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
}
@@ -293,8 +272,8 @@ int snd_cs4236_create(struct snd_card *card,
return err;
if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) {
- snd_printd("chip is not CS4236+, hardware=0x%x\n",
- chip->hardware);
+ dev_dbg(card->dev, "chip is not CS4236+, hardware=0x%x\n",
+ chip->hardware);
*rchip = chip;
return 0;
}
@@ -302,27 +281,25 @@ int snd_cs4236_create(struct snd_card *card,
{
int idx;
for (idx = 0; idx < 8; idx++)
- snd_printk(KERN_DEBUG "CD%i = 0x%x\n",
- idx, inb(chip->cport + idx));
+ dev_dbg(card->dev, "CD%i = 0x%x\n",
+ idx, inb(chip->cport + idx));
for (idx = 0; idx < 9; idx++)
- snd_printk(KERN_DEBUG "C%i = 0x%x\n",
- idx, snd_cs4236_ctrl_in(chip, idx));
+ dev_dbg(card->dev, "C%i = 0x%x\n",
+ idx, snd_cs4236_ctrl_in(chip, idx));
}
#endif
if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "please, specify control port "
- "for CS4236+ chips\n");
- snd_device_free(card, chip);
+ dev_err(card->dev, "please, specify control port for CS4236+ chips\n");
return -ENODEV;
}
ver1 = snd_cs4236_ctrl_in(chip, 1);
ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
- cport, ver1, ver2);
+ dev_dbg(card->dev, "CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
+ cport, ver1, ver2);
if (ver1 != ver2) {
- snd_printk(KERN_ERR "CS4236+ chip detected, but "
- "control port 0x%lx is not valid\n", cport);
- snd_device_free(card, chip);
+ dev_err(card->dev,
+ "CS4236+ chip detected, but control port 0x%lx is not valid\n",
+ cport);
return -ENODEV;
}
snd_cs4236_ctrl_out(chip, 0, 0x00);
@@ -419,15 +396,13 @@ static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -436,7 +411,6 @@ static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -448,11 +422,10 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val;
change = val != chip->eimage[CS4236_REG(reg)];
snd_cs4236_ext_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -465,15 +438,13 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -482,7 +453,6 @@ static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -494,11 +464,10 @@ static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->cimage[reg] & ~(mask << shift)) | val;
change = val != chip->cimage[reg];
snd_cs4236_ctrl_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -532,7 +501,6 @@ static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -540,10 +508,9 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -554,7 +521,6 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -572,7 +538,7 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1;
val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
@@ -584,7 +550,6 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
change = val1 != chip->eimage[CS4236_REG(left_reg)];
snd_cs4236_ext_out(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -608,7 +573,6 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -616,10 +580,9 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -630,7 +593,6 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -648,13 +610,12 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
snd_wss_out(chip, left_reg, val1);
snd_cs4236_ext_out(chip, right_reg, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -674,31 +635,27 @@ static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f);
ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1, val2;
val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f);
val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1;
val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2;
change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)];
snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1);
snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -735,31 +692,27 @@ static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]);
ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1, val2;
val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]);
val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
snd_wss_out(chip, CS4235_LEFT_MASTER, val1);
snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -772,7 +725,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),
@@ -867,7 +820,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),
@@ -945,62 +898,58 @@ WSS_DOUBLE("Analog Loopback Switch", 0,
static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
#if 0
- printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
- "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_wss_in(chip, CS4231_ALT_FEATURE_1),
- snd_cs4236_ctrl_in(chip, 3),
- snd_cs4236_ctrl_in(chip, 4),
- snd_cs4236_ctrl_in(chip, 5),
- snd_cs4236_ctrl_in(chip, 6),
- snd_cs4236_ctrl_in(chip, 8));
+ dev_dbg(chip->card->dev,
+ "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
+ snd_cs4236_ctrl_in(chip, 3),
+ snd_cs4236_ctrl_in(chip, 4),
+ snd_cs4236_ctrl_in(chip, 5),
+ snd_cs4236_ctrl_in(chip, 6),
+ snd_cs4236_ctrl_in(chip, 8));
#endif
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short enable, val;
enable = ucontrol->value.integer.value[0] & 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
- change = val != chip->image[CS4231_ALT_FEATURE_1];
- snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
- val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
- snd_cs4236_ctrl_out(chip, 4, val);
- udelay(100);
- val &= ~0x40;
- snd_cs4236_ctrl_out(chip, 4, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
+ change = val != chip->image[CS4231_ALT_FEATURE_1];
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
+ val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
+ snd_cs4236_ctrl_out(chip, 4, val);
+ udelay(100);
+ val &= ~0x40;
+ snd_cs4236_ctrl_out(chip, 4, val);
+ }
snd_wss_mce_down(chip);
- mutex_unlock(&chip->mce_mutex);
#if 0
- printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
- "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_wss_in(chip, CS4231_ALT_FEATURE_1),
- snd_cs4236_ctrl_in(chip, 3),
- snd_cs4236_ctrl_in(chip, 4),
- snd_cs4236_ctrl_in(chip, 5),
- snd_cs4236_ctrl_in(chip, 6),
- snd_cs4236_ctrl_in(chip, 8));
+ dev_dbg(chip->card->dev,
+ "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
+ snd_cs4236_ctrl_in(chip, 3),
+ snd_cs4236_ctrl_in(chip, 4),
+ snd_cs4236_ctrl_in(chip, 5),
+ snd_cs4236_ctrl_in(chip, 6),
+ snd_cs4236_ctrl_in(chip, 8));
#endif
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),
@@ -1009,12 +958,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),
@@ -1022,7 +971,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),
@@ -1034,22 +983,24 @@ 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;
card = chip->card;
- strcpy(card->mixername, snd_wss_chip_id(chip));
+ strscpy(card->mixername, snd_wss_chip_id(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;
}
}
@@ -1072,13 +1023,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..7d6c44a8eaad 100644
--- a/sound/isa/es1688/Makefile
+++ b/sound/isa/es1688/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-es1688-lib-objs := es1688_lib.o
-snd-es1688-objs := es1688.o
+snd-es1688-lib-y := es1688_lib.o
+snd-es1688-y := es1688.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index a826c138e7f5..6a95dfb7600a 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>
@@ -41,11 +26,6 @@
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 */
@@ -99,9 +79,9 @@ 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;
@@ -148,11 +128,11 @@ static int 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, chip->pcm->name, sizeof(card->shortname));
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port,
- chip->irq, chip->dma8);
+ strscpy(card->driver, "ES1688", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port,
+ chip->irq, chip->dma8);
if (fm_port[n] == SNDRV_AUTO_PORT)
fm_port[n] = port[n]; /* share the same port */
@@ -186,37 +166,27 @@ static int snd_es1688_isa_probe(struct device *dev, unsigned int n)
struct snd_card *card;
int error;
- error = snd_card_new(dev, 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;
+ 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 snd_es1688_isa_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- return 0;
}
static struct isa_driver snd_es1688_driver = {
.match = snd_es1688_match,
.probe = snd_es1688_isa_probe,
- .remove = snd_es1688_isa_remove,
#if 0 /* FIXME */
.suspend = snd_es1688_suspend,
.resume = snd_es1688_resume,
@@ -243,7 +213,7 @@ static int snd_card_es968_pnp(struct snd_card *card, unsigned int n,
error = pnp_activate_dev(pdev);
if (error < 0) {
- snd_printk(KERN_ERR "ES968 pnp configure failure\n");
+ dev_err(card->dev, "ES968 pnp configure failure\n");
return error;
}
port[n] = pnp_port_start(pdev, 0);
@@ -260,7 +230,6 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
struct snd_card *card;
static unsigned int dev;
int error;
- struct snd_es1688 *chip;
if (snd_es968_pnp_is_probed)
return -EBUSY;
@@ -271,18 +240,15 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
if (dev == SNDRV_CARDS)
return -ENODEV;
- error = snd_card_new(&pcard->card->dev,
- 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;
- }
error = snd_es1688_probe(card, dev);
if (error < 0)
return error;
@@ -293,8 +259,6 @@ static int snd_es968_pnp_detect(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;
}
@@ -303,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;
}
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index f9c0662e9a22..59987dbc9ae9 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>
@@ -45,9 +30,7 @@ static int snd_es1688_dsp_command(struct snd_es1688 *chip, unsigned char val)
outb(val, ES1688P(chip, COMMAND));
return 1;
}
-#ifdef CONFIG_SND_DEBUG
- printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val);
-#endif
+ dev_dbg(chip->card->dev, "%s: timeout (0x%x)\n", __func__, val);
return 0;
}
@@ -58,7 +41,8 @@ static int snd_es1688_dsp_get_byte(struct snd_es1688 *chip)
for (i = 1000; i; i--)
if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80)
return inb(ES1688P(chip, READ));
- snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
+ dev_dbg(chip->card->dev, "es1688 get byte failed: 0x%lx = 0x%x!!!\n",
+ ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
return -ENODEV;
}
@@ -110,7 +94,8 @@ int snd_es1688_reset(struct snd_es1688 *chip)
udelay(30);
for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++);
if (inb(ES1688P(chip, READ)) != 0xaa) {
- snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port);
+ dev_dbg(chip->card->dev, "ess_reset at 0x%lx: failed!!!\n",
+ chip->port);
return -ENODEV;
}
snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */
@@ -120,85 +105,82 @@ 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;
/*
* initialization sequence
*/
- spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */
-
- if (snd_es1688_reset(chip) < 0) {
- snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ)));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
- }
- snd_es1688_dsp_command(chip, 0xe7); /* return identification */
-
- for (i = 1000, major = minor = 0; i; i--) {
- if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
- if (major == 0) {
- major = inb(ES1688P(chip, READ));
- } else {
- minor = inb(ES1688P(chip, READ));
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) { /* Some ESS1688 cards need this */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */
+
+ if (snd_es1688_reset(chip) < 0) {
+ dev_dbg(chip->card->dev, "ESS: [0x%lx] reset failed... 0x%x\n",
+ chip->port, inb(ES1688P(chip, READ)));
+ return -ENODEV;
+ }
+ snd_es1688_dsp_command(chip, 0xe7); /* return identification */
+
+ for (i = 1000, major = minor = 0; i; i--) {
+ if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
+ if (major == 0)
+ major = inb(ES1688P(chip, READ));
+ else
+ minor = inb(ES1688P(chip, READ));
}
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor);
+ dev_dbg(chip->card->dev,
+ "ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n",
+ chip->port, major, minor);
chip->version = (major << 8) | minor;
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);
+ dev_err(chip->card->dev,
+ "[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 "
- "with version 0x%x (Jazz16 soundcard?)\n",
- chip->port, chip->version);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n",
+ chip->port, chip->version);
return -ENODEV;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
- snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
+ snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
+ }
/* enable joystick, but disable OPL3 */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_es1688_mixer_write(chip, 0x40, 0x01);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_es1688_mixer_write(chip, 0x40, 0x01);
+ }
return 0;
}
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};
- unsigned long flags;
+ static const int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
/* ok.. setup MPU-401 port and joystick and OPL3 */
@@ -227,38 +209,36 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
}
}
}
-#if 0
- snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg);
-#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_mixer_write(chip, 0x40, cfg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_mixer_write(chip, 0x40, cfg);
+ }
/* --- */
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_read(chip, 0xb1);
- snd_es1688_read(chip, 0xb2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_read(chip, 0xb1);
+ snd_es1688_read(chip, 0xb2);
+ }
if (enable) {
cfg = 0xf0; /* enable only DMA counter interrupt */
irq_bits = irqs[chip->irq & 0x0f];
if (irq_bits < 0) {
- snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d "
- "for ES1688 chip!!\n",
- chip->port, chip->irq);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n",
+ chip->port, chip->irq);
#if 0
irq_bits = 0;
cfg = 0x10;
#endif
return -EINVAL;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
+ }
cfg = 0xf0; /* extended mode DMA enable */
dma = chip->dma8;
if (dma > 3 || dma == 2) {
- snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d "
- "for ES1688 chip!!\n", chip->port, dma);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n",
+ chip->port, dma);
#if 0
dma_bits = 0;
cfg = 0x00; /* disable all DMA */
@@ -269,20 +249,20 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
if (dma != 3)
dma_bits++;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
+ }
} else {
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
- snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
+ snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
+ }
+ }
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_read(chip, 0xb1);
+ snd_es1688_read(chip, 0xb2);
+ snd_es1688_reset(chip);
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_read(chip, 0xb1);
- snd_es1688_read(chip, 0xb2);
- snd_es1688_reset(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -326,12 +306,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;
@@ -341,85 +315,70 @@ static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char va
} else if (cmd != SNDRV_PCM_TRIGGER_START) {
return -EINVAL;
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->trigger_value = value;
val = snd_es1688_read(chip, 0xb8);
- if ((val < 0) || (val & 0x0f) == value) {
- spin_unlock(&chip->reg_lock);
+ if ((val < 0) || (val & 0x0f) == value)
return -EINVAL; /* something is wrong */
- }
#if 0
- printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value);
- printk(KERN_DEBUG "trigger: pointer = 0x%x\n",
- snd_dma_pointer(chip->dma8, chip->dma_size));
+ dev_dbg(chip->card->dev, "trigger: val = 0x%x, value = 0x%x\n", val, value);
+ dev_dbg(chip->card->dev, "trigger: pointer = 0x%x\n",
+ snd_dma_pointer(chip->dma8, chip->dma_size));
#endif
snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
- spin_unlock(&chip->reg_lock);
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;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_reset(chip);
- snd_es1688_set_rate(chip, substream);
- snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */
- snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
- snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
- if (runtime->channels == 1) {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit mono */
- snd_es1688_write(chip, 0xb6, 0x80);
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0xd0);
- } else {
- /* 16. bit mono */
- snd_es1688_write(chip, 0xb6, 0x00);
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xf4);
- }
- } else {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit stereo */
- snd_es1688_write(chip, 0xb6, 0x80);
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0x98);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_reset(chip);
+ snd_es1688_set_rate(chip, substream);
+ snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */
+ snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+ snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
+ if (runtime->channels == 1) {
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit mono */
+ snd_es1688_write(chip, 0xb6, 0x80);
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0xd0);
+ } else {
+ /* 16. bit mono */
+ snd_es1688_write(chip, 0xb6, 0x00);
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xf4);
+ }
} else {
- /* 16. bit stereo */
- snd_es1688_write(chip, 0xb6, 0x00);
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xbc);
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit stereo */
+ snd_es1688_write(chip, 0xb6, 0x80);
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0x98);
+ } else {
+ /* 16. bit stereo */
+ snd_es1688_write(chip, 0xb6, 0x00);
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xbc);
+ }
}
+ snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+ snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+ snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
}
- snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
- snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
- snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xa4, (unsigned char) count);
- snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xa4, (unsigned char) count);
+ snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+ }
return 0;
}
@@ -432,51 +391,50 @@ static int snd_es1688_playback_trigger(struct snd_pcm_substream *substream,
static int snd_es1688_capture_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_reset(chip);
- snd_es1688_set_rate(chip, substream);
- snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
- snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */
- snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
- snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
- if (runtime->channels == 1) {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit mono */
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0xd0);
- } else {
- /* 16. bit mono */
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xf4);
- }
- } else {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit stereo */
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0x98);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_reset(chip);
+ snd_es1688_set_rate(chip, substream);
+ snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
+ snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */
+ snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+ snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
+ if (runtime->channels == 1) {
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit mono */
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0xd0);
+ } else {
+ /* 16. bit mono */
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xf4);
+ }
} else {
- /* 16. bit stereo */
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xbc);
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit stereo */
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0x98);
+ } else {
+ /* 16. bit stereo */
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xbc);
+ }
}
+ snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+ snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
}
- snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
- snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xa4, (unsigned char) count);
- snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xa4, (unsigned char) count);
+ snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+ }
return 0;
}
@@ -646,7 +604,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,
};
@@ -654,28 +612,30 @@ int snd_es1688_create(struct snd_card *card,
if (chip == NULL)
return -ENOMEM;
+ chip->card = card;
chip->irq = -1;
chip->dma8 = -1;
chip->hardware = ES1688_HW_UNDEF;
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);
+ dev_err(card->dev, "es1688: can't grab port 0x%lx\n", port + 4);
err = -EBUSY;
goto exit;
}
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);
+ dev_err(card->dev, "es1688: can't grab IRQ %d\n", irq);
goto exit;
}
chip->irq = irq;
+ 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);
+ dev_err(card->dev, "es1688: can't grab DMA8 %d\n", dma8);
goto exit;
}
chip->dma8 = dma8;
@@ -709,9 +669,6 @@ exit:
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,
@@ -720,9 +677,6 @@ static const struct snd_pcm_ops snd_es1688_playback_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,
@@ -742,12 +696,11 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- strcpy(pcm->name, snd_es1688_chip_id(chip));
+ strscpy(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024, 64*1024);
return 0;
}
@@ -775,19 +728,17 @@ static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_es1688_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval, nval;
int change;
if (ucontrol->value.enumerated.item[0] > 8)
return -EINVAL;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV);
nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15);
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -811,15 +762,13 @@ static int snd_es1688_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -828,7 +777,6 @@ static int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -840,13 +788,12 @@ static int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (invert)
nval = mask - nval;
nval <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = snd_es1688_mixer_read(chip, reg);
nval = (oval & ~(mask << shift)) | nval;
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, reg, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -870,7 +817,6 @@ static int snd_es1688_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -879,7 +825,7 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
unsigned char left, right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg < 0xa0)
left = snd_es1688_mixer_read(chip, left_reg);
else
@@ -891,7 +837,6 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
right = snd_es1688_read(chip, right_reg);
} else
right = left;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
if (invert) {
@@ -904,7 +849,6 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -922,7 +866,7 @@ static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
if (left_reg < 0xa0)
oval1 = snd_es1688_mixer_read(chip, left_reg);
@@ -960,11 +904,10 @@ static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
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),
@@ -986,7 +929,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 },
@@ -1008,10 +951,11 @@ int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
if (snd_BUG_ON(!chip || !card))
return -EINVAL;
- strcpy(card->mixername, snd_es1688_chip_id(chip));
+ strscpy(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++) {
@@ -1029,19 +973,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 2a6960c3e2a4..1da7b400a17b 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:
*
@@ -97,14 +82,10 @@
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#define PFX "es18xx: "
-
struct snd_es18xx {
+ struct snd_card *card;
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 */
@@ -183,7 +164,7 @@ static int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val)
outb(val, chip->port + 0x0C);
return 0;
}
- snd_printk(KERN_ERR "dsp_command: timeout (0x%x)\n", val);
+ dev_err(chip->card->dev, "dsp_command: timeout (0x%x)\n", val);
return -EINVAL;
}
@@ -194,8 +175,8 @@ static int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
for(i = MILLISECOND/10; i; i--)
if (inb(chip->port + 0x0C) & 0x40)
return inb(chip->port + 0x0A);
- snd_printk(KERN_ERR "dsp_get_byte failed: 0x%lx = 0x%x!!!\n",
- chip->port + 0x0A, inb(chip->port + 0x0A));
+ dev_err(chip->card->dev, "dsp_get_byte failed: 0x%lx = 0x%x!!!\n",
+ chip->port + 0x0A, inb(chip->port + 0x0A));
return -ENODEV;
}
@@ -204,40 +185,35 @@ static int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
static int snd_es18xx_write(struct snd_es18xx *chip,
unsigned char reg, unsigned char data)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, data);
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Reg %02x set to %02x\n", reg, data);
#endif
return ret;
}
static int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg)
{
- unsigned long flags;
int ret, data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, 0xC0);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
data = snd_es18xx_dsp_get_byte(chip);
ret = data;
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x now is %02x (%d)\n", reg, data, ret);
+ dev_dbg(chip->card->dev, "Reg %02x now is %02x (%d)\n", reg, data, ret);
#endif
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return ret;
}
@@ -247,62 +223,55 @@ static int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg,
{
int ret;
unsigned char old, new, oval;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, 0xC0);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_get_byte(chip);
- if (ret < 0) {
- goto end;
- }
+ if (ret < 0)
+ return ret;
old = ret;
oval = old & mask;
if (val != oval) {
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
new = (old & ~mask) | (val & mask);
ret = snd_es18xx_dsp_command(chip, new);
if (ret < 0)
- goto end;
+ return ret;
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x (%d)\n",
- reg, old, new, ret);
+ dev_dbg(chip->card->dev, "Reg %02x was %02x, set to %02x (%d)\n",
+ reg, old, new, ret);
#endif
}
- ret = oval;
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return ret;
+ return oval;
}
static inline void snd_es18xx_mixer_write(struct snd_es18xx *chip,
unsigned char reg, unsigned char data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
outb(data, chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Mixer reg %02x set to %02x\n", reg, data);
#endif
}
static inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg)
{
- unsigned long flags;
int data;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
data = inb(chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Mixer reg %02x now is %02x\n", reg, data);
#endif
return data;
}
@@ -312,8 +281,8 @@ static inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char r
unsigned char mask, unsigned char val)
{
unsigned char old, new, oval;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
old = inb(chip->port + 0x05);
oval = old & mask;
@@ -321,11 +290,10 @@ static inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char r
new = (old & ~mask) | (val & mask);
outb(new, chip->port + 0x05);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
- reg, old, new);
+ 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;
}
@@ -333,17 +301,16 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
unsigned char mask)
{
int old, expected, new;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
old = inb(chip->port + 0x05);
expected = old ^ mask;
outb(expected, chip->port + 0x05);
new = inb(chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x, now is %02x\n",
- reg, old, expected, new);
+ dev_dbg(chip->card->dev, "Mixer reg %02x was %02x, set to %02x, now is %02x\n",
+ reg, old, expected, new);
#endif
return expected == new;
}
@@ -449,7 +416,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)
@@ -468,16 +435,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)
{
@@ -558,7 +518,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) &&
@@ -572,8 +532,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;
}
@@ -930,7 +888,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;
}
@@ -939,7 +896,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;
}
@@ -981,7 +937,7 @@ static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
case 0x1887:
case 0x1888:
return snd_ctl_enum_info(uinfo, 1, 5, texts5Source);
- case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */
+ case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */
case 0x1879:
return snd_ctl_enum_info(uinfo, 1, 8, texts8Source);
default:
@@ -991,7 +947,7 @@ static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
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)) {
@@ -1008,7 +964,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;
@@ -1024,6 +980,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:
@@ -1271,7 +1228,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),
@@ -1290,7 +1247,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),
@@ -1302,35 +1259,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[] = {
+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,
@@ -1341,13 +1298,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",
@@ -1365,18 +1322,15 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
};
-static struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = {
+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)
{
- int data;
-
outb(reg, chip->ctrl_port);
- data = inb(chip->ctrl_port + 1);
- return data;
+ return inb(chip->ctrl_port + 1);
}
static void snd_es18xx_config_write(struct snd_es18xx *chip,
@@ -1387,7 +1341,7 @@ static void snd_es18xx_config_write(struct snd_es18xx *chip,
outb(reg, chip->ctrl_port);
outb(data, chip->ctrl_port + 1);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Config reg %02x set to %02x\n", reg, data);
#endif
}
@@ -1456,7 +1410,7 @@ static int snd_es18xx_initialize(struct snd_es18xx *chip,
irqmask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid irq %d\n", chip->irq);
+ dev_err(chip->card->dev, "invalid irq %d\n", chip->irq);
return -ENODEV;
}
switch (chip->dma1) {
@@ -1470,7 +1424,7 @@ static int snd_es18xx_initialize(struct snd_es18xx *chip,
dma1mask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1);
+ dev_err(chip->card->dev, "invalid dma1 %d\n", chip->dma1);
return -ENODEV;
}
switch (chip->dma2) {
@@ -1487,7 +1441,7 @@ static int snd_es18xx_initialize(struct snd_es18xx *chip,
dma2mask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2);
+ dev_err(chip->card->dev, "invalid dma2 %d\n", chip->dma2);
return -ENODEV;
}
@@ -1556,13 +1510,13 @@ static int snd_es18xx_initialize(struct snd_es18xx *chip,
return 0;
}
-static int snd_es18xx_identify(struct snd_es18xx *chip)
+static int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip)
{
int hi,lo;
/* reset */
if (snd_es18xx_reset(chip) < 0) {
- snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port);
+ dev_err(card->dev, "reset at 0x%lx failed!!!\n", chip->port);
return -ENODEV;
}
@@ -1598,8 +1552,9 @@ static int 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) {
- snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port);
+ if (!devm_request_region(card->dev, chip->ctrl_port, 8,
+ "ES18xx - CTRL")) {
+ dev_err(card->dev, "unable go grab port 0x%lx\n", chip->ctrl_port);
return -EBUSY;
}
@@ -1625,12 +1580,13 @@ static int snd_es18xx_identify(struct snd_es18xx *chip)
return 0;
}
-static int snd_es18xx_probe(struct snd_es18xx *chip,
+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) {
- snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
+ if (snd_es18xx_identify(card, chip) < 0) {
+ dev_err(card->dev, "[0x%lx] ESS chip not found\n", chip->port);
return -ENODEV;
}
@@ -1652,12 +1608,12 @@ static int snd_es18xx_probe(struct snd_es18xx *chip,
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",
- chip->port, chip->version);
+ dev_err(card->dev, "[0x%lx] unsupported chip ES%x\n",
+ chip->port, chip->version);
return -ENODEV;
}
- snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);
+ dev_dbg(card->dev, "[0x%lx] ESS%x chip found\n", chip->port, chip->version);
if (chip->dma1 == chip->dma2)
chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
@@ -1668,9 +1624,7 @@ static int snd_es18xx_probe(struct snd_es18xx *chip,
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,
@@ -1679,9 +1633,7 @@ static const struct snd_pcm_ops snd_es18xx_playback_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,
@@ -1715,10 +1667,9 @@ static int 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);
+ 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;
}
@@ -1730,8 +1681,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);
@@ -1753,31 +1702,6 @@ 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 snd_es18xx_new_device(struct snd_card *card,
unsigned long port,
unsigned long mpu_port,
@@ -1785,11 +1709,8 @@ static int snd_es18xx_new_device(struct snd_card *card,
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;
+ chip->card = card;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->port = port;
@@ -1799,44 +1720,34 @@ static int 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);
- snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
+ if (!devm_request_region(card->dev, port, 16, "ES18xx")) {
+ dev_err(card->dev, "unable to grab ports 0x%lx-0x%lx\n", port, port + 16 - 1);
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, 0, "ES18xx",
- (void *) card)) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
+ if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx",
+ (void *) card)) {
+ dev_err(card->dev, "unable to grab 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);
- snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
+ if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) {
+ dev_err(card->dev, "unable to grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
+ if (dma2 != dma1 &&
+ snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) {
+ dev_err(card->dev, "unable to grab 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;
}
@@ -1846,7 +1757,7 @@ static int snd_es18xx_mixer(struct snd_card *card)
int err;
unsigned int idx;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
struct snd_kcontrol *kctl;
@@ -1863,41 +1774,48 @@ static int 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;
}
}
@@ -1910,7 +1828,8 @@ static int 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;
}
@@ -1960,17 +1879,9 @@ static int snd_es18xx_mixer(struct snd_card *card)
/* 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 */
@@ -2029,7 +1940,7 @@ MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
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");
+ dev_err(&pdev->dev, "PnP configure failure (out of resources?)\n");
return -EBUSY;
}
/* ok. hack using Vendor-Defined Card-Level registers */
@@ -2048,8 +1959,12 @@ static int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
- snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+ dev_dbg(&pdev->dev,
+ "PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n",
+ port[dev], fm_port[dev], mpu_port[dev]);
+ dev_dbg(&pdev->dev,
+ "PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n",
+ dma1[dev], dma2[dev], irq[dev]);
return 0;
}
@@ -2097,11 +2012,12 @@ static int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
/* Control port initialization */
if (pnp_activate_dev(chip->devc) < 0) {
- snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
+ dev_err(chip->card->dev,
+ "PnP control configure failure (out of resources?)\n");
return -EAGAIN;
}
- snd_printdd("pnp: port=0x%llx\n",
- (unsigned long long)pnp_port_start(chip->devc, 0));
+ dev_dbg(chip->card->dev, "pnp: port=0x%llx\n",
+ (unsigned long long)pnp_port_start(chip->devc, 0));
if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
@@ -2118,8 +2034,8 @@ static int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
static int snd_es18xx_card_new(struct device *pdev, int dev,
struct snd_card **cardp)
{
- return snd_card_new(pdev, 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 snd_audiodrive_probe(struct snd_card *card, int dev)
@@ -2159,9 +2075,9 @@ static int snd_audiodrive_probe(struct snd_card *card, int dev)
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING PFX
- "opl3 not detected at 0x%lx\n",
- fm_port[dev]);
+ dev_warn(card->dev,
+ "opl3 not detected at 0x%lx\n",
+ fm_port[dev]);
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
@@ -2193,10 +2109,9 @@ static int snd_es18xx_isa_probe1(int dev, struct device *devptr)
err = snd_es18xx_card_new(devptr, dev, &card);
if (err < 0)
return err;
- 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;
}
@@ -2204,24 +2119,27 @@ static int snd_es18xx_isa_probe1(int dev, struct device *devptr)
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) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
+ dev_err(pdev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
@@ -2229,7 +2147,7 @@ static int 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];
@@ -2241,13 +2159,6 @@ static int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int snd_es18xx_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_es18xx_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -2266,7 +2177,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 = snd_es18xx_isa_remove,
#ifdef CONFIG_PM
.suspend = snd_es18xx_isa_suspend,
.resume = snd_es18xx_isa_resume,
@@ -2297,24 +2207,17 @@ static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
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;
- }
- 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 snd_audiodrive_pnp_remove(struct pnp_dev *pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
-}
-
#ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -2330,7 +2233,6 @@ static struct pnp_driver es18xx_pnp_driver = {
.name = "es18xx-pnpbios",
.id_table = snd_audiodrive_pnpbiosids,
.probe = snd_audiodrive_pnp_detect,
- .remove = snd_audiodrive_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnp_suspend,
.resume = snd_audiodrive_pnp_resume,
@@ -2355,26 +2257,18 @@ static int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
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;
- }
- 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 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)
{
@@ -2393,7 +2287,6 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
.name = "es18xx",
.id_table = snd_audiodrive_pnpids,
.probe = snd_audiodrive_pnpc_detect,
- .remove = 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..2dbd519860a6 100644
--- a/sound/isa/galaxy/Makefile
+++ b/sound/isa/galaxy/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
-snd-azt1605-objs := azt1605.o
-snd-azt2316-objs := azt2316.o
+snd-azt1605-y := azt1605.o
+snd-azt2316-y := azt2316.o
obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
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 b9994cc9f5fb..b0f1562d0fc0 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>
@@ -260,6 +247,7 @@ static int 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 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;
@@ -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 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,60 +486,64 @@ static int snd_galaxy_probe(struct device *dev, unsigned int n)
u8 type;
int err;
- err = snd_card_new(dev, 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;
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);
- strcpy(card->shortname, DRV_NAME);
+ strscpy(card->driver, DRV_NAME);
+ strscpy(card->shortname, DRV_NAME);
sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
card->shortname, port[n], wss_port[n], irq[n], dma1[n],
dma2[n]);
@@ -567,25 +551,25 @@ static int 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);
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);
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], NULL);
if (err < 0)
- goto error;
+ return err;
}
if (fm_port[n] >= 0) {
@@ -595,39 +579,33 @@ static int 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 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));
- 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 = snd_galaxy_remove,
.driver = {
.name = DEV_NAME
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
index c6f32ffd3420..4924c1904fa4 100644
--- a/sound/isa/gus/Makefile
+++ b/sound/isa/gus/Makefile
@@ -4,18 +4,18 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-gus-lib-objs := gus_main.o \
+snd-gus-lib-y := gus_main.o \
gus_io.o gus_irq.o gus_timer.o \
gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
gus_pcm.o gus_mixer.o \
gus_uart.o \
gus_reset.o
-snd-gusclassic-objs := gusclassic.o
-snd-gusextreme-objs := gusextreme.o
-snd-gusmax-objs := gusmax.o
-snd-interwave-objs := interwave.o
-snd-interwave-stb-objs := interwave-stb.o
+snd-gusclassic-y := gusclassic.o
+snd-gusextreme-y := gusextreme.o
+snd-gusmax-y := gusmax.o
+snd-interwave-y := interwave.o
+snd-interwave-stb-y := interwave-stb.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index 7f95f452f106..ffc69e26227e 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>
@@ -26,12 +11,9 @@
static void snd_gf1_dma_ack(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_dma_program(struct snd_gus_card * gus,
@@ -40,20 +22,22 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
unsigned int count,
unsigned int cmd)
{
- unsigned long flags;
unsigned int address;
unsigned char dma_cmd;
unsigned int address_high;
- snd_printdd("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
- addr, buf_addr, count);
+ dev_dbg(gus->card->dev,
+ "dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
+ addr, buf_addr, count);
if (gus->gf1.dma1 > 3) {
if (gus->gf1.enh_mode) {
address = addr >> 1;
} else {
if (addr & 0x1f) {
- snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
+ dev_dbg(gus->card->dev,
+ "%s: unaligned address (0x%x)?\n",
+ __func__, addr);
return;
}
address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
@@ -78,10 +62,11 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
snd_gf1_dma_ack(gus);
snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
#if 0
- snd_printk(KERN_DEBUG "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
- address << 1, count, dma_cmd);
+ dev_dbg(gus->card->dev,
+ "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
+ address << 1, count, dma_cmd);
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
if (gus->gf1.enh_mode) {
address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
@@ -89,7 +74,6 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
} else
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
@@ -131,39 +115,37 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
snd_gf1_dma_ack(gus);
if (gus->gf1.dma_ack)
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
- spin_lock(&gus->dma_lock);
- if (gus->gf1.dma_data_pcm == NULL &&
- gus->gf1.dma_data_synth == NULL) {
- gus->gf1.dma_ack = NULL;
- gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
- spin_unlock(&gus->dma_lock);
- return;
+ scoped_guard(spinlock, &gus->dma_lock) {
+ if (gus->gf1.dma_data_pcm == NULL &&
+ gus->gf1.dma_data_synth == NULL) {
+ gus->gf1.dma_ack = NULL;
+ gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
+ return;
+ }
+ block = snd_gf1_dma_next_block(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
- snd_printd(KERN_DEBUG "program dma (IRQ) - "
- "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
- block->addr, block->buf_addr, block->count, block->cmd);
+ dev_dbg(gus->card->dev,
+ "program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, block->buf_addr, block->count, block->cmd);
#endif
}
int snd_gf1_dma_init(struct snd_gus_card * gus)
{
- mutex_lock(&gus->dma_mutex);
+ guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared++;
- if (gus->gf1.dma_shared > 1) {
- mutex_unlock(&gus->dma_mutex);
+ if (gus->gf1.dma_shared > 1)
return 0;
- }
gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
gus->gf1.dma_data_pcm =
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth =
gus->gf1.dma_data_synth_last = NULL;
- mutex_unlock(&gus->dma_mutex);
return 0;
}
@@ -171,7 +153,7 @@ int snd_gf1_dma_done(struct snd_gus_card * gus)
{
struct snd_gf1_dma_block *block;
- mutex_lock(&gus->dma_mutex);
+ guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared--;
if (!gus->gf1.dma_shared) {
snd_dma_disable(gus->gf1.dma1);
@@ -188,7 +170,6 @@ int snd_gf1_dma_done(struct snd_gus_card * gus)
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth_last = NULL;
}
- mutex_unlock(&gus->dma_mutex);
return 0;
}
@@ -197,8 +178,8 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
int atomic,
int synth)
{
- unsigned long flags;
struct snd_gf1_dma_block *block;
+ struct snd_gf1_dma_block *free_block = NULL;
block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
if (!block)
@@ -207,43 +188,48 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
*block = *__block;
block->next = NULL;
- snd_printdd("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
- block->addr, (long) block->buffer, block->count,
- block->cmd);
-
- snd_printdd("gus->gf1.dma_data_pcm_last = 0x%lx\n",
- (long)gus->gf1.dma_data_pcm_last);
- snd_printdd("gus->gf1.dma_data_pcm = 0x%lx\n",
- (long)gus->gf1.dma_data_pcm);
-
- spin_lock_irqsave(&gus->dma_lock, flags);
- if (synth) {
- if (gus->gf1.dma_data_synth_last) {
- gus->gf1.dma_data_synth_last->next = block;
- gus->gf1.dma_data_synth_last = block;
+ dev_dbg(gus->card->dev,
+ "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, (long) block->buffer, block->count,
+ block->cmd);
+
+ dev_dbg(gus->card->dev,
+ "gus->gf1.dma_data_pcm_last = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm_last);
+ dev_dbg(gus->card->dev,
+ "gus->gf1.dma_data_pcm = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm);
+
+ scoped_guard(spinlock_irqsave, &gus->dma_lock) {
+ if (synth) {
+ if (gus->gf1.dma_data_synth_last) {
+ gus->gf1.dma_data_synth_last->next = block;
+ gus->gf1.dma_data_synth_last = block;
+ } else {
+ gus->gf1.dma_data_synth =
+ gus->gf1.dma_data_synth_last = block;
+ }
} else {
- gus->gf1.dma_data_synth =
- gus->gf1.dma_data_synth_last = block;
+ if (gus->gf1.dma_data_pcm_last) {
+ gus->gf1.dma_data_pcm_last->next = block;
+ gus->gf1.dma_data_pcm_last = block;
+ } else {
+ gus->gf1.dma_data_pcm =
+ gus->gf1.dma_data_pcm_last = block;
+ }
}
- } else {
- if (gus->gf1.dma_data_pcm_last) {
- gus->gf1.dma_data_pcm_last->next = block;
- gus->gf1.dma_data_pcm_last = block;
- } else {
- gus->gf1.dma_data_pcm =
- gus->gf1.dma_data_pcm_last = block;
+ if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
+ gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
+ free_block = snd_gf1_dma_next_block(gus);
}
}
- if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
- gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
- block = snd_gf1_dma_next_block(gus);
- spin_unlock_irqrestore(&gus->dma_lock, flags);
- if (block == NULL)
- return 0;
- snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
- kfree(block);
- return 0;
+
+ if (free_block) {
+ snd_gf1_dma_program(gus, free_block->addr, free_block->buf_addr,
+ free_block->count,
+ (unsigned short)free_block->cmd);
+ kfree(free_block);
}
- spin_unlock_irqrestore(&gus->dma_lock, flags);
+
return 0;
}
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
index fd2e2e2ed4e7..50fe738ee3ea 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>
@@ -28,7 +13,6 @@
static int snd_gus_dram_poke(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size)
{
- unsigned long flags;
unsigned int size1, size2;
char buffer[256], *pbuffer;
@@ -37,11 +21,10 @@ static int snd_gus_dram_poke(struct snd_gus_card *gus, char __user *_buffer,
if (copy_from_user(buffer, _buffer, size1))
return -EFAULT;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
snd_gf1_dram_addr(gus, address);
outsb(GUSP(gus, DRAM), buffer, size1);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
address += size1;
} else {
pbuffer = buffer;
@@ -66,19 +49,17 @@ static int snd_gus_dram_peek(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size,
int rom)
{
- unsigned long flags;
unsigned int size1, size2;
char buffer[256], *pbuffer;
while (size > 0) {
size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
snd_gf1_dram_addr(gus, address);
insb(GUSP(gus, DRAM), buffer, size1);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
address += size1;
} else {
pbuffer = buffer;
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
index ca79878d8d8c..3e6f35084f26 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>
@@ -192,99 +177,37 @@ unsigned int snd_gf1_read_addr(struct snd_gus_card * gus,
void snd_gf1_i_ctrl_stop(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_ctrl_stop(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
void snd_gf1_i_write8(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write8(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned char snd_gf1_i_look8(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
- unsigned char res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_look8(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ guard(spinlock_irqsave)(&gus->reg_lock);
+ return __snd_gf1_look8(gus, reg);
}
void snd_gf1_i_write16(struct snd_gus_card * gus,
unsigned char reg,
unsigned int data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write16(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned short snd_gf1_i_look16(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
- unsigned short res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_look16(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
-}
-
-#if 0
-
-void snd_gf1_i_adlib_write(struct snd_gus_card * gus,
- unsigned char reg,
- unsigned char data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- __snd_gf1_adlib_write(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-}
-
-void snd_gf1_i_write_addr(struct snd_gus_card * gus, unsigned char reg,
- unsigned int addr, short w_16bit)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- __snd_gf1_write_addr(gus, reg, addr, w_16bit);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-}
-
-#endif /* 0 */
-
-#ifdef CONFIG_SND_DEBUG
-static unsigned int snd_gf1_i_read_addr(struct snd_gus_card * gus,
- unsigned char reg, short w_16bit)
-{
- unsigned int res;
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_read_addr(gus, reg, w_16bit);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ guard(spinlock_irqsave)(&gus->reg_lock);
+ return __snd_gf1_look16(gus, reg);
}
-#endif
-
-/*
-
- */
void snd_gf1_dram_addr(struct snd_gus_card * gus, unsigned int addr)
{
@@ -300,9 +223,7 @@ void snd_gf1_dram_addr(struct snd_gus_card * gus, unsigned int addr)
void snd_gf1_poke(struct snd_gus_card * gus, unsigned int addr, unsigned char data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -312,15 +233,11 @@ void snd_gf1_poke(struct snd_gus_card * gus, unsigned int addr, unsigned char da
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(data, gus->gf1.reg_dram);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned char snd_gf1_peek(struct snd_gus_card * gus, unsigned int addr)
{
- unsigned long flags;
- unsigned char res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -329,22 +246,16 @@ unsigned char snd_gf1_peek(struct snd_gus_card * gus, unsigned int addr)
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
- res = inb(gus->gf1.reg_dram);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ return inb(gus->gf1.reg_dram);
}
#if 0
void snd_gf1_pokew(struct snd_gus_card * gus, unsigned int addr, unsigned short data)
{
- unsigned long flags;
-
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_pokew - GF1!!!\n");
-#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -356,19 +267,13 @@ void snd_gf1_pokew(struct snd_gus_card * gus, unsigned int addr, unsigned short
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
outw(data, gus->gf1.reg_data16);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned short snd_gf1_peekw(struct snd_gus_card * gus, unsigned int addr)
{
- unsigned long flags;
- unsigned short res;
-
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_peekw - GF1!!!\n");
-#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -379,25 +284,20 @@ unsigned short snd_gf1_peekw(struct snd_gus_card * gus, unsigned int addr)
mb();
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
- res = inw(gus->gf1.reg_data16);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ return inw(gus->gf1.reg_data16);
}
void snd_gf1_dram_setmem(struct snd_gus_card * gus, unsigned int addr,
unsigned short value, unsigned int count)
{
unsigned long port;
- unsigned long flags;
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_dram_setmem - GF1!!!\n");
-#endif
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
addr &= ~1;
count >>= 1;
port = GUSP(gus, GF1DATALOW);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -409,7 +309,6 @@ void snd_gf1_dram_setmem(struct snd_gus_card * gus, unsigned int addr,
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
while (count--)
outw(value, port);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
#endif /* 0 */
@@ -418,7 +317,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
@@ -439,102 +338,3 @@ void snd_gf1_select_active_voices(struct snd_gus_card * gus)
udelay(100);
}
}
-
-#ifdef CONFIG_SND_DEBUG
-
-void snd_gf1_print_voice_registers(struct snd_gus_card * gus)
-{
- unsigned char mode;
- int voice, ctrl;
-
- voice = gus->gf1.active_voice;
- printk(KERN_INFO " -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d));
- printk(KERN_INFO " -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1));
- printk(KERN_INFO " -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4));
- printk(KERN_INFO " -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6));
- printk(KERN_INFO" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9));
- printk(KERN_INFO " -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4));
- if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */
- mode = snd_gf1_i_read8(gus, 0x15);
- printk(KERN_INFO " -%i- GFA1 mode = 0x%x\n", voice, mode);
- if (mode & 0x01) { /* Effect processor */
- 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));
- }
- 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);
- printk(KERN_INFO " -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4);
- printk(KERN_INFO " -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4);
- printk(KERN_INFO " -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4);
- } else
- printk(KERN_INFO " -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
- } else
- printk(KERN_INFO " -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
-}
-
-#if 0
-
-void snd_gf1_print_global_registers(struct snd_gus_card * gus)
-{
- unsigned char global_mode = 0x00;
-
- printk(KERN_INFO " -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES));
- if (gus->interwave) {
- global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE);
- printk(KERN_INFO " -G- GF1 global mode = 0x%x\n", global_mode);
- }
- if (global_mode & 0x02) /* LFO enabled? */
- printk(KERN_INFO " -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE));
- printk(KERN_INFO " -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ));
- printk(KERN_INFO " -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL));
- printk(KERN_INFO " -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW));
- printk(KERN_INFO " -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW));
- if (!gus->interwave)
- printk(KERN_INFO " -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL));
- printk(KERN_INFO " -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16));
- if (gus->gf1.enh_mode) {
- printk(KERN_INFO " -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG));
- printk(KERN_INFO " -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL));
- printk(KERN_INFO " -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR));
- printk(KERN_INFO " -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR));
- printk(KERN_INFO " -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE));
- }
-}
-
-void snd_gf1_print_setup_registers(struct snd_gus_card * gus)
-{
- printk(KERN_INFO " -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG)));
- printk(KERN_INFO " -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT)));
- printk(KERN_INFO " -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL)));
- printk(KERN_INFO " -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA)));
- printk(KERN_INFO " -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS)));
- printk(KERN_INFO " -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL));
- printk(KERN_INFO " -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2));
- printk(KERN_INFO " -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET));
- if (gus->interwave) {
- printk(KERN_INFO " -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY));
- printk(KERN_INFO " -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL));
- printk(KERN_INFO " -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER));
- printk(KERN_INFO " -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B));
- printk(KERN_INFO " -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ));
- }
-}
-
-void snd_gf1_peek_print_block(struct snd_gus_card * gus, unsigned int addr, int count, int w_16bit)
-{
- if (!w_16bit) {
- while (count-- > 0)
- printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++));
- } else {
- while (count-- > 0) {
- printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8));
- addr += 2;
- }
- }
-}
-
-#endif /* 0 */
-
-#endif
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
index 2055aff71b50..0e1054402c91 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>
@@ -41,7 +26,6 @@ __again:
if (status == 0)
return IRQ_RETVAL(handled);
handled = 1;
- /* snd_printk(KERN_DEBUG "IRQ: status = 0x%x\n", status); */
if (status & 0x02) {
STAT_ADD(gus->gf1.interrupt_stat_midi_in);
if (gus->gf1.interrupt_handler_midi_in)
@@ -65,9 +49,9 @@ __again:
continue; /* multi request */
already |= _current_; /* mark request */
#if 0
- printk(KERN_DEBUG "voice = %i, voice_status = 0x%x, "
- "voice_verify = %i\n",
- voice, voice_status, inb(GUSP(gus, GF1PAGE)));
+ dev_dbg(gus->card->dev,
+ "voice = %i, voice_status = 0x%x, voice_verify = %i\n",
+ voice, voice_status, inb(GUSP(gus, GF1PAGE)));
#endif
pvoice = &gus->gf1.voices[voice];
if (pvoice->use) {
@@ -140,10 +124,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 3cf9b13c780a..5f50a39c6f16 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>
@@ -37,18 +22,6 @@ MODULE_LICENSE("GPL");
static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches);
-int snd_gus_use_inc(struct snd_gus_card * gus)
-{
- if (!try_module_get(gus->card->module))
- return 0;
- return 1;
-}
-
-void snd_gus_use_dec(struct snd_gus_card * gus)
-{
- module_put(gus->card->module);
-}
-
static int snd_gus_joystick_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -69,16 +42,14 @@ static int snd_gus_joystick_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval;
nval = ucontrol->value.integer.value[0] & 31;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
change = gus->joystick_dac != nval;
gus->joystick_dac = nval;
snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return change;
}
@@ -140,7 +111,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,
};
@@ -171,31 +142,34 @@ 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) {
- snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n", port);
+ gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)");
+ if (!gus->gf1.res_port1) {
+ dev_err(card->dev, "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) {
- snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n", port + 0x100);
+ gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)");
+ if (!gus->gf1.res_port2) {
+ dev_err(card->dev, "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, 0, "GUS GF1", (void *) gus)) {
- snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq);
+ dev_err(card->dev, "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);
+ dev_err(card->dev, "gus: can't grab DMA1 %d\n", dma1);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.dma1 = dma1;
if (dma2 >= 0 && dma1 != dma2) {
if (request_dma(dma2, "GUS - 2")) {
- snd_printk(KERN_ERR "gus: can't grab DMA2 %d\n", dma2);
+ dev_err(card->dev, "gus: can't grab DMA2 %d\n", dma2);
snd_gus_free(gus);
return -EBUSY;
}
@@ -220,7 +194,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;
}
@@ -240,7 +215,9 @@ static int snd_gus_detect_memory(struct snd_gus_card * gus)
snd_gf1_poke(gus, 0L, 0xaa);
snd_gf1_poke(gus, 1L, 0x55);
if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) {
- snd_printk(KERN_ERR "plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port);
+ dev_err(gus->card->dev,
+ "plain GF1 card at 0x%lx without onboard DRAM?\n",
+ gus->gf1.port);
return -ENOMEM;
}
for (idx = 1, d = 0xab; idx < 4; idx++, d++) {
@@ -270,11 +247,10 @@ static int snd_gus_detect_memory(struct snd_gus_card * gus)
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))
@@ -298,14 +274,14 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
if ((dma1 & 7) == 0 || (dma2 & 7) == 0) {
- snd_printk(KERN_ERR "Error! DMA isn't defined.\n");
+ dev_err(gus->card->dev, "Error! DMA isn't defined.\n");
return -EINVAL;
}
irq = gus->gf1.irq;
irq = abs(irq);
irq = irqs[irq & 0x0f];
if (irq == 0) {
- snd_printk(KERN_ERR "Error! IRQ isn't defined.\n");
+ dev_err(gus->card->dev, "Error! IRQ isn't defined.\n");
return -EINVAL;
}
irq |= 0x40;
@@ -313,34 +289,34 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
card->mixer.mix_ctrl_reg |= 0x10;
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(5, GUSP(gus, REGCNTRLS));
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(0x00, GUSP(gus, IRQDMACNTRLREG));
- outb(0, GUSP(gus, REGCNTRLS));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(5, GUSP(gus, REGCNTRLS));
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(0x00, GUSP(gus, IRQDMACNTRLREG));
+ outb(0, GUSP(gus, REGCNTRLS));
+ }
udelay(100);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(dma1, GUSP(gus, IRQDMACNTRLREG));
- if (latches) {
- outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+ if (latches) {
+ outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ }
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
udelay(100);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(dma1, GUSP(gus, IRQDMACNTRLREG));
- if (latches) {
- outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+ if (latches) {
+ outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ }
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
snd_gf1_delay(gus);
@@ -348,29 +324,28 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
gus->mix_cntrl_reg |= 0x08; /* enable latches */
else
gus->mix_cntrl_reg &= ~0x08; /* disable latches */
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(0, GUSP(gus, GF1PAGE));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(0, GUSP(gus, GF1PAGE));
+ }
return 0;
}
static int snd_gus_check_version(struct snd_gus_card * gus)
{
- unsigned long flags;
unsigned char val, rev;
struct snd_card *card;
card = gus->card;
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x20, GUSP(gus, REGCNTRLS));
- val = inb(GUSP(gus, REGCNTRLS));
- rev = inb(GUSP(gus, BOARDVERSION));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
- strcpy(card->driver, "GUS");
- strcpy(card->longname, "Gravis UltraSound Classic (2.4)");
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x20, GUSP(gus, REGCNTRLS));
+ val = inb(GUSP(gus, REGCNTRLS));
+ rev = inb(GUSP(gus, BOARDVERSION));
+ }
+ dev_dbg(card->dev, "GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
+ strscpy(card->driver, "GUS");
+ strscpy(card->longname, "Gravis UltraSound Classic (2.4)");
if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) {
if (rev >= 5 && rev <= 9) {
gus->ics_flag = 1;
@@ -381,24 +356,27 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
}
if (rev >= 10 && rev != 255) {
if (rev >= 10 && rev <= 11) {
- strcpy(card->driver, "GUS MAX");
- strcpy(card->longname, "Gravis UltraSound MAX");
+ strscpy(card->driver, "GUS MAX");
+ strscpy(card->longname, "Gravis UltraSound MAX");
gus->max_flag = 1;
} else if (rev == 0x30) {
- strcpy(card->driver, "GUS ACE");
- strcpy(card->longname, "Gravis UltraSound Ace");
+ strscpy(card->driver, "GUS ACE");
+ strscpy(card->longname, "Gravis UltraSound Ace");
gus->ace_flag = 1;
} else if (rev == 0x50) {
- strcpy(card->driver, "GUS Extreme");
- strcpy(card->longname, "Gravis UltraSound Extreme");
+ strscpy(card->driver, "GUS Extreme");
+ strscpy(card->longname, "Gravis UltraSound Extreme");
gus->ess_flag = 1;
} else {
- snd_printk(KERN_ERR "unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val);
- snd_printk(KERN_ERR " please - report to <perex@perex.cz>\n");
+ dev_err(card->dev,
+ "unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n",
+ gus->gf1.port, rev, val);
+ dev_err(card->dev,
+ " please - report to <perex@perex.cz>\n");
}
}
}
- 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;
@@ -409,14 +387,17 @@ int snd_gus_initialize(struct snd_gus_card *gus)
int err;
if (!gus->interwave) {
- if ((err = snd_gus_check_version(gus)) < 0) {
- snd_printk(KERN_ERR "version check failed\n");
+ err = snd_gus_check_version(gus);
+ if (err < 0) {
+ dev_err(gus->card->dev, "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;
@@ -446,8 +427,6 @@ EXPORT_SYMBOL(snd_gf1_new_mixer);
/* gus_pcm.c */
EXPORT_SYMBOL(snd_gf1_pcm_new);
/* gus.c */
-EXPORT_SYMBOL(snd_gus_use_inc);
-EXPORT_SYMBOL(snd_gus_use_dec);
EXPORT_SYMBOL(snd_gus_create);
EXPORT_SYMBOL(snd_gus_initialize);
/* gus_irq.c */
@@ -464,20 +443,3 @@ EXPORT_SYMBOL(snd_gf1_translate_freq);
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..8d95d8d5abdf 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>
@@ -30,17 +15,9 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
#endif
-void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
-{
- if (!xup) {
- mutex_lock(&alloc->memory_mutex);
- } else {
- mutex_unlock(&alloc->memory_mutex);
- }
-}
-
-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 +25,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) {
@@ -58,8 +41,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
alloc->first = nblock;
else
nblock->prev->next = nblock;
- mutex_unlock(&alloc->memory_mutex);
- return NULL;
+ return nblock;
}
pblock = pblock->next;
}
@@ -79,7 +61,6 @@ int snd_gf1_mem_xfree(struct snd_gf1_mem * alloc, struct snd_gf1_mem_block * blo
{
if (block->share) { /* ok.. shared block */
block->share--;
- mutex_unlock(&alloc->memory_mutex);
return 0;
}
if (alloc->first == block) {
@@ -191,46 +172,37 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne
{
struct snd_gf1_mem_block block, *nblock;
- snd_gf1_mem_lock(alloc, 0);
+ guard(mutex)(&alloc->memory_mutex);
if (share_id != NULL) {
nblock = snd_gf1_mem_share(alloc, share_id);
if (nblock != NULL) {
if (size != (int)nblock->size) {
/* TODO: remove in the future */
- snd_printk(KERN_ERR "snd_gf1_mem_alloc - share: sizes differ\n");
+ pr_err("%s - share: sizes differ\n", __func__);
goto __std;
}
nblock->share++;
- snd_gf1_mem_lock(alloc, 1);
return NULL;
}
}
__std:
- if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) {
- snd_gf1_mem_lock(alloc, 1);
+ if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0)
return NULL;
- }
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);
- snd_gf1_mem_lock(alloc, 1);
+ nblock = snd_gf1_mem_xalloc(alloc, &block, name);
return nblock;
}
int snd_gf1_mem_free(struct snd_gf1_mem * alloc, unsigned int address)
{
- int result;
struct snd_gf1_mem_block *block;
- snd_gf1_mem_lock(alloc, 0);
- if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
- result = snd_gf1_mem_xfree(alloc, block);
- snd_gf1_mem_lock(alloc, 1);
- return result;
- }
- snd_gf1_mem_lock(alloc, 1);
+ guard(mutex)(&alloc->memory_mutex);
+ block = snd_gf1_mem_look(alloc, address);
+ if (block)
+ return snd_gf1_mem_xfree(alloc, block);
return -EINVAL;
}
@@ -238,9 +210,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 +222,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;
}
@@ -296,7 +262,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
gus = entry->private_data;
alloc = &gus->gf1.mem_alloc;
- mutex_lock(&alloc->memory_mutex);
+ guard(mutex)(&alloc->memory_mutex);
snd_iprintf(buffer, "8-bit banks : \n ");
for (i = 0; i < 4; i++)
snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
@@ -310,7 +276,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])
@@ -340,7 +306,6 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
}
snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n",
total, used, total - used);
- mutex_unlock(&alloc->memory_mutex);
#if 0
ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
ultra_memory_free_size(card, &card->gf1.mem_alloc),
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 3b5d9a7a63eb..9bfdb4e45a5d 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>
@@ -52,7 +37,6 @@ static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
int change;
@@ -62,13 +46,12 @@ static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
if (invert)
nval ^= 1;
nval <<= shift;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
oval = gus->mix_cntrl_reg;
nval = (oval & ~(1 << shift)) | nval;
change = nval != oval;
outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG));
outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return change;
}
@@ -90,14 +73,12 @@ static int snd_ics_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int addr = kcontrol->private_value & 0xff;
unsigned char left, right;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
left = gus->gf1.ics_regs[addr][0];
right = gus->gf1.ics_regs[addr][1];
- spin_unlock_irqrestore(&gus->reg_lock, flags);
ucontrol->value.integer.value[0] = left & 127;
ucontrol->value.integer.value[1] = right & 127;
return 0;
@@ -106,14 +87,13 @@ static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int addr = kcontrol->private_value & 0xff;
int change;
unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
oval1 = gus->gf1.ics_regs[addr][0];
oval2 = gus->gf1.ics_regs[addr][1];
change = val1 != oval1 || val2 != oval2;
@@ -131,17 +111,16 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
outb(2, GUSP(gus, MIXDATAPORT));
outb(addr | 3, GUSP(gus, MIXCNTRLPORT));
outb((unsigned char) val2, GUSP(gus, MIXDATAPORT));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
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),
@@ -167,7 +146,7 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)
if (gus->ics_flag)
snd_component_add(card, "ICS2101");
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
+ strscpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
} else {
if (gus->ics_flag)
strcat(card->mixername, ",ICS2101");
@@ -177,12 +156,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 131b28997e1d..9249cbff30f3 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,22 +8,6 @@
*
* 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>
@@ -82,10 +67,6 @@ static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream,
count += offset & 31;
offset &= ~31;
- /*
- snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n",
- offset, count);
- */
memset(&block, 0, sizeof(block));
block.cmd = SNDRV_GF1_DMA_IRQ;
if (snd_pcm_format_unsigned(runtime->format))
@@ -108,7 +89,6 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
struct snd_gus_card * gus = pcmp->gus;
- unsigned long flags;
unsigned char voice_ctrl, ramp_ctrl;
unsigned short rate;
unsigned int curr, begin, end;
@@ -116,14 +96,12 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
unsigned char pan;
unsigned int voice;
- spin_lock_irqsave(&pcmp->lock, flags);
- if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
- spin_unlock_irqrestore(&pcmp->lock, flags);
- return;
+ scoped_guard(spinlock_irqsave, &pcmp->lock) {
+ if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)
+ return;
+ pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
+ pcmp->final_volume = 0;
}
- pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
- pcmp->final_volume = 0;
- spin_unlock_irqrestore(&pcmp->lock, flags);
rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
/* enable WAVE IRQ */
voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
@@ -138,14 +116,9 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
end = curr + (pcmp->block_size / runtime->channels);
end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
- /*
- snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, "
- "ctrl=0x%x, ramp=0x%x, rate=0x%x\n",
- curr, begin, end, voice_ctrl, ramp_ctrl, rate);
- */
pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
@@ -161,9 +134,9 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
- spin_lock_irqsave(&gus->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&gus->reg_lock);
for (voice = 0; voice < pcmp->voices; voice++) {
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
if (gus->gf1.enh_mode)
@@ -180,7 +153,6 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
voice_ctrl &= ~0x20; /* disable IRQ for next voice */
}
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
@@ -193,65 +165,65 @@ static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
unsigned int end, step;
if (!pvoice->private_data) {
- snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+ dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
pcmp = pvoice->private_data;
if (pcmp == NULL) {
- snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+ dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
gus = pcmp->gus;
runtime = pcmp->substream->runtime;
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
- ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
+ scoped_guard(spinlock, &gus->reg_lock) {
+ snd_gf1_select_voice(gus, pvoice->number);
+ voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
+ ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
#if 0
- snd_gf1_select_voice(gus, pvoice->number);
- printk(KERN_DEBUG "position = 0x%x\n",
- (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
- snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
- printk(KERN_DEBUG "position = 0x%x\n",
- (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
- snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_select_voice(gus, pvoice->number);
+ dev_dbg(gus->card->dev, "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
+ dev_dbg(gus->card->dev, "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ snd_gf1_select_voice(gus, pvoice->number);
#endif
- pcmp->bpos++;
- pcmp->bpos %= pcmp->blocks;
- if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */
- voice_ctrl |= 0x08; /* enable loop */
- } else {
- ramp_ctrl |= 0x04; /* enable rollover */
- }
- end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
- end -= voice_ctrl & 4 ? 2 : 1;
- step = pcmp->dma_size / runtime->channels;
- voice_ctrl |= 0x20;
- if (!pcmp->final_volume) {
- ramp_ctrl |= 0x20;
- ramp_ctrl &= ~0x03;
- }
- for (idx = 0; idx < pcmp->voices; idx++, end += step) {
- snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
- snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
- snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
- snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
- voice_ctrl &= ~0x20;
- }
- if (!gus->gf1.enh_mode) {
- snd_gf1_delay(gus);
+ pcmp->bpos++;
+ pcmp->bpos %= pcmp->blocks;
+ if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */
+ voice_ctrl |= 0x08; /* enable loop */
+ } else {
+ ramp_ctrl |= 0x04; /* enable rollover */
+ }
+ end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
+ end -= voice_ctrl & 4 ? 2 : 1;
+ step = pcmp->dma_size / runtime->channels;
voice_ctrl |= 0x20;
- for (idx = 0; idx < pcmp->voices; idx++) {
+ if (!pcmp->final_volume) {
+ ramp_ctrl |= 0x20;
+ ramp_ctrl &= ~0x03;
+ }
+ for (idx = 0; idx < pcmp->voices; idx++, end += step) {
snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+ snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
voice_ctrl &= ~0x20;
}
+ if (!gus->gf1.enh_mode) {
+ snd_gf1_delay(gus);
+ voice_ctrl |= 0x20;
+ for (idx = 0; idx < pcmp->voices; idx++) {
+ snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+ snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+ snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+ voice_ctrl &= ~0x20;
+ }
+ }
}
- spin_unlock(&gus->reg_lock);
snd_pcm_period_elapsed(pcmp->substream);
#if 0
@@ -276,10 +248,10 @@ static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
struct gus_pcm_private *pcmp = pvoice->private_data;
/* stop ramp, but leave rollover bit untouched */
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- spin_unlock(&gus->reg_lock);
+ scoped_guard(spinlock, &gus->reg_lock) {
+ snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ }
if (pcmp == NULL)
return;
/* are we active? */
@@ -290,11 +262,10 @@ static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
if (pcmp->substream == NULL)
return;
vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
snd_gf1_select_voice(gus, pvoice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
pcmp->final_volume = 1;
- spin_unlock(&gus->reg_lock);
}
static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus)
@@ -306,20 +277,14 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
int w16, int invert)
{
unsigned int len;
- unsigned long flags;
- /*
- printk(KERN_DEBUG
- "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n",
- (int)buf, pos, count, gus->gf1.port);
- */
while (count > 0) {
len = count;
if (len > 512) /* limit, to allow IRQ */
len = 512;
count -= len;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
snd_gf1_dram_addr(gus, pos);
if (w16) {
@@ -328,7 +293,6 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
} else {
outsb(GUSP(gus, DRAM), buf, len);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
buf += 512;
pos += 512;
} else {
@@ -384,7 +348,7 @@ static int playback_copy_ack(struct snd_pcm_substream *substream,
static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
int voice, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
@@ -393,28 +357,12 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
bpos = get_bpos(pcmp, voice, pos, len);
if (bpos < 0)
- return pos;
- if (copy_from_user(runtime->dma_area + bpos, src, len))
+ return bpos;
+ if (copy_from_iter(runtime->dma_area + bpos, len, src) != len)
return -EFAULT;
return playback_copy_ack(substream, bpos, len);
}
-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, unsigned long pos,
unsigned long count)
@@ -426,7 +374,7 @@ static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
bpos = get_bpos(pcmp, voice, pos, len);
if (bpos < 0)
- return pos;
+ return bpos;
snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
bytes_to_samples(runtime, count));
return playback_copy_ack(substream, bpos, len);
@@ -438,27 +386,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;
@@ -466,7 +413,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;
@@ -486,7 +434,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;
@@ -525,9 +472,9 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
if (cmd == SNDRV_PCM_TRIGGER_START) {
snd_gf1_pcm_trigger_up(substream);
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- spin_lock(&pcmp->lock);
- pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
- spin_unlock(&pcmp->lock);
+ scoped_guard(spinlock, &pcmp->lock) {
+ pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
+ }
voice = pcmp->pvoices[0]->number;
snd_gf1_stop_voices(gus, voice, voice);
if (pcmp->pvoices[1]) {
@@ -549,7 +496,7 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *
unsigned char voice_ctrl;
pos = 0;
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
@@ -558,7 +505,6 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *
pos <<= 1;
pos = bytes_to_frames(runtime, pos);
}
- spin_unlock(&gus->reg_lock);
return pos;
}
@@ -589,12 +535,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)
@@ -623,10 +564,9 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
- spin_unlock(&gus->reg_lock);
return 0;
}
@@ -710,10 +650,12 @@ static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
runtime->private_free = snd_gf1_pcm_playback_free;
#if 0
- printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
- (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
+ dev_dbg(gus->card->dev,
+ "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;
@@ -731,7 +673,7 @@ static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream)
struct gus_pcm_private *pcmp = runtime->private_data;
if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ))
- snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n");
+ dev_err(gus->card->dev, "gf1 pcm - serious DMA problem\n");
snd_gf1_dma_done(gus);
return 0;
@@ -773,19 +715,16 @@ static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+ guard(spinlock_irqsave)(&gus->pcm_volume_level_lock);
ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
- spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
return 0;
}
static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned int idx;
unsigned short val1, val2, vol;
@@ -794,33 +733,32 @@ static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
- spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
- change = val1 != gus->gf1.pcm_volume_level_left1 ||
- val2 != gus->gf1.pcm_volume_level_right1;
- gus->gf1.pcm_volume_level_left1 = val1;
- gus->gf1.pcm_volume_level_right1 = val2;
- gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
- gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
- spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->pcm_volume_level_lock) {
+ change = val1 != gus->gf1.pcm_volume_level_left1 ||
+ val2 != gus->gf1.pcm_volume_level_right1;
+ gus->gf1.pcm_volume_level_left1 = val1;
+ gus->gf1.pcm_volume_level_right1 = val2;
+ gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
+ gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
+ }
/* are we active? */
- spin_lock_irqsave(&gus->voice_alloc, flags);
- for (idx = 0; idx < 32; idx++) {
- pvoice = &gus->gf1.voices[idx];
- if (!pvoice->pcm)
- continue;
- pcmp = pvoice->private_data;
- if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
- continue;
- /* load real volume - better precision */
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
- pcmp->final_volume = 1;
- spin_unlock(&gus->reg_lock);
+ scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
+ for (idx = 0; idx < 32; idx++) {
+ pvoice = &gus->gf1.voices[idx];
+ if (!pvoice->pcm)
+ continue;
+ pcmp = pvoice->private_data;
+ if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+ continue;
+ /* load real volume - better precision */
+ guard(spinlock)(&gus->reg_lock);
+ snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+ snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+ pcmp->final_volume = 1;
+ }
}
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return change;
}
@@ -845,23 +783,19 @@ static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
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_user = snd_gf1_pcm_playback_copy,
- .copy_kernel = snd_gf1_pcm_playback_copy_kernel,
+ .copy = snd_gf1_pcm_playback_copy,
.fill_silence = snd_gf1_pcm_playback_silence,
};
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,
@@ -890,9 +824,9 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
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;
@@ -900,11 +834,11 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
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);
+ strscpy(pcm->name, pcm->id);
if (gus->interwave) {
sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
}
@@ -915,9 +849,10 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
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)
- return err;
kctl->id.index = control_index;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
return 0;
}
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
index 3d1fed0c2620..5cbace8369f8 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);
@@ -100,26 +83,20 @@ void snd_gf1_set_default_handlers(struct snd_gus_card * gus, unsigned int what)
static void snd_gf1_clear_regs(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
inb(GUSP(gus, IRQSTAT));
snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */
snd_gf1_write8(gus, 0x45, 0); /* Timer Control */
snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_look_regs(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */
snd_gf1_look8(gus, 0x49); /* Sampling Control Register */
inb(GUSP(gus, IRQSTAT));
snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
/*
@@ -128,42 +105,35 @@ static void snd_gf1_look_regs(struct snd_gus_card * gus)
void snd_gf1_smart_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
- printk(KERN_DEBUG " -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+ dev_dbg(gus->card->dev,
+ " -%i- smart stop voice - volume = 0x%x\n",
+ voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
void snd_gf1_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
- printk(KERN_DEBUG " -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+ dev_dbg(gus->card->dev,
+ " -%i- stop voice - volume = 0x%x\n",
+ voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-#if 0
- snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO);
- snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO);
-#endif
}
static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min,
unsigned short v_max)
{
- unsigned long flags;
unsigned int daddr;
unsigned short i, w_16;
@@ -173,7 +143,7 @@ static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min
if (gus->gf1.syn_voices)
gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC;
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */
@@ -194,23 +164,17 @@ static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-#if 0
- snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO);
- snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO);
-#endif
}
}
void snd_gf1_stop_voices(struct snd_gus_card * gus, unsigned short v_min, unsigned short v_max)
{
- unsigned long flags;
short i, ramp_ok;
unsigned short ramp_end;
if (!in_interrupt()) { /* this can't be done in interrupt */
for (i = v_min, ramp_ok = 0; i <= v_max; i++) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
ramp_end = snd_gf1_read16(gus, 9) >> 8;
if (ramp_end > SNDRV_GF1_MIN_OFFSET) {
@@ -224,7 +188,6 @@ void snd_gf1_stop_voices(struct snd_gus_card * gus, unsigned short v_min, unsign
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);
}
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
msleep_interruptible(50);
}
@@ -257,21 +220,17 @@ static void snd_gf1_alloc_voice_use(struct snd_gus_card * gus,
struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, int client, int port)
{
struct snd_gus_voice *pvoice;
- unsigned long flags;
int idx;
- spin_lock_irqsave(&gus->voice_alloc, flags);
+ guard(spinlock_irqsave)(&gus->voice_alloc);
if (type == SNDRV_GF1_VOICE_TYPE_PCM) {
- if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) {
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
+ if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels)
return NULL;
- }
}
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (!pvoice->use) {
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return pvoice;
}
}
@@ -280,34 +239,29 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i
if (pvoice->midi && !pvoice->client) {
snd_gf1_clear_voices(gus, pvoice->number, pvoice->number);
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return pvoice;
}
}
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return NULL;
}
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;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number);
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)
- gus->gf1.pcm_alloc_voices--;
- voice->use = voice->pcm = 0;
- voice->sample_ops = NULL;
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
+ scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
+ private_free = voice->private_free;
+ voice->private_free = NULL;
+ voice->private_data = NULL;
+ if (voice->pcm)
+ gus->gf1.pcm_alloc_voices--;
+ voice->use = voice->pcm = 0;
+ voice->sample_ops = NULL;
+ }
if (private_free)
private_free(voice);
}
@@ -318,7 +272,6 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
int snd_gf1_start(struct snd_gus_card * gus)
{
- unsigned long flags;
unsigned int i;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
@@ -350,9 +303,7 @@ int snd_gf1_start(struct snd_gus_card * gus)
} else {
gus->gf1.sw_lfo = 1;
}
-#if 0
- snd_gf1_lfo_init(gus);
-#endif
+
if (gus->gf1.memory > 0)
for (i = 0; i < 4; i++)
snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0);
@@ -369,10 +320,10 @@ int snd_gf1_start(struct snd_gus_card * gus)
}
while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ }
snd_gf1_timers_init(gus);
snd_gf1_look_regs(gus);
@@ -406,8 +357,6 @@ int snd_gf1_stop(struct snd_gus_card * gus)
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */
snd_gf1_timers_done(gus);
snd_gf1_mem_done(gus);
-#if 0
- snd_gf1_lfo_done(gus);
-#endif
+
return 0;
}
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..e3a8847e02cf 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>
@@ -31,33 +16,29 @@
static int snd_gf1_timer1_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 4);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
static int snd_gf1_timer1_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~4);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
@@ -67,33 +48,29 @@ static int snd_gf1_timer1_stop(struct snd_timer * timer)
static int snd_gf1_timer2_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 8);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
static int snd_gf1_timer2_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~8);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
@@ -123,7 +100,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 +109,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,
@@ -171,7 +148,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus)
tid.subdevice = 0;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
- strcpy(timer->name, "GF1 timer #1");
+ strscpy(timer->name, "GF1 timer #1");
timer->private_data = gus;
timer->private_free = snd_gf1_timer1_free;
timer->hw = snd_gf1_timer1;
@@ -181,7 +158,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus)
tid.device++;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
- strcpy(timer->name, "GF1 timer #2");
+ strscpy(timer->name, "GF1 timer #2");
timer->private_data = gus;
timer->private_free = snd_gf1_timer2_free;
timer->hw = snd_gf1_timer2;
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
index ac5f5687d1a3..770d8f3e4cff 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;
@@ -63,13 +49,12 @@ static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
{
char byte;
- unsigned long flags;
/* try unlock output */
if (snd_gf1_uart_stat(gus) & 0x01)
snd_gf1_interrupt_midi_in(gus);
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */
if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
@@ -77,7 +62,6 @@ static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
snd_gf1_uart_put(gus, byte);
}
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
@@ -91,31 +75,30 @@ static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
snd_gf1_uart_reset(gus, 0);
}
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
gus->midi_substream_output = substream;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
#if 0
- snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+ dev_dbg(gus->card->dev,
+ "write init - cmd = 0x%x, stat = 0x%x\n",
+ gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
#endif
return 0;
}
static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
int i;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
snd_gf1_uart_reset(gus, 0);
}
@@ -125,60 +108,53 @@ static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
snd_gf1_uart_get(gus); /* clean Rx */
if (i >= 1000)
- snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
+ dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
#if 0
- snd_printk(KERN_DEBUG
- "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
- gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
- snd_printk(KERN_DEBUG
- "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
- "(page = 0x%x)\n",
- gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
- inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
+ dev_dbg(gus->card->dev,
+ "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
+ gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+ dev_dbg(gus->card->dev,
+ "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
+ gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
+ inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
#endif
return 0;
}
static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
gus->midi_substream_output = NULL;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return 0;
}
static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
gus->midi_substream_input = NULL;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return 0;
}
static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_gus_card *gus;
- unsigned long flags;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (up) {
if ((gus->gf1.uart_cmd & 0x80) == 0)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
@@ -186,7 +162,6 @@ static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream,
if (gus->gf1.uart_cmd & 0x80)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -246,9 +221,10 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
struct snd_rawmidi *rmidi;
int err;
- 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");
+ strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
index 3dd841ae708a..e729621756cf 100644
--- a/sound/isa/gus/gus_volume.c
+++ b/sound/isa/gus/gus_volume.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/time.h>
@@ -77,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,
@@ -119,7 +104,8 @@ unsigned short snd_gf1_translate_freq(struct snd_gus_card * gus, unsigned int fr
freq16 = 50;
if (freq16 & 0xf8000000) {
freq16 = ~0xf8000000;
- snd_printk(KERN_ERR "snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16);
+ dev_err(gus->card->dev, "%s: overflow - freq = 0x%x\n",
+ __func__, freq16);
}
return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
}
@@ -128,7 +114,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,
@@ -136,7 +122,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);
@@ -160,7 +147,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;
@@ -203,14 +190,14 @@ unsigned short snd_gf1_compute_freq(unsigned int freq,
fc = (freq << 10) / rate;
if (fc > 97391L) {
fc = 97391;
- snd_printk(KERN_ERR "patch: (1) fc frequency overflow - %u\n", fc);
+ pr_err("patch: (1) fc frequency overflow - %u\n", fc);
}
fc = (fc * 44100UL) / mix_rate;
while (scale--)
fc <<= 1;
if (fc > 65535L) {
fc = 65535;
- snd_printk(KERN_ERR "patch: (2) fc frequency overflow - %u\n", fc);
+ pr_err("patch: (2) fc frequency overflow - %u\n", fc);
}
return (unsigned short) fc;
}
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 92a997ab1229..101202acefb3 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>
@@ -38,7 +23,6 @@
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 */
@@ -82,9 +66,9 @@ 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;
@@ -129,15 +113,17 @@ 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) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[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) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
return 0;
@@ -149,7 +135,7 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n)
struct snd_gus_card *gus;
int error;
- error = snd_card_new(dev, 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;
@@ -158,37 +144,37 @@ static int 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);
if (error < 0)
- goto out;
+ return error;
if (!gus->ace_flag) {
error = snd_gf1_rawmidi_new(gus, 0);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname + strlen(card->longname),
@@ -201,28 +187,17 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int 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 snd_gusclassic_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- return 0;
}
static struct isa_driver snd_gusclassic_driver = {
.match = snd_gusclassic_match,
.probe = snd_gusclassic_probe,
- .remove = snd_gusclassic_remove,
#if 0 /* FIXME */
.suspend = snd_gusclassic_suspend,
- .remove = snd_gusclassic_remove,
#endif
.driver = {
.name = DEV_NAME
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index beb52c0f70ea..ed921b89b00a 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>
@@ -42,7 +27,6 @@
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 */
@@ -98,9 +82,9 @@ 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;
@@ -137,8 +121,8 @@ 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);
@@ -161,7 +145,6 @@ static int snd_gusextreme_gus_card_create(struct snd_card *card,
static int snd_gusextreme_detect(struct snd_gus_card *gus,
struct snd_es1688 *es1688)
{
- unsigned long flags;
unsigned char d;
/*
@@ -178,30 +161,32 @@ static int snd_gusextreme_detect(struct snd_gus_card *gus,
* 0x260 = 2,2,1
*/
- spin_lock_irqsave(&es1688->mixer_lock, flags);
- snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */
- spin_unlock_irqrestore(&es1688->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &es1688->mixer_lock) {
+ snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */
+ }
- spin_lock_irqsave(&es1688->reg_lock, flags);
- outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
- outb(0, 0x201);
- outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
- outb(0, 0x201);
- outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
- spin_unlock_irqrestore(&es1688->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &es1688->reg_lock) {
+ outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
+ outb(0, 0x201);
+ outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
+ outb(0, 0x201);
+ outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
+ }
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) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[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) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
@@ -218,15 +203,15 @@ static int snd_gusextreme_mixer(struct snd_card *card)
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX to SYNTHESIZER */
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Synth Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
/* reassign Master Playback Switch to Synth Playback Switch */
- strcpy(id1.name, "Master Playback Switch");
- strcpy(id2.name, "Synth Playback Switch");
+ strscpy(id1.name, "Master Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
@@ -242,8 +227,8 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n)
struct snd_opl3 *opl3;
int error;
- error = snd_card_new(dev, 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;
@@ -257,56 +242,56 @@ static int 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);
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);
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)
@@ -314,14 +299,14 @@ static int 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], NULL);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, "
@@ -330,25 +315,15 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int 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 snd_gusextreme_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- return 0;
}
static struct isa_driver snd_gusextreme_driver = {
.match = snd_gusextreme_match,
.probe = snd_gusextreme_probe,
- .remove = snd_gusextreme_remove,
#if 0 /* FIXME */
.suspend = snd_gusextreme_suspend,
.resume = snd_gusextreme_resume,
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 63309a453140..b572411c4422 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>
@@ -36,7 +21,6 @@
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 */
@@ -80,22 +64,22 @@ struct snd_gusmax {
unsigned short pcm_status_reg;
};
-#define PFX "gusmax: "
-
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) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[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) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
@@ -150,22 +134,26 @@ static int snd_gusmax_mixer(struct snd_wss *chip)
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* 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)
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
+ 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)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
+ 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)
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
+ 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)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
#if 0
/* reassign Mono Input to MIC */
@@ -189,16 +177,6 @@ static int 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 snd_gusmax_match(struct device *pdev, unsigned int dev)
{
return enable[dev];
@@ -206,45 +184,44 @@ static int snd_gusmax_match(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_new(pdev, 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) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto _err;
+ xirq = snd_legacy_find_free_irq(possible_irqs);
+ if (xirq < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
xdma1 = dma1[dev];
if (xdma1 == SNDRV_AUTO_DMA) {
- if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
- err = -EBUSY;
- goto _err;
+ xdma1 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma1 < 0) {
+ dev_err(pdev, "unable to find a free DMA1\n");
+ return -EBUSY;
}
}
xdma2 = dma2[dev];
if (xdma2 == SNDRV_AUTO_DMA) {
- if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
- err = -EBUSY;
- goto _err;
+ xdma2 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma2 < 0) {
+ dev_err(pdev, "unable to find a free DMA2\n");
+ return -EBUSY;
}
}
@@ -256,7 +233,7 @@ static int 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 +251,32 @@ static int 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;
+ dev_err(pdev, "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
+ return -ENODEV;
}
- if (request_irq(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;
+ if (devm_request_irq(card->dev, xirq, snd_gusmax_interrupt, 0,
+ "GUS MAX", (void *)maxcard)) {
+ dev_err(pdev, "unable to grab IRQ %d\n", xirq);
+ 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,31 +286,32 @@ static int 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);
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);
if (err < 0)
- goto _err;
+ return err;
if (pcm_channels[dev] > 0) {
- if ((err = snd_gf1_pcm_new(gus, 1, 1)) < 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);
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)
@@ -339,23 +319,13 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
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 snd_gusmax_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
}
#define DEV_NAME "gusmax"
@@ -363,7 +333,6 @@ static int 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 = snd_gusmax_remove,
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index a6fc26bf0af2..18adcd35e117 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>
@@ -43,14 +28,8 @@ 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 */
@@ -73,11 +52,9 @@ static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
static int effect[SNDRV_CARDS];
#ifdef SNDRV_STB
-#define PFX "interwave-stb: "
#define INTERWAVE_DRIVER "snd_interwave_stb"
#define INTERWAVE_PNP_DRIVER "interwave-stb"
#else
-#define PFX "interwave: "
#define INTERWAVE_DRIVER "snd_interwave"
#define INTERWAVE_PNP_DRIVER "interwave"
#endif
@@ -169,7 +146,7 @@ static void snd_interwave_i2c_setlines(struct snd_i2c_bus *bus, int ctrl, int da
unsigned long port = bus->private_value;
#if 0
- printk(KERN_DEBUG "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
+ dev_dbg(bus->card->dev, "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
#endif
outb((data << 1) | ctrl, port);
udelay(10);
@@ -182,7 +159,7 @@ static int snd_interwave_i2c_getclockline(struct snd_i2c_bus *bus)
res = inb(port) & 1;
#if 0
- printk(KERN_DEBUG "i2c_getclockline - 0x%lx -> %i\n", port, res);
+ dev_dbg(bus->card->dev, "i2c_getclockline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -196,7 +173,7 @@ static int snd_interwave_i2c_getdataline(struct snd_i2c_bus *bus, int ack)
udelay(10);
res = (inb(port) & 2) >> 1;
#if 0
- printk(KERN_DEBUG "i2c_getdataline - 0x%lx -> %i\n", port, res);
+ dev_dbg(bus->card->dev, "i2c_getdataline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -225,24 +202,29 @@ static int 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");
+ dev_err(card->dev, "interwave: can't grab i2c bus port\n");
return -ENODEV;
}
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;
@@ -257,34 +239,38 @@ static int snd_interwave_detect(struct snd_interwave *iwcard,
#endif
)
{
- unsigned long flags;
unsigned char rev1, rev2;
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) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[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) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
- spin_lock_irqsave(&gus->reg_lock, flags);
- rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
- rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
+ rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
+ }
+ dev_dbg(gus->card->dev,
+ "[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n",
+ gus->gf1.port, rev1, rev2);
if ((rev1 & 0xf0) == (rev2 & 0xf0) &&
(rev1 & 0x0f) != (rev2 & 0x0f)) {
- snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port);
+ dev_dbg(gus->card->dev,
+ "[0x%lx] InterWave check - passed\n", gus->gf1.port);
gus->interwave = 1;
- strcpy(gus->card->shortname, "AMD InterWave");
+ strscpy(gus->card->shortname, "AMD InterWave");
gus->revision = rev1 >> 4;
#ifndef SNDRV_STB
return 0; /* ok.. We have an InterWave board */
@@ -292,7 +278,7 @@ static int snd_interwave_detect(struct snd_interwave *iwcard,
return snd_interwave_detect_stb(iwcard, gus, dev, rbus);
#endif
}
- snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port);
+ dev_dbg(gus->card->dev, "[0x%lx] InterWave check - failed\n", gus->gf1.port);
return -ENODEV;
}
@@ -341,7 +327,7 @@ static void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes)
snd_gf1_poke(gus, local, d);
snd_gf1_poke(gus, local + 1, d + 1);
#if 0
- printk(KERN_DEBUG "d = 0x%x, local = 0x%x, "
+ dev_dbg(gus->card->dev, "d = 0x%x, local = 0x%x, "
"local + 1 = 0x%x, idx << 22 = 0x%x\n",
d,
snd_gf1_peek(gus, local),
@@ -356,7 +342,7 @@ static void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes)
}
}
#if 0
- printk(KERN_DEBUG "sizes: %i %i %i %i\n",
+ dev_dbg(gus->card->dev, "sizes: %i %i %i %i\n",
sizes[0], sizes[1], sizes[2], sizes[3]);
#endif
}
@@ -379,7 +365,7 @@ struct rom_hdr {
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,
@@ -411,12 +397,12 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
lmct = (psizes[3] << 24) | (psizes[2] << 16) |
(psizes[1] << 8) | psizes[0];
#if 0
- printk(KERN_DEBUG "lmct = 0x%08x\n", lmct);
+ dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct);
#endif
for (i = 0; i < ARRAY_SIZE(lmc); i++)
if (lmct == lmc[i]) {
#if 0
- printk(KERN_DEBUG "found !!! %i\n", i);
+ dev_dbg(gus->card->dev, "found !!! %i\n", i);
#endif
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
snd_interwave_bank_sizes(gus, psizes);
@@ -470,18 +456,16 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
static void snd_interwave_init(int dev, struct snd_gus_card *gus)
{
- unsigned long flags;
-
/* ok.. some InterWave specific initialization */
- spin_lock_irqsave(&gus->reg_lock, flags);
- snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
- snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
- snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
- snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
- snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
- snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+ }
gus->equal_irq = 1;
gus->codec_flag = 1;
gus->interwave = 1;
@@ -490,7 +474,7 @@ static void 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,
@@ -513,38 +497,46 @@ static int snd_interwave_mixer(struct snd_wss *chip)
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
#if 0
/* remove mono microphone controls */
- strcpy(id1.name, "Mic Playback Switch");
- if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+ strscpy(id1.name, "Mic Playback Switch");
+ 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)
+ strscpy(id1.name, "Mic Playback Volume");
+ 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);
snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
/* reassign AUXA to SYNTHESIZER */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "Synth Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
+ 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)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
+ 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)
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
+ 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)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
return 0;
}
@@ -572,12 +564,12 @@ static int snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n");
+ dev_err(&pdev->dev, "InterWave PnP configure failure (out of resources?)\n");
return err;
}
if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) ||
pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) {
- snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n");
+ dev_err(&pdev->dev, "PnP configure failure (wrong ports)\n");
return -ENOENT;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -585,40 +577,31 @@ static int snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
if (dma2[dev] >= 0)
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
- (unsigned long long)pnp_port_start(pdev, 0),
- (unsigned long long)pnp_port_start(pdev, 1),
- (unsigned long long)pnp_port_start(pdev, 2));
- snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0),
+ (unsigned long long)pnp_port_start(pdev, 1),
+ (unsigned long long)pnp_port_start(pdev, 2));
+ dev_dbg(&pdev->dev,
+ "isapnp IW: dma1=%i, dma2=%i, irq=%i\n",
+ dma1[dev], dma2[dev], irq[dev]);
#ifdef SNDRV_STB
/* Tone Control initialization */
pdev = iwcard->devtc;
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n");
+ dev_err(&pdev->dev,
+ "InterWave ToneControl PnP configure failure (out of resources?)\n");
return err;
}
port_tc[dev] = pnp_port_start(pdev, 0);
- snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
+ dev_dbg(&pdev->dev, "isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
#endif
return 0;
}
#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(struct device *pdev, int dev,
struct snd_card **cardp)
{
@@ -626,24 +609,30 @@ static int snd_interwave_card_new(struct device *pdev, int dev,
struct snd_interwave *iwcard;
int err;
- err = snd_card_new(pdev, 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 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
@@ -654,18 +643,12 @@ static int 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;
@@ -673,15 +656,17 @@ static int 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, 0,
- "InterWave", iwcard)) {
- snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
+ if (devm_request_irq(card->dev, xirq, snd_interwave_interrupt, 0,
+ "InterWave", iwcard)) {
+ dev_err(card->dev, "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,
@@ -725,22 +710,26 @@ static int snd_interwave_probe(struct snd_card *card, int dev)
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id1.name, "Master Playback Switch");
- strcpy(id2.name, id1.name);
+ strscpy(id1.name, "Master Playback Switch");
+ strscpy(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)
+ strscpy(id1.name, "Master Playback Volume");
+ strscpy(id2.name, id1.name);
+ 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)) < 0)
+ err = snd_gf1_rawmidi_new(gus, 0);
+ if (err < 0)
return err;
#ifndef SNDRV_STB
@@ -750,8 +739,8 @@ static int snd_interwave_probe(struct snd_card *card, int dev)
#else
str = "InterWave STB";
#endif
- strcpy(card->driver, str);
- strcpy(card->shortname, str);
+ strscpy(card->driver, str);
+ strscpy(card->shortname, str);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d",
str,
gus->gf1.port,
@@ -769,23 +758,6 @@ static int snd_interwave_probe(struct snd_card *card, int dev)
return 0;
}
-static int snd_interwave_isa_probe1(int dev, struct device *devptr)
-{
- struct snd_card *card;
- int err;
-
- err = snd_interwave_card_new(devptr, dev, &card);
- if (err < 0)
- return err;
-
- if ((err = snd_interwave_probe(card, dev)) < 0) {
- snd_card_free(card);
- return err;
- }
- dev_set_drvdata(devptr, card);
- return 0;
-}
-
static int snd_interwave_isa_match(struct device *pdev,
unsigned int dev)
{
@@ -801,54 +773,64 @@ static int snd_interwave_isa_match(struct device *pdev,
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) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
+ dev_err(pdev, "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 snd_interwave_isa_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
+ 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 = snd_interwave_isa_remove,
/* FIXME: suspend,resume */
.driver = {
.name = INTERWAVE_DRIVER
@@ -861,6 +843,7 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
{
static int dev;
struct snd_card *card;
+ struct snd_gus_card *gus;
int res;
for ( ; dev < SNDRV_CARDS; dev++) {
@@ -874,31 +857,25 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
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;
- }
- 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 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 = snd_interwave_pnp_remove,
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/msnd/Makefile b/sound/isa/msnd/Makefile
index ec231a7b1d5e..d56412aae857 100644
--- a/sound/isa/msnd/Makefile
+++ b/sound/isa/msnd/Makefile
@@ -1,8 +1,8 @@
# 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
-snd-msnd-classic-objs := msnd_classic.o
+snd-msnd-lib-y := msnd.o msnd_pinnacle_mixer.o
+snd-msnd-pinnacle-y := msnd_pinnacle.o
+snd-msnd-classic-y := msnd_classic.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_MSND_PINNACLE) += snd-msnd-pinnacle.o snd-msnd-lib.o
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c
index 569897f64fda..5e350234d572 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,20 +20,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>
@@ -54,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);
@@ -89,17 +76,13 @@ static int snd_msnd_wait_HC0(struct snd_msnd *dev)
int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_HC0(dev) == 0) {
outb(cmd, dev->io + HP_CVR);
- spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
- spin_unlock_irqrestore(&dev->lock, flags);
- snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Send DSP command timeout\n");
return -EIO;
}
@@ -117,7 +100,7 @@ int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
return 0;
}
- snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Send host word timeout\n");
return -EIO;
}
@@ -128,7 +111,7 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
int i;
if (len % 3 != 0) {
- snd_printk(KERN_ERR LOGNAME
+ dev_err(dev->card->dev, LOGNAME
": Upload host data not multiple of 3!\n");
return -EINVAL;
}
@@ -146,14 +129,12 @@ EXPORT_SYMBOL(snd_msnd_upload_host);
int snd_msnd_enable_irq(struct snd_msnd *dev)
{
- unsigned long flags;
-
if (dev->irq_ref++)
return 0;
- snd_printdd(LOGNAME ": Enabling IRQ\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n");
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_TXDE(dev) == 0) {
outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
@@ -164,12 +145,10 @@ int snd_msnd_enable_irq(struct snd_msnd *dev)
enable_irq(dev->irq);
snd_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);
- snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Enable IRQ failed\n");
return -EIO;
}
@@ -177,29 +156,25 @@ EXPORT_SYMBOL(snd_msnd_enable_irq);
int snd_msnd_disable_irq(struct snd_msnd *dev)
{
- unsigned long flags;
-
if (--dev->irq_ref > 0)
return 0;
if (dev->irq_ref < 0)
- snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
- dev->irq_ref);
+ dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
+ dev->irq_ref);
- snd_printdd(LOGNAME ": Disabling IRQ\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n");
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_TXDE(dev) == 0) {
outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
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);
- snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Disable IRQ failed\n");
return -EIO;
}
@@ -233,8 +208,8 @@ void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
snd_msnd_disable_irq(chip);
if (file) {
- snd_printd(KERN_INFO LOGNAME
- ": Stopping read for %p\n", file);
+ dev_dbg(chip->card->dev, LOGNAME
+ ": Stopping read for %p\n", file);
chip->mode &= ~FMODE_READ;
}
clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
@@ -246,8 +221,8 @@ void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
}
snd_msnd_disable_irq(chip);
if (file) {
- snd_printd(KERN_INFO
- LOGNAME ": Stopping write for %p\n", file);
+ dev_dbg(chip->card->dev,
+ LOGNAME ": Stopping write for %p\n", file);
chip->mode &= ~FMODE_WRITE;
}
clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
@@ -270,7 +245,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;
@@ -309,7 +284,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 */
@@ -342,12 +317,6 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
++nbanks;
/* Then advance the tail */
- /*
- if (protect)
- snd_printd(KERN_INFO "B %X %lX\n",
- bank_num, xtime.tv_usec);
- */
-
DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
/* Tell the DSP to play the bank */
@@ -356,10 +325,6 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
if (2 == bank_num)
break;
}
- /*
- if (protect)
- snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
- */
/* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
return nbanks;
}
@@ -370,7 +335,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);
@@ -398,8 +363,7 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
unsigned int pcm_count)
{
int n;
- void *pDAQ;
- /* unsigned long flags; */
+ void __iomem *pDAQ;
/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
@@ -411,15 +375,15 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
chip->DARQ + JQS_wTail);
#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
- spin_lock_irqsave(&chip->lock, flags);
- outb(HPBLKSEL_1, chip->io + HP_BLKS);
- memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
- outb(HPBLKSEL_0, chip->io + HP_BLKS);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ }
#endif
chip->capturePeriodBytes = pcm_count;
- snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
+ dev_dbg(chip->card->dev, "%s() %i\n", __func__, pcm_count);
pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
@@ -438,7 +402,7 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
}
static const struct snd_pcm_hardware snd_msnd_playback = {
- .info = SNDRV_PCM_INFO_MMAP |
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -457,7 +421,7 @@ static const struct snd_pcm_hardware snd_msnd_playback = {
};
static const struct snd_pcm_hardware snd_msnd_capture = {
- .info = SNDRV_PCM_INFO_MMAP |
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -485,7 +449,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;
@@ -508,7 +473,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);
@@ -545,21 +510,21 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
int result = 0;
if (cmd == SNDRV_PCM_TRIGGER_START) {
- snd_printdd("snd_msnd_playback_trigger(START)\n");
+ dev_dbg(chip->card->dev, "%s(START)\n", __func__);
chip->banksPlayed = 0;
set_bit(F_WRITING, &chip->flags);
snd_msnd_DAPQ(chip, 1);
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- snd_printdd("snd_msnd_playback_trigger(STop)\n");
+ dev_dbg(chip->card->dev, "%s(STOP)\n", __func__);
/* interrupt diagnostic, comment this out later */
clear_bit(F_WRITING, &chip->flags);
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
} else {
- snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
+ dev_dbg(chip->card->dev, "%s(?????)\n", __func__);
result = -EINVAL;
}
- snd_printdd("snd_msnd_playback_trigger() ENDE\n");
+ dev_dbg(chip->card->dev, "%s() ENDE\n", __func__);
return result;
}
@@ -575,11 +540,11 @@ snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
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)
@@ -589,7 +554,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;
@@ -654,7 +620,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);
@@ -672,11 +638,11 @@ static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
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,
};
@@ -694,7 +660,7 @@ int snd_msnd_pcm(struct snd_card *card, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, "Hurricane");
+ strscpy(pcm->name, "Hurricane");
return 0;
}
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index 5f3c7dcd9f9d..3d7810ed9186 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
@@ -229,7 +216,6 @@ struct snd_msnd {
int captureLimit;
int capturePeriods;
struct snd_card *card;
- void *msndmidi_mpu;
struct snd_rawmidi *rmidi;
/* Hardware resources */
@@ -283,7 +269,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,
@@ -299,9 +285,6 @@ 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);
-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 snd_msndmix_new(struct snd_card *card);
int snd_msndmix_force_recsrc(struct snd_msnd *chip, int recsrc);
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
deleted file mode 100644
index 013d8d1170fe..000000000000
--- a/sound/isa/msnd/msnd_midi.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Copyright (c) 2009 by Krzysztof Helt
- * 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
- * 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>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/export.h>
-#include <sound/core.h>
-#include <sound/rawmidi.h>
-
-#include "msnd.h"
-
-#define MSNDMIDI_MODE_BIT_INPUT 0
-#define MSNDMIDI_MODE_BIT_OUTPUT 1
-#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
-#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3
-
-struct snd_msndmidi {
- struct snd_msnd *dev;
-
- unsigned long mode; /* MSNDMIDI_MODE_XXXX */
-
- struct snd_rawmidi_substream *substream_input;
-
- spinlock_t input_lock;
-};
-
-/*
- * input/output open/close - protected by open_mutex in rawmidi.c
- */
-static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
-{
- struct snd_msndmidi *mpu;
-
- snd_printdd("snd_msndmidi_input_open()\n");
-
- mpu = substream->rmidi->private_data;
-
- mpu->substream_input = substream;
-
- snd_msnd_enable_irq(mpu->dev);
-
- snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
- set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
- return 0;
-}
-
-static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
-{
- struct snd_msndmidi *mpu;
-
- mpu = substream->rmidi->private_data;
- snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
- clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
- mpu->substream_input = NULL;
- snd_msnd_disable_irq(mpu->dev);
- return 0;
-}
-
-static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
-{
- u16 tail;
-
- tail = readw(mpu->dev->MIDQ + JQS_wTail);
- writew(tail, mpu->dev->MIDQ + JQS_wHead);
-}
-
-/*
- * trigger input
- */
-static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
- int up)
-{
- unsigned long flags;
- struct snd_msndmidi *mpu;
-
- snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
-
- mpu = substream->rmidi->private_data;
- spin_lock_irqsave(&mpu->input_lock, flags);
- if (up) {
- if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
- &mpu->mode))
- snd_msndmidi_input_drop(mpu);
- } else {
- clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
- }
- spin_unlock_irqrestore(&mpu->input_lock, flags);
- if (up)
- snd_msndmidi_input_read(mpu);
-}
-
-void snd_msndmidi_input_read(void *mpuv)
-{
- unsigned long flags;
- struct snd_msndmidi *mpu = mpuv;
- void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
- u16 head, tail, size;
-
- spin_lock_irqsave(&mpu->input_lock, flags);
- 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 const struct snd_rawmidi_ops snd_msndmidi_input = {
- .open = snd_msndmidi_input_open,
- .close = snd_msndmidi_input_close,
- .trigger = snd_msndmidi_input_trigger,
-};
-
-static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
-{
- struct snd_msndmidi *mpu = rmidi->private_data;
- kfree(mpu);
-}
-
-int snd_msndmidi_new(struct snd_card *card, int device)
-{
- struct snd_msnd *chip = card->private_data;
- struct snd_msndmidi *mpu;
- struct snd_rawmidi *rmidi;
- int err;
-
- err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
- if (err < 0)
- return err;
- mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
- if (mpu == NULL) {
- snd_device_free(card, rmidi);
- return -ENOMEM;
- }
- mpu->dev = chip;
- chip->msndmidi_mpu = mpu;
- rmidi->private_data = mpu;
- rmidi->private_free = snd_msndmidi_free;
- spin_lock_init(&mpu->input_lock);
- strcpy(rmidi->name, "MSND MIDI");
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
- &snd_msndmidi_input);
- rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
- return 0;
-}
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 45e561c425bf..c4eec391cd29 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>
@@ -82,10 +68,10 @@
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;
}
@@ -95,11 +81,12 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
switch (HIBYTE(wMessage)) {
case HIMT_PLAY_DONE: {
if (chip->banksPlayed < 3)
- snd_printdd("%08X: HIMT_PLAY_DONE: %i\n",
+ dev_dbg(chip->card->dev, "%08X: HIMT_PLAY_DONE: %i\n",
(unsigned)jiffies, LOBYTE(wMessage));
if (chip->last_playbank == LOBYTE(wMessage)) {
- snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n");
+ dev_dbg(chip->card->dev,
+ "chip.last_playbank == LOBYTE(wMessage)\n");
break;
}
chip->banksPlayed++;
@@ -135,33 +122,29 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
case HIDSP_PLAY_UNDER:
#endif
case HIDSP_INT_PLAY_UNDER:
- snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n",
+ dev_dbg(chip->card->dev,
+ LOGNAME ": Play underflow %i\n",
chip->banksPlayed);
if (chip->banksPlayed > 2)
clear_bit(F_WRITING, &chip->flags);
break;
case HIDSP_INT_RECORD_OVER:
- snd_printd(KERN_WARNING LOGNAME ": Record overflow\n");
+ dev_dbg(chip->card->dev, LOGNAME ": Record overflow\n");
clear_bit(F_READING, &chip->flags);
break;
default:
- snd_printd(KERN_WARNING LOGNAME
- ": DSP message %d 0x%02x\n",
- LOBYTE(wMessage), LOBYTE(wMessage));
+ dev_dbg(chip->card->dev, LOGNAME
+ ": DSP message %d 0x%02x\n",
+ LOBYTE(wMessage), LOBYTE(wMessage));
break;
}
break;
- case HIMT_MIDI_IN_UCHAR:
- if (chip->msndmidi_mpu)
- snd_msndmidi_input_read(chip->msndmidi_mpu);
- break;
-
default:
- snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n",
- HIBYTE(wMessage), HIBYTE(wMessage));
+ dev_dbg(chip->card->dev, LOGNAME ": HIMT message %d 0x%02x\n",
+ HIBYTE(wMessage), HIBYTE(wMessage));
break;
}
}
@@ -169,7 +152,7 @@ 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 */
@@ -194,8 +177,9 @@ static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
}
-static int snd_msnd_reset_dsp(long io, unsigned char *info)
+static int snd_msnd_reset_dsp(struct snd_msnd *chip, unsigned char *info)
{
+ long io = chip->io;
int timeout = 100;
outb(HPDSPRESET_ON, io + HP_DSPR);
@@ -211,7 +195,7 @@ static int snd_msnd_reset_dsp(long io, unsigned char *info)
return 0;
msleep(1);
}
- snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
+ dev_err(chip->card->dev, LOGNAME ": Cannot reset DSP\n");
return -EIO;
}
@@ -227,19 +211,19 @@ static int snd_msnd_probe(struct snd_card *card)
#endif
if (!request_region(chip->io, DSP_NUMIO, "probing")) {
- snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n");
+ dev_err(card->dev, LOGNAME ": I/O port conflict\n");
return -ENODEV;
}
- if (snd_msnd_reset_dsp(chip->io, &info) < 0) {
+ if (snd_msnd_reset_dsp(chip, &info) < 0) {
release_region(chip->io, DSP_NUMIO);
return -ENODEV;
}
#ifdef MSND_CLASSIC
- strcpy(card->shortname, "Classic/Tahiti/Monterey");
- strcpy(card->longname, "Turtle Beach Multisound");
- printk(KERN_INFO LOGNAME ": %s, "
+ strscpy(card->shortname, "Classic/Tahiti/Monterey");
+ strscpy(card->longname, "Turtle Beach Multisound");
+ dev_info(card->dev, LOGNAME ": %s, "
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
card->shortname,
chip->io, chip->io + DSP_NUMIO - 1,
@@ -267,39 +251,39 @@ static int snd_msnd_probe(struct snd_card *card)
switch (info & 0x7) {
case 0x0:
rev = "I";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x1:
rev = "F";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x2:
rev = "G";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x3:
rev = "H";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x4:
rev = "E";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x5:
rev = "C";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x6:
rev = "D";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x7:
rev = "A-B (Fiji) or A-E (Pinnacle)";
- strcpy(card->shortname, pinfiji);
+ strscpy(card->shortname, pinfiji);
break;
}
- strcpy(card->longname, "Turtle Beach Multisound Pinnacle");
- printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
+ strscpy(card->longname, "Turtle Beach Multisound Pinnacle");
+ dev_info(card->dev, LOGNAME ": %s revision %s, Xilinx version %s, "
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
card->shortname,
rev, xv,
@@ -316,7 +300,6 @@ static int snd_msnd_init_sma(struct snd_msnd *chip)
{
static int initted;
u16 mastVolLeft, mastVolRight;
- unsigned long flags;
#ifdef MSND_CLASSIC
outb(chip->memid, chip->io + HP_MEMM);
@@ -333,11 +316,11 @@ static int snd_msnd_init_sma(struct snd_msnd *chip)
memset_io(chip->mappedbase, 0, 0x8000);
/* Critical section: bank 1 access */
- spin_lock_irqsave(&chip->lock, flags);
- outb(HPBLKSEL_1, chip->io + HP_BLKS);
- memset_io(chip->mappedbase, 0, 0x8000);
- outb(HPBLKSEL_0, chip->io + HP_BLKS);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, 0x8000);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ }
/* Digital audio play queue */
chip->DAPQ = chip->mappedbase + DAPQ_OFFSET;
@@ -391,22 +374,22 @@ static int upload_dsp_code(struct snd_card *card)
err = request_firmware(&init_fw, INITCODEFILE, card->dev);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
+ dev_err(card->dev, LOGNAME ": Error loading " INITCODEFILE);
goto cleanup1;
}
err = request_firmware(&perm_fw, PERMCODEFILE, card->dev);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
+ dev_err(card->dev, LOGNAME ": Error loading " PERMCODEFILE);
goto cleanup;
}
memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size);
if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) {
- printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
+ dev_warn(card->dev, LOGNAME ": Error uploading to DSP\n");
err = -ENODEV;
goto cleanup;
}
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
+ dev_info(card->dev, LOGNAME ": DSP firmware uploaded\n");
err = 0;
cleanup:
@@ -439,17 +422,17 @@ static int snd_msnd_initialize(struct snd_card *card)
#endif
err = snd_msnd_init_sma(chip);
if (err < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
+ dev_warn(card->dev, LOGNAME ": Cannot initialize SMA\n");
return err;
}
- err = snd_msnd_reset_dsp(chip->io, NULL);
+ err = snd_msnd_reset_dsp(chip, NULL);
if (err < 0)
return err;
err = upload_dsp_code(card);
if (err < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
+ dev_warn(card->dev, LOGNAME ": Cannot upload DSP code\n");
return err;
}
@@ -458,7 +441,7 @@ static int snd_msnd_initialize(struct snd_card *card)
while (readw(chip->mappedbase)) {
msleep(1);
if (!timeout--) {
- snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n");
+ dev_err(card->dev, LOGNAME ": DSP reset timeout\n");
return -EIO;
}
}
@@ -480,17 +463,12 @@ static int snd_msnd_dsp_full_reset(struct snd_card *card)
rv = snd_msnd_initialize(card);
if (rv)
- printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+ dev_warn(card->dev, LOGNAME ": DSP reset failed\n");
snd_msndmix_force_recsrc(chip, 0);
clear_bit(F_RESETTING, &chip->flags);
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)
{
@@ -502,7 +480,7 @@ static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
{
- snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
+ dev_dbg(chip->card->dev, "snd_msnd_calibrate_adc(%i)\n", srate);
writew(srate, chip->SMA + SMA_wCalFreqAtoD);
if (chip->calibrate_signal == 0)
writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
@@ -515,7 +493,7 @@ static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
schedule_timeout_interruptible(msecs_to_jiffies(333));
return 0;
}
- printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
+ dev_warn(chip->card->dev, LOGNAME ": ADC calibration failed\n");
return -EIO;
}
@@ -542,57 +520,47 @@ 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);
+ dev_err(card->dev, 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)) {
- printk(KERN_ERR LOGNAME
+ if (!devm_request_mem_region(card->dev, chip->base, BUFFSIZE,
+ card->shortname)) {
+ dev_err(card->dev, 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
+ dev_err(card->dev, 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);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
- goto err_release_region;
+ dev_err(card->dev, LOGNAME ": error creating new PCM device\n");
+ return err;
}
err = snd_msndmix_new(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
- goto err_release_region;
+ dev_err(card->dev, LOGNAME ": error creating new Mixer device\n");
+ return err;
}
@@ -606,9 +574,9 @@ static int snd_msnd_attach(struct snd_card *card)
mpu_irq[0],
&chip->rmidi);
if (err < 0) {
- printk(KERN_ERR LOGNAME
+ dev_err(card->dev, LOGNAME
": error creating new Midi device\n");
- goto err_release_region;
+ return err;
}
mpu = chip->rmidi->private_data;
@@ -623,131 +591,114 @@ static int 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:
- iounmap(chip->mappedbase);
- release_mem_region(chip->base, BUFFSIZE);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
- return err;
}
-static void 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 snd_msnd_write_cfg(int cfg, int reg, int value)
+static int snd_msnd_write_cfg(struct snd_msnd *chip, int cfg, int reg, int value)
{
outb(reg, cfg);
outb(value, cfg + 1);
if (value != inb(cfg + 1)) {
- printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n");
+ dev_err(chip->card->dev, LOGNAME ": %s: I/O error\n", __func__);
return -EIO;
}
return 0;
}
-static int snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io0(struct snd_msnd *chip, int cfg, int num, u16 io)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO0_BASEHI, HIBYTE(io)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO0_BASELO, LOBYTE(io)))
return -EIO;
return 0;
}
-static int snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io1(struct snd_msnd *chip, int cfg, int num, u16 io)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO1_BASEHI, HIBYTE(io)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO1_BASELO, LOBYTE(io)))
return -EIO;
return 0;
}
-static int snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
+static int snd_msnd_write_cfg_irq(struct snd_msnd *chip, int cfg, int num, u16 irq)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
return -EIO;
return 0;
}
-static int snd_msnd_write_cfg_mem(int cfg, int num, int mem)
+static int snd_msnd_write_cfg_mem(struct snd_msnd *chip, int cfg, int num, int mem)
{
u16 wmem;
mem >>= 8;
wmem = (u16)(mem & 0xfff);
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_MEMBASELO, LOBYTE(wmem)))
return -EIO;
- if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL,
+ if (wmem && snd_msnd_write_cfg(chip, cfg, IREG_MEMCONTROL,
MEMTYPE_HIADDR | MEMTYPE_16BIT))
return -EIO;
return 0;
}
-static int snd_msnd_activate_logical(int cfg, int num)
+static int snd_msnd_activate_logical(struct snd_msnd *chip, int cfg, int num)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_ACTIVATE, LD_ACTIVATE))
return -EIO;
return 0;
}
-static int snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
+static int snd_msnd_write_cfg_logical(struct snd_msnd *chip,
+ int cfg, int num, u16 io0,
u16 io1, u16 irq, int mem)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg_io0(cfg, num, io0))
+ if (snd_msnd_write_cfg_io0(chip, cfg, num, io0))
return -EIO;
- if (snd_msnd_write_cfg_io1(cfg, num, io1))
+ if (snd_msnd_write_cfg_io1(chip, cfg, num, io1))
return -EIO;
- if (snd_msnd_write_cfg_irq(cfg, num, irq))
+ if (snd_msnd_write_cfg_irq(chip, cfg, num, irq))
return -EIO;
- if (snd_msnd_write_cfg_mem(cfg, num, mem))
+ if (snd_msnd_write_cfg_mem(chip, cfg, num, mem))
return -EIO;
- if (snd_msnd_activate_logical(cfg, num))
+ if (snd_msnd_activate_logical(chip, cfg, num))
return -EIO;
return 0;
}
-static int snd_msnd_pinnacle_cfg_reset(int cfg)
+static int snd_msnd_pinnacle_cfg_reset(struct snd_msnd *chip, int cfg)
{
int i;
/* Reset devices if told to */
- printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+ dev_info(chip->card->dev, LOGNAME ": Resetting all devices\n");
for (i = 0; i < 4; ++i)
- if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+ if (snd_msnd_write_cfg_logical(chip, cfg, i, 0, 0, 0, 0))
return -EIO;
return 0;
@@ -757,9 +708,9 @@ static int 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;
@@ -801,22 +752,22 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE(INITCODEFILE);
MODULE_FIRMWARE(PERMCODEFILE);
-module_param_hw_array(io, long, ioport, NULL, S_IRUGO);
+module_param_hw_array(io, long, ioport, NULL, 0444);
MODULE_PARM_DESC(io, "IO port #");
-module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
-module_param_hw_array(mem, long, iomem, 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_hw_array(cfg, long, ioport, NULL, S_IRUGO);
-module_param_array(reset, int, 0, S_IRUGO);
-module_param_hw_array(mpu_io, long, ioport, NULL, S_IRUGO);
-module_param_hw_array(mpu_irq, int, irq, NULL, S_IRUGO);
-module_param_hw_array(ide_io0, long, ioport, NULL, S_IRUGO);
-module_param_hw_array(ide_io1, long, ioport, NULL, S_IRUGO);
-module_param_hw_array(ide_irq, int, irq, NULL, S_IRUGO);
-module_param_hw_array(joystick_io, long, ioport, 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
@@ -826,7 +777,7 @@ static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
return 0;
if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) {
- printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
+ dev_warn(pdev, LOGNAME ": io, irq and mem must be set\n");
return 0;
}
@@ -839,14 +790,14 @@ static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
io[i] == 0x220 ||
io[i] == 0x210 ||
io[i] == 0x3e0)) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set "
+ dev_err(pdev, LOGNAME ": \"io\" - DSP I/O base must be set "
" to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, "
"or 0x3E0\n");
return 0;
}
#else
if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) {
- printk(KERN_ERR LOGNAME
+ dev_err(pdev, LOGNAME
": \"io\" - DSP I/O base must within the range 0x100 "
"to 0x3E0 and must be evenly divisible by 0x10\n");
return 0;
@@ -859,7 +810,7 @@ static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
irq[i] == 10 ||
irq[i] == 11 ||
irq[i] == 12)) {
- printk(KERN_ERR LOGNAME
+ dev_err(pdev, LOGNAME
": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
return 0;
}
@@ -870,7 +821,7 @@ static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
mem[i] == 0xd8000 ||
mem[i] == 0xe0000 ||
mem[i] == 0xe8000)) {
- printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
+ dev_err(pdev, LOGNAME ": \"mem\" - must be set to "
"0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or "
"0xe8000\n");
return 0;
@@ -878,9 +829,9 @@ static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
#ifndef MSND_CLASSIC
if (cfg[i] == SNDRV_AUTO_PORT) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ dev_info(pdev, LOGNAME ": Assuming PnP mode\n");
} else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Config port must be 0x250, 0x260 or 0x270 "
"(or unspecified for PnP mode)\n");
return 0;
@@ -901,12 +852,12 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
|| cfg[idx] == SNDRV_AUTO_PORT
#endif
) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ dev_info(pdev, LOGNAME ": Assuming PnP mode\n");
return -ENODEV;
}
- err = snd_card_new(pdev, 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;
@@ -944,73 +895,70 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
chip->memid = HPMEM_E800; break;
}
#else
- printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
- cfg[idx]);
+ dev_info(pdev, LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
+ cfg[idx]);
- if (!request_region(cfg[idx], 2, "Pinnacle/Fiji Config")) {
- printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
- cfg[idx]);
- snd_card_free(card);
+ if (!devm_request_region(card->dev, cfg[idx], 2,
+ "Pinnacle/Fiji Config")) {
+ dev_err(pdev, LOGNAME ": Config port 0x%lx conflict\n",
+ cfg[idx]);
return -EIO;
}
if (reset[idx])
- if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) {
- err = -EIO;
- goto cfg_error;
- }
+ if (snd_msnd_pinnacle_cfg_reset(chip, cfg[idx]))
+ return -EIO;
/* DSP */
- err = snd_msnd_write_cfg_logical(cfg[idx], 0,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 0,
io[idx], 0,
irq[idx], mem[idx]);
if (err)
- goto cfg_error;
+ return err;
/* The following are Pinnacle specific */
/* MPU */
if (mpu_io[idx] != SNDRV_AUTO_PORT
&& mpu_irq[idx] != SNDRV_AUTO_IRQ) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring MPU to I/O 0x%lx IRQ %d\n",
mpu_io[idx], mpu_irq[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 1,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 1,
mpu_io[idx], 0,
mpu_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* IDE */
if (ide_io0[idx] != SNDRV_AUTO_PORT
&& ide_io1[idx] != SNDRV_AUTO_PORT
&& ide_irq[idx] != SNDRV_AUTO_IRQ) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n",
ide_io0[idx], ide_io1[idx], ide_irq[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 2,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 2,
ide_io0[idx], ide_io1[idx],
ide_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* Joystick */
if (joystick_io[idx] != SNDRV_AUTO_PORT) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring joystick to I/O 0x%lx\n",
joystick_io[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 3,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 3,
joystick_io[idx], 0,
0, 0);
if (err)
- goto cfg_error;
+ return err;
}
- release_region(cfg[idx], 2);
#endif /* MSND_CLASSIC */
@@ -1039,39 +987,23 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
spin_lock_init(&chip->lock);
err = snd_msnd_probe(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- snd_card_free(card);
+ dev_err(pdev, LOGNAME ": Probe failed\n");
return err;
}
err = snd_msnd_attach(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- snd_card_free(card);
+ dev_err(pdev, LOGNAME ": Attach failed\n");
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 snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_msnd_unload(dev_get_drvdata(pdev));
- return 0;
}
static struct isa_driver snd_msnd_driver = {
.match = snd_msnd_isa_match,
.probe = snd_msnd_isa_probe,
- .remove = snd_msnd_isa_remove,
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -1108,12 +1040,12 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
return -ENODEV;
if (!pnp_is_active(pnp_dev) && pnp_activate_dev(pnp_dev) < 0) {
- printk(KERN_INFO "msnd_pinnacle: device is inactive\n");
+ dev_info(&pcard->card->dev, "msnd_pinnacle: device is inactive\n");
return -EBUSY;
}
if (!pnp_is_active(mpu_dev) && pnp_activate_dev(mpu_dev) < 0) {
- printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n");
+ dev_info(&pcard->card->dev, "msnd_pinnacle: MPU device is inactive\n");
return -EBUSY;
}
@@ -1121,9 +1053,9 @@ static int 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_new(&pcard->card->dev,
- 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;
@@ -1164,29 +1096,19 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
spin_lock_init(&chip->lock);
ret = snd_msnd_probe(card);
if (ret < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- goto _release_card;
+ dev_err(&pcard->card->dev, LOGNAME ": Probe failed\n");
+ return ret;
}
ret = snd_msnd_attach(card);
if (ret < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- goto _release_card;
+ dev_err(&pcard->card->dev, LOGNAME ": Attach failed\n");
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void 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;
@@ -1205,7 +1127,6 @@ static struct pnp_card_driver msnd_pnpc_driver = {
.name = "msnd_pinnacle",
.id_table = msnd_pnpids,
.probe = snd_msnd_pnp_detect,
- .remove = 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 b408540798c1..ec354483b9f8 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,10 +9,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. *
* *
***************************************************************************/
@@ -139,14 +136,12 @@ static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
{
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- unsigned long flags;
- spin_lock_irqsave(&msnd->mixer_lock, flags);
+ guard(spinlock_irqsave)(&msnd->mixer_lock);
ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
ucontrol->value.integer.value[0] /= 0xFFFF;
ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
ucontrol->value.integer.value[1] /= 0xFFFF;
- spin_unlock_irqrestore(&msnd->mixer_lock, flags);
return 0;
}
@@ -222,11 +217,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) */
@@ -258,15 +251,13 @@ static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
int change, addr = kcontrol->private_value;
int left, right;
- unsigned long flags;
left = ucontrol->value.integer.value[0] % 101;
right = ucontrol->value.integer.value[1] % 101;
- spin_lock_irqsave(&msnd->mixer_lock, flags);
+ guard(spinlock_irqsave)(&msnd->mixer_lock);
change = msnd->left_levels[addr] != left
|| msnd->right_levels[addr] != right;
snd_msndmix_set(msnd, addr, left, right);
- spin_unlock_irqrestore(&msnd->mixer_lock, flags);
return change;
}
@@ -278,7 +269,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),
@@ -304,7 +295,7 @@ int snd_msndmix_new(struct snd_card *card)
if (snd_BUG_ON(!chip))
return -EINVAL;
spin_lock_init(&chip->mixer_lock);
- strcpy(card->mixername, "MSND Pinnacle Mixer");
+ strscpy(card->mixername, "MSND Pinnacle Mixer");
for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
err = snd_ctl_add(card,
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 7cce4cd4a23b..8c1767697b62 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>
@@ -37,11 +22,6 @@
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 */
@@ -128,6 +108,7 @@ struct snd_opl3sa2 {
int irq;
int single_dma;
spinlock_t reg_lock;
+ struct snd_card *card;
struct snd_hwdep *synth;
struct snd_rawmidi *rmidi;
struct snd_wss *wss;
@@ -177,12 +158,12 @@ static unsigned char __snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char
unsigned char result;
#if 0
outb(0x1d, port); /* password */
- printk(KERN_DEBUG "read [0x%lx] = 0x%x\n", port, inb(port));
+ dev_dbg(chip->card->dev, "read [0x%lx] = 0x%x\n", port, inb(port));
#endif
outb(reg, chip->port); /* register */
result = inb(chip->port + 1);
#if 0
- printk(KERN_DEBUG "read [0x%lx] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "read [0x%lx] = 0x%x [0x%x]\n",
port, result, inb(port));
#endif
return result;
@@ -191,13 +172,8 @@ static unsigned char __snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char
/* read control port (with spinlock) */
static unsigned char snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char reg)
{
- unsigned long flags;
- unsigned char result;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- result = __snd_opl3sa2_read(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __snd_opl3sa2_read(chip, reg);
}
/* write control port (w/o spinlock) */
@@ -214,10 +190,8 @@ static void __snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, uns
/* write control port (with spinlock) */
static void snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsigned char value)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__snd_opl3sa2_write(chip, reg, value);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static int snd_opl3sa2_detect(struct snd_card *card)
@@ -228,18 +202,16 @@ static int 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) {
- snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
+ chip->res_port = devm_request_region(card->dev, port, 2,
+ "OPL3-SA control");
+ if (!chip->res_port) {
+ dev_err(card->dev, "can't grab port 0x%lx\n", port);
return -EBUSY;
}
- /*
- snd_printk(KERN_DEBUG "REG 0A = 0x%x\n",
- snd_opl3sa2_read(chip, 0x0a));
- */
chip->version = 0;
tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC);
if (tmp == 0xff) {
- snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
+ dev_dbg(card->dev, "OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
return -ENODEV;
}
switch (tmp & 0x07) {
@@ -259,15 +231,17 @@ static int 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) {
- snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC);
+ if (tmp1 != tmp) {
+ dev_dbg(card->dev, "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) {
- snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC);
+ if ((tmp1 & 0x9f) != 0x8a) {
+ dev_dbg(card->dev, "OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f);
@@ -355,15 +329,13 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -372,7 +344,6 @@ static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -384,12 +355,11 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = chip->ctlregs[reg];
val = (oval & ~(mask << shift)) | val;
change = val != oval;
__snd_opl3sa2_write(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -410,7 +380,6 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -418,10 +387,9 @@ static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -432,7 +400,6 @@ static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -450,7 +417,7 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
oval1 = chip->ctlregs[left_reg];
oval2 = chip->ctlregs[right_reg];
@@ -465,14 +432,13 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
change = val1 != oval1;
__snd_opl3sa2_write(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
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),
@@ -482,7 +448,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)
@@ -507,34 +473,40 @@ static int snd_opl3sa2_mixer(struct snd_card *card)
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* 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) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "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) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "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) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "FM Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "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) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "FM Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "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;
@@ -542,9 +514,11 @@ static int 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;
}
@@ -599,7 +573,7 @@ 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");
+ dev_err(chip->card->dev, "PnP configure failure (out of resources?)\n");
return -EBUSY;
}
sb_port[dev] = pnp_port_start(pdev, 0);
@@ -610,22 +584,14 @@ static int snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
+ dev_dbg(chip->card->dev, "%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]);
- snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
+ dev_dbg(chip->card->dev, "%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]);
return 0;
}
#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(struct device *pdev, int dev,
struct snd_card **cardp)
{
@@ -633,16 +599,15 @@ static int snd_opl3sa2_card_new(struct device *pdev, int dev,
struct snd_opl3sa2 *chip;
int err;
- err = snd_card_new(pdev, 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");
- strcpy(card->shortname, "Yamaha OPL3-SA");
+ strscpy(card->driver, "OPL3SA2");
+ strscpy(card->shortname, "Yamaha OPL3-SA");
chip = card->private_data;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
- card->private_free = snd_opl3sa2_free;
*cardp = card;
return 0;
}
@@ -657,6 +622,7 @@ static int snd_opl3sa2_probe(struct snd_card *card, int dev)
/* initialise this card from supplied (or default) parameter*/
chip = card->private_data;
+ chip->card = card;
chip->ymode = opl3sa3_ymode[dev] & 0x03 ;
chip->port = port[dev];
xirq = irq[dev];
@@ -667,19 +633,20 @@ static int 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, 0,
- "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);
+ dev_err(card->dev, "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,
WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss);
if (err < 0) {
- snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
+ dev_dbg(card->dev, "Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
return err;
}
chip->wss = wss;
@@ -696,20 +663,24 @@ static int snd_opl3sa2_probe(struct snd_card *card, int dev)
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],
- MPU401_INFO_IRQ_HOOK, -1,
- &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",
@@ -740,24 +711,17 @@ static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
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;
- }
- 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 snd_opl3sa2_pnp_remove(struct pnp_dev *pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -773,7 +737,6 @@ static struct pnp_driver opl3sa2_pnp_driver = {
.name = "snd-opl3sa2-pnpbios",
.id_table = snd_opl3sa2_pnpbiosids,
.probe = snd_opl3sa2_pnp_detect,
- .remove = snd_opl3sa2_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_suspend,
.resume = snd_opl3sa2_pnp_resume,
@@ -790,7 +753,7 @@ static int snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
pdev = pnp_request_card_device(pcard, id->devs[0].id, NULL);
if (pdev == NULL) {
- snd_printk(KERN_ERR PFX "can't get pnp device from id '%s'\n",
+ dev_err(&pcard->card->dev, "can't get pnp device from id '%s'\n",
id->devs[0].id);
return -EBUSY;
}
@@ -804,25 +767,17 @@ static int snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
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;
- }
- 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 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)
{
@@ -839,7 +794,6 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = {
.name = "snd-opl3sa2-cpnp",
.id_table = snd_opl3sa2_pnpids,
.probe = snd_opl3sa2_pnp_cdetect,
- .remove = snd_opl3sa2_pnp_cremove,
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_csuspend,
.resume = snd_opl3sa2_pnp_cresume,
@@ -857,19 +811,19 @@ static int snd_opl3sa2_isa_match(struct device *pdev,
return 0;
#endif
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify port\n");
+ dev_err(pdev, "specify port\n");
return 0;
}
if (wss_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify wss_port\n");
+ dev_err(pdev, "specify wss_port\n");
return 0;
}
if (fm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify fm_port\n");
+ dev_err(pdev, "specify fm_port\n");
return 0;
}
if (midi_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify midi_port\n");
+ dev_err(pdev, "specify midi_port\n");
return 0;
}
return 1;
@@ -884,21 +838,13 @@ static int snd_opl3sa2_isa_probe(struct device *pdev,
err = snd_opl3sa2_card_new(pdev, dev, &card);
if (err < 0)
return err;
- 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 snd_opl3sa2_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -917,7 +863,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 = 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 a9dcdeb502bd..44a2fb220456 100644
--- a/sound/isa/opti9xx/Makefile
+++ b/sound/isa/opti9xx/Makefile
@@ -4,10 +4,10 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opti92x-ad1848-objs := opti92x-ad1848.o
-snd-opti92x-cs4231-objs := opti92x-cs4231.o
-snd-opti93x-objs := opti93x.o
-snd-miro-objs := miro.o
+snd-opti92x-ad1848-y := opti92x-ad1848.o
+snd-opti92x-cs4231-y := opti92x-cs4231.o
+snd-opti93x-y := opti93x.o
+snd-miro-y := miro.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 8894c7c18ad6..c320af3e9a05 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>
@@ -46,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 */
@@ -127,12 +111,13 @@ struct snd_miro {
long mpu_port;
int mpu_irq;
+ struct snd_card *card;
struct snd_miro_aci *aci;
};
static struct snd_miro_aci aci_device;
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
@@ -167,8 +152,9 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
byte = inb(aci->aci_port + ACI_REG_BUSY);
if ((byte & 1) == 0) {
if (timeout >= ACI_MINTIME)
- snd_printd("aci ready in round %ld.\n",
- timeout-ACI_MINTIME);
+ dev_dbg(aci->card->dev,
+ "aci ready in round %ld.\n",
+ timeout-ACI_MINTIME);
return byte;
}
if (timeout >= ACI_MINTIME) {
@@ -176,10 +162,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);
@@ -187,7 +176,7 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
}
}
}
- snd_printk(KERN_ERR "aci_busy_wait() time out\n");
+ dev_err(aci->card->dev, "%s() time out\n", __func__);
return -EBUSY;
}
@@ -197,7 +186,7 @@ static inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
outb(byte, aci->aci_port + ACI_REG_COMMAND);
return 0;
} else {
- snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
+ dev_err(aci->card->dev, "aci busy, %s(0x%x) stopped.\n", __func__, byte);
return -EBUSY;
}
}
@@ -210,7 +199,7 @@ static inline int aci_read(struct snd_miro_aci *aci)
byte = inb(aci->aci_port + ACI_REG_STATUS);
return byte;
} else {
- snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
+ dev_err(aci->card->dev, "aci busy, %s() stopped.\n", __func__);
return -EBUSY;
}
}
@@ -273,8 +262,8 @@ static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
value = aci_getvalue(miro->aci, ACI_S_GENERAL);
if (value < 0) {
- snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
- value);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ value);
return value;
}
@@ -293,8 +282,8 @@ static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
- error);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ error);
return error;
}
@@ -335,8 +324,8 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
if (value < 0) {
- snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
- value);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ value);
return value;
}
@@ -355,8 +344,8 @@ static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
- error);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ error);
return error;
}
@@ -387,7 +376,8 @@ static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
+ dev_err(miro->card->dev, "%s() to %d failed: %d\n", __func__,
+ value, error);
return error;
}
@@ -443,13 +433,15 @@ static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
right_val = aci_getvalue(miro->aci, right_reg);
if (right_val < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ right_reg, right_val);
return right_val;
}
left_val = aci_getvalue(miro->aci, left_reg);
if (left_val < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ left_reg, left_val);
return left_val;
}
@@ -502,13 +494,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
left_old = aci_getvalue(aci, getreg_left);
if (left_old < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ getreg_left, left_old);
return left_old;
}
right_old = aci_getvalue(aci, getreg_right);
if (right_old < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ getreg_right, right_old);
return right_old;
}
@@ -528,15 +522,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
if (left >= 0) {
error = aci_setvalue(aci, setreg_left, left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ left, error);
return error;
}
} else {
error = aci_setvalue(aci, setreg_left, 0x80 - left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x80 - left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x80 - left, error);
return error;
}
}
@@ -544,15 +538,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
if (right >= 0) {
error = aci_setvalue(aci, setreg_right, right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ right, error);
return error;
}
} else {
error = aci_setvalue(aci, setreg_right, 0x80 - right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x80 - right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x80 - right, error);
return error;
}
}
@@ -570,14 +564,14 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
error = aci_setvalue(aci, setreg_left, 0x20 - left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x20 - left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x20 - left, error);
return error;
}
error = aci_setvalue(aci, setreg_right, 0x20 - right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x20 - right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x20 - right, error);
return error;
}
}
@@ -587,7 +581,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_miro_controls[] = {
+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),
@@ -599,7 +593,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[] = {
+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),
@@ -609,15 +603,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[] = {
+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[] = {
+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[] = {
+static const struct snd_kcontrol_new snd_miro_preamp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Boost",
@@ -627,7 +621,7 @@ static struct snd_kcontrol_new snd_miro_preamp_control[] = {
.put = snd_miro_put_preamp,
}};
-static struct snd_kcontrol_new snd_miro_amp_control[] = {
+static const struct snd_kcontrol_new snd_miro_amp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Boost",
@@ -637,7 +631,7 @@ static struct snd_kcontrol_new snd_miro_amp_control[] = {
.put = snd_miro_put_amp,
}};
-static struct snd_kcontrol_new snd_miro_capture_control[] = {
+static const struct snd_kcontrol_new snd_miro_capture_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Switch",
@@ -647,7 +641,7 @@ static struct snd_kcontrol_new snd_miro_capture_control[] = {
.put = snd_miro_put_capture,
}};
-static unsigned char aci_init_values[][2] = {
+static const unsigned char aci_init_values[][2] = {
{ ACI_SET_MUTE, 0x00 },
{ ACI_SET_POWERAMP, 0x00 },
{ ACI_SET_PREAMP, 0x00 },
@@ -680,7 +674,7 @@ static int snd_set_aci_init_values(struct snd_miro *miro)
if ((aci->aci_product == 'A') && wss) {
error = aci_setvalue(aci, ACI_SET_WSS, wss);
if (error < 0) {
- snd_printk(KERN_ERR "enabling WSS mode failed\n");
+ dev_err(miro->card->dev, "enabling WSS mode failed\n");
return error;
}
}
@@ -690,7 +684,7 @@ static int snd_set_aci_init_values(struct snd_miro *miro)
if (ide) {
error = aci_setvalue(aci, ACI_SET_IDE, ide);
if (error < 0) {
- snd_printk(KERN_ERR "enabling IDE port failed\n");
+ dev_err(miro->card->dev, "enabling IDE port failed\n");
return error;
}
}
@@ -701,8 +695,8 @@ static int snd_set_aci_init_values(struct snd_miro *miro)
error = aci_setvalue(aci, aci_init_values[idx][0],
aci_init_values[idx][1]);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- aci_init_values[idx][0], error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ aci_init_values[idx][0], error);
return error;
}
}
@@ -724,10 +718,10 @@ static int snd_miro_mixer(struct snd_card *card,
switch (miro->hardware) {
case OPTi9XX_HW_82C924:
- strcpy(card->mixername, "ACI & OPTi924");
+ strscpy(card->mixername, "ACI & OPTi924");
break;
case OPTi9XX_HW_82C929:
- strcpy(card->mixername, "ACI & OPTi929");
+ strscpy(card->mixername, "ACI & OPTi929");
break;
default:
snd_BUG();
@@ -735,35 +729,43 @@ static int 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;
}
}
@@ -774,10 +776,10 @@ static int snd_miro_mixer(struct snd_card *card,
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]);
+ strscpy(chip->name, snd_opti9xx_names[hardware]);
chip->mc_base_size = opti9xx_mc_size[hardware];
@@ -810,7 +812,7 @@ static int snd_miro_init(struct snd_miro *chip,
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", hardware);
return -ENODEV;
}
@@ -820,10 +822,9 @@ static int snd_miro_init(struct snd_miro *chip,
static unsigned char snd_miro_read(struct snd_miro *chip,
unsigned char reg)
{
- unsigned long flags;
unsigned char retval = 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -834,25 +835,23 @@ 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);
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", chip->hardware);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return retval;
}
static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -863,22 +862,24 @@ 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);
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", chip->hardware);
}
-
- 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
@@ -992,10 +993,7 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
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);
}
/*
@@ -1009,7 +1007,6 @@ static int snd_miro_configure(struct snd_miro *chip)
unsigned char dma_bits;
unsigned char mpu_port_bits = 0;
unsigned char mpu_irq_bits;
- unsigned long flags;
snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
@@ -1025,7 +1022,7 @@ static int snd_miro_configure(struct snd_miro *chip)
snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
break;
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
@@ -1048,7 +1045,7 @@ static int snd_miro_configure(struct snd_miro *chip)
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base);
+ dev_err(chip->card->dev, "WSS port 0x%lx not valid\n", chip->wss_base);
goto __skip_base;
}
snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -1071,7 +1068,7 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq);
+ dev_err(chip->card->dev, "WSS irq # %d not valid\n", chip->irq);
goto __skip_resources;
}
@@ -1086,12 +1083,12 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1);
+ dev_err(chip->card->dev, "WSS dma1 # %d not valid\n", chip->dma1);
goto __skip_resources;
}
if (chip->dma1 == chip->dma2) {
- snd_printk(KERN_ERR "don't want to share dmas\n");
+ dev_err(chip->card->dev, "don't want to share dmas\n");
return -EBUSY;
}
@@ -1100,14 +1097,14 @@ __skip_base:
case 1:
break;
default:
- snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2);
+ dev_err(chip->card->dev, "WSS dma2 # %d not valid\n", chip->dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
- spin_lock_irqsave(&chip->lock, flags);
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ }
__skip_resources:
if (chip->hardware > OPTi9XX_HW_82C928) {
@@ -1128,8 +1125,8 @@ __skip_resources:
mpu_port_bits = 0x00;
break;
default:
- snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n",
- chip->mpu_port);
+ dev_err(chip->card->dev, "MPU-401 port 0x%lx not valid\n",
+ chip->mpu_port);
goto __skip_mpu;
}
@@ -1147,8 +1144,8 @@ __skip_resources:
mpu_irq_bits = 0x01;
break;
default:
- snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n",
- chip->mpu_irq);
+ dev_err(chip->card->dev, "MPU-401 irq # %d not valid\n",
+ chip->mpu_irq);
goto __skip_mpu;
}
@@ -1162,12 +1159,13 @@ __skip_mpu:
return 0;
}
-static int 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;
@@ -1176,7 +1174,7 @@ static int 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;
@@ -1189,10 +1187,11 @@ static int snd_card_miro_detect(struct snd_card *card,
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;
}
@@ -1209,6 +1208,7 @@ static int snd_card_miro_aci_detect(struct snd_card *card,
miro->aci = aci;
+ aci->card = card;
mutex_init(&aci->aci_mutex);
/* get ACI port from OPTi9xx MC 4 */
@@ -1216,55 +1216,46 @@ static int 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);
+ dev_err(card->dev, "aci i/o area 0x%lx-0x%lx already used.\n",
+ aci->aci_port, aci->aci_port+2);
return -ENOMEM;
}
/* force ACI into a known state */
for (i = 0; i < 3; i++)
if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
- snd_printk(KERN_ERR "can't force aci into known state.\n");
+ dev_err(card->dev, "can't force aci into known state.\n");
return -ENXIO;
}
aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
if (aci->aci_vendor < 0 || aci->aci_product < 0) {
- snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
- aci->aci_port);
+ dev_err(card->dev, "can't read aci id on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
if (aci->aci_version < 0) {
- snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
- aci->aci_port);
+ dev_err(card->dev, "can't read aci version on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
- snd_printk(KERN_ERR "can't initialize aci.\n");
+ dev_err(card->dev, "can't initialize aci.\n");
return -ENXIO;
}
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 snd_miro_probe(struct snd_card *card)
{
int error;
@@ -1273,18 +1264,19 @@ static int snd_miro_probe(struct snd_card *card)
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");
+ dev_err(card->dev, "request for OPTI9xx MC failed\n");
return -ENOMEM;
}
}
error = snd_card_miro_aci_detect(card, miro);
if (error < 0) {
- snd_printk(KERN_ERR "unable to detect aci chip\n");
+ dev_err(card->dev, "unable to detect aci chip\n");
return -ENODEV;
}
@@ -1344,19 +1336,19 @@ static int snd_miro_probe(struct snd_card *card)
default:
sprintf(card->shortname,
"unknown miro");
- snd_printk(KERN_INFO "unknown miro aci id\n");
+ dev_info(card->dev, "unknown miro aci id\n");
break;
}
} else {
- snd_printk(KERN_INFO "found unsupported aci card\n");
+ dev_info(card->dev, "found unsupported aci card\n");
sprintf(card->shortname, "unknown Cardinal Technologies");
}
- strcpy(card->driver, "miro");
- 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);
+ strscpy(card->driver, "miro");
+ scnprintf(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;
@@ -1364,8 +1356,8 @@ static int snd_miro_probe(struct snd_card *card)
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpu_port, 0, miro->mpu_irq, &rmidi);
if (error < 0)
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- mpu_port);
+ dev_warn(card->dev, "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
}
if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
@@ -1374,8 +1366,8 @@ static int snd_miro_probe(struct snd_card *card)
if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0)
- snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
- fm_port);
+ dev_warn(card->dev, "no OPL4 device at 0x%lx\n",
+ fm_port);
}
error = snd_set_aci_init_values(miro);
@@ -1398,38 +1390,35 @@ static int snd_miro_isa_match(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_new(devptr, 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");
+ dev_err(card->dev, "unable to detect OPTi9xx chip\n");
return -ENODEV;
}
if (port == SNDRV_AUTO_PORT) {
port = snd_legacy_find_free_ioport(possible_ports, 4);
if (port < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ dev_err(card->dev, "unable to find a free WSS port\n");
return -EBUSY;
}
}
@@ -1437,9 +1426,8 @@ static int 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");
+ dev_err(card->dev,
+ "unable to find a free MPU401 port\n");
return -EBUSY;
}
}
@@ -1447,60 +1435,46 @@ static int 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");
+ dev_err(card->dev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (mpu_irq == SNDRV_AUTO_IRQ) {
mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
if (mpu_irq < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR
- "unable to find a free MPU401 IRQ\n");
+ dev_err(card->dev,
+ "unable to find a free MPU401 IRQ\n");
return -EBUSY;
}
}
if (dma1 == SNDRV_AUTO_DMA) {
dma1 = snd_legacy_find_free_dma(possible_dma1s);
if (dma1 < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ dev_err(card->dev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2 == SNDRV_AUTO_DMA) {
dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
if (dma2 < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ dev_err(card->dev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
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 snd_miro_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#define DEV_NAME "miro"
static struct isa_driver snd_miro_driver = {
.match = snd_miro_isa_match,
.probe = snd_miro_isa_probe,
- .remove = snd_miro_isa_remove,
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1532,14 +1506,14 @@ static int snd_card_miro_pnp(struct snd_miro *chip,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "AUDIO pnp configure failure: %d\n", err);
return err;
}
err = pnp_activate_dev(devmc);
if (err < 0) {
- snd_printk(KERN_ERR "MC pnp configure failure: %d\n",
- err);
+ dev_err(chip->card->dev, "MC pnp configure failure: %d\n",
+ err);
return err;
}
@@ -1560,7 +1534,7 @@ static int snd_card_miro_pnp(struct snd_miro *chip,
if (mpu_port > 0) {
err = pnp_activate_dev(devmpu);
if (err < 0) {
- snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ dev_err(chip->card->dev, "MPU401 pnp configure failure\n");
mpu_port = -1;
return err;
}
@@ -1581,39 +1555,32 @@ static int snd_miro_pnp_probe(struct pnp_card_link *pcard,
return -EBUSY;
if (!isapnp)
return -ENODEV;
- err = snd_card_new(&pcard->card->dev, index, id, THIS_MODULE,
- sizeof(struct snd_miro), &card);
+ 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;
+ miro->card = card;
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);
+ dev_err(card->dev, "OPTI chip not found\n");
return err;
}
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;
@@ -1621,8 +1588,6 @@ static int snd_miro_pnp_probe(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;
}
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 505cd81e19fa..abaa3ed3ab5c 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
*/
@@ -48,17 +36,11 @@ 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 */
@@ -127,6 +109,7 @@ MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* OPTi93X */
struct snd_opti9xx {
+ struct snd_card *card;
unsigned short hardware;
unsigned char password;
char name[7];
@@ -175,7 +158,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
#define DEV_NAME KBUILD_MODNAME
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
@@ -185,10 +168,10 @@ static char * snd_opti9xx_names[] = {
static int 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]);
+ strscpy(chip->name, snd_opti9xx_names[hardware]);
spin_lock_init(&chip->lock);
@@ -236,7 +219,7 @@ static int snd_opti9xx_init(struct snd_opti9xx *chip,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", hardware);
return -ENODEV;
}
return 0;
@@ -245,10 +228,9 @@ static int snd_opti9xx_init(struct snd_opti9xx *chip,
static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
unsigned char reg)
{
- unsigned long flags;
unsigned char retval = 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -261,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:
@@ -278,19 +261,16 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return retval;
}
static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -303,6 +283,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:
@@ -320,17 +301,18 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
}
-
- spin_unlock_irqrestore(&chip->lock, flags);
}
-#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 snd_opti9xx_configure(struct snd_opti9xx *chip,
long port,
@@ -350,6 +332,7 @@ static int 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 */
@@ -386,7 +369,9 @@ static int snd_opti9xx_configure(struct snd_opti9xx *chip,
case OPTi9XX_HW_82C931:
/* disable 3D sound (set GPIO1 as output, low) */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
- case OPTi9XX_HW_82C933: /* FALL THROUGH */
+ fallthrough;
+
+ case OPTi9XX_HW_82C933:
/*
* The BTC 1817DW has QS1000 wavetable which is connected
* to the serial digital input of the OPTI931.
@@ -397,7 +382,9 @@ static int 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 |
@@ -408,7 +395,7 @@ static int snd_opti9xx_configure(struct snd_opti9xx *chip,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
@@ -431,7 +418,7 @@ static int snd_opti9xx_configure(struct snd_opti9xx *chip,
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
+ dev_warn(chip->card->dev, "WSS port 0x%lx not valid\n", port);
goto __skip_base;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -456,7 +443,7 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
+ dev_warn(chip->card->dev, "WSS irq # %d not valid\n", irq);
goto __skip_resources;
}
@@ -471,13 +458,13 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
+ dev_warn(chip->card->dev, "WSS dma1 # %d not valid\n", dma1);
goto __skip_resources;
}
#if defined(CS4231) || defined(OPTi93X)
if (dma1 == dma2) {
- snd_printk(KERN_ERR "don't want to share dmas\n");
+ dev_err(chip->card->dev, "don't want to share dmas\n");
return -EBUSY;
}
@@ -486,14 +473,14 @@ __skip_base:
case 1:
break;
default:
- snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
+ dev_warn(chip->card->dev, "WSS dma2 # %d not valid\n", dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
#endif /* CS4231 || OPTi93X */
#ifndef OPTi93X
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ outb(irq_bits << 3 | dma_bits, chip->wss_base);
#else /* OPTi93X */
snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
#endif /* OPTi93X */
@@ -517,8 +504,8 @@ __skip_resources:
mpu_port_bits = 0x00;
break;
default:
- snd_printk(KERN_WARNING
- "MPU-401 port 0x%lx not valid\n", mpu_port);
+ dev_warn(chip->card->dev,
+ "MPU-401 port 0x%lx not valid\n", mpu_port);
goto __skip_mpu;
}
@@ -536,8 +523,8 @@ __skip_resources:
mpu_irq_bits = 0x01;
break;
default:
- snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
- mpu_irq);
+ dev_warn(chip->card->dev, "MPU-401 irq # %d not valid\n",
+ mpu_irq);
goto __skip_mpu;
}
@@ -557,7 +544,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,
@@ -601,35 +588,35 @@ static int snd_opti93x_mixer(struct snd_wss *chip)
card = chip->card;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX0 switch to CD */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "CD Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+ dev_err(card->dev, "Cannot rename opti93x control\n");
return err;
}
/* reassign AUX1 switch to FM */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "FM Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "FM Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+ dev_err(card->dev, "Cannot rename opti93x control\n");
return err;
}
/* remove AUX1 volume */
- strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
+ strscpy(id1.name, "Aux Playback Volume"); id1.index = 1;
snd_ctl_remove_id(card, &id1);
/* Replace WSS volume controls with OPTi93x volume controls */
id1.index = 0;
for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
- strcpy(id1.name, snd_opti93x_controls[idx].name);
+ strscpy(id1.name, snd_opti93x_controls[idx].name);
snd_ctl_remove_id(card, &id1);
err = snd_ctl_add(card,
@@ -662,16 +649,15 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
#endif /* OPTi93X */
-static int 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));
@@ -679,25 +665,26 @@ static int 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, 2,
- "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);
- outb(chip->password, chip->mc_base + chip->pwd_reg);
- outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(chip->password, chip->mc_base + chip->pwd_reg);
+ outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
+ }
value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
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;
@@ -717,7 +704,7 @@ static int 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
@@ -745,7 +732,7 @@ static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "AUDIO pnp configure failure: %d\n", err);
return err;
}
@@ -762,7 +749,7 @@ static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
err = pnp_activate_dev(devmc);
if (err < 0) {
- snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "MC pnp configure failure: %d\n", err);
return err;
}
@@ -786,7 +773,7 @@ static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
if (devmpu && mpu_port > 0) {
err = pnp_activate_dev(devmpu);
if (err < 0) {
- snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ dev_err(chip->card->dev, "MPU401 pnp configure failure\n");
mpu_port = -1;
} else {
mpu_port = pnp_port_start(devmpu, 0);
@@ -797,25 +784,9 @@ static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
}
#endif /* CONFIG_PNP */
-static void snd_card_opti9xx_free(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 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;
@@ -832,7 +803,7 @@ static int snd_opti9xx_probe(struct snd_card *card)
if (port == SNDRV_AUTO_PORT) {
port = snd_legacy_find_free_ioport(possible_ports, 4);
if (port < 0) {
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ dev_err(card->dev, "unable to find a free WSS port\n");
return -EBUSY;
}
}
@@ -868,26 +839,27 @@ static int snd_opti9xx_probe(struct snd_card *card)
return error;
#endif
#ifdef OPTi93X
- error = request_irq(irq, snd_opti93x_interrupt,
- 0, 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);
+ dev_err(card->dev, "opti9xx: can't grab IRQ %d\n", irq);
return error;
}
#endif
chip->irq = irq;
- strcpy(card->driver, chip->name);
+ card->sync_irq = chip->irq;
+ strscpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
- 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);
+ scnprintf(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
- 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);
+ scnprintf(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)
@@ -896,8 +868,8 @@ static int snd_opti9xx_probe(struct snd_card *card)
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpu_port, 0, mpu_irq, &rmidi);
if (error)
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- mpu_port);
+ dev_warn(card->dev, "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
}
if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
@@ -920,8 +892,8 @@ static int snd_opti9xx_probe(struct snd_card *card)
#endif /* !OPTi93X */
if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- fm_port, fm_port + 4 - 1);
+ dev_warn(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port, fm_port + 4 - 1);
}
if (opl3) {
error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
@@ -938,11 +910,10 @@ static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp)
struct snd_card *card;
int err;
- err = snd_card_new(pdev, 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;
}
@@ -964,46 +935,51 @@ static int snd_opti9xx_isa_probe(struct device *devptr,
{
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) {
- snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
+ mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+ if (mpu_port < 0) {
+ dev_err(devptr, "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) {
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ irq = snd_legacy_find_free_irq(possible_irqs);
+ if (irq < 0) {
+ dev_err(devptr, "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) {
- snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
+ mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+ if (mpu_irq < 0) {
+ dev_err(devptr, "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) {
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ dma1 = snd_legacy_find_free_dma(possible_dma1s);
+ if (dma1 < 0) {
+ dev_err(devptr, "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) {
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+ if (dma2 < 0) {
+ dev_err(devptr, "unable to find a free DMA2\n");
return -EBUSY;
}
}
@@ -1013,25 +989,16 @@ static int snd_opti9xx_isa_probe(struct device *devptr,
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;
- }
- 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 snd_opti9xx_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_opti9xx_suspend(struct snd_card *card)
{
@@ -1076,7 +1043,6 @@ static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_opti9xx_driver = {
.match = snd_opti9xx_isa_match,
.probe = snd_opti9xx_isa_probe,
- .remove = snd_opti9xx_isa_remove,
#ifdef CONFIG_PM
.suspend = snd_opti9xx_isa_suspend,
.resume = snd_opti9xx_isa_resume,
@@ -1102,6 +1068,7 @@ static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
if (error < 0)
return error;
chip = card->private_data;
+ chip->card = card;
hw = snd_card_opti9xx_pnp(chip, pcard, pid);
switch (hw) {
@@ -1115,24 +1082,20 @@ static int 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);
+ dev_err(card->dev, "OPTI chip not found\n");
return error;
}
- 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;
@@ -1140,8 +1103,6 @@ static int snd_opti9xx_pnp_probe(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;
}
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index f174a5b3c8e4..96a926feb17a 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -4,15 +4,15 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-sb-common-objs := sb_common.o sb_mixer.o
-snd-sb8-dsp-objs := sb8_main.o sb8_midi.o
-snd-sb16-dsp-objs := sb16_main.o
-snd-sb16-csp-objs := sb16_csp.o
-snd-sb8-objs := sb8.o
-snd-sb16-objs := sb16.o
-snd-sbawe-objs := sbawe.o emu8000.o
-snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
-snd-jazz16-objs := jazz16.o
+snd-sb-common-y := sb_common.o sb_mixer.o
+snd-sb8-dsp-y := sb8_main.o sb8_midi.o
+snd-sb16-dsp-y := sb16_main.o
+snd-sb16-csp-y := sb16_csp.o
+snd-sb8-y := sb8.o
+snd-sb16-y := sb16.o
+snd-sbawe-y := sbawe.o emu8000.o
+snd-emu8000-synth-y := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
+snd-jazz16-y := jazz16.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index d56973b770c7..12c296ee34ec 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1,23 +1,10 @@
+// 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>
@@ -27,6 +14,7 @@
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/emu8000.h>
#include <sound/emu8000_reg.h>
@@ -47,60 +35,49 @@
/* Write a word */
void snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short)val, port); /* Send data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a word */
unsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
- unsigned short res;
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
- res = inw(port); /* Read data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
- return res;
+ return inw(port); /* Read data */
}
/* Write a double word */
void snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short)val, port); /* Send low word of data */
outw((unsigned short)(val>>16), port+2); /* Send high word of data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a double word */
unsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
unsigned short low;
- unsigned int res;
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
low = inw(port); /* Read low word of data */
- res = low + (inw(port+2) << 16);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
- return res;
+ return low + (inw(port+2) << 16);
}
/*
@@ -173,8 +150,8 @@ snd_emu8000_detect(struct snd_emu8000 *emu)
if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
return -ENODEV;
- snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
- emu->port1);
+ dev_dbg(emu->card->dev, "EMU8000 [0x%lx]: Synth chip found\n",
+ emu->port1);
return 0;
}
@@ -235,7 +212,7 @@ init_dma(struct snd_emu8000 *emu)
/*
* initialization arrays; from ADIP
*/
-static unsigned short init1[128] = {
+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,
@@ -257,7 +234,7 @@ static unsigned short init1[128] = {
0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
};
-static unsigned short init2[128] = {
+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,
@@ -279,7 +256,7 @@ static unsigned short init2[128] = {
0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
};
-static unsigned short init3[128] = {
+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,
@@ -301,7 +278,7 @@ static unsigned short init3[128] = {
0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
};
-static unsigned short init4[128] = {
+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,
@@ -328,10 +305,10 @@ static unsigned short init4[128] = {
* is meant to work
*/
static void
-send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
+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++)
@@ -468,8 +445,6 @@ skip_detect:
/*exported*/ void
snd_emu8000_init_fm(struct snd_emu8000 *emu)
{
- unsigned long flags;
-
/* Initialize the last two channels for DRAM refresh and producing
the reverb and chorus effects for Yamaha OPL-3 synthesizer */
@@ -491,12 +466,12 @@ snd_emu8000_init_fm(struct snd_emu8000 *emu)
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
- spin_lock_irqsave(&emu->reg_lock, flags);
- while (!(inw(EMU8000_PTR(emu)) & 0x1000))
- ;
- while ((inw(EMU8000_PTR(emu)) & 0x1000))
- ;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->reg_lock) {
+ while (!(inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ while ((inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ }
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
/* this is really odd part.. */
outb(0x3C, EMU8000_PTR(emu));
@@ -561,7 +536,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 */
@@ -576,7 +551,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},
@@ -665,7 +640,7 @@ snd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user
{
struct soundfont_chorus_fx rec;
if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
- snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode);
+ dev_warn(emu->card->dev, "invalid chorus mode %d for uploading\n", mode);
return -EINVAL;
}
if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
@@ -793,7 +768,7 @@ snd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user
struct soundfont_reverb_fx rec;
if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
- snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode);
+ dev_warn(emu->card->dev, "invalid reverb mode %d for uploading\n", mode);
return -EINVAL;
}
if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
@@ -850,25 +825,24 @@ static int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0] % 12;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- change = val1 != emu->treble_level;
- emu->treble_level = val1;
- } else {
- change = val1 != emu->bass_level;
- emu->bass_level = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ change = val1 != emu->treble_level;
+ emu->treble_level = val1;
+ } else {
+ change = val1 != emu->bass_level;
+ emu->bass_level = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
snd_emu8000_update_equalizer(emu);
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",
@@ -878,7 +852,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",
@@ -911,21 +885,20 @@ static int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl
static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
- change = val1 != emu->chorus_mode;
- emu->chorus_mode = val1;
- } else {
- val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
- change = val1 != emu->reverb_mode;
- emu->reverb_mode = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
+ change = val1 != emu->chorus_mode;
+ emu->chorus_mode = val1;
+ } else {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
+ change = val1 != emu->reverb_mode;
+ emu->reverb_mode = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
if (change) {
if (kcontrol->private_value)
snd_emu8000_update_chorus_mode(emu);
@@ -935,7 +908,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",
@@ -945,7 +918,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",
@@ -978,26 +951,25 @@ static int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0] % 256;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- change = val1 != emu->fm_chorus_depth;
- emu->fm_chorus_depth = val1;
- } else {
- change = val1 != emu->fm_reverb_depth;
- emu->fm_reverb_depth = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ change = val1 != emu->fm_chorus_depth;
+ emu->fm_chorus_depth = val1;
+ } else {
+ change = val1 != emu->fm_reverb_depth;
+ emu->fm_reverb_depth = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
if (change)
snd_emu8000_init_fm(emu);
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",
@@ -1007,7 +979,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",
@@ -1018,7 +990,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,
@@ -1033,6 +1005,7 @@ static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
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))
@@ -1042,42 +1015,20 @@ 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;
__error:
- for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- down_write(&card->controls_rwsem);
- if (emu->controls[i])
- snd_ctl_remove(card, emu->controls[i]);
- up_write(&card->controls_rwsem);
- }
+ for (i = 0; i < EMU8000_NUM_CONTROLS; i++)
+ snd_ctl_remove(card, emu->controls[i]);
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.
*/
@@ -1088,9 +1039,6 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
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;
@@ -1098,7 +1046,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);
@@ -1106,11 +1054,11 @@ 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"))) {
- 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);
+ 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")) {
+ dev_err(card->dev, "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n",
+ hw->port1, hw->port2, hw->port3);
return -EBUSY;
}
hw->mem_size = 0;
@@ -1123,25 +1071,17 @@ 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 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");
+ strscpy(awe->name, "EMU-8000");
*(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
}
#else
diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c
index 5a485504f607..7609a5b640cb 100644
--- a/sound/isa/sb/emu8000_callback.c
+++ b/sound/isa/sb/emu8000_callback.c
@@ -1,22 +1,9 @@
+// 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"
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 c2e41d2762f7..d60174ec8b39 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -1,22 +1,9 @@
+// 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"
@@ -161,16 +148,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
if (snd_BUG_ON(!sp))
return -EINVAL;
- if (sp->v.size == 0)
- 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;
- }
-
/* compute true data size to be loaded */
truesize = sp->v.size;
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
@@ -180,25 +157,18 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
sp->block = snd_util_mem_alloc(hdr, truesize * 2);
if (sp->block == NULL) {
- /*snd_printd("EMU8000: out of memory\n");*/
/* not ENOMEM (for compatibility) */
return -ENOSPC;
}
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;
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
/* dram position (in word) -- mem_offset is byte */
dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
dram_start = dram_offset;
@@ -207,7 +177,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 bc5af71d3bdb..656a655d618d 100644
--- a/sound/isa/sb/emu8000_pcm.c
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -1,21 +1,8 @@
+// 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"
@@ -195,30 +182,32 @@ static inline int emu8k_get_curpos(struct snd_emu8k_pcm *rec, int ch)
*/
static void emu8k_pcm_timer_func(struct timer_list *t)
{
- struct snd_emu8k_pcm *rec = from_timer(rec, t, timer);
+ struct snd_emu8k_pcm *rec = timer_container_of(rec, t, timer);
int ptr, delta;
+ bool period_elapsed = false;
+
+ scoped_guard(spinlock, &rec->timer_lock) {
+ /* update the current pointer */
+ ptr = emu8k_get_curpos(rec, 0);
+ if (ptr < rec->last_ptr)
+ delta = ptr + rec->buf_size - rec->last_ptr;
+ else
+ delta = ptr - rec->last_ptr;
+ rec->period_pos += delta;
+ rec->last_ptr = ptr;
+
+ /* reprogram timer */
+ mod_timer(&rec->timer, jiffies + 1);
- spin_lock(&rec->timer_lock);
- /* update the current pointer */
- ptr = emu8k_get_curpos(rec, 0);
- if (ptr < rec->last_ptr)
- delta = ptr + rec->buf_size - rec->last_ptr;
- else
- delta = ptr - rec->last_ptr;
- rec->period_pos += delta;
- rec->last_ptr = ptr;
-
- /* reprogram timer */
- mod_timer(&rec->timer, jiffies + 1);
+ /* update period */
+ if (rec->period_pos >= (int)rec->period_size) {
+ rec->period_pos %= rec->period_size;
+ period_elapsed = true;
+ }
+ }
- /* update period */
- if (rec->period_pos >= (int)rec->period_size) {
- rec->period_pos %= rec->period_size;
- spin_unlock(&rec->timer_lock);
+ if (period_elapsed)
snd_pcm_period_elapsed(rec->substream);
- return;
- }
- spin_unlock(&rec->timer_lock);
}
@@ -249,7 +238,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;
}
@@ -334,7 +323,6 @@ static void setup_voice(struct snd_emu8k_pcm *rec, int ch)
*/
static void start_voice(struct snd_emu8k_pcm *rec, int ch)
{
- unsigned long flags;
struct snd_emu8000 *hw = rec->emu;
unsigned int temp, aux;
int pt = calc_pitch_target(rec->pitch);
@@ -356,12 +344,11 @@ static void start_voice(struct snd_emu8k_pcm *rec, int ch)
EMU8000_CPF_WRITE(hw, ch, pt << 16);
/* start timer */
- spin_lock_irqsave(&rec->timer_lock, flags);
+ guard(spinlock_irqsave)(&rec->timer_lock);
if (! rec->timer_running) {
mod_timer(&rec->timer, jiffies + 1);
rec->timer_running = 1;
}
- spin_unlock_irqrestore(&rec->timer_lock, flags);
}
/*
@@ -369,18 +356,16 @@ static void start_voice(struct snd_emu8k_pcm *rec, int ch)
*/
static void stop_voice(struct snd_emu8k_pcm *rec, int ch)
{
- unsigned long flags;
struct snd_emu8000 *hw = rec->emu;
EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
/* stop timer */
- spin_lock_irqsave(&rec->timer_lock, flags);
+ guard(spinlock_irqsave)(&rec->timer_lock);
if (rec->timer_running) {
- del_timer(&rec->timer);
+ timer_delete(&rec->timer);
rec->timer_running = 0;
}
- spin_unlock_irqrestore(&rec->timer_lock, flags);
}
static int emu8k_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
@@ -422,39 +407,25 @@ do { \
return -EAGAIN;\
} while (0)
-enum {
- COPY_USER, COPY_KERNEL, FILL_SILENCE,
-};
-
-#define GET_VAL(sval, buf, mode) \
+#define GET_VAL(sval, iter) \
do { \
- switch (mode) { \
- case FILL_SILENCE: \
+ if (!iter) \
sval = 0; \
- break; \
- case COPY_KERNEL: \
- sval = *buf++; \
- break; \
- default: \
- if (get_user(sval, (unsigned short __user *)buf)) \
- return -EFAULT; \
- buf++; \
- break; \
- } \
+ else if (copy_from_iter(&sval, 2, iter) != 2) \
+ return -EFAULT; \
} while (0)
#ifdef USE_NONINTERLEAVE
-#define LOOP_WRITE(rec, offset, _buf, count, mode) \
+#define LOOP_WRITE(rec, offset, iter, count) \
do { \
struct snd_emu8000 *emu = (rec)->emu; \
- unsigned short *buf = (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); \
+ GET_VAL(sval, iter); \
EMU8000_SMLD_WRITE(emu, sval); \
count--; \
} \
@@ -463,27 +434,14 @@ enum {
/* copy one channel block */
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
int voice, unsigned long pos,
- void __user *src, unsigned long 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_UESR);
- return 0;
-}
-
-static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs,
- int voice, unsigned long pos,
- void *src, unsigned long count)
+ struct iov_iter *src, unsigned long 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_KERNEL);
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
@@ -496,16 +454,15 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
/* convert to word unit */
pos = (pos << 1) + rec->loop_start[voice];
count <<= 1;
- LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE);
+ LOOP_WRITE(rec, pos, NULL, count);
return 0;
}
#else /* interleave */
-#define LOOP_WRITE(rec, pos, _buf, count, mode) \
+#define LOOP_WRITE(rec, pos, iter, count) \
do { \
struct snd_emu8000 *emu = rec->emu; \
- unsigned short *buf = (unsigned short *)(_buf); \
snd_emu8000_write_wait(emu, 1); \
EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \
if (rec->voices > 1) \
@@ -513,11 +470,11 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
while (count > 0) { \
unsigned short sval; \
CHECK_SCHEDULER(); \
- GET_VAL(sval, buf, mode); \
+ GET_VAL(sval, iter); \
EMU8000_SMLD_WRITE(emu, sval); \
if (rec->voices > 1) { \
CHECK_SCHEDULER(); \
- GET_VAL(sval, buf, mode); \
+ GET_VAL(sval, iter); \
EMU8000_SMRD_WRITE(emu, sval); \
} \
count--; \
@@ -531,27 +488,14 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
*/
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
int voice, unsigned long pos,
- void __user *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_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 iov_iter *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);
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
@@ -563,7 +507,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
/* 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);
+ LOOP_WRITE(rec, pos, NULL, count);
return 0;
}
#endif
@@ -639,7 +583,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;
@@ -673,14 +618,12 @@ static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)
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_user = emu8k_pcm_copy,
- .copy_kernel = emu8k_pcm_copy_kernel,
+ .copy = emu8k_pcm_copy,
.fill_silence = emu8k_pcm_silence,
};
@@ -696,7 +639,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 4aa719cad331..9bec85ec55b4 100644
--- a/sound/isa/sb/emu8000_synth.c
+++ b/sound/isa/sb/emu8000_synth.c
@@ -1,23 +1,10 @@
+// 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"
@@ -58,7 +45,7 @@ static int snd_emu8000_probe(struct device *_dev)
emu->num_ports = hw->seq_ports;
if (hw->memhdr) {
- snd_printk(KERN_ERR "memhdr is already initialized!?\n");
+ dev_err(hw->card->dev, "memhdr is already initialized!?\n");
snd_util_memhdr_free(hw->memhdr);
}
hw->memhdr = snd_util_memhdr_new(hw->mem_size);
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index bfa0055e1fd6..69d9bfb6c14c 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/string.h>
#include <asm/dma.h>
#include <linux/isa.h>
#include <sound/core.h>
@@ -28,9 +29,6 @@
#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");
@@ -78,13 +76,14 @@ static irqreturn_t jazz16_interrupt(int irq, void *chip)
return snd_sb8dsp_interrupt(chip);
}
-static int jazz16_configure_ports(unsigned long port,
+static int jazz16_configure_ports(struct snd_card *card,
+ unsigned long port,
unsigned long mpu_port, int idx)
{
unsigned char val;
if (!request_region(0x201, 1, "jazz16 config")) {
- snd_printk(KERN_ERR "config port region is already in use.\n");
+ dev_err(card->dev, "config port region is already in use.\n");
return -EBUSY;
}
outb(SB_JAZZ16_WAKEUP - idx, 0x201);
@@ -99,15 +98,15 @@ static int jazz16_configure_ports(unsigned long port,
return 0;
}
-static int jazz16_detect_board(unsigned long port,
+static int jazz16_detect_board(struct snd_card *card, unsigned long port,
unsigned long mpu_port)
{
int err;
int val;
- struct snd_sb chip;
+ struct snd_sb chip = {};
if (!request_region(port, 0x10, "jazz16")) {
- snd_printk(KERN_ERR "I/O port region is already in use.\n");
+ dev_err(card->dev, "I/O port region is already in use.\n");
return -EBUSY;
}
/* just to call snd_sbdsp_command/reset/get_byte() */
@@ -116,7 +115,7 @@ static int jazz16_detect_board(unsigned long port,
err = snd_sbdsp_reset(&chip);
if (err < 0)
for (val = 0; val < 4; val++) {
- err = jazz16_configure_ports(port, mpu_port, val);
+ err = jazz16_configure_ports(card, port, mpu_port, val);
if (err < 0)
break;
@@ -146,8 +145,8 @@ static int jazz16_detect_board(unsigned long port,
}
snd_sbdsp_get_byte(&chip);
err = snd_sbdsp_get_byte(&chip);
- snd_printd("Media Vision Jazz16 board detected: rev 0x%x, model 0x%x\n",
- val, err);
+ dev_dbg(card->dev, "Media Vision Jazz16 board detected: rev 0x%x, model 0x%x\n",
+ val, err);
err = 0;
@@ -158,9 +157,9 @@ err_unmap:
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 ||
@@ -188,31 +187,31 @@ static int snd_jazz16_match(struct device *devptr, unsigned int dev)
if (!enable[dev])
return 0;
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "please specify port\n");
+ dev_err(devptr, "please specify port\n");
return 0;
} else if (port[dev] == 0x200 || (port[dev] & ~0x270)) {
- snd_printk(KERN_ERR "incorrect port specified\n");
+ dev_err(devptr, "incorrect port specified\n");
return 0;
}
if (dma8[dev] != SNDRV_AUTO_DMA &&
dma8[dev] != 1 && dma8[dev] != 3) {
- snd_printk(KERN_ERR "dma8 must be 1 or 3\n");
+ dev_err(devptr, "dma8 must be 1 or 3\n");
return 0;
}
if (dma16[dev] != SNDRV_AUTO_DMA &&
dma16[dev] != 5 && dma16[dev] != 7) {
- snd_printk(KERN_ERR "dma16 must be 5 or 7\n");
+ dev_err(devptr, "dma16 must be 5 or 7\n");
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
(mpu_port[dev] & ~0x030) != 0x300) {
- snd_printk(KERN_ERR "incorrect mpu_port specified\n");
+ dev_err(devptr, "incorrect mpu_port specified\n");
return 0;
}
if (mpu_irq[dev] != SNDRV_AUTO_DMA &&
mpu_irq[dev] != 2 && mpu_irq[dev] != 3 &&
mpu_irq[dev] != 5 && mpu_irq[dev] != 7) {
- snd_printk(KERN_ERR "mpu_irq must be 2, 3, 5 or 7\n");
+ dev_err(devptr, "mpu_irq must be 2, 3, 5 or 7\n");
return 0;
}
return 1;
@@ -224,13 +223,13 @@ static int snd_jazz16_probe(struct device *devptr, unsigned int dev)
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_new(devptr, 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;
@@ -240,37 +239,34 @@ static int snd_jazz16_probe(struct device *devptr, unsigned int dev)
if (xirq == SNDRV_AUTO_IRQ) {
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;
+ dev_err(devptr, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
xdma8 = dma8[dev];
if (xdma8 == SNDRV_AUTO_DMA) {
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;
+ dev_err(devptr, "unable to find a free DMA8\n");
+ return -EBUSY;
}
}
xdma16 = dma16[dev];
if (xdma16 == SNDRV_AUTO_DMA) {
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;
+ dev_err(devptr, "unable to find a free DMA16\n");
+ return -EBUSY;
}
}
xmpu_port = mpu_port[dev];
if (xmpu_port == SNDRV_AUTO_PORT)
xmpu_port = 0;
- err = jazz16_detect_board(port[dev], xmpu_port);
+ err = jazz16_detect_board(card, port[dev], xmpu_port);
if (err < 0) {
- printk(KERN_ERR "Media Vision Jazz16 board not detected\n");
- goto err_free;
+ dev_err(devptr, "Media Vision Jazz16 board not detected\n");
+ return err;
}
err = snd_sbdsp_create(card, port[dev], irq[dev],
jazz16_interrupt,
@@ -278,41 +274,41 @@ static int 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)
xmpu_irq = 0;
err = jazz16_configure_board(chip, xmpu_irq);
if (err < 0) {
- printk(KERN_ERR "Media Vision Jazz16 configuration failed\n");
- goto err_free;
+ dev_err(devptr, "Media Vision Jazz16 configuration failed\n");
+ return err;
}
jazz16->chip = chip;
- strcpy(card->driver, "jazz16");
- strcpy(card->shortname, "Media Vision Jazz16");
+ strscpy(card->driver, "jazz16");
+ strscpy(card->shortname, "Media Vision Jazz16");
sprintf(card->longname,
"Media Vision Jazz16 at 0x%lx, irq %d, dma8 %d, dma16 %d",
port[dev], xirq, xdma8, xdma16);
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);
if (err < 0)
- snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- chip->port, chip->port + 2);
+ dev_warn(devptr, "no OPL device at 0x%lx-0x%lx\n",
+ chip->port, chip->port + 2);
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)
@@ -323,28 +319,16 @@ static int snd_jazz16_probe(struct device *devptr, unsigned int dev)
mpu_port[dev], 0,
mpu_irq[dev],
NULL) < 0)
- snd_printk(KERN_ERR "no MPU-401 device at 0x%lx\n",
- mpu_port[dev]);
+ dev_err(devptr, "no MPU-401 device at 0x%lx\n",
+ mpu_port[dev]);
}
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 snd_jazz16_remove(struct device *devptr, unsigned int dev)
-{
- struct snd_card *card = dev_get_drvdata(devptr);
-
- snd_card_free(card);
- return 0;
}
#ifdef CONFIG_PM
@@ -356,7 +340,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;
}
@@ -377,7 +360,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 = snd_jazz16_remove,
#ifdef CONFIG_PM
.suspend = snd_jazz16_suspend,
.resume = snd_jazz16_resume,
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 8f9ebeb998f6..208d1942a015 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>
@@ -25,6 +10,7 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/module.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/sb16_csp.h>
@@ -36,26 +22,12 @@
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#ifdef SNDRV_SBAWE
-#define PFX "sbawe: "
-#else
-#define PFX "sb16: "
-#endif
-
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
@@ -269,7 +241,7 @@ static int snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -278,10 +250,10 @@ static int snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
dma8[dev] = pnp_dma(pdev, 0);
dma16[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
- port[dev], mpu_port[dev], fm_port[dev]);
- snd_printdd("pnp SB16: dma1=%i, dma2=%i, irq=%i\n",
- dma8[dev], dma16[dev], irq[dev]);
+ dev_dbg(&pdev->dev, "pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
+ port[dev], mpu_port[dev], fm_port[dev]);
+ dev_dbg(&pdev->dev, "pnp SB16: dma1=%i, dma2=%i, irq=%i\n",
+ dma8[dev], dma16[dev], irq[dev]);
#ifdef SNDRV_SBAWE_EMU8000
/* WaveTable initialization */
pdev = acard->devwt;
@@ -291,13 +263,13 @@ static int snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
goto __wt_error;
}
awe_port[dev] = pnp_port_start(pdev, 0);
- snd_printdd("pnp SB16: wavetable port=0x%llx\n",
- (unsigned long long)pnp_port_start(pdev, 0));
+ dev_dbg(&pdev->dev, "pnp SB16: wavetable port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0));
} else {
__wt_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "WaveTable pnp configure failure\n");
+ dev_err(&pdev->dev, "WaveTable pnp configure failure\n");
}
acard->devwt = NULL;
awe_port[dev] = -1;
@@ -308,15 +280,6 @@ __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
@@ -329,11 +292,10 @@ static int snd_sb16_card_new(struct device *devptr, int dev,
struct snd_card *card;
int err;
- err = snd_card_new(devptr, 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;
}
@@ -348,41 +310,39 @@ static int snd_sb16_probe(struct snd_card *card, int dev)
#ifdef CONFIG_SND_SB16_CSP
struct snd_hwdep *xcsp = NULL;
#endif
- unsigned long flags;
int err;
xirq = irq[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;
if (chip->hardware != SB_HW_16) {
- snd_printk(KERN_ERR PFX "SB 16 chip was not detected at 0x%lx\n", port[dev]);
+ dev_err(card->dev, "SB 16 chip was not detected at 0x%lx\n", port[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)) < 0)
+ err = snd_sb16dsp_pcm(chip, 0);
+ if (err < 0)
return err;
- strcpy(card->driver,
+ strscpy(card->driver,
#ifdef SNDRV_SBAWE_EMU8000
awe_port[dev] > 0 ? "SB AWE" :
#endif
"SB16");
- strcpy(card->shortname, chip->name);
+ strscpy(card->shortname, chip->name);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma ",
chip->name,
chip->port,
@@ -394,10 +354,11 @@ static int 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,
- MPU401_INFO_IRQ_HOOK, -1,
- &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;
}
@@ -412,20 +373,22 @@ static int snd_sb16_probe(struct snd_card *card, int dev)
OPL3_HW_OPL3,
acard->fm_res != NULL || fm_port[dev] == port[dev],
&opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port[dev], fm_port[dev] + 2);
} else {
#ifdef SNDRV_SBAWE_EMU8000
int seqdev = awe_port[dev] > 0 ? 2 : 1;
#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
@@ -436,15 +399,20 @@ static int snd_sb16_probe(struct snd_card *card, int dev)
chip->csp = xcsp->private_data;
chip->hardware = SB_HW_16CSP;
} else {
- snd_printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1);
+ dev_info(card->dev,
+ "warning - CSP chip not detected on soundcard #%i\n",
+ dev + 1);
}
}
#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) {
- snd_printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]);
+ err = snd_emu8000_new(card, 1, awe_port[dev],
+ seq_ports[dev], NULL);
+ if (err < 0) {
+ dev_err(card->dev,
+ "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n",
+ awe_port[dev]);
return err;
}
@@ -452,13 +420,14 @@ static int snd_sb16_probe(struct snd_card *card, int dev)
#endif
/* setup Mic AGC */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
- (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
- (mic_agc[dev] ? 0x00 : 0x01));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
+ (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
+ (mic_agc[dev] ? 0x00 : 0x01));
+ }
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
return err;
return 0;
@@ -471,7 +440,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;
}
@@ -502,16 +470,16 @@ static int 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
- 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;
}
@@ -525,25 +493,28 @@ static int snd_sb16_isa_match(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) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n");
+ dma8[dev] = snd_legacy_find_free_dma(possible_dmas8);
+ if (dma8[dev] < 0) {
+ dev_err(pdev, "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) {
- snd_printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n");
+ dma16[dev] = snd_legacy_find_free_dma(possible_dmas16);
+ if (dma16[dev] < 0) {
+ dev_err(pdev, "unable to find a free 16-bit DMA\n");
return -EBUSY;
}
}
@@ -551,7 +522,7 @@ static int 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];
@@ -563,12 +534,6 @@ static int snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int snd_sb16_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_sb16_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -591,7 +556,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 = snd_sb16_isa_remove,
#ifdef CONFIG_PM
.suspend = snd_sb16_isa_suspend,
.resume = snd_sb16_isa_resume,
@@ -616,11 +580,12 @@ static int snd_sb16_pnp_detect(struct pnp_card_link *pcard,
res = snd_sb16_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- 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_card_sb16_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0)
+ return res;
+ res = snd_sb16_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
@@ -629,12 +594,6 @@ static int snd_sb16_pnp_detect(struct pnp_card_link *pcard,
return -ENODEV;
}
-static void 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)
{
@@ -655,7 +614,6 @@ static struct pnp_card_driver sb16_pnpc_driver = {
#endif
.id_table = snd_sb16_pnpids,
.probe = snd_sb16_pnp_detect,
- .remove = 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 fa5780bb0c68..9ad71a9fc18d 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,27 +7,13 @@
*
* 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 <linux/string_choices.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -60,18 +47,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;
};
/*
@@ -93,7 +80,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);
@@ -116,7 +103,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;
@@ -126,10 +113,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;
}
@@ -276,14 +265,10 @@ static int snd_sb_csp_release(struct snd_hwdep * hw, struct file *file)
*/
static int snd_sb_csp_use(struct snd_sb_csp * p)
{
- mutex_lock(&p->access_mutex);
- if (p->used) {
- mutex_unlock(&p->access_mutex);
+ guard(mutex)(&p->access_mutex);
+ if (p->used)
return -EAGAIN;
- }
p->used++;
- mutex_unlock(&p->access_mutex);
-
return 0;
}
@@ -293,10 +278,8 @@ static int snd_sb_csp_use(struct snd_sb_csp * p)
*/
static int snd_sb_csp_unuse(struct snd_sb_csp * p)
{
- mutex_lock(&p->access_mutex);
+ guard(mutex)(&p->access_mutex);
p->used--;
- mutex_unlock(&p->access_mutex);
-
return 0;
}
@@ -308,16 +291,16 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
struct snd_sb_csp_microcode __user * mcode)
{
struct snd_sb_csp_mc_header info;
+ struct device *dev = p->chip->card->dev;
unsigned char __user *data_ptr;
unsigned char __user *data_end;
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;
int err;
if (copy_from_user(&info, mcode, sizeof(info)))
@@ -326,9 +309,9 @@ 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__);
+ dev_dbg(dev, "%s: Invalid RIFF header\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(file_h);
@@ -336,8 +319,8 @@ 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) {
- snd_printd("%s: Invalid RIFF file type\n", __func__);
+ if (le32_to_cpu(item_type) != CSP__HEADER) {
+ dev_dbg(dev, "%s: Invalid RIFF file type\n", __func__);
return -EINVAL;
}
data_ptr += sizeof (item_type);
@@ -346,12 +329,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;
@@ -378,7 +361,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),
@@ -391,8 +374,8 @@ 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) {
- snd_printd("%s: Missing 'main' microcode\n", __func__);
+ if (le32_to_cpu(code_h.name) != MAIN_HEADER) {
+ dev_dbg(dev, "%s: Missing 'main' microcode\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(code_h);
@@ -402,7 +385,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)) {
@@ -435,9 +418,9 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
default: /* other codecs are unsupported */
p->acc_format = p->acc_width = p->acc_rates = 0;
p->mode = 0;
- snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
- __func__,
- le16_to_cpu(funcdesc_h.VOC_type));
+ dev_dbg(dev, "%s: Unsupported CSP codec type: 0x%04x\n",
+ __func__,
+ le16_to_cpu(funcdesc_h.VOC_type));
return -EINVAL;
}
p->acc_channels = le16_to_cpu(funcdesc_h.flags_stereo_mono);
@@ -445,17 +428,16 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
p->acc_rates = le16_to_cpu(funcdesc_h.flags_rates);
/* Decouple CSP from IRQ and DMAREQ lines */
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
set_mode_register(p->chip, 0xfc);
set_mode_register(p->chip, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
/* finished loading successfully */
p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */
return 0;
}
}
- snd_printd("%s: Function #%d not found\n", __func__, info.func_req);
+ dev_dbg(dev, "%s: Function #%d not found\n", __func__, info.func_req);
return -EINVAL;
}
@@ -558,10 +540,8 @@ static int set_mode_register(struct snd_sb *chip, unsigned char mode)
static int csp_detect(struct snd_sb *chip, int *version)
{
unsigned char csp_test1, csp_test2;
- unsigned long flags;
- int result = -ENODEV;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
set_codec_parameter(chip, 0x00, 0x00);
set_mode_register(chip, 0xfc); /* 0xfc = ?? */
@@ -570,23 +550,21 @@ static int csp_detect(struct snd_sb *chip, int *version)
set_register(chip, 0x83, ~csp_test1);
csp_test2 = read_register(chip, 0x83);
if (csp_test2 != (csp_test1 ^ 0xff))
- goto __fail;
+ return -ENODEV;
set_register(chip, 0x83, csp_test1);
csp_test2 = read_register(chip, 0x83);
if (csp_test2 != csp_test1)
- goto __fail;
+ return -ENODEV;
set_mode_register(chip, 0x00); /* 0x00 = ? */
*version = get_version(chip);
snd_sbdsp_reset(chip); /* reset DSP after getversion! */
if (*version >= 0x10 && *version <= 0x1f)
- result = 0; /* valid version id */
+ return 0; /* valid version id */
- __fail:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ return -ENODEV;
}
/*
@@ -609,7 +587,9 @@ static int get_version(struct snd_sb *chip)
static int snd_sb_csp_check_version(struct snd_sb_csp * p)
{
if (p->version < 0x10 || p->version > 0x1f) {
- snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version);
+ dev_dbg(p->chip->card->dev,
+ "%s: Invalid CSP version: 0x%x\n",
+ __func__, p->version);
return 1;
}
return 0;
@@ -622,14 +602,12 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
{
int status, i;
int err;
- int result = -EIO;
- unsigned long flags;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
snd_sbdsp_command(p->chip, 0x01); /* CSP download command */
if (snd_sbdsp_get_byte(p->chip)) {
- snd_printd("%s: Download command failed\n", __func__);
- goto __fail;
+ dev_dbg(p->chip->card->dev, "%s: Download command failed\n", __func__);
+ return -EIO;
}
/* Send CSP low byte (size - 1) */
snd_sbdsp_command(p->chip, (unsigned char)(size - 1));
@@ -639,10 +617,10 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
/* load from kernel space */
while (size--) {
if (!snd_sbdsp_command(p->chip, *buf++))
- goto __fail;
+ return -EIO;
}
if (snd_sbdsp_get_byte(p->chip))
- goto __fail;
+ return -EIO;
if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) {
i = 0;
@@ -655,8 +633,10 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
udelay (10);
}
if (status != 0x55) {
- snd_printd("%s: Microcode initialization failed\n", __func__);
- goto __fail;
+ dev_dbg(p->chip->card->dev,
+ "%s: Microcode initialization failed\n",
+ __func__);
+ return -EIO;
}
} else {
/*
@@ -664,24 +644,21 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
* Start CSP chip if no 16bit DMA channel is set - some kind
* of autorun or perhaps a bugfix?
*/
- spin_lock(&p->chip->mixer_lock);
- status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
- spin_unlock(&p->chip->mixer_lock);
+ scoped_guard(spinlock, &p->chip->mixer_lock) {
+ status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
+ }
if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) {
err = (set_codec_parameter(p->chip, 0xaa, 0x00) ||
set_codec_parameter(p->chip, 0xff, 0x00));
snd_sbdsp_reset(p->chip); /* really! */
if (err)
- goto __fail;
+ return -EIO;
set_mode_register(p->chip, 0xc0); /* c0 = STOP */
set_mode_register(p->chip, 0x70); /* 70 = RUN */
}
}
- result = 0;
- __fail:
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
- return result;
+ return 0;
}
static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags)
@@ -726,9 +703,8 @@ 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;
/* if CSP is running or manually loaded then exit */
@@ -736,7 +712,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) {
@@ -769,10 +745,9 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
default:
/* Decouple CSP from IRQ and DMAREQ lines */
if (p->running & SNDRV_SB_CSP_ST_AUTO) {
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
set_mode_register(p->chip, 0xfc);
set_mode_register(p->chip, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
p->running = 0; /* clear autoloaded flag */
}
return -EINVAL;
@@ -800,76 +775,77 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
*/
static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channels)
{
+ struct device *dev = p->chip->card->dev;
unsigned char s_type; /* sample type */
unsigned char mixL, mixR;
int result = -EIO;
- unsigned long flags;
if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
- snd_printd("%s: Microcode not loaded\n", __func__);
+ dev_dbg(dev, "%s: Microcode not loaded\n", __func__);
return -ENXIO;
}
if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
- snd_printd("%s: CSP already running\n", __func__);
+ dev_dbg(dev, "%s: CSP already running\n", __func__);
return -EBUSY;
}
if (!(sample_width & p->acc_width)) {
- snd_printd("%s: Unsupported PCM sample width\n", __func__);
+ dev_dbg(dev, "%s: Unsupported PCM sample width\n", __func__);
return -EINVAL;
}
if (!(channels & p->acc_channels)) {
- snd_printd("%s: Invalid number of channels\n", __func__);
+ dev_dbg(dev, "%s: Invalid number of channels\n", __func__);
return -EINVAL;
}
/* Mute PCM volume */
- spin_lock_irqsave(&p->chip->mixer_lock, flags);
- mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
- 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_lock(&p->chip->reg_lock);
- set_mode_register(p->chip, 0xc0); /* c0 = STOP */
- set_mode_register(p->chip, 0x70); /* 70 = RUN */
-
- s_type = 0x00;
- if (channels == SNDRV_SB_CSP_MONO)
- s_type = 0x11; /* 000n 000n (n = 1 if mono) */
- if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
- s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
-
- if (set_codec_parameter(p->chip, 0x81, s_type)) {
- snd_printd("%s: Set sample type command failed\n", __func__);
- goto __fail;
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ 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);
}
- if (set_codec_parameter(p->chip, 0x80, 0x00)) {
- snd_printd("%s: Codec start command failed\n", __func__);
- goto __fail;
- }
- p->run_width = sample_width;
- p->run_channels = channels;
- p->running |= SNDRV_SB_CSP_ST_RUNNING;
+ scoped_guard(spinlock, &p->chip->reg_lock) {
+ set_mode_register(p->chip, 0xc0); /* c0 = STOP */
+ set_mode_register(p->chip, 0x70); /* 70 = RUN */
- if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
- set_codec_parameter(p->chip, 0xe0, 0x01);
- /* enable QSound decoder */
- set_codec_parameter(p->chip, 0x00, 0xff);
- set_codec_parameter(p->chip, 0x01, 0xff);
- p->running |= SNDRV_SB_CSP_ST_QSOUND;
- /* set QSound startup value */
- snd_sb_csp_qsound_transfer(p);
- }
- result = 0;
+ s_type = 0x00;
+ if (channels == SNDRV_SB_CSP_MONO)
+ s_type = 0x11; /* 000n 000n (n = 1 if mono) */
+ if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
+ s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
- __fail:
- spin_unlock(&p->chip->reg_lock);
+ if (set_codec_parameter(p->chip, 0x81, s_type)) {
+ dev_dbg(dev, "%s: Set sample type command failed\n", __func__);
+ break;
+ }
+ if (set_codec_parameter(p->chip, 0x80, 0x00)) {
+ dev_dbg(dev, "%s: Codec start command failed\n", __func__);
+ break;
+ }
+ p->run_width = sample_width;
+ p->run_channels = channels;
+
+ p->running |= SNDRV_SB_CSP_ST_RUNNING;
+
+ if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* enable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0xff);
+ set_codec_parameter(p->chip, 0x01, 0xff);
+ p->running |= SNDRV_SB_CSP_ST_QSOUND;
+ /* set QSound startup value */
+ snd_sb_csp_qsound_transfer(p);
+ }
+ result = 0;
+ }
/* restore PCM volume */
- 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);
+ if (result < 0) {
+ guard(spinlock_irqsave)(&p->chip->mixer_lock);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ }
return result;
}
@@ -881,34 +857,35 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
{
int result;
unsigned char mixL, mixR;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
return 0;
/* Mute PCM volume */
- spin_lock_irqsave(&p->chip->mixer_lock, flags);
- mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
- 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);
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ 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_lock(&p->chip->reg_lock);
- if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
- set_codec_parameter(p->chip, 0xe0, 0x01);
- /* disable QSound decoder */
- set_codec_parameter(p->chip, 0x00, 0x00);
- set_codec_parameter(p->chip, 0x01, 0x00);
+ scoped_guard(spinlock, &p->chip->reg_lock) {
+ if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* disable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0x00);
+ set_codec_parameter(p->chip, 0x01, 0x00);
- p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+ p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+ }
+ result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */
}
- result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */
- spin_unlock(&p->chip->reg_lock);
/* restore PCM volume */
- 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);
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ }
if (!(result))
p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING);
@@ -921,14 +898,13 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
static int snd_sb_csp_pause(struct snd_sb_csp * p)
{
int result;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
return -EBUSY;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
- result = set_codec_parameter(p->chip, 0x80, 0xff);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &p->chip->reg_lock) {
+ result = set_codec_parameter(p->chip, 0x80, 0xff);
+ }
if (!(result))
p->running |= SNDRV_SB_CSP_ST_PAUSED;
@@ -941,14 +917,13 @@ static int snd_sb_csp_pause(struct snd_sb_csp * p)
static int snd_sb_csp_restart(struct snd_sb_csp * p)
{
int result;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_PAUSED))
return -EBUSY;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
- result = set_codec_parameter(p->chip, 0x80, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &p->chip->reg_lock) {
+ result = set_codec_parameter(p->chip, 0x80, 0x00);
+ }
if (!(result))
p->running &= ~SNDRV_SB_CSP_ST_PAUSED;
@@ -974,15 +949,13 @@ static int snd_sb_qsound_switch_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_sb_qsound_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval;
nval = ucontrol->value.integer.value[0] & 0x01;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
change = p->q_enabled != nval;
p->q_enabled = nval;
- spin_unlock_irqrestore(&p->q_lock, flags);
return change;
}
@@ -998,19 +971,16 @@ static int snd_sb_qsound_space_info(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_sb_qsound_space_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
ucontrol->value.integer.value[0] = p->qpos_left;
ucontrol->value.integer.value[1] = p->qpos_right;
- spin_unlock_irqrestore(&p->q_lock, flags);
return 0;
}
static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval1, nval2;
@@ -1020,12 +990,11 @@ static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl
nval2 = ucontrol->value.integer.value[1];
if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
change = p->qpos_left != nval1 || p->qpos_right != nval2;
p->qpos_left = nval1;
p->qpos_right = nval2;
p->qpos_changed = change;
- spin_unlock_irqrestore(&p->q_lock, flags);
return change;
}
@@ -1048,6 +1017,7 @@ static const 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))
@@ -1059,10 +1029,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;
@@ -1074,24 +1050,20 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)
static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
{
struct snd_card *card;
- unsigned long flags;
if (snd_BUG_ON(!p))
return;
card = p->chip->card;
- down_write(&card->controls_rwsem);
- if (p->qsound_switch)
- snd_ctl_remove(card, p->qsound_switch);
- if (p->qsound_space)
- snd_ctl_remove(card, p->qsound_space);
- up_write(&card->controls_rwsem);
+ snd_ctl_remove(card, p->qsound_switch);
+ p->qsound_switch = NULL;
+ snd_ctl_remove(card, p->qsound_space);
+ p->qsound_space = NULL;
/* cancel pending transfer of QSound parameters */
- spin_lock_irqsave (&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
p->qpos_changed = 0;
- spin_unlock_irqrestore (&p->q_lock, flags);
}
/*
@@ -1102,7 +1074,7 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
{
int err = -ENXIO;
- spin_lock(&p->q_lock);
+ guard(spinlock)(&p->q_lock);
if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
set_codec_parameter(p->chip, 0xe0, 0x01);
/* left channel */
@@ -1114,7 +1086,6 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
err = 0;
}
p->qpos_changed = 0;
- spin_unlock(&p->q_lock);
return err;
}
@@ -1126,10 +1097,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;
}
@@ -1155,8 +1125,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
}
if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
- snd_iprintf(buffer, "QSound decoder %sabled\n",
- p->q_enabled ? "en" : "dis");
+ snd_iprintf(buffer, "QSound decoder %s\n",
+ str_enabled_disabled(p->q_enabled));
} else {
snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
p->acc_format,
@@ -1185,19 +1155,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 3e39ba220c39..4d64db4f5852 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,22 +16,6 @@
* 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 <linux/io.h>
@@ -49,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)
{
@@ -58,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 */
@@ -66,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) {
@@ -106,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 */
@@ -142,9 +130,8 @@ static void snd_sb16_csp_update(struct snd_sb *chip)
struct snd_sb_csp *csp = chip->csp;
if (csp->qpos_changed) {
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
csp->ops.csp_qsound_transfer (csp);
- spin_unlock(&chip->reg_lock);
}
}
}
@@ -225,9 +212,7 @@ static void snd_sb16_setup_rate(struct snd_sb *chip,
unsigned short rate,
int channel)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16))
snd_sb_ack_16bit(chip);
else
@@ -241,24 +226,10 @@ static void snd_sb16_setup_rate(struct snd_sb *chip,
snd_sbdsp_command(chip, rate >> 8);
snd_sbdsp_command(chip, rate & 0xff);
}
- 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;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned char format;
@@ -277,7 +248,7 @@ static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & SB_MODE_PLAYBACK_16) {
count >>= 1;
count--;
@@ -294,7 +265,6 @@ static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -302,9 +272,8 @@ static int snd_sb16_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -320,15 +289,13 @@ static int snd_sb16_playback_trigger(struct snd_pcm_substream *substream,
chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return result;
+ return 0;
}
static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned char format;
@@ -346,7 +313,7 @@ static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & SB_MODE_CAPTURE_16) {
count >>= 1;
count--;
@@ -363,7 +330,6 @@ static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -371,9 +337,8 @@ static int snd_sb16_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -389,10 +354,9 @@ static int snd_sb16_capture_trigger(struct snd_pcm_substream *substream,
chip->mode &= ~SB_RATE_LOCK_CAPTURE;
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return result;
+ return 0;
}
irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
@@ -401,9 +365,9 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
unsigned char status;
int ok;
- spin_lock(&chip->mixer_lock);
- status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ }
if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback)
chip->rmidi_callback(irq, chip->rmidi->private_data);
if (status & SB_IRQTYPE_8BIT) {
@@ -417,11 +381,11 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
snd_pcm_period_elapsed(chip->capture_substream);
ok++;
}
- spin_lock(&chip->reg_lock);
- if (!ok)
- snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
- snd_sb_ack_8bit(chip);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ snd_sb_ack_8bit(chip);
+ }
}
if (status & SB_IRQTYPE_16BIT) {
ok = 0;
@@ -434,11 +398,11 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
snd_pcm_period_elapsed(chip->capture_substream);
ok++;
}
- spin_lock(&chip->reg_lock);
- if (!ok)
- snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
- snd_sb_ack_16bit(chip);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+ snd_sb_ack_16bit(chip);
+ }
}
return IRQ_HANDLED;
}
@@ -515,15 +479,12 @@ static const struct snd_pcm_hardware snd_sb16_capture =
static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->mode & SB_MODE_PLAYBACK) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
+ if (chip->mode & SB_MODE_PLAYBACK)
return -EAGAIN;
- }
runtime->hw = snd_sb16_playback;
/* skip if 16 bit DMA was reserved for capture */
@@ -557,7 +518,6 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
runtime->hw.period_bytes_max = 64 * 1024;
goto __open_ok;
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
return -EAGAIN;
__open_ok:
@@ -571,34 +531,28 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->playback_substream = substream;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_playback_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
snd_sb16_csp_playback_close(chip);
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->playback_substream = NULL;
chip->mode &= ~SB_MODE_PLAYBACK;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->mode & SB_MODE_CAPTURE) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
+ if (chip->mode & SB_MODE_CAPTURE)
return -EAGAIN;
- }
runtime->hw = snd_sb16_capture;
/* skip if 16 bit DMA was reserved for playback */
@@ -632,7 +586,6 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
runtime->hw.period_bytes_max = 64 * 1024;
goto __open_ok;
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
return -EAGAIN;
__open_ok:
@@ -646,20 +599,17 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->capture_substream = substream;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_capture_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
snd_sb16_csp_capture_close(chip);
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->capture_substream = NULL;
chip->mode &= ~SB_MODE_CAPTURE;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
@@ -712,28 +662,33 @@ static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_c
static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char nval, oval;
int change;
- if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+ if (chip->mode & (SB_MODE_PLAYBACK | SB_MODE_CAPTURE))
+ return -EBUSY;
+
+ 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);
- change = nval != oval;
- snd_sb16_set_dma_mode(chip, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ oval = snd_sb16_get_dma_mode(chip);
+ change = nval != oval;
+ snd_sb16_set_dma_mode(chip, nval);
+ }
+ if (change) {
+ snd_dma_disable(chip->dma8);
+ snd_dma_disable(chip->dma16);
+ }
return change;
}
@@ -751,15 +706,13 @@ static const struct snd_kcontrol_new snd_sb16_dma_control = {
int snd_sb16dsp_configure(struct snd_sb * chip)
{
- unsigned long flags;
unsigned char irqreg = 0, dmareg = 0, mpureg;
unsigned char realirq, realdma, realmpureg;
/* note: mpu register should be present only on SB16 Vibra soundcards */
- // printk(KERN_DEBUG "codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16);
- spin_lock_irqsave(&chip->mixer_lock, flags);
- mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
+ }
switch (chip->irq) {
case 2:
case 9:
@@ -817,22 +770,27 @@ int snd_sb16dsp_configure(struct snd_sb * chip)
default:
mpureg |= 0x02; /* disable MPU */
}
- spin_lock_irqsave(&chip->mixer_lock, flags);
-
- snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
- realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
- snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
- realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
+ realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
- snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
- realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
+ realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
+ realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+ }
if ((~realirq) & irqreg || (~realdma) & dmareg) {
- snd_printk(KERN_ERR "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port);
- snd_printk(KERN_ERR "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg);
- snd_printk(KERN_ERR "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n",
+ chip->port);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n",
+ chip->port, realirq, realdma, realmpureg);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n",
+ chip->port, irqreg, dmareg, mpureg);
return -ENODEV;
}
return 0;
@@ -841,9 +799,6 @@ int snd_sb16dsp_configure(struct snd_sb * chip)
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,
@@ -852,9 +807,6 @@ static const struct snd_pcm_ops snd_sb16_playback_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,
@@ -866,7 +818,8 @@ int snd_sb16dsp_pcm(struct snd_sb *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -881,9 +834,8 @@ int snd_sb16dsp_pcm(struct snd_sb *chip, int device)
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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, 128*1024);
return 0;
}
@@ -897,19 +849,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 d77dcba276b5..6d5131265913 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -1,22 +1,7 @@
+// 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>
@@ -24,6 +9,7 @@
#include <linux/isa.h>
#include <linux/ioport.h>
#include <linux/module.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/opl3.h>
@@ -32,7 +18,6 @@
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 */
@@ -70,15 +55,6 @@ 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 snd_sb8_match(struct device *pdev, unsigned int dev)
{
if (!enable[dev])
@@ -102,27 +78,29 @@ static int snd_sb8_probe(struct device *pdev, unsigned int dev)
struct snd_opl3 *opl3;
int err;
- err = snd_card_new(pdev, 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,74 +117,65 @@ static int 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;
if (chip->hardware >= SB_HW_16) {
if (chip->hardware == SB_HW_ALS100)
- snd_printk(KERN_WARNING "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
- port[dev]);
+ dev_warn(pdev, "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
+ port[dev]);
else
- snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
- port[dev]);
- err = -ENODEV;
- goto _err;
+ dev_warn(pdev, "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
+ port[dev]);
+ return -ENODEV;
}
- if ((err = snd_sb8dsp_pcm(chip, 0)) < 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) {
- snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
- }
+ err = snd_opl3_create(card, chip->port + 8, 0,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0)
+ dev_warn(pdev, "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) {
- snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
+ err = snd_opl3_create(card, chip->port, chip->port + 2,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0) {
+ dev_warn(pdev, "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)) < 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);
+ strscpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
+ strscpy(card->shortname, chip->name);
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
chip->name,
chip->port,
irq[dev], dma8[dev]);
- 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 snd_sb8_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- return 0;
}
#ifdef CONFIG_PM
@@ -218,7 +187,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;
}
@@ -241,7 +209,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 = snd_sb8_remove,
#ifdef CONFIG_PM
.suspend = snd_sb8_suspend,
.resume = snd_sb8_resume,
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index d45df5c54423..a4b5725255cf 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>
@@ -103,7 +89,6 @@ static int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params,
static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int mixreg, rate, size, count;
@@ -130,13 +115,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;
@@ -156,48 +141,48 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
}
size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
- if (chip->hardware == SB_HW_JAZZ16)
- snd_sbdsp_command(chip, format);
- else if (stereo) {
- /* set playback stereo mode */
- spin_lock(&chip->mixer_lock);
- mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
- snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
- spin_unlock(&chip->mixer_lock);
-
- /* Soundblaster hardware programming reference guide, 3-23 */
- snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
- runtime->dma_area[0] = 0x80;
- snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
- /* force interrupt */
- snd_sbdsp_command(chip, SB_DSP_OUTPUT);
- snd_sbdsp_command(chip, 0);
- snd_sbdsp_command(chip, 0);
- }
- snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
- if (stereo) {
- snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
- spin_lock(&chip->mixer_lock);
- /* save output filter status and turn it off */
- mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
- snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
- spin_unlock(&chip->mixer_lock);
- /* just use force_mode16 for temporary storate... */
- chip->force_mode16 = mixreg;
- } else {
- snd_sbdsp_command(chip, 256 - runtime->rate_den);
- }
- if (chip->playback_format != SB_DSP_OUTPUT) {
- if (chip->mode & SB_MODE_PLAYBACK_16)
- count /= 2;
- count--;
- snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
- snd_sbdsp_command(chip, count & 0xff);
- snd_sbdsp_command(chip, count >> 8);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+ if (chip->hardware == SB_HW_JAZZ16)
+ snd_sbdsp_command(chip, format);
+ else if (stereo) {
+ /* set playback stereo mode */
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
+ snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
+ }
+
+ /* Soundblaster hardware programming reference guide, 3-23 */
+ snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
+ runtime->dma_area[0] = 0x80;
+ snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
+ /* force interrupt */
+ snd_sbdsp_command(chip, SB_DSP_OUTPUT);
+ snd_sbdsp_command(chip, 0);
+ snd_sbdsp_command(chip, 0);
+ }
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (stereo) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* save output filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
+ snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
+ }
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->playback_format != SB_DSP_OUTPUT) {
+ if (chip->mode & SB_MODE_PLAYBACK_16)
+ count /= 2;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_dma_program(dma, runtime->dma_addr,
size, DMA_MODE_WRITE | DMA_AUTOINIT);
return 0;
@@ -206,11 +191,10 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int count;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_sbdsp_command(chip, chip->playback_format);
@@ -225,35 +209,20 @@ static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_sbdsp_reset(chip);
if (runtime->channels > 1) {
- spin_lock(&chip->mixer_lock);
+ guard(spinlock)(&chip->mixer_lock);
/* restore output filter and set hardware to mono mode */
snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
- spin_unlock(&chip->mixer_lock);
}
} else {
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- 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;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int mixreg, rate, size, count;
@@ -287,7 +256,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;
@@ -307,34 +276,34 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
}
size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
- if (chip->hardware == SB_HW_JAZZ16)
- snd_sbdsp_command(chip, format);
- else if (stereo)
- snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
- snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
- if (stereo) {
- snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
- spin_lock(&chip->mixer_lock);
- /* save input filter status and turn it off */
- mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
- snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
- spin_unlock(&chip->mixer_lock);
- /* just use force_mode16 for temporary storate... */
- chip->force_mode16 = mixreg;
- } else {
- snd_sbdsp_command(chip, 256 - runtime->rate_den);
- }
- if (chip->capture_format != SB_DSP_INPUT) {
- if (chip->mode & SB_MODE_PLAYBACK_16)
- count /= 2;
- count--;
- snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
- snd_sbdsp_command(chip, count & 0xff);
- snd_sbdsp_command(chip, count >> 8);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+ if (chip->hardware == SB_HW_JAZZ16)
+ snd_sbdsp_command(chip, format);
+ else if (stereo)
+ snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (stereo) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* save input filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
+ }
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->capture_format != SB_DSP_INPUT) {
+ if (chip->mode & SB_MODE_PLAYBACK_16)
+ count /= 2;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_dma_program(dma, runtime->dma_addr,
size, DMA_MODE_READ | DMA_AUTOINIT);
return 0;
@@ -343,11 +312,10 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int count;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_sbdsp_command(chip, chip->capture_format);
@@ -363,9 +331,9 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
snd_sbdsp_reset(chip);
if (runtime->channels > 1) {
/* restore input filter status */
- spin_lock(&chip->mixer_lock);
- snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
+ }
/* set hardware to mono mode */
snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
}
@@ -374,24 +342,21 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
}
snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
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);
@@ -399,10 +364,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);
@@ -493,15 +457,12 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_PCM;
}
- chip->open |= SB_OPEN_PCM;
- spin_unlock_irqrestore(&chip->open_lock, flags);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
chip->playback_substream = substream;
runtime->hw = snd_sb8_playback;
@@ -535,6 +496,7 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
} else {
runtime->hw.rate_max = 15000;
}
+ break;
default:
break;
}
@@ -553,18 +515,16 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
static int snd_sb8_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->playback_substream = NULL;
chip->capture_substream = NULL;
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->open &= ~SB_OPEN_PCM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
chip->mode &= ~SB_MODE_PLAYBACK;
else
chip->mode &= ~SB_MODE_CAPTURE;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
@@ -575,9 +535,6 @@ static int snd_sb8_close(struct snd_pcm_substream *substream)
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,
@@ -586,9 +543,6 @@ static const struct snd_pcm_ops snd_sb8_playback_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,
@@ -601,7 +555,8 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device)
int err;
size_t max_prealloc = 64 * 1024;
- 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;
@@ -612,9 +567,8 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device)
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);
return 0;
}
@@ -624,19 +578,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 4affdcb78f72..1d41f2470697 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>
@@ -27,6 +14,7 @@
*/
#include <linux/io.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -47,7 +35,7 @@ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
return IRQ_NONE;
}
- spin_lock(&chip->midi_input_lock);
+ guard(spinlock)(&chip->midi_input_lock);
while (max-- > 0) {
if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
byte = inb(SBP(chip, READ));
@@ -56,108 +44,90 @@ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
}
}
}
- spin_unlock(&chip->midi_input_lock);
return IRQ_HANDLED;
}
static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
unsigned int valid_open_flags;
chip = substream->rmidi->private_data;
valid_open_flags = chip->hardware >= SB_HW_20
? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open & ~valid_open_flags) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
- }
- chip->open |= SB_OPEN_MIDI_INPUT;
- chip->midi_substream_input = substream;
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- if (chip->hardware >= SB_HW_20)
- snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open & ~valid_open_flags)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_MIDI_INPUT;
+ chip->midi_substream_input = substream;
+ if (chip->open & SB_OPEN_MIDI_OUTPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
return 0;
}
static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
unsigned int valid_open_flags;
chip = substream->rmidi->private_data;
valid_open_flags = chip->hardware >= SB_HW_20
? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open & ~valid_open_flags) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
- }
- chip->open |= SB_OPEN_MIDI_OUTPUT;
- chip->midi_substream_output = substream;
- if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- if (chip->hardware >= SB_HW_20)
- snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open & ~valid_open_flags)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_MIDI_OUTPUT;
+ chip->midi_substream_output = substream;
+ if (chip->open & SB_OPEN_MIDI_INPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
return 0;
}
static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
- chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
- chip->midi_substream_input = NULL;
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
+ chip->midi_substream_input = NULL;
+ if (chip->open & SB_OPEN_MIDI_OUTPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
return 0;
}
static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
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;
- if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ timer_delete_sync(&chip->midi_timer);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
+ chip->midi_substream_output = NULL;
+ if (chip->open & SB_OPEN_MIDI_INPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
return 0;
}
static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
if (up) {
if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
if (chip->hardware < SB_HW_20)
@@ -171,12 +141,10 @@ static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substrea
chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
}
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
}
static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
char byte;
int max = 32;
@@ -184,11 +152,10 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
/* how big is Tx FIFO? */
chip = substream->rmidi->private_data;
while (max-- > 0) {
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
- del_timer(&chip->midi_timer);
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ timer_delete(&chip->midi_timer);
break;
}
if (chip->hardware >= SB_HW_20) {
@@ -197,7 +164,6 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
;
if (timeout == 0) {
/* Tx FIFO full - try again later */
- spin_unlock_irqrestore(&chip->open_lock, flags);
break;
}
outb(byte, SBP(chip, WRITE));
@@ -206,40 +172,37 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
snd_sbdsp_command(chip, byte);
}
snd_rawmidi_transmit_ack(substream, 1);
- spin_unlock_irqrestore(&chip->open_lock, flags);
}
}
static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
{
- struct snd_sb *chip = from_timer(chip, t, midi_timer);
+ struct snd_sb *chip = timer_container_of(chip, t, midi_timer);
struct snd_rawmidi_substream *substream = chip->midi_substream_output;
- unsigned long flags;
- spin_lock_irqsave(&chip->open_lock, flags);
- mod_timer(&chip->midi_timer, 1 + jiffies);
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ mod_timer(&chip->midi_timer, 1 + jiffies);
+ }
snd_sb8dsp_midi_output_write(substream);
}
static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (up) {
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
- mod_timer(&chip->midi_timer, 1 + jiffies);
- chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
- }
- } else {
- if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
- chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (up) {
+ if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
+ mod_timer(&chip->midi_timer, 1 + jiffies);
+ chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
+ } else {
+ if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
+ chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
}
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
if (up)
snd_sb8dsp_midi_output_write(substream);
@@ -264,9 +227,10 @@ int snd_sb8dsp_midi(struct snd_sb *chip, int device)
struct snd_rawmidi *rmidi;
int err;
- 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");
+ strscpy(rmidi->name, "SB8 MIDI");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index 787a4ade4afd..f2848559e6da 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>
@@ -45,14 +31,14 @@ int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
{
int i;
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "command 0x%x\n", val);
+ dev_dbg(chip->card->dev, "command 0x%x\n", val);
#endif
for (i = BUSY_LOOPS; i; i--)
if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
outb(val, SBP(chip, COMMAND));
return 1;
}
- snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
+ dev_dbg(chip->card->dev, "%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
return 0;
}
@@ -64,12 +50,12 @@ int snd_sbdsp_get_byte(struct snd_sb *chip)
if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
val = inb(SBP(chip, READ));
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "get_byte 0x%x\n", val);
+ dev_dbg(chip->card->dev, "get_byte 0x%x\n", val);
#endif
return val;
}
}
- snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
+ dev_dbg(chip->card->dev, "%s [0x%lx]: timeout\n", __func__, chip->port);
return -ENODEV;
}
@@ -88,13 +74,14 @@ int snd_sbdsp_reset(struct snd_sb *chip)
else
break;
}
- snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
+ if (chip->card)
+ dev_dbg(chip->card->dev, "%s [0x%lx] failed...\n", __func__, chip->port);
return -ENODEV;
}
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;
@@ -107,27 +94,22 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
int version;
int major, minor;
char *str;
- unsigned long flags;
/*
* initialization sequence
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (snd_sbdsp_reset(chip) < 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
- }
- version = snd_sbdsp_version(chip);
- if (version < 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (snd_sbdsp_reset(chip) < 0)
+ return -ENODEV;
+ version = snd_sbdsp_version(chip);
+ if (version < 0)
+ return -ENODEV;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
major = version >> 8;
minor = version & 0xff;
- snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
- chip->port, major, minor);
+ dev_dbg(chip->card->dev, "SB [0x%lx]: DSP chip found, version = %i.%i\n",
+ chip->port, major, minor);
switch (chip->hardware) {
case SB_HW_AUTO:
@@ -154,8 +136,8 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
str = "16";
break;
default:
- snd_printk(KERN_INFO "SB [0x%lx]: unknown DSP chip version %i.%i\n",
- chip->port, major, minor);
+ dev_info(chip->card->dev, "SB [0x%lx]: unknown DSP chip version %i.%i\n",
+ chip->port, major, minor);
return -ENODEV;
}
break;
@@ -182,31 +164,6 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
return 0;
}
-static int snd_sbdsp_free(struct snd_sb *chip)
-{
- 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 +175,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 +191,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 : 0,
- "SoundBlaster", (void *) chip)) {
- snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
- snd_sbdsp_free(chip);
+ if (devm_request_irq(card->dev, irq, irq_handler,
+ (hardware == SB_HW_ALS4000 ||
+ hardware == SB_HW_CS5530) ?
+ IRQF_SHARED : 0,
+ "SoundBlaster", (void *) chip)) {
+ dev_err(card->dev, "sb: can't grab irq %d\n", irq);
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) {
- snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
- snd_sbdsp_free(chip);
+ chip->res_port = devm_request_region(card->dev, port, 16,
+ "SoundBlaster");
+ if (!chip->res_port) {
+ dev_err(card->dev, "sb: can't grab port 0x%lx\n", port);
return -EBUSY;
}
#ifdef CONFIG_ISA
- if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
- snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
- snd_sbdsp_free(chip);
+ if (dma8 >= 0 && snd_devm_request_dma(card->dev, dma8,
+ "SoundBlaster - 8bit")) {
+ dev_err(card->dev, "sb: can't grab DMA8 %d\n", dma8);
return -EBUSY;
}
chip->dma8 = dma8;
@@ -268,9 +223,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")) {
- snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
- snd_sbdsp_free(chip);
+ } else if (snd_devm_request_dma(card->dev, dma16,
+ "SoundBlaster - 16bit")) {
+ dev_err(card->dev, "sb: can't grab DMA16 %d\n", dma16);
return -EBUSY;
}
}
@@ -280,14 +235,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);
- return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_sbdsp_free(chip);
+ err = snd_sbdsp_probe(chip);
+ if (err < 0)
return err;
- }
*r_chip = chip;
return 0;
}
@@ -305,19 +255,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 add1d3f99609..95173b18cee3 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -1,26 +1,12 @@
+// 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 <linux/io.h>
#include <linux/delay.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -35,7 +21,7 @@ void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char dat
outb(data, SBP(chip, MIXER_DATA));
udelay(10);
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
+ dev_dbg(chip->card->dev, "mixer_write 0x%x 0x%x\n", reg, data);
#endif
}
@@ -48,7 +34,7 @@ unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
result = inb(SBP(chip, MIXER_DATA));
udelay(10);
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
+ dev_dbg(chip->card->dev, "mixer_read 0x%x 0x%x\n", reg, result);
#endif
return result;
}
@@ -71,15 +57,13 @@ static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 16) & 0xff;
int mask = (kcontrol->private_value >> 24) & 0xff;
unsigned char val;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -87,7 +71,6 @@ static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 16) & 0x07;
int mask = (kcontrol->private_value >> 24) & 0xff;
@@ -95,13 +78,12 @@ static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
unsigned char val, oval;
val = (ucontrol->value.integer.value[0] & mask) << shift;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, reg);
val = (oval & ~(mask << shift)) | val;
change = val != oval;
if (change)
snd_sbmixer_write(sb, reg, val);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -123,7 +105,6 @@ static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x07;
@@ -131,10 +112,9 @@ static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
unsigned char left, right;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = left;
ucontrol->value.integer.value[1] = right;
return 0;
@@ -143,7 +123,6 @@ static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x07;
@@ -154,7 +133,7 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
left = (ucontrol->value.integer.value[0] & mask) << left_shift;
right = (ucontrol->value.integer.value[1] & mask) << right_shift;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
if (left_reg == right_reg) {
oleft = snd_sbmixer_read(sb, left_reg);
left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
@@ -172,7 +151,6 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
snd_sbmixer_write(sb, right_reg, right);
}
}
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -192,12 +170,11 @@ static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
- oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &sb->mixer_lock) {
+ oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+ }
switch (oval & 0x07) {
case SB_DT019X_CAP_CD:
ucontrol->value.enumerated.item[0] = 0;
@@ -228,7 +205,6 @@ static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
@@ -253,12 +229,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl
default:
nval = SB_DT019X_CAP_MAIN;
}
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -280,12 +255,10 @@ static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
oval >>= 6;
if (oval > 2)
oval = 2;
@@ -298,13 +271,12 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
if (ucontrol->value.enumerated.item[0] > 2)
return -EINVAL;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
nval = (oval & ~(3 << 6))
@@ -312,7 +284,6 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -333,12 +304,10 @@ static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
switch ((oval >> 0x01) & 0x03) {
case SB_DSP_MIXS_CD:
ucontrol->value.enumerated.item[0] = 1;
@@ -356,7 +325,6 @@ static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
@@ -373,13 +341,12 @@ static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_el
nval = SB_DSP_MIXS_MIC;
}
nval <<= 1;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
nval |= oval & ~0x06;
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -399,17 +366,15 @@ static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd
static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg1 = kcontrol->private_value & 0xff;
int reg2 = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
int right_shift = (kcontrol->private_value >> 24) & 0x0f;
unsigned char val1, val2;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
val1 = snd_sbmixer_read(sb, reg1);
val2 = snd_sbmixer_read(sb, reg2);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
@@ -420,7 +385,6 @@ static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_
static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg1 = kcontrol->private_value & 0xff;
int reg2 = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
@@ -428,7 +392,7 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
int change;
unsigned char val1, val2, oval1, oval2;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval1 = snd_sbmixer_read(sb, reg1);
oval2 = snd_sbmixer_read(sb, reg2);
val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
@@ -442,7 +406,6 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
snd_sbmixer_write(sb, reg1, val1);
snd_sbmixer_write(sb, reg2, val2);
}
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -453,7 +416,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,
@@ -497,10 +460,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;
}
@@ -509,14 +473,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 },
};
@@ -524,7 +488,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",
@@ -544,7 +508,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 },
@@ -553,7 +517,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",
@@ -591,7 +555,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 },
@@ -607,7 +571,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",
@@ -637,7 +601,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 },
@@ -652,7 +616,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",
@@ -686,7 +650,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 },
@@ -704,26 +668,24 @@ 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)
{
- unsigned long flags;
struct snd_card *card = chip->card;
int idx, err;
/* mixer reset */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, 0x00, 0x00);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, 0x00, 0x00);
+ }
/* mute and zero volume channels */
for (idx = 0; idx < map_count; idx++) {
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
}
for (idx = 0; idx < controls_count; idx++) {
@@ -732,7 +694,7 @@ static int snd_sbmixer_init(struct snd_sb *chip,
return err;
}
snd_component_add(card, name);
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
return 0;
}
@@ -751,33 +713,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:
@@ -790,12 +755,13 @@ 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:
@@ -809,20 +775,20 @@ int snd_sbmixer_new(struct snd_sb *chip)
return err;
break;
default:
- strcpy(card->mixername, "???");
+ strscpy(card->mixername, "???");
}
return 0;
}
#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,
@@ -834,7 +800,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,
@@ -852,7 +818,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,
@@ -865,7 +831,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,
@@ -887,7 +853,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)))
@@ -896,7 +862,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 c09d9b914efe..6d618cc2ba45 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,9 +29,6 @@
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 */
@@ -220,7 +204,7 @@ static int sc6000_read(char __iomem *vport)
}
-static int sc6000_write(char __iomem *vport, int cmd)
+static int sc6000_write(struct device *devptr, char __iomem *vport, int cmd)
{
unsigned char val;
int loop = 500000;
@@ -237,18 +221,19 @@ static int sc6000_write(char __iomem *vport, int cmd)
cpu_relax();
} while (loop--);
- snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd);
+ dev_err(devptr, "DSP Command (0x%x) timeout.\n", cmd);
return -EIO;
}
-static int sc6000_dsp_get_answer(char __iomem *vport, int command,
+static int sc6000_dsp_get_answer(struct device *devptr,
+ char __iomem *vport, int command,
char *data, int data_len)
{
int len = 0;
- if (sc6000_write(vport, command)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command);
+ if (sc6000_write(devptr, vport, command)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", command);
return -EIO;
}
@@ -281,82 +266,86 @@ static int sc6000_dsp_reset(char __iomem *vport)
}
/* detection and initialization */
-static int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
+static int sc6000_hw_cfg_write(struct device *devptr,
+ char __iomem *vport, const int *cfg)
{
- if (sc6000_write(vport, COMMAND_6C) < 0) {
- snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
+ if (sc6000_write(devptr, vport, COMMAND_6C) < 0) {
+ dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C);
return -EIO;
}
- if (sc6000_write(vport, COMMAND_5C) < 0) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
+ if (sc6000_write(devptr, vport, COMMAND_5C) < 0) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", COMMAND_5C);
return -EIO;
}
- if (sc6000_write(vport, cfg[0]) < 0) {
- snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
+ if (sc6000_write(devptr, vport, cfg[0]) < 0) {
+ dev_err(devptr, "DATA 0x%x: failed!\n", cfg[0]);
return -EIO;
}
- if (sc6000_write(vport, cfg[1]) < 0) {
- snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
+ if (sc6000_write(devptr, vport, cfg[1]) < 0) {
+ dev_err(devptr, "DATA 0x%x: failed!\n", cfg[1]);
return -EIO;
}
- if (sc6000_write(vport, COMMAND_C5) < 0) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
+ if (sc6000_write(devptr, vport, COMMAND_C5) < 0) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", COMMAND_C5);
return -EIO;
}
return 0;
}
-static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
+static int sc6000_cfg_write(struct device *devptr,
+ char __iomem *vport, unsigned char softcfg)
{
- if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ if (sc6000_write(devptr, vport, WRITE_MDIRQ_CFG)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
return -EIO;
}
- if (sc6000_write(vport, softcfg)) {
- snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
+ if (sc6000_write(devptr, vport, softcfg)) {
+ dev_err(devptr, "%s: failed!\n", __func__);
return -EIO;
}
return 0;
}
-static int sc6000_setup_board(char __iomem *vport, int config)
+static int sc6000_setup_board(struct device *devptr,
+ char __iomem *vport, int config)
{
int loop = 10;
do {
- if (sc6000_write(vport, COMMAND_88)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n",
- COMMAND_88);
+ if (sc6000_write(devptr, vport, COMMAND_88)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n",
+ COMMAND_88);
return -EIO;
}
} while ((sc6000_wait_data(vport) < 0) && loop--);
if (sc6000_read(vport) < 0) {
- snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n",
- COMMAND_88);
+ dev_err(devptr, "sc6000_read after CMD 0x%x: failed\n",
+ COMMAND_88);
return -EIO;
}
- if (sc6000_cfg_write(vport, config))
+ if (sc6000_cfg_write(devptr, vport, config))
return -ENODEV;
return 0;
}
-static int sc6000_init_mss(char __iomem *vport, int config,
+static int sc6000_init_mss(struct device *devptr,
+ 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",
- DSP_INIT_MSS);
+ if (sc6000_write(devptr, vport, DSP_INIT_MSS)) {
+ dev_err(devptr, "%s [0x%x]: failed!\n", __func__,
+ DSP_INIT_MSS);
return -EIO;
}
msleep(10);
- if (sc6000_cfg_write(vport, config))
+ if (sc6000_cfg_write(devptr, vport, config))
return -EIO;
iowrite8(mss_config, vmss_port);
@@ -364,7 +353,8 @@ static int sc6000_init_mss(char __iomem *vport, int config,
return 0;
}
-static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
+static void sc6000_hw_cfg_encode(struct device *devptr,
+ char __iomem *vport, int *cfg,
long xport, long xmpu,
long xmss_port, int joystick)
{
@@ -383,10 +373,11 @@ static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
cfg[0] |= 0x02;
cfg[1] |= 0x80; /* enable WSS system */
cfg[1] &= ~0x40; /* disable IDE */
- snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
+ dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]);
}
-static int sc6000_init_board(char __iomem *vport,
+static int sc6000_init_board(struct device *devptr,
+ char __iomem *vport,
char __iomem *vmss_port, int dev)
{
char answer[15];
@@ -400,14 +391,14 @@ static int sc6000_init_board(char __iomem *vport,
err = sc6000_dsp_reset(vport);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n");
+ dev_err(devptr, "sc6000_dsp_reset: failed!\n");
return err;
}
memset(answer, 0, sizeof(answer));
- err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15);
+ err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15);
if (err <= 0) {
- snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n");
+ dev_err(devptr, "sc6000_dsp_copyright: failed!\n");
return -ENODEV;
}
/*
@@ -415,52 +406,52 @@ static int sc6000_init_board(char __iomem *vport,
* if we have something different, we have to be warned.
*/
if (strncmp("SC-6000", answer, 7))
- snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
+ dev_warn(devptr, "Warning: non SC-6000 audio card!\n");
- if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) {
- snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n");
+ if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) {
+ dev_err(devptr, "sc6000_dsp_version: failed!\n");
return -ENODEV;
}
- printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
+ dev_info(devptr, "Detected model: %s, DSP version %d.%d\n",
answer, version[0], version[1]);
/* set configuration */
- sc6000_write(vport, COMMAND_5C);
+ sc6000_write(devptr, vport, COMMAND_5C);
if (sc6000_read(vport) < 0)
old = 1;
if (!old) {
int cfg[2];
- sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
+ sc6000_hw_cfg_encode(devptr,
+ vport, &cfg[0], port[dev], mpu_port[dev],
mss_port[dev], joystick[dev]);
- if (sc6000_hw_cfg_write(vport, cfg) < 0) {
- snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
+ if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) {
+ dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
return -EIO;
}
}
- err = sc6000_setup_board(vport, config);
+ err = sc6000_setup_board(devptr, vport, config);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+ dev_err(devptr, "sc6000_setup_board: failed!\n");
return -ENODEV;
}
sc6000_dsp_reset(vport);
if (!old) {
- sc6000_write(vport, COMMAND_60);
- sc6000_write(vport, 0x02);
+ sc6000_write(devptr, vport, COMMAND_60);
+ sc6000_write(devptr, vport, 0x02);
sc6000_dsp_reset(vport);
}
- err = sc6000_setup_board(vport, config);
+ err = sc6000_setup_board(devptr, vport, config);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+ dev_err(devptr, "sc6000_setup_board: failed!\n");
return -ENODEV;
}
- err = sc6000_init_mss(vport, config, vmss_port, mss_config);
+ err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot initialize "
- "Microsoft Sound System mode.\n");
+ dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
return -ENODEV;
}
@@ -478,24 +469,24 @@ static int snd_sc6000_mixer(struct snd_wss *chip)
id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX0 to FM */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "FM Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "FM Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "FM Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "FM Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
/* reassign AUX1 to CD */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "CD Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
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");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
@@ -507,149 +498,147 @@ static int snd_sc6000_match(struct device *devptr, unsigned int dev)
if (!enable[dev])
return 0;
if (port[dev] == SNDRV_AUTO_PORT) {
- printk(KERN_ERR PFX "specify IO port\n");
+ dev_err(devptr, "specify IO port\n");
return 0;
}
if (mss_port[dev] == SNDRV_AUTO_PORT) {
- printk(KERN_ERR PFX "specify MSS port\n");
+ dev_err(devptr, "specify MSS port\n");
return 0;
}
if (port[dev] != 0x220 && port[dev] != 0x240) {
- printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n");
+ dev_err(devptr, "Port must be 0x220 or 0x240\n");
return 0;
}
if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) {
- printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n");
+ dev_err(devptr, "MSS port must be 0x530 or 0xe80\n");
return 0;
}
if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) {
- printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]);
+ dev_err(devptr, "invalid IRQ %d\n", irq[dev]);
return 0;
}
if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) {
- printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]);
+ dev_err(devptr, "invalid DMA %d\n", dma[dev]);
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
(mpu_port[dev] & ~0x30L) != 0x300) {
- printk(KERN_ERR PFX "invalid MPU-401 port %lx\n",
+ dev_err(devptr, "invalid MPU-401 port %lx\n",
mpu_port[dev]);
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 &&
!sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) {
- printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
+ dev_err(devptr, "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
return 0;
}
return 1;
}
-static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
+static void snd_sc6000_free(struct snd_card *card)
{
- static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
- static int possible_dmas[] = { 1, 3, 0, -1 };
+ char __iomem *vport = (char __force __iomem *)card->private_data;
+
+ if (vport)
+ sc6000_setup_board(card->dev, vport, 0);
+}
+
+static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
+{
+ 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_new(devptr, 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;
+ dev_err(devptr, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
if (xdma == SNDRV_AUTO_DMA) {
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;
+ dev_err(devptr, "unable to find a free DMA\n");
+ return -EBUSY;
}
}
- if (!request_region(port[dev], 0x10, DRV_NAME)) {
- snd_printk(KERN_ERR PFX
- "I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_exit;
+ if (!devm_request_region(devptr, port[dev], 0x10, DRV_NAME)) {
+ dev_err(devptr, "I/O port region is already in use.\n");
+ return -EBUSY;
}
- *vport = devm_ioport_map(devptr, port[dev], 0x10);
- if (*vport == NULL) {
- snd_printk(KERN_ERR PFX
- "I/O port cannot be iomaped.\n");
- err = -EBUSY;
- goto err_unmap1;
+ vport = devm_ioport_map(devptr, port[dev], 0x10);
+ if (!vport) {
+ dev_err(devptr, "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)) {
- snd_printk(KERN_ERR PFX
- "SC-6000 port I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_unmap1;
+ if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
+ dev_err(devptr,
+ "SC-6000 port I/O port region is already in use.\n");
+ 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;
+ dev_err(devptr, "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]);
+ dev_dbg(devptr, "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(devptr, 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);
if (err < 0) {
- snd_printk(KERN_ERR PFX
- "error creating new WSS PCM device\n");
- goto err_unmap2;
+ dev_err(devptr, "error creating new WSS PCM device\n");
+ return err;
}
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
- goto err_unmap2;
+ dev_err(devptr, "error creating new WSS mixer\n");
+ return err;
}
err = snd_sc6000_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
- goto err_unmap2;
+ dev_err(devptr, "the mixer rewrite failed\n");
+ return err;
}
if (snd_opl3_create(card,
0x388, 0x388 + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
- 0x388, 0x388 + 2);
+ dev_err(devptr, "no OPL device at 0x%x-0x%x ?\n",
+ 0x388, 0x388 + 2);
} 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) {
@@ -659,51 +648,31 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
MPU401_HW_MPU401,
mpu_port[dev], 0,
mpu_irq[dev], NULL) < 0)
- snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
- mpu_port[dev]);
+ dev_err(devptr, "no MPU-401 device at 0x%lx ?\n",
+ mpu_port[dev]);
}
- strcpy(card->driver, DRV_NAME);
- strcpy(card->shortname, "SC-6000");
+ strscpy(card->driver, DRV_NAME);
+ strscpy(card->shortname, "SC-6000");
sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
mss_port[dev], xirq, xdma);
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 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);
-
- 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 = snd_sc6000_remove,
/* FIXME: suspend/resume */
.driver = {
.name = DRV_NAME,
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 733adee5afbf..a31ca75774a6 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,24 +1,10 @@
+// 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>
@@ -152,6 +138,7 @@ struct soundscape {
struct snd_wss *chip;
unsigned char midi_vol;
+ struct device *dev;
};
#define INVALID_IRQ ((unsigned)-1)
@@ -167,16 +154,17 @@ 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",
- size);
+ dev_err(s->dev,
+ "sscape: Failed to allocate %lu bytes for DMA\n",
+ size);
return NULL;
}
}
@@ -212,11 +200,8 @@ static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
static void sscape_write(struct soundscape *s, enum GA_REG reg,
unsigned char val)
{
- unsigned long flags;
-
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
sscape_write_unsafe(s->io_base, reg, val);
- spin_unlock_irqrestore(&s->lock, flags);
}
/*
@@ -321,7 +306,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)
{
@@ -341,18 +326,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)
@@ -390,12 +364,11 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
do {
- unsigned long flags;
int x;
- spin_lock_irqsave(&s->lock, flags);
- x = host_read_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
+ scoped_guard(spinlock_irqsave, &s->lock) {
+ x = host_read_unsafe(s->io_base);
+ }
if (x == 0xfe || x == 0xff)
return 1;
@@ -417,12 +390,11 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
do {
- unsigned long flags;
int x;
- spin_lock_irqsave(&s->lock, flags);
- x = host_read_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
+ scoped_guard(spinlock_irqsave, &s->lock) {
+ x = host_read_unsafe(s->io_base);
+ }
if (x == 0xfe)
return 1;
@@ -438,71 +410,64 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
static int upload_dma_data(struct soundscape *s, const unsigned char *data,
size_t size)
{
- unsigned long flags;
struct snd_dma_buffer dma;
int ret;
unsigned char val;
- if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
+ if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
- spin_lock_irqsave(&s->lock, flags);
-
- /*
- * Reset the board ...
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
+ scoped_guard(spinlock_irqsave, &s->lock) {
- /*
- * Enable the DMA channels and configure them ...
- */
- val = (s->chip->dma1 << 4) | DMA_8BIT;
- sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
- sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
-
- /*
- * Take the board out of reset ...
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
-
- /*
- * Upload the firmware to the SoundScape
- * board through the DMA channel ...
- */
- while (size != 0) {
- unsigned long len;
+ /*
+ * Reset the board ...
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
- len = min(size, dma.bytes);
- memcpy(dma.area, data, len);
- data += len;
- size -= len;
+ /*
+ * Enable the DMA channels and configure them ...
+ */
+ val = (s->chip->dma1 << 4) | DMA_8BIT;
+ sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
+ sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
- snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
- sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
- if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
- /*
- * Don't forget to release this spinlock we're holding
- */
- spin_unlock_irqrestore(&s->lock, flags);
+ /*
+ * Take the board out of reset ...
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
- snd_printk(KERN_ERR
- "sscape: DMA upload has timed out\n");
- ret = -EAGAIN;
- goto _release_dma;
- }
- } /* while */
+ /*
+ * Upload the firmware to the SoundScape
+ * board through the DMA channel ...
+ */
+ while (size != 0) {
+ unsigned long len;
+
+ len = min(size, dma.bytes);
+ memcpy(dma.area, data, len);
+ data += len;
+ size -= len;
+
+ snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
+ sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
+ if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
+ dev_err(s->dev, "sscape: DMA upload has timed out\n");
+ ret = -EAGAIN;
+ goto _release_dma;
+ }
+ } /* while */
- set_host_mode_unsafe(s->io_base);
- outb(0x0, s->io_base);
+ set_host_mode_unsafe(s->io_base);
+ outb(0x0, s->io_base);
- /*
- * Boot the board ... (I think)
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
- spin_unlock_irqrestore(&s->lock, flags);
+ /*
+ * Boot the board ... (I think)
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
+ }
/*
* If all has gone well, then the board should acknowledge
@@ -511,12 +476,11 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data,
*/
ret = 0;
if (!obp_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: No response "
- "from on-board processor after upload\n");
+ dev_err(s->dev,
+ "sscape: No response from on-board processor after upload\n");
ret = -EAGAIN;
} else if (!host_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR
- "sscape: SoundScape failed to initialise\n");
+ dev_err(s->dev, "sscape: SoundScape failed to initialise\n");
ret = -EAGAIN;
}
@@ -538,33 +502,30 @@ _release_dma:
static int sscape_upload_bootblock(struct snd_card *card)
{
struct soundscape *sscape = get_card_soundscape(card);
- unsigned long flags;
const struct firmware *init_fw = NULL;
int data = 0;
int ret;
ret = request_firmware(&init_fw, "scope.cod", card->dev);
if (ret < 0) {
- snd_printk(KERN_ERR "sscape: Error loading scope.cod");
+ dev_err(card->dev, "sscape: Error loading scope.cod");
return ret;
}
ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
release_firmware(init_fw);
- spin_lock_irqsave(&sscape->lock, flags);
+ guard(spinlock_irqsave)(&sscape->lock);
if (ret == 0)
data = host_read_ctrl_unsafe(sscape->io_base, 100);
if (data & 0x10)
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
- spin_unlock_irqrestore(&sscape->lock, flags);
-
data &= 0xf;
if (ret == 0 && data > 7) {
- snd_printk(KERN_ERR
- "sscape: timeout reading firmware version\n");
+ dev_err(card->dev,
+ "sscape: timeout reading firmware version\n");
ret = -EAGAIN;
}
@@ -581,18 +542,18 @@ static int sscape_upload_microcode(struct snd_card *card, int version)
char name[14];
int err;
- snprintf(name, sizeof(name), "sndscape.co%d", version);
+ scnprintf(name, sizeof(name), "sndscape.co%d", version);
err = request_firmware(&init_fw, name, card->dev);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
- version);
+ dev_err(card->dev, "sscape: Error loading sndscape.co%d",
+ version);
return err;
}
err = upload_dma_data(sscape, init_fw->data, init_fw->size);
if (err == 0)
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded %zu KBs\n",
- init_fw->size >> 10);
+ dev_info(card->dev, "sscape: MIDI firmware loaded %zu KBs\n",
+ init_fw->size >> 10);
release_firmware(init_fw);
@@ -618,11 +579,9 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
register struct soundscape *s = get_card_soundscape(card);
- unsigned long flags;
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
uctl->value.integer.value[0] = s->midi_vol;
- spin_unlock_irqrestore(&s->lock, flags);
return 0;
}
@@ -632,11 +591,10 @@ static int sscape_midi_put(struct snd_kcontrol *kctl,
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
struct soundscape *s = get_card_soundscape(card);
- unsigned long flags;
int change;
unsigned char new_val;
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
new_val = uctl->value.integer.value[0] & 127;
/*
@@ -667,7 +625,6 @@ __skip_change:
*/
set_midi_mode_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
return change;
}
@@ -807,8 +764,8 @@ _done:
static int mpu401_open(struct snd_mpu401 *mpu)
{
if (!verify_mpu401(mpu)) {
- snd_printk(KERN_ERR "sscape: MIDI disabled, "
- "please load firmware\n");
+ dev_err(mpu->rmidi->card->dev,
+ "sscape: MIDI disabled, please load firmware\n");
return -ENODEV;
}
@@ -816,7 +773,7 @@ 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 create_mpu401(struct snd_card *card, int devnum,
unsigned long port, int irq)
@@ -877,8 +834,6 @@ static int create_ad1845(struct snd_card *card, unsigned port,
err = snd_wss_create(card, port, -1, irq, dma1, dma2,
codec_type, WSS_HWSHARE_DMA1, &chip);
if (!err) {
- unsigned long flags;
-
if (sscape->type != SSCAPE_VIVO) {
/*
* The input clock frequency on the SoundScape must
@@ -886,31 +841,31 @@ static int create_ad1845(struct snd_card *card, unsigned port,
* to get the playback to sound correct ...
*/
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, AD1845_CLOCK, 0x20);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, AD1845_CLOCK, 0x20);
+ }
snd_wss_mce_down(chip);
}
err = snd_wss_pcm(chip, 0);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No PCM device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No PCM device for AD1845 chip\n");
goto _error;
}
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No mixer device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No mixer device for AD1845 chip\n");
goto _error;
}
if (chip->hardware != WSS_HW_AD1848) {
err = snd_wss_timer(chip, 0);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No timer device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No timer device for AD1845 chip\n");
goto _error;
}
}
@@ -919,8 +874,8 @@ static int create_ad1845(struct snd_card *card, unsigned port,
err = snd_ctl_add(card,
snd_ctl_new1(&midi_mixer_ctl, chip));
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Could not create "
- "MIDI mixer control\n");
+ dev_err(card->dev,
+ "sscape: Could not create MIDI mixer control\n");
goto _error;
}
}
@@ -945,7 +900,6 @@ static int create_sscape(int dev, struct snd_card *card)
unsigned mpu_irq_cfg;
struct resource *io_res;
struct resource *wss_res;
- unsigned long flags;
int err;
int val;
const char *name;
@@ -954,30 +908,30 @@ static int 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]);
+ dev_err(card->dev,
+ "sscape: can't grab port 0x%lx\n", port[dev]);
return -EBUSY;
}
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;
+ dev_err(card->dev, "sscape: can't grab port 0x%lx\n",
+ wss_port[dev]);
+ 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;
+ dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]);
+ return err;
}
spin_lock_init(&sscape->lock);
@@ -986,10 +940,9 @@ static int create_sscape(int dev, struct snd_card *card)
sscape->io_base = port[dev];
if (!detect_sscape(sscape, wss_port[dev])) {
- printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
+ dev_err(card->dev, "sscape: hardware not detected at 0x%x\n",
sscape->io_base);
- err = -ENODEV;
- goto _release_dma;
+ return -ENODEV;
}
switch (sscape->type) {
@@ -1010,58 +963,56 @@ static int create_sscape(int dev, struct snd_card *card)
break;
}
- printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
- name, sscape->io_base, irq[dev], dma[dev]);
+ dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
+ name, sscape->io_base, irq[dev], dma[dev]);
/*
* Check that the user didn't pass us garbage data ...
*/
irq_cfg = get_irq_config(sscape->type, irq[dev]);
if (irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
- err = -ENXIO;
- goto _release_dma;
+ dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]);
+ return -ENXIO;
}
mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
if (mpu_irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
- err = -ENXIO;
- goto _release_dma;
+ dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
+ return -ENXIO;
}
/*
* Tell the on-board devices where their resources are (I think -
* I can't be sure without a datasheet ... So many magic values!)
*/
- spin_lock_irqsave(&sscape->lock, flags);
+ scoped_guard(spinlock_irqsave, &sscape->lock) {
- sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
- sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
- /*
- * Enable and configure the DMA channels ...
- */
- sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
- dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
- sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
- sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
-
- mpu_irq_cfg |= mpu_irq_cfg << 2;
- val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
- if (joystick[dev])
- val |= 8;
- sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
- sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
- sscape_write_unsafe(sscape->io_base,
- GA_CDCFG_REG, 0x09 | DMA_8BIT
- | (dma[dev] << 4) | (irq_cfg << 1));
- /*
- * Enable the master IRQ ...
- */
- sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
+ /*
+ * Enable and configure the DMA channels ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
+ dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
+ sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
+ sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
+
+ mpu_irq_cfg |= mpu_irq_cfg << 2;
+ val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
+ if (joystick[dev])
+ val |= 8;
+ sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
+ sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
+ sscape_write_unsafe(sscape->io_base,
+ GA_CDCFG_REG, 0x09 | DMA_8BIT
+ | (dma[dev] << 4) | (irq_cfg << 1));
+ /*
+ * Enable the master IRQ ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
- spin_unlock_irqrestore(&sscape->lock, flags);
+ }
/*
* We have now enabled the codec chip, and so we should
@@ -1070,13 +1021,13 @@ static int create_sscape(int dev, struct snd_card *card)
err = create_ad1845(card, wss_port[dev], irq[dev],
dma[dev], dma2[dev]);
if (err < 0) {
- snd_printk(KERN_ERR
- "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
- wss_port[dev], irq[dev]);
- goto _release_dma;
+ dev_err(card->dev,
+ "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
+ wss_port[dev], irq[dev]);
+ return err;
}
- strcpy(card->driver, "SoundScape");
- strcpy(card->shortname, name);
+ strscpy(card->driver, "SoundScape");
+ strscpy(card->shortname, name);
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
name, sscape->chip->port, sscape->chip->irq,
@@ -1092,16 +1043,16 @@ static int create_sscape(int dev, struct snd_card *card)
err = create_mpu401(card, MIDI_DEVNUM, port[dev],
mpu_irq[dev]);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%lx\n",
- port[dev]);
- goto _release_dma;
+ dev_err(card->dev,
+ "sscape: Failed to create MPU-401 device at 0x%lx\n",
+ port[dev]);
+ return err;
}
/*
* Initialize mixer
*/
- spin_lock_irqsave(&sscape->lock, flags);
+ guard(spinlock_irqsave)(&sscape->lock);
sscape->midi_vol = 0;
host_write_ctrl_unsafe(sscape->io_base,
CMD_SET_MIDI_VOL, 100);
@@ -1118,28 +1069,10 @@ static int create_sscape(int dev, struct snd_card *card)
host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
set_midi_mode_unsafe(sscape->io_base);
- spin_unlock_irqrestore(&sscape->lock, flags);
}
}
- /*
- * 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;
}
@@ -1154,9 +1087,8 @@ static int snd_sscape_match(struct device *pdev, unsigned int i)
if (irq[i] == SNDRV_AUTO_IRQ ||
mpu_irq[i] == SNDRV_AUTO_IRQ ||
dma[i] == SNDRV_AUTO_DMA) {
- printk(KERN_INFO
- "sscape: insufficient parameters, "
- "need IO, IRQ, MPU-IRQ and DMA\n");
+ dev_info(pdev,
+ "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
return 0;
}
@@ -1169,37 +1101,28 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
struct soundscape *sscape;
int ret;
- ret = snd_card_new(pdev, 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;
sscape = get_card_soundscape(card);
+ sscape->dev = pdev;
sscape->type = SSCAPE;
dma[dev] &= 0x03;
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;
+ dev_err(pdev, "sscape: Failed to register sound card\n");
+ return ret;
}
dev_set_drvdata(pdev, card);
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static int snd_sscape_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
}
#define DEV_NAME "sscape"
@@ -1207,7 +1130,6 @@ static int 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 = snd_sscape_remove,
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1249,7 +1171,7 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
if (!pnp_is_active(dev)) {
if (pnp_activate_dev(dev) < 0) {
- snd_printk(KERN_INFO "sscape: device is inactive\n");
+ dev_info(&dev->dev, "sscape: device is inactive\n");
return -EBUSY;
}
}
@@ -1258,13 +1180,14 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- ret = snd_card_new(&pcard->card->dev,
- 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;
sscape = get_card_soundscape(card);
+ sscape->dev = card->dev;
/*
* Identify card model ...
@@ -1291,27 +1214,17 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
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;
+ dev_err(card->dev, "sscape: Failed to register sound card\n");
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void 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 = {
@@ -1319,7 +1232,6 @@ static struct pnp_card_driver sscape_pnpc_driver = {
.name = "sscape",
.id_table = sscape_pnpids,
.probe = sscape_pnp_detect,
- .remove = sscape_pnp_remove,
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile
index 601bdddd44d0..3ba85fb2e6cd 100644
--- a/sound/isa/wavefront/Makefile
+++ b/sound/isa/wavefront/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
+snd-wavefront-y := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index b1989facd732..07c68568091d 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>
@@ -34,7 +21,6 @@
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 */
@@ -154,7 +140,7 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP WSS pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP WSS pnp configure failure\n");
return err;
}
@@ -170,7 +156,7 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP ICS2115 pnp configure failure\n");
return err;
}
@@ -188,26 +174,27 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP MPU401 pnp configure failure\n");
cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
} else {
cs4232_mpu_port[dev] = pnp_port_start(pdev, 0);
cs4232_mpu_irq[dev] = pnp_irq(pdev, 0);
}
- snd_printk (KERN_INFO "CS4232 MPU: port=0x%lx, irq=%i\n",
- cs4232_mpu_port[dev],
- cs4232_mpu_irq[dev]);
+ dev_info(&pdev->dev, "CS4232 MPU: port=0x%lx, irq=%i\n",
+ cs4232_mpu_port[dev],
+ cs4232_mpu_irq[dev]);
}
- snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n",
- cs4232_pcm_port[dev],
- fm_port[dev],
- dma1[dev],
- dma2[dev],
- cs4232_pcm_irq[dev],
- ics2115_port[dev],
- ics2115_irq[dev]);
+ dev_dbg(&pdev->dev,
+ "CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n",
+ cs4232_pcm_port[dev],
+ fm_port[dev],
+ dma1[dev],
+ dma2[dev],
+ cs4232_pcm_irq[dev],
+ ics2115_port[dev],
+ ics2115_irq[dev]);
return 0;
}
@@ -247,7 +234,7 @@ static struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card,
if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0)
return NULL;
- strcpy (wavefront_synth->name,
+ strscpy (wavefront_synth->name,
"WaveFront (ICS2115) wavetable synthesizer");
wavefront_synth->ops.open = snd_wavefront_synth_open;
wavefront_synth->ops.release = snd_wavefront_synth_release;
@@ -265,7 +252,7 @@ static struct snd_hwdep *snd_wavefront_new_fx(struct snd_card *card,
struct snd_hwdep *fx_processor;
if (snd_wavefront_fx_start (&acard->wavefront)) {
- snd_printk (KERN_ERR "cannot initialize YSS225 FX processor");
+ dev_err(card->dev, "cannot initialize YSS225 FX processor");
return NULL;
}
@@ -296,7 +283,7 @@ static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card,
first = 0;
acard->wavefront.midi.base = port;
if (snd_wavefront_midi_start (acard)) {
- snd_printk (KERN_ERR "cannot initialize MIDI interface\n");
+ dev_err(card->dev, "cannot initialize MIDI interface\n");
return NULL;
}
}
@@ -305,10 +292,10 @@ static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card,
return NULL;
if (mpu == internal_mpu) {
- strcpy(rmidi->name, "WaveFront MIDI (Internal)");
+ strscpy(rmidi->name, "WaveFront MIDI (Internal)");
rmidi->private_data = &internal_id;
} else {
- strcpy(rmidi->name, "WaveFront MIDI (External)");
+ strscpy(rmidi->name, "WaveFront MIDI (External)");
rmidi->private_data = &external_id;
}
@@ -322,18 +309,6 @@ static struct snd_rawmidi *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(struct device *pdev, int dev,
struct snd_card **cardp)
{
@@ -341,8 +316,8 @@ static int snd_wavefront_card_new(struct device *pdev, int dev,
snd_wavefront_card_t *acard;
int err;
- err = snd_card_new(pdev, 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;
@@ -353,7 +328,6 @@ static int snd_wavefront_card_new(struct device *pdev, int dev,
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;
@@ -376,7 +350,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
cs4232_pcm_irq[dev], dma1[dev], dma2[dev],
WSS_HW_DETECT, 0, &chip);
if (err < 0) {
- snd_printk(KERN_ERR "can't allocate WSS device\n");
+ dev_err(card->dev, "can't allocate WSS device\n");
return err;
}
@@ -396,7 +370,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3_CS, 0, &opl3);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n");
+ dev_err(card->dev, "can't allocate or detect OPL3 synth\n");
return err;
}
@@ -408,29 +382,32 @@ 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);
+ dev_err(card->dev, "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,
- 0, "ICS2115", acard)) {
- snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
+ if (devm_request_irq(card->dev, ics2115_irq[dev],
+ snd_wavefront_ics2115_interrupt,
+ 0, "ICS2115", acard)) {
+ dev_err(card->dev, "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);
if (wavefront_synth == NULL) {
- snd_printk (KERN_ERR "can't create WaveFront synth device\n");
+ dev_err(card->dev, "can't create WaveFront synth device\n");
return -ENOMEM;
}
- strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
+ strscpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115;
hw_dev++;
@@ -438,7 +415,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate mixer device\n");
+ dev_err(card->dev, "can't allocate mixer device\n");
return err;
}
@@ -449,7 +426,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
cs4232_mpu_port[dev], 0,
cs4232_mpu_irq[dev], NULL);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
+ dev_err(card->dev, "can't allocate CS4232 MPU-401 device\n");
return err;
}
midi_dev++;
@@ -465,7 +442,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
ics2115_port[dev],
internal_mpu);
if (ics2115_internal_rmidi == NULL) {
- snd_printk (KERN_ERR "can't setup ICS2115 internal MIDI device\n");
+ dev_err(card->dev, "can't setup ICS2115 internal MIDI device\n");
return -ENOMEM;
}
midi_dev++;
@@ -481,7 +458,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
ics2115_port[dev],
external_mpu);
if (ics2115_external_rmidi == NULL) {
- snd_printk (KERN_ERR "can't setup ICS2115 external MIDI device\n");
+ dev_err(card->dev, "can't setup ICS2115 external MIDI device\n");
return -ENOMEM;
}
midi_dev++;
@@ -495,18 +472,18 @@ snd_wavefront_probe (struct snd_card *card, int dev)
acard,
ics2115_port[dev]);
if (fx_processor == NULL) {
- snd_printk (KERN_ERR "can't setup FX device\n");
+ dev_err(card->dev, "can't setup FX device\n");
return -ENOMEM;
}
hw_dev++;
- strcpy(card->driver, "Tropez+");
- strcpy(card->shortname, "Turtle Beach Tropez+");
+ strscpy(card->driver, "Tropez+");
+ strscpy(card->shortname, "Turtle Beach Tropez+");
} else {
/* Need a way to distinguish between Maui and Tropez */
- strcpy(card->driver, "WaveFront");
- strcpy(card->shortname, "Turtle Beach WaveFront");
+ strscpy(card->driver, "WaveFront");
+ strscpy(card->shortname, "Turtle Beach WaveFront");
}
/* ----- Register the card --------- */
@@ -549,11 +526,11 @@ static int snd_wavefront_isa_match(struct device *pdev,
return 0;
#endif
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify CS4232 port\n");
+ dev_err(pdev, "specify CS4232 port\n");
return 0;
}
if (ics2115_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify ICS2115 port\n");
+ dev_err(pdev, "specify ICS2115 port\n");
return 0;
}
return 1;
@@ -568,28 +545,19 @@ static int snd_wavefront_isa_probe(struct device *pdev,
err = snd_wavefront_card_new(pdev, dev, &card);
if (err < 0)
return err;
- 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 snd_wavefront_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- return 0;
-}
-
#define DEV_NAME "wavefront"
static struct isa_driver snd_wavefront_driver = {
.match = snd_wavefront_isa_match,
.probe = snd_wavefront_isa_probe,
- .remove = snd_wavefront_isa_remove,
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -618,13 +586,13 @@ static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
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);
+ dev_err(card->dev, "isapnp detection failed\n");
return -ENODEV;
}
}
- 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);
@@ -632,18 +600,11 @@ static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void 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 = snd_wavefront_pnp_remove,
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 0608a5a4289d..beca35ce04f3 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -1,19 +1,6 @@
+// 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 <linux/io.h>
@@ -51,7 +38,7 @@ wavefront_fx_idle (snd_wavefront_t *dev)
}
if (x & 0x80) {
- snd_printk ("FX device never idle.\n");
+ dev_err(dev->card->dev, "FX device never idle.\n");
return 0;
}
@@ -77,14 +64,14 @@ wavefront_fx_memset (snd_wavefront_t *dev,
unsigned short *data)
{
if (page < 0 || page > 7) {
- snd_printk ("FX memset: "
- "page must be >= 0 and <= 7\n");
+ dev_err(dev->card->dev,
+ "FX memset: page must be >= 0 and <= 7\n");
return -EINVAL;
}
if (addr < 0 || addr > 0x7f) {
- snd_printk ("FX memset: "
- "addr must be >= 0 and <= 7f\n");
+ dev_err(dev->card->dev,
+ "FX memset: addr must be >= 0 and <= 7f\n");
return -EINVAL;
}
@@ -96,7 +83,7 @@ wavefront_fx_memset (snd_wavefront_t *dev,
outb ((data[0] >> 8), dev->fx_dsp_msb);
outb ((data[0] & 0xff), dev->fx_dsp_lsb);
- snd_printk ("FX: addr %d:%x set to 0x%x\n",
+ dev_err(dev->card->dev, "FX: addr %d:%x set to 0x%x\n",
page, addr, data[0]);
} else {
@@ -115,9 +102,9 @@ wavefront_fx_memset (snd_wavefront_t *dev,
}
if (i != cnt) {
- snd_printk ("FX memset "
- "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
- page, addr, (unsigned long) data, cnt);
+ dev_err(dev->card->dev,
+ "FX memset (0x%x, 0x%x, 0x%lx, %d) incomplete\n",
+ page, addr, (unsigned long) data, cnt);
return -EIO;
}
}
@@ -136,7 +123,7 @@ snd_wavefront_fx_detect (snd_wavefront_t *dev)
*/
if (inb (dev->fx_status) & 0x80) {
- snd_printk ("Hmm, probably a Maui or Tropez.\n");
+ dev_err(dev->card->dev, "Hmm, probably a Maui or Tropez.\n");
return -1;
}
@@ -193,20 +180,20 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
case WFFX_MEMSET:
if (r.data[2] <= 0) {
- snd_printk ("cannot write "
- "<= 0 bytes to FX\n");
+ dev_err(dev->card->dev,
+ "cannot write <= 0 bytes to FX\n");
return -EIO;
} else if (r.data[2] == 1) {
pd = (unsigned short *) &r.data[3];
} else {
if (r.data[2] > 256) {
- snd_printk ("cannot write "
- "> 512 bytes to FX\n");
+ dev_err(dev->card->dev,
+ "cannot write > 512 bytes to FX\n");
return -EIO;
}
- page_data = memdup_user((unsigned char __user *)
- r.data[3],
- r.data[2] * sizeof(short));
+ page_data = memdup_array_user((unsigned char __user *)
+ r.data[3],
+ r.data[2], sizeof(short));
if (IS_ERR(page_data))
return PTR_ERR(page_data);
pd = page_data;
@@ -221,8 +208,8 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
break;
default:
- snd_printk ("FX: ioctl %d not yet supported\n",
- r.request);
+ dev_err(dev->card->dev, "FX: ioctl %d not yet supported\n",
+ r.request);
return -ENOTTY;
}
return err;
@@ -267,8 +254,8 @@ snd_wavefront_fx_start (snd_wavefront_t *dev)
goto out;
}
} else {
- snd_printk(KERN_ERR "invalid address"
- " in register data\n");
+ dev_err(dev->card->dev,
+ "invalid address in register data\n");
err = -1;
goto out;
}
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index 556b14738970..1250ecba659a 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)
@@ -116,7 +113,6 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
{
snd_wavefront_midi_t *midi = &card->wavefront.midi;
snd_wavefront_mpu_id mpu;
- unsigned long flags;
unsigned char midi_byte;
int max = 256, mask = 1;
int timeout;
@@ -145,11 +141,9 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
break;
}
- spin_lock_irqsave (&midi->virtual, flags);
- if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
- spin_unlock_irqrestore (&midi->virtual, flags);
+ guard(spinlock_irqsave)(&midi->virtual);
+ if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0)
goto __second;
- }
if (output_ready (midi)) {
if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
if (!midi->isvirtual ||
@@ -160,17 +154,14 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
} else {
if (midi->istimer) {
if (--midi->istimer <= 0)
- del_timer(&midi->timer);
+ timer_delete(&midi->timer);
}
midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
- spin_unlock_irqrestore (&midi->virtual, flags);
goto __second;
}
} else {
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
__second:
@@ -188,15 +179,13 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
break;
}
- spin_lock_irqsave (&midi->virtual, flags);
+ guard(spinlock_irqsave)(&midi->virtual);
if (!midi->isvirtual)
mask = 0;
mpu = midi->output_mpu ^ mask;
mask = 0; /* don't invert the value from now */
- if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
- spin_unlock_irqrestore (&midi->virtual, flags);
+ if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0)
return;
- }
if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
goto __timer;
if (output_ready (midi)) {
@@ -215,23 +204,19 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
__timer:
if (midi->istimer) {
if (--midi->istimer <= 0)
- del_timer(&midi->timer);
+ timer_delete(&midi->timer);
}
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
} else {
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
}
static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -242,20 +227,19 @@ 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);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] |= MPU401_MODE_INPUT;
midi->substream_input[mpu] = substream;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -266,20 +250,19 @@ 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);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] |= MPU401_MODE_OUTPUT;
midi->substream_output[mpu] = substream;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -290,19 +273,18 @@ 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);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] &= ~MPU401_MODE_INPUT;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -313,18 +295,17 @@ 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);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -336,34 +317,31 @@ 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);
+ guard(spinlock_irqsave)(&midi->virtual);
if (up) {
midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
} else {
midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
static void snd_wavefront_midi_output_timer(struct timer_list *t)
{
- snd_wavefront_midi_t *midi = from_timer(midi, t, timer);
+ snd_wavefront_midi_t *midi = timer_container_of(midi, t, timer);
snd_wavefront_card_t *card = midi->timer_card;
- unsigned long flags;
- spin_lock_irqsave (&midi->virtual, flags);
- mod_timer(&midi->timer, 1 + jiffies);
- spin_unlock_irqrestore (&midi->virtual, flags);
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ mod_timer(&midi->timer, 1 + jiffies);
+ }
snd_wavefront_midi_output_write(card);
}
static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -375,26 +353,26 @@ 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) {
- timer_setup(&midi->timer,
- snd_wavefront_midi_output_timer,
- 0);
- mod_timer(&midi->timer, 1 + jiffies);
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ if (up) {
+ if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
+ if (!midi->istimer) {
+ 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;
}
- midi->istimer++;
- midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
+ } else {
+ midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
}
- } else {
- midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
if (up)
snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
@@ -404,7 +382,6 @@ void
snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
static struct snd_rawmidi_substream *substream = NULL;
static int mpu = external_mpu;
@@ -418,37 +395,37 @@ snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
return;
}
- spin_lock_irqsave (&midi->virtual, flags);
- while (--max) {
-
- if (input_avail (midi)) {
- byte = read_data (midi);
-
- if (midi->isvirtual) {
- if (byte == WF_EXTERNAL_SWITCH) {
- substream = midi->substream_input[external_mpu];
- mpu = external_mpu;
- } else if (byte == WF_INTERNAL_SWITCH) {
- substream = midi->substream_output[internal_mpu];
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ while (--max) {
+
+ if (input_avail(midi)) {
+ byte = read_data(midi);
+
+ if (midi->isvirtual) {
+ if (byte == WF_EXTERNAL_SWITCH) {
+ substream = midi->substream_input[external_mpu];
+ mpu = external_mpu;
+ } else if (byte == WF_INTERNAL_SWITCH) {
+ substream = midi->substream_output[internal_mpu];
+ mpu = internal_mpu;
+ } /* else just leave it as it is */
+ } else {
+ substream = midi->substream_input[internal_mpu];
mpu = internal_mpu;
- } /* else just leave it as it is */
- } else {
- substream = midi->substream_input[internal_mpu];
- mpu = internal_mpu;
- }
+ }
- if (substream == NULL) {
- continue;
- }
+ if (substream == NULL) {
+ continue;
+ }
- if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
- snd_rawmidi_receive(substream, &byte, 1);
+ if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+ } else {
+ break;
}
- } else {
- break;
}
- }
- spin_unlock_irqrestore (&midi->virtual, flags);
+ }
snd_wavefront_midi_output_write(card);
}
@@ -470,13 +447,10 @@ void
snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
{
- unsigned long flags;
-
- spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+ guard(spinlock_irqsave)(&card->wavefront.midi.virtual);
// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
card->wavefront.midi.isvirtual = 0;
- spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
}
int
@@ -500,7 +474,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
for (i = 0; i < 30000 && !output_ready (midi); i++);
if (!output_ready (midi)) {
- snd_printk ("MIDI interface not ready for command\n");
+ dev_err(card->wavefront.card->dev,
+ "MIDI interface not ready for command\n");
return -1;
}
@@ -522,7 +497,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
}
if (!ok) {
- snd_printk ("cannot set UART mode for MIDI interface");
+ dev_err(card->wavefront.card->dev,
+ "cannot set UART mode for MIDI interface");
dev->interrupts_are_midi = 0;
return -1;
}
@@ -530,7 +506,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
/* Route external MIDI to WaveFront synth (by default) */
if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
- snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
+ dev_warn(card->wavefront.card->dev,
+ "can't enable MIDI-IN-2-synth routing.\n");
/* XXX error ? */
}
@@ -546,14 +523,16 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
*/
if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {
- snd_printk ("virtual MIDI mode not disabled\n");
+ dev_warn(card->wavefront.card->dev,
+ "virtual MIDI mode not disabled\n");
return 0; /* We're OK, but missing the external MIDI dev */
}
snd_wavefront_midi_enable_virtual (card);
if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
- snd_printk ("cannot enable virtual MIDI mode.\n");
+ dev_warn(card->wavefront.card->dev,
+ "cannot enable virtual MIDI mode.\n");
snd_wavefront_midi_disable_virtual (card);
}
return 0;
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 0b1e4b34b299..cd5c177943aa 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.
*/
/*
@@ -119,7 +116,7 @@ MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS");
#define DPRINT(cond, ...) \
if ((dev->debug & (cond)) == (cond)) { \
- snd_printk (__VA_ARGS__); \
+ pr_debug(__VA_ARGS__); \
}
#else
#define DPRINT(cond, args...)
@@ -342,8 +339,9 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
int c;
struct wavefront_command *wfcmd;
- if ((wfcmd = wavefront_get_command (cmd)) == NULL) {
- snd_printk ("command 0x%x not supported.\n",
+ wfcmd = wavefront_get_command(cmd);
+ if (!wfcmd) {
+ dev_err(dev->card->dev, "command 0x%x not supported.\n",
cmd);
return 1;
}
@@ -394,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);
@@ -404,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 "
@@ -462,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) {
@@ -478,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",
@@ -580,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++) {
@@ -606,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;
}
@@ -624,7 +623,7 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
/* check sample status */
if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) {
- snd_printk ("cannot request sample count.\n");
+ dev_err(dev->card->dev, "cannot request sample count.\n");
return -1;
}
@@ -636,8 +635,8 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
wbuf[1] = i >> 7;
if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
- snd_printk(KERN_WARNING "cannot identify sample "
- "type of slot %d\n", i);
+ dev_warn(dev->card->dev,
+ "cannot identify sample type of slot %d\n", i);
dev->sample_status[i] = WF_ST_EMPTY;
continue;
}
@@ -662,9 +661,9 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
break;
default:
- snd_printk ("unknown sample type for "
- "slot %d (0x%x)\n",
- i, rbuf[0]);
+ dev_err(dev->card->dev,
+ "unknown sample type for slot %d (0x%x)\n",
+ i, rbuf[0]);
}
if (rbuf[0] != WF_ST_EMPTY) {
@@ -672,9 +671,10 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
}
}
- snd_printk ("%d samples used (%d real, %d aliases, %d multi), "
- "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi,
- WF_MAX_SAMPLE - dev->samples_used);
+ dev_info(dev->card->dev,
+ "%d samples used (%d real, %d aliases, %d multi), %d empty\n",
+ dev->samples_used, sc_real, sc_alias, sc_multi,
+ WF_MAX_SAMPLE - dev->samples_used);
return (0);
@@ -694,8 +694,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;
@@ -706,8 +707,8 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
} else if (x == 3) { /* Bad patch number */
dev->patch_status[i] = 0;
} else {
- snd_printk ("upload patch "
- "error 0x%x\n", x);
+ dev_err(dev->card->dev,
+ "upload patch error 0x%x\n", x);
dev->patch_status[i] = 0;
return 1;
}
@@ -724,7 +725,8 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
}
}
- snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2);
+ dev_info(dev->card->dev, "%d patch slots filled, %d in use\n",
+ cnt, cnt2);
return (0);
}
@@ -741,8 +743,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;
@@ -759,8 +762,8 @@ wavefront_get_program_status (snd_wavefront_t *dev)
} else if (x == 1) { /* Bad program number */
dev->prog_status[i] = 0;
} else {
- snd_printk ("upload program "
- "error 0x%x\n", x);
+ dev_err(dev->card->dev,
+ "upload program error 0x%x\n", x);
dev->prog_status[i] = 0;
}
}
@@ -771,7 +774,7 @@ wavefront_get_program_status (snd_wavefront_t *dev)
}
}
- snd_printk ("%d programs slots in use\n", cnt);
+ dev_info(dev->card->dev, "%d programs slots in use\n", cnt);
return (0);
}
@@ -791,12 +794,11 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
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");
+ dev_err(dev->card->dev, "download patch failed\n");
return -EIO;
}
@@ -837,7 +839,7 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) {
- snd_printk ("download patch failed\n");
+ dev_err(dev->card->dev, "download patch failed\n");
return -EIO;
}
@@ -851,7 +853,7 @@ wavefront_freemem (snd_wavefront_t *dev)
char rbuf[8];
if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, NULL)) {
- snd_printk ("can't get memory stats.\n");
+ dev_err(dev->card->dev, "can't get memory stats.\n");
return -1;
} else {
return demunge_int32 (rbuf, 4);
@@ -898,10 +900,10 @@ 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);
+ dev_info(dev->card->dev, "unspecified sample => %d\n", x);
header->number = x;
}
@@ -935,9 +937,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (dev->rom_samples_rdonly) {
if (dev->sample_status[header->number] & WF_SLOT_ROM) {
- snd_printk ("sample slot %d "
- "write protected\n",
- header->number);
+ dev_err(dev->card->dev,
+ "sample slot %d write protected\n",
+ header->number);
return -EACCES;
}
}
@@ -949,9 +951,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
dev->freemem = wavefront_freemem (dev);
if (dev->freemem < (int)header->size) {
- snd_printk ("insufficient memory to "
- "load %d byte sample.\n",
- header->size);
+ dev_err(dev->card->dev,
+ "insufficient memory to load %d byte sample.\n",
+ header->size);
return -ENOMEM;
}
@@ -960,8 +962,8 @@ wavefront_send_sample (snd_wavefront_t *dev,
skip = WF_GET_CHANNEL(&header->hdr.s);
if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
- snd_printk ("channel selection only "
- "possible on 16-bit samples");
+ dev_err(dev->card->dev,
+ "channel selection only possible on 16-bit samples");
return -EINVAL;
}
@@ -1057,8 +1059,8 @@ wavefront_send_sample (snd_wavefront_t *dev,
header->size ?
WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
NULL, sample_hdr)) {
- snd_printk ("sample %sdownload refused.\n",
- header->size ? "" : "header ");
+ dev_err(dev->card->dev, "sample %sdownload refused.\n",
+ header->size ? "" : "header ");
return -EIO;
}
@@ -1083,8 +1085,8 @@ wavefront_send_sample (snd_wavefront_t *dev,
}
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) {
- snd_printk ("download block "
- "request refused.\n");
+ dev_err(dev->card->dev,
+ "download block request refused.\n");
return -EIO;
}
@@ -1092,7 +1094,8 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (dataptr < data_end) {
- __get_user (sample_short, dataptr);
+ if (get_user(sample_short, dataptr))
+ return -EFAULT;
dataptr += skip;
if (data_is_unsigned) { /* GUS ? */
@@ -1141,15 +1144,16 @@ 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");
+ dev_err(dev->card->dev,
+ "upload sample DMA ack timeout\n");
return -EIO;
} else {
- snd_printk ("upload sample "
- "DMA ack error 0x%x\n",
- dma_ack);
+ dev_err(dev->card->dev,
+ "upload sample DMA ack error 0x%x\n",
+ dma_ack);
return -EIO;
}
}
@@ -1175,7 +1179,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),
@@ -1190,7 +1197,7 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) {
- snd_printk ("download alias failed.\n");
+ dev_err(dev->card->dev, "download alias failed.\n");
return -EIO;
}
@@ -1206,6 +1213,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
int num_samples;
unsigned char *msample_hdr;
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
if (! msample_hdr)
return -ENOMEM;
@@ -1240,7 +1250,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE,
(unsigned char *) (long) ((num_samples*2)+3),
msample_hdr)) {
- snd_printk ("download of multisample failed.\n");
+ dev_err(dev->card->dev, "download of multisample failed.\n");
kfree(msample_hdr);
return -EIO;
}
@@ -1263,7 +1273,7 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
munge_int32 (header->number, number, 2);
if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
- snd_printk ("upload multisample failed.\n");
+ dev_err(dev->card->dev, "upload multisample failed.\n");
return -EIO;
}
@@ -1280,16 +1290,18 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
char d[2];
int val;
- if ((val = wavefront_read (dev)) == -1) {
- snd_printk ("upload multisample failed "
- "during sample loop.\n");
+ val = wavefront_read(dev);
+ if (val == -1) {
+ dev_err(dev->card->dev,
+ "upload multisample failed during sample loop.\n");
return -EIO;
}
d[0] = val;
- if ((val = wavefront_read (dev)) == -1) {
- snd_printk ("upload multisample failed "
- "during sample loop.\n");
+ val = wavefront_read(dev);
+ if (val == -1) {
+ dev_err(dev->card->dev,
+ "upload multisample failed during sample loop.\n");
return -EIO;
}
d[1] = val;
@@ -1324,7 +1336,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");
+ dev_err(dev->card->dev, "download drum failed.\n");
return -EIO;
}
@@ -1342,7 +1354,7 @@ wavefront_find_free_sample (snd_wavefront_t *dev)
return i;
}
}
- snd_printk ("no free sample slots!\n");
+ dev_err(dev->card->dev, "no free sample slots!\n");
return -1;
}
@@ -1358,7 +1370,7 @@ wavefront_find_free_patch (snd_wavefront_t *dev)
return i;
}
}
- snd_printk ("no free patch slots!\n");
+ dev_err(dev->card->dev, "no free patch slots!\n");
return -1;
}
#endif
@@ -1375,7 +1387,7 @@ wavefront_load_patch (snd_wavefront_t *dev, const char __user *addr)
if (copy_from_user (header, addr, sizeof(wavefront_patch_info) -
sizeof(wavefront_any))) {
- snd_printk ("bad address for load patch.\n");
+ dev_err(dev->card->dev, "bad address for load patch.\n");
err = -EFAULT;
goto __error;
}
@@ -1453,8 +1465,8 @@ wavefront_load_patch (snd_wavefront_t *dev, const char __user *addr)
break;
default:
- snd_printk ("unknown patch type %d.\n",
- header->subkey);
+ dev_err(dev->card->dev, "unknown patch type %d.\n",
+ header->subkey);
err = -EINVAL;
break;
}
@@ -1517,13 +1529,13 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
switch (wc->cmd) {
case WFC_DISABLE_INTERRUPTS:
- snd_printk ("interrupts disabled.\n");
+ dev_dbg(dev->card->dev, "interrupts disabled.\n");
outb (0x80|0x20, dev->control_port);
dev->interrupts_are_midi = 1;
return 0;
case WFC_ENABLE_INTERRUPTS:
- snd_printk ("interrupts enabled.\n");
+ dev_dbg(dev->card->dev, "interrupts enabled.\n");
outb (0x80|0x40|0x20, dev->control_port);
dev->interrupts_are_midi = 1;
return 0;
@@ -1540,7 +1552,7 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
case WFC_IDENTIFY_SLOT_TYPE:
i = wc->wbuf[0] | (wc->wbuf[1] << 7);
if (i <0 || i >= WF_MAX_SAMPLE) {
- snd_printk ("invalid slot ID %d\n",
+ dev_err(dev->card->dev, "invalid slot ID %d\n",
i);
wc->status = EINVAL;
return -EINVAL;
@@ -1551,7 +1563,7 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
case WFC_DEBUG_DRIVER:
dev->debug = wc->wbuf[0];
- snd_printk ("debug = 0x%x\n", dev->debug);
+ dev_dbg(dev->card->dev, "debug = 0x%x\n", dev->debug);
return 0;
case WFC_UPLOAD_PATCH:
@@ -1568,8 +1580,8 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
return 0;
case WFC_UPLOAD_SAMPLE_ALIAS:
- snd_printk ("support for sample alias upload "
- "being considered.\n");
+ dev_err(dev->card->dev,
+ "support for sample alias upload being considered.\n");
wc->status = EINVAL;
return -EINVAL;
}
@@ -1610,9 +1622,8 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
break;
case WFC_UPLOAD_SAMPLE_ALIAS:
- snd_printk ("support for "
- "sample aliases still "
- "being considered.\n");
+ dev_err(dev->card->dev,
+ "support for sample aliases still being considered.\n");
break;
case WFC_VMIDI_OFF:
@@ -1730,10 +1741,10 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
return;
}
- spin_lock(&dev->irq_lock);
- dev->irq_ok = 1;
- dev->irq_cnt++;
- spin_unlock(&dev->irq_lock);
+ scoped_guard(spinlock, &dev->irq_lock) {
+ dev->irq_ok = 1;
+ dev->irq_cnt++;
+ }
wake_up(&dev->interrupt_sleeper);
}
@@ -1750,7 +1761,7 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
*/
static int
-snd_wavefront_interrupt_bits (int irq)
+snd_wavefront_interrupt_bits(snd_wavefront_t *dev, int irq)
{
int bits;
@@ -1770,7 +1781,7 @@ snd_wavefront_interrupt_bits (int irq)
break;
default:
- snd_printk ("invalid IRQ %d\n", irq);
+ dev_err(dev->card->dev, "invalid IRQ %d\n", irq);
bits = -1;
}
@@ -1785,11 +1796,11 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
wait_queue_entry_t wait;
init_waitqueue_entry(&wait, current);
- spin_lock_irq(&dev->irq_lock);
- add_wait_queue(&dev->interrupt_sleeper, &wait);
- dev->irq_ok = 0;
- outb (val,port);
- spin_unlock_irq(&dev->irq_lock);
+ scoped_guard(spinlock_irq, &dev->irq_lock) {
+ add_wait_queue(&dev->interrupt_sleeper, &wait);
+ dev->irq_ok = 0;
+ outb(val, port);
+ }
while (!dev->irq_ok && time_before(jiffies, timeout)) {
schedule_timeout_uninterruptible(1);
barrier();
@@ -1805,7 +1816,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
/* IRQ already checked */
- bits = snd_wavefront_interrupt_bits (dev->irq);
+ bits = snd_wavefront_interrupt_bits(dev, dev->irq);
/* try reset of port */
@@ -1875,7 +1886,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
*/
if (!dev->irq_ok) {
- snd_printk ("intr not received after h/w un-reset.\n");
+ dev_err(dev->card->dev, "intr not received after h/w un-reset.\n");
goto gone_bad;
}
@@ -1899,17 +1910,18 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
dev->data_port, ramcheck_time*HZ);
if (!dev->irq_ok) {
- snd_printk ("post-RAM-check interrupt not received.\n");
+ dev_err(dev->card->dev, "post-RAM-check interrupt not received.\n");
goto gone_bad;
}
if (!wavefront_wait (dev, STAT_CAN_READ)) {
- snd_printk ("no response to HW version cmd.\n");
+ dev_err(dev->card->dev, "no response to HW version cmd.\n");
goto gone_bad;
}
- if ((hwv[0] = wavefront_read (dev)) == -1) {
- snd_printk ("board not responding correctly.\n");
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
+ dev_err(dev->card->dev, "board not responding correctly.\n");
goto gone_bad;
}
@@ -1919,12 +1931,13 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
and tell us about it either way.
*/
- if ((hwv[0] = wavefront_read (dev)) == -1) {
- snd_printk ("on-board RAM test failed "
- "(bad error code).\n");
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
+ dev_err(dev->card->dev,
+ "on-board RAM test failed (bad error code).\n");
} else {
- snd_printk ("on-board RAM test failed "
- "(error code: 0x%x).\n",
+ dev_err(dev->card->dev,
+ "on-board RAM test failed (error code: 0x%x).\n",
hwv[0]);
}
goto gone_bad;
@@ -1932,13 +1945,14 @@ 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) {
- snd_printk ("incorrect h/w response.\n");
+ hwv[1] = wavefront_read(dev);
+ if (hwv[1] == -1) {
+ dev_err(dev->card->dev, "incorrect h/w response.\n");
goto gone_bad;
}
- snd_printk ("hardware version %d.%d\n",
- hwv[0], hwv[1]);
+ dev_info(dev->card->dev, "hardware version %d.%d\n",
+ hwv[0], hwv[1]);
return 0;
@@ -1958,7 +1972,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
err = request_firmware(&firmware, path, dev->card->dev);
if (err < 0) {
- snd_printk(KERN_ERR "firmware (%s) download failed!!!\n", path);
+ dev_err(dev->card->dev, "firmware (%s) download failed!!!\n", path);
return 1;
}
@@ -1969,16 +1983,16 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
if (section_length == 0)
break;
if (section_length < 0 || section_length > WF_SECTION_MAX) {
- snd_printk(KERN_ERR
- "invalid firmware section length %d\n",
- section_length);
+ dev_err(dev->card->dev,
+ "invalid firmware section length %d\n",
+ section_length);
goto failure;
}
buf++;
len++;
if (firmware->size < len + section_length) {
- snd_printk(KERN_ERR "firmware section read error.\n");
+ dev_err(dev->card->dev, "firmware section read error.\n");
goto failure;
}
@@ -1995,15 +2009,14 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
/* get ACK */
if (!wavefront_wait(dev, STAT_CAN_READ)) {
- snd_printk(KERN_ERR "time out for firmware ACK.\n");
+ dev_err(dev->card->dev, "time out for firmware ACK.\n");
goto failure;
}
err = inb(dev->data_port);
if (err != WF_ACK) {
- snd_printk(KERN_ERR
- "download of section #%d not "
- "acknowledged, ack = 0x%x\n",
- section_cnt_downloaded + 1, err);
+ dev_err(dev->card->dev,
+ "download of section #%d not acknowledged, ack = 0x%x\n",
+ section_cnt_downloaded + 1, err);
goto failure;
}
@@ -2015,7 +2028,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
failure:
release_firmware(firmware);
- snd_printk(KERN_ERR "firmware download failed!!!\n");
+ dev_err(dev->card->dev, "firmware download failed!!!\n");
return 1;
}
@@ -2027,7 +2040,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
char voices[1];
if (wavefront_reset_to_cleanliness (dev)) {
- snd_printk ("hw reset failed.\n");
+ dev_err(dev->card->dev, "hw reset failed.\n");
goto gone_bad;
}
@@ -2051,7 +2064,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
(osrun_time*HZ));
if (!dev->irq_ok) {
- snd_printk ("no post-OS interrupt.\n");
+ dev_err(dev->card->dev, "no post-OS interrupt.\n");
goto gone_bad;
}
@@ -2061,7 +2074,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
dev->data_port, (10*HZ));
if (!dev->irq_ok) {
- snd_printk ("no post-OS interrupt(2).\n");
+ dev_err(dev->card->dev, "no post-OS interrupt(2).\n");
goto gone_bad;
}
@@ -2077,24 +2090,24 @@ 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);
+ dev_info(dev->card->dev, "available DRAM %dk\n", dev->freemem / 1024);
if (wavefront_write (dev, 0xf0) ||
wavefront_write (dev, 1) ||
(wavefront_read (dev) < 0)) {
dev->debug = 0;
- snd_printk ("MPU emulation mode not set.\n");
+ dev_err(dev->card->dev, "MPU emulation mode not set.\n");
goto gone_bad;
}
voices[0] = 32;
if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, NULL, voices)) {
- snd_printk ("cannot set number of voices to 32.\n");
+ dev_err(dev->card->dev, "cannot set number of voices to 32.\n");
goto gone_bad;
}
@@ -2174,8 +2187,8 @@ snd_wavefront_detect (snd_wavefront_card_t *card)
dev->fw_version[0] = rbuf[0];
dev->fw_version[1] = rbuf[1];
- snd_printk ("firmware %d.%d already loaded.\n",
- rbuf[0], rbuf[1]);
+ dev_info(dev->card->dev, "firmware %d.%d already loaded.\n",
+ rbuf[0], rbuf[1]);
/* check that a command actually works */
@@ -2184,22 +2197,24 @@ snd_wavefront_detect (snd_wavefront_card_t *card)
dev->hw_version[0] = rbuf[0];
dev->hw_version[1] = rbuf[1];
} else {
- snd_printk ("not raw, but no "
- "hardware version!\n");
+ dev_err(dev->card->dev,
+ "not raw, but no hardware version!\n");
return -1;
}
if (!wf_raw) {
return 0;
} else {
- snd_printk ("reloading firmware as you requested.\n");
+ dev_info(dev->card->dev,
+ "reloading firmware as you requested.\n");
dev->israw = 1;
}
} else {
dev->israw = 1;
- snd_printk ("no response to firmware probe, assume raw.\n");
+ dev_info(dev->card->dev,
+ "no response to firmware probe, assume raw.\n");
}
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
index 454fee769a31..f23e71d0d5d4 100644
--- a/sound/isa/wss/Makefile
+++ b/sound/isa/wss/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wss-lib-objs := wss_lib.o
+snd-wss-lib-y := wss_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 8a852042a066..6cf88625bbc3 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>
@@ -52,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,
@@ -86,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 */
@@ -122,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 */
@@ -201,15 +187,16 @@ void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value)
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "out: auto calibration time out "
- "- reg = 0x%x, value = 0x%x\n", reg, value);
+ dev_dbg(chip->card->dev,
+ "out: auto calibration time out - reg = 0x%x, value = 0x%x\n",
+ reg, value);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
wss_outb(chip, CS4231P(REG), value);
chip->image[reg] = value;
mb();
- snd_printdd("codec out - reg 0x%x = 0x%x\n",
- chip->mce_bit | reg, value);
+ dev_dbg(chip->card->dev, "codec out - reg 0x%x = 0x%x\n",
+ chip->mce_bit | reg, value);
}
EXPORT_SYMBOL(snd_wss_out);
@@ -218,8 +205,8 @@ unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg)
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "in: auto calibration time out "
- "- reg = 0x%x\n", reg);
+ dev_dbg(chip->card->dev,
+ "in: auto calibration time out - reg = 0x%x\n", reg);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
mb();
@@ -236,7 +223,7 @@ void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg,
wss_outb(chip, CS4231P(REG), val);
chip->eimage[CS4236_REG(reg)] = val;
#if 0
- printk(KERN_DEBUG "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+ dev_dbg(chip->card->dev, "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
#endif
}
EXPORT_SYMBOL(snd_cs4236_ext_out);
@@ -252,8 +239,8 @@ unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg)
{
unsigned char res;
res = wss_inb(chip, CS4231P(REG));
- printk(KERN_DEBUG "ext in : reg = 0x%x, val = 0x%x\n",
- reg, res);
+ dev_dbg(chip->card->dev, "ext in : reg = 0x%x, val = 0x%x\n",
+ reg, res);
return res;
}
#endif
@@ -264,87 +251,87 @@ EXPORT_SYMBOL(snd_cs4236_ext_in);
static void snd_wss_debug(struct snd_wss *chip)
{
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"CS4231 REGS: INDEX = 0x%02x "
" STATUS = 0x%02x\n",
wss_inb(chip, CS4231P(REGSEL)),
wss_inb(chip, CS4231P(STATUS)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x00: left input = 0x%02x "
" 0x10: alt 1 (CFIG 2) = 0x%02x\n",
snd_wss_in(chip, 0x00),
snd_wss_in(chip, 0x10));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x01: right input = 0x%02x "
" 0x11: alt 2 (CFIG 3) = 0x%02x\n",
snd_wss_in(chip, 0x01),
snd_wss_in(chip, 0x11));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x02: GF1 left input = 0x%02x "
" 0x12: left line in = 0x%02x\n",
snd_wss_in(chip, 0x02),
snd_wss_in(chip, 0x12));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x03: GF1 right input = 0x%02x "
" 0x13: right line in = 0x%02x\n",
snd_wss_in(chip, 0x03),
snd_wss_in(chip, 0x13));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x04: CD left input = 0x%02x "
" 0x14: timer low = 0x%02x\n",
snd_wss_in(chip, 0x04),
snd_wss_in(chip, 0x14));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x05: CD right input = 0x%02x "
" 0x15: timer high = 0x%02x\n",
snd_wss_in(chip, 0x05),
snd_wss_in(chip, 0x15));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x06: left output = 0x%02x "
" 0x16: left MIC (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x06),
snd_wss_in(chip, 0x16));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x07: right output = 0x%02x "
" 0x17: right MIC (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x07),
snd_wss_in(chip, 0x17));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x08: playback format = 0x%02x "
" 0x18: IRQ status = 0x%02x\n",
snd_wss_in(chip, 0x08),
snd_wss_in(chip, 0x18));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x09: iface (CFIG 1) = 0x%02x "
" 0x19: left line out = 0x%02x\n",
snd_wss_in(chip, 0x09),
snd_wss_in(chip, 0x19));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0a: pin control = 0x%02x "
" 0x1a: mono control = 0x%02x\n",
snd_wss_in(chip, 0x0a),
snd_wss_in(chip, 0x1a));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0b: init & status = 0x%02x "
" 0x1b: right line out = 0x%02x\n",
snd_wss_in(chip, 0x0b),
snd_wss_in(chip, 0x1b));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0c: revision & mode = 0x%02x "
" 0x1c: record format = 0x%02x\n",
snd_wss_in(chip, 0x0c),
snd_wss_in(chip, 0x1c));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0d: loopback = 0x%02x "
" 0x1d: var freq (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x0d),
snd_wss_in(chip, 0x1d));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0e: ply upr count = 0x%02x "
" 0x1e: ply lwr count = 0x%02x\n",
snd_wss_in(chip, 0x0e),
snd_wss_in(chip, 0x1e));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0f: rec upr count = 0x%02x "
" 0x1f: rec lwr count = 0x%02x\n",
snd_wss_in(chip, 0x0f),
@@ -373,32 +360,29 @@ static void snd_wss_busy_wait(struct snd_wss *chip)
void snd_wss_mce_up(struct snd_wss *chip)
{
- unsigned long flags;
int timeout;
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG
- "mce_up - auto calibration time out (0)\n");
+ dev_dbg(chip->card->dev,
+ "mce_up - auto calibration time out (0)\n");
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->mce_bit |= CS4231_MCE;
timeout = wss_inb(chip, CS4231P(REGSEL));
if (timeout == 0x80)
- snd_printk(KERN_DEBUG "mce_up [0x%lx]: "
- "serious init problem - codec still busy\n",
- chip->port);
+ dev_dbg(chip->card->dev,
+ "mce_up [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if (!(timeout & CS4231_MCE))
wss_outb(chip, CS4231P(REGSEL),
chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
EXPORT_SYMBOL(snd_wss_mce_up);
void snd_wss_mce_down(struct snd_wss *chip)
{
- unsigned long flags;
unsigned long end_time;
int timeout;
int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;
@@ -407,19 +391,19 @@ void snd_wss_mce_down(struct snd_wss *chip)
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "mce_down [0x%lx] - "
- "auto calibration time out (0)\n",
- (long)CS4231P(REGSEL));
+ dev_dbg(chip->card->dev,
+ "mce_down [0x%lx] - auto calibration time out (0)\n",
+ (long)CS4231P(REGSEL));
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = wss_inb(chip, CS4231P(REGSEL));
- wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ }
if (timeout == 0x80)
- snd_printk(KERN_DEBUG "mce_down [0x%lx]: "
- "serious init problem - codec still busy\n",
- chip->port);
+ dev_dbg(chip->card->dev,
+ "mce_down [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
return;
@@ -430,7 +414,7 @@ void snd_wss_mce_down(struct snd_wss *chip)
*/
msleep(1);
- snd_printdd("(1) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "(1) jiffies = %lu\n", jiffies);
/* check condition up to 250 ms */
end_time = jiffies + msecs_to_jiffies(250);
@@ -438,27 +422,29 @@ void snd_wss_mce_down(struct snd_wss *chip)
CS4231_CALIB_IN_PROGRESS) {
if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - "
- "auto calibration time out (2)\n");
+ dev_err(chip->card->dev,
+ "mce_down - auto calibration time out (2)\n");
return;
}
msleep(1);
}
- snd_printdd("(2) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "(2) jiffies = %lu\n", jiffies);
/* check condition up to 100 ms */
end_time = jiffies + msecs_to_jiffies(100);
while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
+ dev_err(chip->card->dev,
+ "mce_down - auto calibration time out (3)\n");
return;
}
msleep(1);
}
- snd_printdd("(3) jiffies = %lu\n", jiffies);
- snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL)));
+ dev_dbg(chip->card->dev, "(3) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "mce_down - exit = 0x%x\n",
+ wss_inb(chip, CS4231P(REGSEL)));
}
EXPORT_SYMBOL(snd_wss_mce_down);
@@ -507,7 +493,7 @@ static int snd_wss_trigger(struct snd_pcm_substream *substream,
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (do_start) {
chip->image[CS4231_IFACE_CTRL] |= what;
if (chip->trigger)
@@ -518,7 +504,6 @@ static int snd_wss_trigger(struct snd_pcm_substream *substream,
chip->trigger(chip, what, 0);
}
snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock(&chip->reg_lock);
#if 0
snd_wss_debug(chip);
#endif
@@ -541,7 +526,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;
@@ -557,21 +542,18 @@ static unsigned char snd_wss_get_format(struct snd_wss *chip,
if (channels > 1)
rformat |= CS4231_STEREO;
#if 0
- snd_printk(KERN_DEBUG "get_format: 0x%x (mode=0x%x)\n", format, mode);
+ dev_dbg(chip->card->dev, "get_format: 0x%x (mode=0x%x)\n", format, mode);
#endif
return rformat;
}
static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
{
- unsigned long flags;
mute = mute ? 0x80 : 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->calibrate_mute == mute) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ if (chip->calibrate_mute == mute)
return;
- }
if (!mute) {
snd_wss_dout(chip, CS4231_LEFT_INPUT,
chip->image[CS4231_LEFT_INPUT]);
@@ -619,20 +601,18 @@ static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]);
}
chip->calibrate_mute = mute;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_wss_playback_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char pdfr)
{
- unsigned long flags;
int full_calib = 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x10);
@@ -644,7 +624,6 @@ static void snd_wss_playback_format(struct snd_wss *chip,
udelay(100); /* Fixes audible clicks at least on GUS MAX */
full_calib = 0;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
} else if (chip->hardware == WSS_HW_AD1845) {
unsigned rate = params_rate(params);
@@ -657,30 +636,28 @@ static void snd_wss_playback_format(struct snd_wss *chip,
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (pdfr & 0xf0));
snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
full_calib = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
- if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
- pdfr = (pdfr & 0xf0) |
- (chip->image[CS4231_REC_FORMAT] & 0x0f);
- } else {
- chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
+ if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
+ pdfr = (pdfr & 0xf0) |
+ (chip->image[CS4231_REC_FORMAT] & 0x0f);
+ } else {
+ chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ }
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
}
- snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (chip->hardware == WSS_HW_OPL3SA2)
udelay(100); /* this seems to help */
snd_wss_mce_down(chip);
}
- mutex_unlock(&chip->mce_mutex);
}
static void snd_wss_capture_format(struct snd_wss *chip,
@@ -690,10 +667,10 @@ static void snd_wss_capture_format(struct snd_wss *chip,
unsigned long flags;
int full_calib = 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */
(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
@@ -704,7 +681,6 @@ static void snd_wss_capture_format(struct snd_wss *chip,
chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
full_calib = 0;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
} else if (chip->hardware == WSS_HW_AD1845) {
unsigned rate = params_rate(params);
@@ -717,12 +693,11 @@ static void snd_wss_capture_format(struct snd_wss *chip,
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_REC_FORMAT, (cdfr & 0xf0));
snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
full_calib = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
@@ -747,7 +722,6 @@ static void snd_wss_capture_format(struct snd_wss *chip,
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
}
- mutex_unlock(&chip->mce_mutex);
}
/*
@@ -765,10 +739,10 @@ static unsigned long snd_wss_timer_resolution(struct snd_timer *timer)
static int snd_wss_timer_start(struct snd_timer *timer)
{
- unsigned long flags;
unsigned int ticks;
struct snd_wss *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ticks = timer->sticks;
if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
(unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
@@ -783,109 +757,100 @@ static int snd_wss_timer_start(struct snd_timer *timer)
chip->image[CS4231_ALT_FEATURE_1] |
CS4231_TIMER_ENABLE);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_wss_timer_stop(struct snd_timer *timer)
{
- unsigned long flags;
struct snd_wss *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static void snd_wss_init(struct snd_wss *chip)
{
- unsigned long flags;
-
snd_wss_calibrate_mute(chip, 1);
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (1)\n");
+ dev_dbg(chip->card->dev, "init: (1)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
- CS4231_PLAYBACK_PIO |
- CS4231_RECORD_ENABLE |
- CS4231_RECORD_PIO |
- CS4231_CALIB_MODE);
- chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
- snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
+ CS4231_PLAYBACK_PIO |
+ CS4231_RECORD_ENABLE |
+ CS4231_RECORD_PIO |
+ CS4231_CALIB_MODE);
+ chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (2)\n");
+ dev_dbg(chip->card->dev, "init: (2)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
- snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- snd_wss_out(chip,
- CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ snd_wss_out(chip,
+ CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (3) - afei = 0x%x\n",
- chip->image[CS4231_ALT_FEATURE_1]);
+ dev_dbg(chip->card->dev, "init: (3) - afei = 0x%x\n",
+ chip->image[CS4231_ALT_FEATURE_1]);
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_ALT_FEATURE_2,
- chip->image[CS4231_ALT_FEATURE_2]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, CS4231_ALT_FEATURE_2,
+ chip->image[CS4231_ALT_FEATURE_2]);
+ }
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
- chip->image[CS4231_PLAYBK_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (4)\n");
+ dev_dbg(chip->card->dev, "init: (4)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (!(chip->hardware & WSS_HW_AD1848_MASK))
- snd_wss_out(chip, CS4231_REC_FORMAT,
- chip->image[CS4231_REC_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ snd_wss_out(chip, CS4231_REC_FORMAT,
+ chip->image[CS4231_REC_FORMAT]);
+ }
snd_wss_mce_down(chip);
snd_wss_calibrate_mute(chip, 0);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (5)\n");
+ dev_dbg(chip->card->dev, "init: (5)\n");
#endif
}
static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
{
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
if ((chip->mode & mode) ||
- ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) {
- mutex_unlock(&chip->open_mutex);
+ ((chip->mode & WSS_MODE_OPEN) && chip->single_dma))
return -EAGAIN;
- }
if (chip->mode & WSS_MODE_OPEN) {
chip->mode |= mode;
- mutex_unlock(&chip->open_mutex);
return 0;
}
/* ok. now enable and ack CODEC IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
snd_wss_out(chip, CS4231_IRQ_STATUS,
CS4231_PLAYBACK_IRQ |
@@ -904,10 +869,8 @@ static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
CS4231_TIMER_IRQ);
snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
chip->mode = mode;
- mutex_unlock(&chip->open_mutex);
return 0;
}
@@ -915,12 +878,10 @@ static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
{
unsigned long flags;
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
chip->mode &= ~mode;
- if (chip->mode & WSS_MODE_OPEN) {
- mutex_unlock(&chip->open_mutex);
+ if (chip->mode & WSS_MODE_OPEN)
return;
- }
/* disable IRQ */
spin_lock_irqsave(&chip->reg_lock, flags);
if (!(chip->hardware & WSS_HW_AD1848_MASK))
@@ -954,7 +915,6 @@ static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
spin_unlock_irqrestore(&chip->reg_lock, flags);
chip->mode = 0;
- mutex_unlock(&chip->open_mutex);
}
/*
@@ -975,7 +935,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,
@@ -996,10 +956,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));
@@ -1007,27 +964,20 @@ 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);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->p_dma_size = size;
chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
#if 0
snd_wss_debug(chip);
#endif
@@ -1039,10 +989,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));
@@ -1050,20 +997,14 @@ 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);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->c_dma_size = size;
chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
@@ -1083,18 +1024,16 @@ static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
snd_wss_out(chip, CS4231_REC_UPR_CNT,
(unsigned char) (count >> 8));
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
void snd_wss_overrange(struct snd_wss *chip)
{
- unsigned long flags;
unsigned char res;
- spin_lock_irqsave(&chip->reg_lock, flags);
- res = snd_wss_in(chip, CS4231_TEST_INIT);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ res = snd_wss_in(chip, CS4231_TEST_INIT);
+ }
if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */
chip->capture_substream->runtime->overrange++;
}
@@ -1140,13 +1079,12 @@ irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
}
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
status = ~CS4231_ALL_IRQS | ~status;
if (chip->hardware & WSS_HW_AD1848_MASK)
wss_outb(chip, CS4231P(STATUS), 0);
else
snd_wss_out(chip, CS4231_IRQ_STATUS, status);
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(snd_wss_interrupt);
@@ -1180,10 +1118,8 @@ static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *subst
static int snd_ad1848_probe(struct snd_wss *chip)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- unsigned long flags;
unsigned char r;
unsigned short hardware = 0;
- int err = 0;
int i;
while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
@@ -1191,7 +1127,7 @@ static int snd_ad1848_probe(struct snd_wss *chip)
return -ENODEV;
cond_resched();
}
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set CS423x MODE 1 */
snd_wss_dout(chip, CS4231_MISC_INFO, 0);
@@ -1200,19 +1136,15 @@ static int snd_ad1848_probe(struct snd_wss *chip)
r = snd_wss_in(chip, CS4231_RIGHT_INPUT);
if (r != 0x45) {
/* RMGE always high on AD1847 */
- if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) {
- err = -ENODEV;
- goto out;
- }
+ if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45)
+ return -ENODEV;
hardware = WSS_HW_AD1847;
} else {
snd_wss_dout(chip, CS4231_LEFT_INPUT, 0xaa);
r = snd_wss_in(chip, CS4231_LEFT_INPUT);
/* L/RMGE always low on AT2320 */
- if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) {
- err = -ENODEV;
- goto out;
- }
+ if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa)
+ return -ENODEV;
}
/* clear pending IRQ */
@@ -1221,11 +1153,11 @@ static int snd_ad1848_probe(struct snd_wss *chip)
mb();
if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT)
- goto out;
+ return 0;
if (hardware) {
chip->hardware = hardware;
- goto out;
+ return 0;
}
r = snd_wss_in(chip, CS4231_MISC_INFO);
@@ -1254,14 +1186,11 @@ static int snd_ad1848_probe(struct snd_wss *chip)
chip->hardware = WSS_HW_AD1848;
out_mode:
snd_wss_dout(chip, CS4231_MISC_INFO, 0);
-out:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return err;
+ return 0;
}
static int snd_wss_probe(struct snd_wss *chip)
{
- unsigned long flags;
int i, id, rev, regnum;
unsigned char *ptr;
unsigned int hw;
@@ -1277,21 +1206,21 @@ static int snd_wss_probe(struct snd_wss *chip)
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
msleep(2);
else {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_MISC_INFO,
CS4231_MODE2);
id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (id == 0x0a)
break; /* this is valid value */
}
}
- snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
+ dev_dbg(chip->card->dev, "wss: port = 0x%lx, id = 0x%x\n",
+ chip->port, id);
if (id != 0x0a)
return -ENODEV; /* no valid device found */
rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
- snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+ dev_dbg(chip->card->dev, "CS4231: VERSION (I25) = 0x%x\n", rev);
if (rev == 0x80) {
unsigned char tmp = snd_wss_in(chip, 23);
snd_wss_out(chip, 23, ~tmp);
@@ -1310,16 +1239,16 @@ static int snd_wss_probe(struct snd_wss *chip)
} else if (rev == 0x03) {
chip->hardware = WSS_HW_CS4236B;
} else {
- snd_printk(KERN_ERR
- "unknown CS chip with version 0x%x\n", rev);
+ dev_err(chip->card->dev,
+ "unknown CS chip with version 0x%x\n", rev);
return -ENODEV; /* unknown CS4231 chip? */
}
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- wss_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
- wss_outb(chip, CS4231P(STATUS), 0);
- mb();
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ wss_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0);
+ mb();
+ }
if (!(chip->hardware & WSS_HW_AD1848_MASK))
chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
@@ -1354,10 +1283,10 @@ static int snd_wss_probe(struct snd_wss *chip)
ptr = (unsigned char *) &chip->image;
regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
snd_wss_mce_down(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (i = 0; i < regnum; i++) /* ok.. fill all registers */
- snd_wss_out(chip, i, *ptr++);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (i = 0; i < regnum; i++) /* ok.. fill all registers */
+ snd_wss_out(chip, i, *ptr++);
+ }
snd_wss_mce_up(chip);
snd_wss_mce_down(chip);
@@ -1370,7 +1299,9 @@ static int snd_wss_probe(struct snd_wss *chip)
snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
id = snd_cs4236_ext_in(chip, CS4236_VERSION);
snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
- snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+ dev_dbg(chip->card->dev,
+ "CS4231: ext version; rev = 0x%x, id = 0x%x\n",
+ rev, id);
if ((id & 0x1f) == 0x1d) { /* CS4235 */
chip->hardware = WSS_HW_CS4235;
switch (id >> 5) {
@@ -1379,10 +1310,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 6:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4235 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4235 chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
switch (id >> 5) {
@@ -1393,10 +1323,9 @@ static int snd_wss_probe(struct snd_wss *chip)
chip->hardware = WSS_HW_CS4236B;
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4236 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4236 chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x08) { /* CS4237B */
chip->hardware = WSS_HW_CS4237B;
@@ -1407,10 +1336,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 7:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4237B chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4237B chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x09) { /* CS4238B */
chip->hardware = WSS_HW_CS4238B;
@@ -1420,10 +1348,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 7:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4238B chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4238B chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x1e) { /* CS4239 */
chip->hardware = WSS_HW_CS4239;
@@ -1433,15 +1360,14 @@ static int snd_wss_probe(struct snd_wss *chip)
case 6:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4239 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4239 chip (enhanced version = 0x%x)\n",
+ id);
}
} else {
- snd_printk(KERN_WARNING
- "unknown CS4236/CS423xB chip "
- "(enhanced version = 0x%x)\n", id);
+ dev_warn(chip->card->dev,
+ "unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n",
+ id);
}
}
}
@@ -1523,7 +1449,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 +1458,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 +1490,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 +1499,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;
@@ -1625,13 +1551,11 @@ static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on)
static void snd_wss_suspend(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
- snd_pcm_suspend_all(chip->pcm);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++)
- chip->image[reg] = snd_wss_in(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++)
+ chip->image[reg] = snd_wss_in(chip, reg);
+ }
if (chip->thinkpad_flag)
snd_wss_thinkpad_twiddle(chip, 0);
}
@@ -1640,27 +1564,26 @@ static void snd_wss_suspend(struct snd_wss *chip)
static void snd_wss_resume(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
/* int timeout; */
if (chip->thinkpad_flag)
snd_wss_thinkpad_twiddle(chip, 1);
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++) {
- switch (reg) {
- case CS4231_VERSION:
- break;
- default:
- snd_wss_out(chip, reg, chip->image[reg]);
- break;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++) {
+ switch (reg) {
+ case CS4231_VERSION:
+ break;
+ default:
+ snd_wss_out(chip, reg, chip->image[reg]);
+ 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]);
}
- /* 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);
#else
@@ -1669,14 +1592,15 @@ static void snd_wss_resume(struct snd_wss *chip)
include rescheduling. -- iwai
*/
snd_wss_busy_wait(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = wss_inb(chip, CS4231P(REGSEL));
- wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ }
if (timeout == 0x80)
- snd_printk(KERN_ERR "down [0x%lx]: serious init problem "
- "- codec still busy\n", chip->port);
+ dev_err(chip->card->dev
+ "down [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if ((timeout & CS4231_MCE) == 0 ||
!(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
return;
@@ -1686,36 +1610,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) {
@@ -1769,7 +1663,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;
@@ -1805,9 +1699,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;
@@ -1819,41 +1710,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);
+ dev_err(chip->card->dev, "wss: can't grab port 0x%lx\n", port);
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
+ dev_err(chip->card->dev,
"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, 0,
- "WSS", (void *) chip)) {
- snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
- snd_wss_free(chip);
+ if (devm_request_irq(card->dev, irq, snd_wss_interrupt, 0,
+ "WSS", (void *) chip)) {
+ dev_err(chip->card->dev, "wss: can't grab IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
- if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) {
- snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
- snd_wss_free(chip);
+ card->sync_irq = chip->irq;
+ if (!(hwshare & WSS_HWSHARE_DMA1) &&
+ snd_devm_request_dma(card->dev, dma1, "WSS - 1")) {
+ dev_err(chip->card->dev, "wss: can't grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 &&
- dma2 >= 0 && request_dma(dma2, "WSS - 2")) {
- snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
- snd_wss_free(chip);
+ if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 &&
+ snd_devm_request_dma(card->dev, dma2, "WSS - 2")) {
+ dev_err(chip->card->dev, "wss: can't grab DMA2 %d\n", dma2);
return -EBUSY;
}
if (dma1 == dma2 || dma2 < 0) {
@@ -1869,27 +1758,18 @@ 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
if (chip->hardware & WSS_HW_CS4232_MASK) {
if (chip->res_cport == NULL)
- snd_printk(KERN_ERR "CS4232 control port features are "
- "not accessible\n");
+ dev_err(chip->card->dev,
+ "CS4232 control port features are not accessible\n");
}
#endif
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_wss_free(chip);
- return err;
- }
-
#ifdef CONFIG_PM
/* Power Management */
chip->suspend = snd_wss_suspend;
@@ -1904,9 +1784,7 @@ EXPORT_SYMBOL(snd_wss_create);
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,
@@ -1915,9 +1793,7 @@ static const struct snd_pcm_ops snd_wss_playback_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,
@@ -1942,11 +1818,10 @@ int snd_wss_pcm(struct snd_wss *chip, int device)
pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
if (chip->hardware != WSS_HW_INTERWAVE)
pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
- strcpy(pcm->name, snd_wss_chip_id(chip));
+ strscpy(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;
return 0;
@@ -1971,9 +1846,10 @@ int snd_wss_timer(struct snd_wss *chip, int device)
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));
+ strscpy(timer->name, snd_wss_chip_id(chip));
timer->private_data = chip;
timer->private_free = snd_wss_timer_free;
timer->hw = snd_wss_timer_table;
@@ -2021,12 +1897,10 @@ static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -2034,7 +1908,6 @@ static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short left, right;
int change;
@@ -2043,14 +1916,13 @@ static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
return -EINVAL;
left = ucontrol->value.enumerated.item[0] << 6;
right = ucontrol->value.enumerated.item[1] << 6;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
change = left != chip->image[CS4231_LEFT_INPUT] ||
right != chip->image[CS4231_RIGHT_INPUT];
snd_wss_out(chip, CS4231_LEFT_INPUT, left);
snd_wss_out(chip, CS4231_RIGHT_INPUT, right);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -2071,15 +1943,13 @@ int snd_wss_get_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -2090,7 +1960,6 @@ int snd_wss_put_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -2102,11 +1971,10 @@ int snd_wss_put_single(struct snd_kcontrol *kcontrol,
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->image[reg] & ~(mask << shift)) | val;
change = val != chip->image[reg];
snd_wss_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
EXPORT_SYMBOL(snd_wss_put_single);
@@ -2128,7 +1996,6 @@ int snd_wss_get_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -2136,10 +2003,9 @@ int snd_wss_get_double(struct snd_kcontrol *kcontrol,
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -2152,7 +2018,6 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -2170,7 +2035,7 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
@@ -2184,7 +2049,6 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
change = val1 != chip->image[left_reg];
snd_wss_out(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
EXPORT_SYMBOL(snd_wss_put_double);
@@ -2194,7 +2058,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,
@@ -2253,7 +2117,7 @@ int snd_wss_mixer(struct snd_wss *chip)
card = chip->card;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
/* Use only the first 11 entries on AD1848 */
if (chip->hardware & WSS_HW_AD1848_MASK)
@@ -2279,19 +2143,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 43f222825038..f0bb98780e70 100644
--- a/sound/last.c
+++ b/sound/last.c
@@ -1,22 +1,7 @@
+// 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>
@@ -24,14 +9,18 @@
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;
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 4a4705031cb9..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
@@ -13,15 +14,22 @@ config SND_SGI_O2
tristate "SGI O2 Audio"
depends on SGI_IP32
select SND_PCM
- help
- Sound support for the SGI O2 Workstation.
+ help
+ Sound support for the SGI O2 Workstation.
config SND_SGI_HAL2
- tristate "SGI HAL2 Audio"
- depends on SGI_HAS_HAL2
+ tristate "SGI HAL2 Audio"
+ depends on SGI_HAS_HAL2
select SND_PCM
- help
- Sound support for the SGI Indy and Indigo2 Workstation.
+ help
+ Sound support for the SGI Indy and Indigo2 Workstation.
+
+config SND_N64
+ bool "N64 Audio"
+ depends on MACH_NINTENDO64 && SND=y
+ select SND_PCM
+ help
+ Sound support for the N64.
endif # SND_MIPS
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index b977c44330d6..bfbf3bda487b 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -1,10 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-snd-sgi-o2-objs := sgio2audio.o ad1843.o
-snd-sgi-hal2-objs := hal2.o
+snd-sgi-o2-y := sgio2audio.o ad1843.o
+snd-sgi-hal2-y := hal2.o
# Toplevel Module Dependency
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 586907500ca5..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>
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
index c8904e732aaa..f88e6a6733a5 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>
@@ -454,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_attrs(NULL, H2_BUF_SIZE, &buffer_dma,
- GFP_KERNEL, DMA_ATTR_NON_CONSISTENT);
+ codec->buffer = dma_alloc_noncoherent(dev, H2_BUF_SIZE, &buffer_dma,
+ buffer_dir, GFP_KERNEL);
if (!codec->buffer)
return -ENOMEM;
- desc = dma_alloc_attrs(NULL, count * sizeof(struct hal2_desc),
- &desc_dma, GFP_KERNEL, DMA_ATTR_NON_CONSISTENT);
+ desc = dma_alloc_noncoherent(dev, count * sizeof(struct hal2_desc),
+ &desc_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
if (!desc) {
- dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, buffer_dma,
- DMA_ATTR_NON_CONSISTENT);
+ dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, buffer_dma,
+ buffer_dir);
return -ENOMEM;
}
codec->buffer_dma = buffer_dma;
@@ -482,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_attrs(NULL, codec->desc_count * sizeof(struct hal2_desc),
- codec->desc, codec->desc_dma, DMA_ATTR_NON_CONSISTENT);
- dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, codec->buffer_dma,
- DMA_ATTR_NON_CONSISTENT);
+ 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 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,
@@ -514,42 +508,20 @@ static const 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;
}
@@ -563,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);
@@ -575,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:
@@ -606,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);
}
@@ -615,7 +588,6 @@ 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;
return snd_pcm_indirect_playback_transfer(substream,
&dac->pcm_indirect,
hal2_playback_transfer);
@@ -625,22 +597,16 @@ 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;
}
@@ -655,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);
@@ -667,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:
@@ -697,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);
}
@@ -714,9 +680,6 @@ static int hal2_capture_ack(struct snd_pcm_substream *substream)
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,
@@ -726,9 +689,6 @@ static const struct snd_pcm_ops hal2_playback_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,
@@ -746,16 +706,15 @@ static int hal2_pcm_create(struct snd_hal2 *hal2)
return err;
pcm->private_data = hal2;
- strcpy(pcm->name, "SGI HAL2");
+ strscpy(pcm->name, "SGI HAL2");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&hal2_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&hal2_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, 1024 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 0, 1024 * 1024);
return 0;
}
@@ -769,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,
};
@@ -903,8 +862,8 @@ static int hal2_probe(struct platform_device *pdev)
return err;
}
- strcpy(card->driver, "SGI HAL2 Audio");
- strcpy(card->shortname, "SGI HAL2 Audio");
+ strscpy(card->driver, "SGI HAL2 Audio");
+ strscpy(card->shortname, "SGI HAL2 Audio");
sprintf(card->longname, "%s irq %i",
card->shortname,
SGI_HPCDMA_IRQ);
@@ -918,17 +877,16 @@ static int hal2_probe(struct platform_device *pdev)
return 0;
}
-static int 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);
- return 0;
}
static struct platform_driver hal2_driver = {
.probe = hal2_probe,
- .remove = hal2_remove,
+ .remove = hal2_remove,
.driver = {
.name = "sgihal2",
}
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 9fb68b35de5a..077fdf2181c1 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/string.h>
#include <linux/module.h>
#include <asm/ip32/ip32_ints.h>
@@ -46,7 +33,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 */
@@ -117,9 +103,8 @@ static int read_ad1843_reg(void *priv, int reg)
{
struct snd_sgio2audio *chip = priv;
int val;
- unsigned long flags;
- spin_lock_irqsave(&chip->ad1843_lock, flags);
+ guard(spinlock_irqsave)(&chip->ad1843_lock);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
@@ -129,7 +114,6 @@ static int read_ad1843_reg(void *priv, int reg)
val = readq(&mace->perif.audio.codec_read);
- spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return val;
}
@@ -140,9 +124,8 @@ static int write_ad1843_reg(void *priv, int reg, int word)
{
struct snd_sgio2audio *chip = priv;
int val;
- unsigned long flags;
- spin_lock_irqsave(&chip->ad1843_lock, flags);
+ guard(spinlock_irqsave)(&chip->ad1843_lock);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
(word << CODEC_CONTROL_WORD_SHIFT),
@@ -151,7 +134,6 @@ static int write_ad1843_reg(void *priv, int reg, int word)
val = readq(&mace->perif.audio.codec_control); /* flush bus */
udelay(200);
- spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return 0;
}
@@ -365,10 +347,9 @@ static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
u64 *src;
s16 *dst;
u64 x;
- unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
@@ -397,7 +378,6 @@ static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
chip->channel[ch].pos = dst_pos;
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
@@ -413,10 +393,9 @@ static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
int src_pos;
u64 *dst;
s16 *src;
- unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
@@ -447,7 +426,6 @@ static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
chip->channel[ch].pos = src_pos;
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
@@ -591,21 +569,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)
{
@@ -613,9 +576,8 @@ static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
int ch = chan->idx;
- unsigned long flags;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
/* Setup the pseudo-dma transfer pointers. */
chip->channel[ch].pos = 0;
@@ -639,7 +601,6 @@ static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
runtime->channels);
break;
}
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return 0;
}
@@ -678,40 +639,25 @@ snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
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 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 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,
};
/*
@@ -730,13 +676,14 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "SGI O2 DAC1");
+ strscpy(pcm->name, "SGI O2 DAC1");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback1_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_sgio2audio_capture_ops);
+ 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);
@@ -744,11 +691,12 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "SGI O2 DAC2");
+ strscpy(pcm->name, "SGI O2 DAC2");
/* 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;
}
@@ -808,7 +756,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 */
@@ -823,7 +771,7 @@ 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,
};
@@ -846,8 +794,9 @@ static int snd_sgio2audio_create(struct snd_card *card,
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");
@@ -934,8 +883,8 @@ static int snd_sgio2audio_probe(struct platform_device *pdev)
return err;
}
- strcpy(card->driver, "SGI O2 Audio");
- strcpy(card->shortname, "SGI O2 Audio");
+ strscpy(card->driver, "SGI O2 Audio");
+ strscpy(card->shortname, "SGI O2 Audio");
sprintf(card->longname, "%s irq %i-%i",
card->shortname,
MACEISA_AUDIO1_DMAT_IRQ,
@@ -950,18 +899,17 @@ static int snd_sgio2audio_probe(struct platform_device *pdev)
return 0;
}
-static int 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);
- return 0;
}
static struct platform_driver sgio2audio_driver = {
.probe = snd_sgio2audio_probe,
.remove = snd_sgio2audio_remove,
- .driver = {
+ .driver = {
.name = "sgio2audio",
}
};
diff --git a/sound/mips/snd-n64.c b/sound/mips/snd-n64.c
new file mode 100644
index 000000000000..f17e63f2ff5a
--- /dev/null
+++ b/sound/mips/snd-n64.c
@@ -0,0 +1,369 @@
+// 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 <linux/string.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;
+ u32 count;
+
+ guard(spinlock_irqsave)(&priv->chan.lock);
+
+ 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;
+}
+
+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);
+
+ // 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)) {
+ scoped_guard(spinlock_irqsave, &priv->chan.lock) {
+ priv->chan.pos = priv->chan.nextpos;
+ }
+
+ 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);
+
+ guard(spinlock_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);
+
+ 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;
+ strscpy(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);
+
+ strscpy(card->driver, "N64 Audio");
+ strscpy(card->shortname, "N64 Audio");
+ strscpy(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 12a3920d6fb6..ac678430408b 100644
--- a/sound/oss/.gitignore
+++ b/sound/oss/.gitignore
@@ -1,3 +1,3 @@
-#Ignore generated files
+# SPDX-License-Identifier: GPL-2.0-only
pss_boot.h
trix_boot.h
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 c1c52b479da2..f065840c0efb 100644
--- a/sound/oss/dmasound/dmasound.h
+++ b/sound/oss/dmasound/dmasound.h
@@ -88,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 */
@@ -114,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);
@@ -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 a1a2979c0bb1..6188469de8af 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
*
@@ -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);
@@ -1617,4 +1618,6 @@ static void __exit dmasound_atari_cleanup(void)
module_init(dmasound_atari_init);
module_exit(dmasound_atari_cleanup);
+
+MODULE_DESCRIPTION("Atari TT and Falcon DMA Sound Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 6b57f8aac1b7..dea2d9b18fc9 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -204,14 +204,13 @@ module_param(numWriteBufs, int, 0);
static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */
module_param(writeBufSize, int, 0);
+MODULE_DESCRIPTION("Atari/Amiga/Q40 core DMA sound driver");
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;
@@ -355,8 +354,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;
@@ -382,17 +381,14 @@ static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
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;
@@ -420,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++) {
@@ -684,7 +680,7 @@ static __poll_t 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;
}
@@ -998,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.
@@ -1019,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.
@@ -1043,7 +1036,6 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
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:
@@ -1122,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:
/*
*/
@@ -1163,10 +1154,10 @@ static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
static const struct file_operations sq_fops =
{
.owner = THIS_MODULE,
- .llseek = no_llseek,
.write = sq_write,
.poll = sq_poll,
.unlocked_ioctl = sq_unlocked_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = sq_open,
.release = sq_release,
};
@@ -1174,9 +1165,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) {
@@ -1232,31 +1220,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 ;
}
@@ -1370,7 +1349,6 @@ static ssize_t state_read(struct file *file, char __user *buf, size_t count,
static const struct file_operations state_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = state_read,
.open = state_open,
.release = state_release,
@@ -1378,9 +1356,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 ;
@@ -1398,10 +1373,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. */
@@ -1420,9 +1394,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) +
@@ -1436,8 +1408,6 @@ int dmasound_init(void)
return 0;
}
-#ifdef MODULE
-
void dmasound_deinit(void)
{
if (irq_installed) {
@@ -1456,9 +1426,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;
@@ -1476,13 +1444,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 ;
@@ -1501,8 +1469,6 @@ static int dmasound_setup(char *str)
__setup("dmasound=", dmasound_setup);
-#endif /* !MODULE */
-
/*
* Conversion tables
*/
@@ -1589,9 +1555,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 81eb82c4675a..8d443a3663d3 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
*
@@ -719,20 +720,26 @@ static int __init amiga_audio_probe(struct platform_device *pdev)
return dmasound_init();
}
-static int __exit amiga_audio_remove(struct platform_device *pdev)
+static void __exit amiga_audio_remove(struct platform_device *pdev)
{
dmasound_deinit();
- return 0;
}
-static struct platform_driver amiga_audio_driver = {
+/*
+ * amiga_audio_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver amiga_audio_driver __refdata = {
.remove = __exit_p(amiga_audio_remove),
- .driver = {
+ .driver = {
.name = "amiga-audio",
},
};
module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
+MODULE_DESCRIPTION("Amiga Paula DMA Sound Driver");
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 be4fe15cfd6b..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
*
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..84c71490fb72 100644
--- a/sound/parisc/Makefile
+++ b/sound/parisc/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-snd-harmony-objs := harmony.o
+snd-harmony-y := harmony.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_HARMONY) += snd-harmony.o
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index f36e7006e00c..4b5a54da25fb 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>
@@ -153,32 +140,25 @@ harmony_enable_interrupts(struct snd_harmony *h)
static void
harmony_mute(struct snd_harmony *h)
{
- unsigned long flags;
-
- spin_lock_irqsave(&h->mixer_lock, flags);
+ guard(spinlock_irqsave)(&h->mixer_lock);
harmony_wait_for_control(h);
harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
- spin_unlock_irqrestore(&h->mixer_lock, flags);
}
static void
harmony_unmute(struct snd_harmony *h)
{
- unsigned long flags;
-
- spin_lock_irqsave(&h->mixer_lock, flags);
+ guard(spinlock_irqsave)(&h->mixer_lock);
harmony_wait_for_control(h);
harmony_write(h, HARMONY_GAINCTL, h->st.gain);
- spin_unlock_irqrestore(&h->mixer_lock, flags);
}
static void
harmony_set_control(struct snd_harmony *h)
{
u32 ctrl;
- unsigned long flags;
- spin_lock_irqsave(&h->lock, flags);
+ guard(spinlock_irqsave)(&h->lock);
ctrl = (HARMONY_CNTL_C |
(h->st.format << 6) |
@@ -187,8 +167,6 @@ harmony_set_control(struct snd_harmony *h)
harmony_wait_for_control(h);
harmony_write(h, HARMONY_CNTL, ctrl);
-
- spin_unlock_irqrestore(&h->lock, flags);
}
static irqreturn_t
@@ -197,53 +175,53 @@ snd_harmony_interrupt(int irq, void *dev)
u32 dstatus;
struct snd_harmony *h = dev;
- spin_lock(&h->lock);
- harmony_disable_interrupts(h);
- harmony_wait_for_control(h);
- dstatus = harmony_read(h, HARMONY_DSTATUS);
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_disable_interrupts(h);
+ harmony_wait_for_control(h);
+ dstatus = harmony_read(h, HARMONY_DSTATUS);
+ }
if (dstatus & HARMONY_DSTATUS_PN) {
if (h->psubs && h->st.playing) {
- spin_lock(&h->lock);
- h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
- h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
-
- harmony_write(h, HARMONY_PNXTADD,
- h->pbuf.addr + h->pbuf.buf);
- h->stats.play_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
+ h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
+
+ harmony_write(h, HARMONY_PNXTADD,
+ h->pbuf.addr + h->pbuf.buf);
+ h->stats.play_intr++;
+ }
snd_pcm_period_elapsed(h->psubs);
} else {
- spin_lock(&h->lock);
- harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
- h->stats.silence_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
+ h->stats.silence_intr++;
+ }
}
}
if (dstatus & HARMONY_DSTATUS_RN) {
if (h->csubs && h->st.capturing) {
- spin_lock(&h->lock);
- h->cbuf.buf += h->cbuf.count;
- h->cbuf.buf %= h->cbuf.size;
-
- harmony_write(h, HARMONY_RNXTADD,
- h->cbuf.addr + h->cbuf.buf);
- h->stats.rec_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ h->cbuf.buf += h->cbuf.count;
+ h->cbuf.buf %= h->cbuf.size;
+
+ harmony_write(h, HARMONY_RNXTADD,
+ h->cbuf.addr + h->cbuf.buf);
+ h->stats.rec_intr++;
+ }
snd_pcm_period_elapsed(h->csubs);
} else {
- spin_lock(&h->lock);
- harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
- h->stats.graveyard_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
+ h->stats.graveyard_intr++;
+ }
}
}
- spin_lock(&h->lock);
- harmony_enable_interrupts(h);
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_enable_interrupts(h);
+ }
return IRQ_HANDLED;
}
@@ -310,7 +288,7 @@ snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
if (h->st.capturing)
return -EBUSY;
- spin_lock(&h->lock);
+ guard(spinlock)(&h->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
h->st.playing = 1;
@@ -329,11 +307,9 @@ snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_SUSPEND:
default:
- spin_unlock(&h->lock);
snd_BUG();
return -EINVAL;
}
- spin_unlock(&h->lock);
return 0;
}
@@ -346,7 +322,7 @@ snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
if (h->st.playing)
return -EBUSY;
- spin_lock(&h->lock);
+ guard(spinlock)(&h->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
h->st.capturing = 1;
@@ -365,11 +341,9 @@ snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_SUSPEND:
default:
- spin_unlock(&h->lock);
snd_BUG();
return -EINVAL;
}
- spin_unlock(&h->lock);
return 0;
}
@@ -576,32 +550,9 @@ 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 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,
@@ -610,9 +561,6 @@ static const struct snd_pcm_ops snd_harmony_playback_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,
@@ -640,7 +588,7 @@ snd_harmony_pcm_init(struct snd_harmony *h)
pcm->private_data = h;
pcm->info_flags = 0;
- strcpy(pcm->name, "harmony");
+ strscpy(pcm->name, "harmony");
h->pcm = pcm;
h->psubs = NULL;
@@ -669,14 +617,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);
@@ -719,7 +661,7 @@ snd_harmony_volume_get(struct snd_kcontrol *kc,
int invert = (kc->private_value >> 24) & 0xff;
int left, right;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
left = (h->st.gain >> shift_left) & mask;
right = (h->st.gain >> shift_right) & mask;
@@ -732,8 +674,6 @@ snd_harmony_volume_get(struct snd_kcontrol *kc,
if (shift_left != shift_right)
ucontrol->value.integer.value[1] = right;
- spin_unlock_irq(&h->mixer_lock);
-
return 0;
}
@@ -749,7 +689,7 @@ snd_harmony_volume_put(struct snd_kcontrol *kc,
int left, right;
int old_gain = h->st.gain;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
left = ucontrol->value.integer.value[0] & mask;
if (invert)
@@ -767,8 +707,6 @@ snd_harmony_volume_put(struct snd_kcontrol *kc,
snd_harmony_set_new_gain(h);
- spin_unlock_irq(&h->mixer_lock);
-
return h->st.gain != old_gain;
}
@@ -788,13 +726,11 @@ snd_harmony_captureroute_get(struct snd_kcontrol *kc,
struct snd_harmony *h = snd_kcontrol_chip(kc);
int value;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
ucontrol->value.enumerated.item[0] = value;
- spin_unlock_irq(&h->mixer_lock);
-
return 0;
}
@@ -806,7 +742,7 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
int value;
int old_gain = h->st.gain;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
value = ucontrol->value.enumerated.item[0] & 1;
h->st.gain &= ~HARMONY_GAIN_IS_MASK;
@@ -814,8 +750,6 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
snd_harmony_set_new_gain(h);
- spin_unlock_irq(&h->mixer_lock);
-
return h->st.gain != old_gain;
}
@@ -828,7 +762,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,
@@ -868,7 +802,7 @@ snd_harmony_mixer_init(struct snd_harmony *h)
if (snd_BUG_ON(!h))
return -EINVAL;
card = h->card;
- strcpy(card->mixername, "Harmony Gain control interface");
+ strscpy(card->mixername, "Harmony Gain control interface");
for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
err = snd_ctl_add(card,
@@ -912,7 +846,7 @@ snd_harmony_create(struct snd_card *card,
{
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,
};
@@ -926,7 +860,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);
@@ -946,10 +880,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;
- }
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
+ if (err < 0)
+ goto free_and_ret;
*rchip = h;
@@ -983,8 +916,8 @@ snd_harmony_probe(struct parisc_device *padev)
if (err < 0)
goto free_and_ret;
- strcpy(card->driver, "harmony");
- strcpy(card->shortname, "Harmony");
+ strscpy(card->driver, "harmony");
+ strscpy(card->shortname, "Harmony");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, h->hpa, h->irq);
@@ -1000,11 +933,10 @@ free_and_ret:
return err;
}
-static int __exit
+static void __exit
snd_harmony_remove(struct parisc_device *padev)
{
snd_card_free(parisc_get_drvdata(padev));
- return 0;
}
static struct parisc_driver snd_harmony_driver __refdata = {
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index d9f3fdb777e4..e0996a9d90b0 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,7 +26,7 @@ config SND_ALS300
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
@@ -35,6 +36,7 @@ config SND_ALS300
config SND_ALS4000
tristate "Avance Logic ALS4000"
depends on ISA_DMA_API
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -50,7 +52,7 @@ config SND_ALI5451
tristate "ALi M5451 PCI Audio Controller"
select SND_MPU401_UART
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for the integrated AC97 sound
device on motherboards using the ALi M5451 Audio Controller
@@ -95,6 +97,7 @@ config SND_ATIIXP_MODEM
config SND_AU8810
tristate "Aureal Advantage"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -109,6 +112,7 @@ config SND_AU8810
config SND_AU8820
tristate "Aureal Vortex"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -122,6 +126,7 @@ config SND_AU8820
config SND_AU8830
tristate "Aureal Vortex 2"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -156,7 +161,7 @@ config SND_AZT3328
select SND_RAWMIDI
select SND_AC97_CODEC
select SND_TIMER
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
@@ -175,7 +180,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.
@@ -192,6 +197,7 @@ config SND_BT87X_OVERCLOCK
config SND_CA0106
tristate "SB Audigy LS / Live 24bit"
+ depends on HAS_IOPORT
select SND_AC97_CODEC
select SND_RAWMIDI
select SND_VMASTER
@@ -204,22 +210,24 @@ config SND_CA0106
config SND_CMIPCI
tristate "C-Media 8338, 8738, 8768, 8770"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
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 8786, 8787, 8788 (Oxygen)"
+ depends on HAS_IOPORT
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@@ -245,6 +253,7 @@ config SND_OXYGEN
config SND_CS4281
tristate "Cirrus Logic (Sound Fusion) CS4281"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_RAWMIDI
select SND_AC97_CODEC
@@ -256,6 +265,7 @@ config SND_CS4281
config SND_CS46XX
tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
select FW_LOADER
@@ -278,6 +288,7 @@ config SND_CS46XX_NEW_DSP
config SND_CS5530
tristate "CS5530 Audio"
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.
@@ -288,6 +299,7 @@ config SND_CS5530
config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio"
depends on X86_32 || MIPS || COMPILE_TEST
+ depends on HAS_IOPORT
select SND_PCM
select SND_AC97_CODEC
help
@@ -305,6 +317,7 @@ config SND_CS5535AUDIO
config SND_CTXFI
tristate "Creative Sound Blaster X-Fi"
+ depends on HAS_IOPORT
select SND_PCM
help
If you want to use soundcards based on Creative Sound Blastr X-Fi
@@ -459,21 +472,21 @@ 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
+ depends on ZONE_DMA && HAS_IOPORT
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.
@@ -489,7 +502,7 @@ config SND_EMU10K1X
tristate "Emu10k1X (Dell OEM Version)"
select SND_AC97_CODEC
select SND_RAWMIDI
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for the Dell OEM version of the
Sound Blaster Live!.
@@ -499,6 +512,7 @@ config SND_EMU10K1X
config SND_ENS1370
tristate "(Creative) Ensoniq AudioPCI 1370"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_PCM
help
@@ -509,6 +523,7 @@ config SND_ENS1370
config SND_ENS1371
tristate "(Creative) Ensoniq AudioPCI 1371/1373"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
help
@@ -523,7 +538,7 @@ config SND_ES1938
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Solo-1
(ES1938, ES1946, ES1969) chips.
@@ -535,7 +550,7 @@ config SND_ES1968
tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
select SND_MPU401_UART
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Maestro
1/2/2E chips.
@@ -557,7 +572,7 @@ config SND_ES1968_RADIO
bool "Enable TEA5757 radio tuner support for es1968"
depends on SND_ES1968
depends on MEDIA_RADIO_SUPPORT
- depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_ES1968
select RADIO_ADAPTERS
select RADIO_TEA575X
@@ -567,6 +582,7 @@ config SND_ES1968_RADIO
config SND_FM801
tristate "ForteMedia FM801"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -581,7 +597,7 @@ config SND_FM801_TEA575X_BOOL
bool "ForteMedia FM801 + TEA5757 tuner"
depends on SND_FM801
depends on MEDIA_RADIO_SUPPORT
- depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_FM801
select RADIO_ADAPTERS
select RADIO_TEA575X
help
@@ -622,7 +638,7 @@ config SND_ICE1712
select SND_MPU401_UART
select SND_AC97_CODEC
select BITREVERSE
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
@@ -638,6 +654,7 @@ config SND_ICE1712
config SND_ICE1724
tristate "ICE/VT1724/1720 (Envy24HT/PT)"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
select SND_VMASTER
@@ -710,7 +727,7 @@ config SND_LX6464ES
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Maestro 3
(Allegro) chips.
@@ -735,7 +752,7 @@ config SND_MIXART
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.
@@ -751,6 +768,7 @@ config SND_NM256
config SND_PCXHR
tristate "Digigram PCXHR"
+ depends on HAS_IOPORT
select FW_LOADER
select SND_PCM
select SND_HWDEP
@@ -762,6 +780,7 @@ config SND_PCXHR
config SND_RIPTIDE
tristate "Conexant Riptide"
+ depends on HAS_IOPORT
select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -806,6 +825,7 @@ config SND_RME9652
config SND_SE6X
tristate "Studio Evolution SE6X"
depends on SND_OXYGEN=n && SND_VIRTUOSO=n # PCI ID conflict
+ depends on HAS_IOPORT
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@@ -828,7 +848,7 @@ config SND_SONICVIBES
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on the S3
SonicVibes chip.
@@ -840,7 +860,7 @@ config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
select SND_MPU401_UART
select SND_AC97_CODEC
- depends on ZONE_DMA
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on Trident
4D-Wave DX/NX or SiS 7018 chips.
@@ -850,6 +870,7 @@ config SND_TRIDENT
config SND_VIA82XX
tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -861,6 +882,7 @@ config SND_VIA82XX
config SND_VIA82XX_MODEM
tristate "VIA 82C686A/B, 8233 based Modems"
+ depends on HAS_IOPORT
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -871,6 +893,7 @@ config SND_VIA82XX_MODEM
config SND_VIRTUOSO
tristate "Asus Virtuoso 66/100/200 (Xonar)"
+ depends on HAS_IOPORT
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@@ -887,6 +910,7 @@ config SND_VIRTUOSO
config SND_VX222
tristate "Digigram VX222"
+ depends on HAS_IOPORT
select SND_VX_LIB
help
Say Y here to include support for Digigram VX222 soundcards.
@@ -896,6 +920,7 @@ config SND_VX222
config SND_YMFPCI
tristate "Yamaha YMF724/740/744/754"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -908,5 +933,3 @@ 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 04cac7469139..9d5e8e12ae73 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -4,30 +4,30 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1889-objs := ad1889.o
-snd-als300-objs := als300.o
-snd-als4000-objs := als4000.o
-snd-atiixp-objs := atiixp.o
-snd-atiixp-modem-objs := atiixp_modem.o
-snd-azt3328-objs := azt3328.o
-snd-bt87x-objs := bt87x.o
-snd-cmipci-objs := cmipci.o
-snd-cs4281-objs := cs4281.o
-snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o ak4531_codec.o
-snd-ens1371-objs := ens1371.o
-snd-es1938-objs := es1938.o
-snd-es1968-objs := es1968.o
-snd-fm801-objs := fm801.o
-snd-intel8x0-objs := intel8x0.o
-snd-intel8x0m-objs := intel8x0m.o
-snd-maestro3-objs := maestro3.o
-snd-rme32-objs := rme32.o
-snd-rme96-objs := rme96.o
-snd-sis7019-objs := sis7019.o
-snd-sonicvibes-objs := sonicvibes.o
-snd-via82xx-objs := via82xx.o
-snd-via82xx-modem-objs := via82xx_modem.o
+snd-ad1889-y := ad1889.o
+snd-als300-y := als300.o
+snd-als4000-y := als4000.o
+snd-atiixp-y := atiixp.o
+snd-atiixp-modem-y := atiixp_modem.o
+snd-azt3328-y := azt3328.o
+snd-bt87x-y := bt87x.o
+snd-cmipci-y := cmipci.o
+snd-cs4281-y := cs4281.o
+snd-cs5530-y := cs5530.o
+snd-ens1370-y := ens1370.o ak4531_codec.o
+snd-ens1371-y := ens1371.o
+snd-es1938-y := es1938.o
+snd-es1968-y := es1968.o
+snd-fm801-y := fm801.o
+snd-intel8x0-y := intel8x0.o
+snd-intel8x0m-y := intel8x0m.o
+snd-maestro3-y := maestro3.o
+snd-rme32-y := rme32.o
+snd-rme96-y := rme96.o
+snd-sis7019-y := sis7019.o
+snd-sonicvibes-y := sonicvibes.o
+snd-via82xx-y := via82xx.o
+snd-via82xx-modem-y := via82xx_modem.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1889) += snd-ad1889.o
@@ -69,7 +69,6 @@ obj-$(CONFIG_SND) += \
lx6464es/ \
echoaudio/ \
emu10k1/ \
- hda/ \
ice1712/ \
korg1212/ \
mixart/ \
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 526175333710..c74e769f3523 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/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/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 1ef7cdf1d3e8..c54bdefa5afe 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.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/delay.h>
@@ -167,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 },
@@ -233,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)
@@ -342,11 +326,10 @@ void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned sh
{
if (!snd_ac97_valid_reg(ac97, reg))
return;
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
ac97->regs[reg] = value;
ac97->bus->ops->write(ac97, reg, value);
set_bit(reg, ac97->reg_accessed);
- mutex_unlock(&ac97->reg_mutex);
}
EXPORT_SYMBOL(snd_ac97_write_cache);
@@ -369,14 +352,13 @@ int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short va
if (!snd_ac97_valid_reg(ac97, reg))
return -EINVAL;
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
change = ac97->regs[reg] != value;
if (change) {
ac97->regs[reg] = value;
ac97->bus->ops->write(ac97, reg, value);
}
set_bit(reg, ac97->reg_accessed);
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -397,14 +379,10 @@ EXPORT_SYMBOL(snd_ac97_update);
*/
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
{
- int change;
-
if (!snd_ac97_valid_reg(ac97, reg))
return -EINVAL;
- mutex_lock(&ac97->reg_mutex);
- change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
- mutex_unlock(&ac97->reg_mutex);
- return change;
+ guard(mutex)(&ac97->reg_mutex);
+ return snd_ac97_update_bits_nolock(ac97, reg, mask, value);
}
EXPORT_SYMBOL(snd_ac97_update_bits);
@@ -432,12 +410,12 @@ static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, uns
int change;
unsigned short old, new, cfg;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
old = ac97->spec.ad18xx.pcmreg[codec];
new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG);
ac97->spec.ad18xx.pcmreg[codec] = new;
/* select single codec */
@@ -449,9 +427,7 @@ static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, uns
/* select all codecs */
ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
cfg | 0x7000);
- mutex_unlock(&ac97->reg_mutex);
}
- mutex_unlock(&ac97->page_mutex);
return change;
}
@@ -611,11 +587,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)
@@ -737,12 +708,11 @@ static int snd_ac97_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_
{
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
- mutex_unlock(&ac97->reg_mutex);
return 0;
}
@@ -781,7 +751,7 @@ static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_
}
}
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
change = ac97->spdif_status != new;
ac97->spdif_status = new;
@@ -815,7 +785,6 @@ static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
}
}
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -824,7 +793,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;
@@ -832,7 +801,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
value = (ucontrol->value.integer.value[0] & mask);
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
mask <<= shift;
value <<= shift;
old = snd_ac97_read_cache(ac97, reg);
@@ -846,7 +815,6 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
if (extst & AC97_EA_SPDIF)
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
}
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -957,10 +925,9 @@ static int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
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);
- mutex_unlock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
+ 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);
return 0;
}
@@ -1306,15 +1273,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;
@@ -1353,9 +1322,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* 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);
@@ -1367,9 +1338,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);
@@ -1382,23 +1355,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;
}
@@ -1406,7 +1382,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) {
@@ -1422,9 +1400,12 @@ 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,
@@ -1437,8 +1418,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* 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;
}
}
@@ -1446,26 +1428,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;
}
}
@@ -1473,8 +1459,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;
}
}
@@ -1482,8 +1469,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;
}
}
@@ -1495,26 +1483,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;
}
@@ -1535,7 +1535,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",
@@ -1543,7 +1544,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);
@@ -1551,52 +1554,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 & AC97_BC_SIM_STEREO) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+ 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 & AC97_BC_LOUDNESS) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+ 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;
}
@@ -1612,11 +1625,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);
@@ -1633,14 +1650,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 */
@@ -1652,9 +1673,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);
@@ -1662,7 +1685,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;
}
@@ -1684,14 +1708,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;
}
@@ -1773,10 +1801,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
};
@@ -1800,7 +1828,8 @@ static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id
return NULL;
}
-void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem)
+void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name,
+ size_t maxlen, int modem)
{
const struct ac97_codec_id *pid;
@@ -1812,7 +1841,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
if (! pid)
return;
- strcpy(name, pid->name);
+ strscpy(name, pid->name, maxlen);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
@@ -1821,17 +1850,19 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
pid = look_for_codec_id(snd_ac97_codec_ids, id);
if (pid) {
- strcat(name, " ");
- strcat(name, pid->name);
+ strlcat(name, " ", maxlen);
+ strlcat(name, pid->name, maxlen);
if (pid->mask != 0xffffffff)
- sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+ sprintf(name + strlen(name), " rev %u", id & ~pid->mask);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
}
- } else
- sprintf(name + strlen(name), " id %x", id & 0xff);
+ } else {
+ int l = strlen(name);
+ snprintf(name + l, maxlen - l, " id %x", id & 0xff);
+ }
}
/**
@@ -1914,12 +1945,13 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
*
* 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,
};
@@ -1935,7 +1967,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;
}
@@ -1963,8 +1996,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) {
+ 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;
}
@@ -2019,16 +2054,15 @@ 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,
};
- if (rac97)
- *rac97 = NULL;
- if (snd_BUG_ON(!bus || !template))
+ if (snd_BUG_ON(!bus || !template || !rac97))
return -EINVAL;
+ *rac97 = NULL;
if (snd_BUG_ON(template->num >= 4))
return -EINVAL;
if (bus->codec[template->num])
@@ -2114,7 +2148,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) {
@@ -2251,15 +2286,15 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
/* additional initializations */
if (bus->ops->init)
bus->ops->init(ac97);
- snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97));
- snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code
+ snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97));
+ snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code
if (! ac97->build_ops)
ac97->build_ops = &null_build_ops;
if (ac97_is_audio(ac97)) {
char comp[16];
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
} else {
if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
strcat(card->mixername, ",");
@@ -2267,7 +2302,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;
}
@@ -2279,7 +2315,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (ac97_is_modem(ac97)) {
char comp[16];
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
} else {
if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
strcat(card->mixername, ",");
@@ -2287,7 +2323,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;
}
@@ -2299,7 +2336,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;
}
@@ -2365,7 +2403,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,
@@ -2414,8 +2452,7 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
* (for avoiding loud click noises for many (OSS) apps
* that open/close frequently)
*/
- schedule_delayed_work(&ac97->power_work,
- msecs_to_jiffies(power_save * 1000));
+ schedule_delayed_work(&ac97->power_work, secs_to_jiffies(power_save));
else {
cancel_delayed_work(&ac97->power_work);
update_power_regs(ac97);
@@ -2607,11 +2644,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,
@@ -2638,8 +2682,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;
@@ -2658,11 +2705,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;
@@ -2848,7 +2901,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 },
@@ -2876,7 +2929,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];
@@ -2941,19 +2994,3 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97,
}
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 941a5062cc20..965284eb4b33 100644
--- a/sound/pci/ac97/ac97_local.h
+++ b/sound/pci/ac97/ac97_local.h
@@ -1,29 +1,14 @@
+/* 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,
- int modem);
+ size_t maxlen, int modem);
int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 8cf0dc7a07a4..64cc39dd2008 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,22 +6,6 @@
* 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"
@@ -34,7 +19,7 @@ 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 *slaves);
+ const char * const *followers);
/*
* Chip specific initialization
@@ -44,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;
}
@@ -54,12 +41,9 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
static void reset_tlv(struct snd_ac97 *ac97, const char *name,
const unsigned int *tlv)
{
- struct snd_ctl_elem_id sid;
struct snd_kcontrol *kctl;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+
+ kctl = snd_ctl_find_id_mixer(ac97->bus->card, name);
if (kctl && kctl->tlv.p)
kctl->tlv.p = tlv;
}
@@ -70,12 +54,11 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi
unsigned short page_save;
int ret;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
ret = snd_ac97_update_bits(ac97, reg, mask, value);
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
- mutex_unlock(&ac97->page_mutex); /* unlock paging */
return ret;
}
@@ -314,7 +297,7 @@ static int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97)
err = snd_ctl_add(ac97->bus->card, kctl);
if (err < 0)
return err;
- strcpy(kctl->id.name, "3D Control - Wide");
+ strscpy(kctl->id.name, "3D Control - Wide");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
err = snd_ctl_add(ac97->bus->card,
@@ -431,7 +414,8 @@ 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;
}
@@ -476,7 +460,8 @@ 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);
@@ -506,7 +491,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 */
@@ -646,7 +632,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);
@@ -813,7 +800,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;
@@ -824,7 +812,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);
@@ -898,9 +887,10 @@ 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");
+ strscpy(kctl->id.name, "3D Control Sigmatel - Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
return 0;
@@ -911,13 +901,17 @@ 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");
+ strscpy(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");
+ strscpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
return 0;
@@ -942,18 +936,26 @@ 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;
}
@@ -973,12 +975,11 @@ static int snd_ac97_stac9708_put_bias(struct snd_kcontrol *kcontrol, struct snd_
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
int err;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
err = snd_ac97_update_bits(ac97, AC97_SIGMATEL_BIAS2, 0x0010,
(ucontrol->value.integer.value[0] & 1) << 4);
snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0);
- mutex_unlock(&ac97->page_mutex);
return err;
}
@@ -999,7 +1000,8 @@ 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);
}
@@ -1226,25 +1228,25 @@ static const struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
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 */
@@ -1277,14 +1279,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;
}
@@ -1339,10 +1344,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 */
@@ -1371,12 +1378,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;
@@ -1485,7 +1492,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]);
@@ -1607,7 +1614,8 @@ 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);
@@ -1806,10 +1814,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 */
@@ -1836,7 +1844,7 @@ 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));
@@ -1850,10 +1858,10 @@ static const 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 */
@@ -1868,7 +1876,7 @@ static unsigned int ad1981_jacks_whitelist[] = {
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);
}
@@ -1890,9 +1898,10 @@ 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));
@@ -2075,7 +2084,8 @@ 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);
}
@@ -2183,7 +2193,8 @@ 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,
@@ -2475,7 +2486,8 @@ 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,
@@ -2597,10 +2609,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)
@@ -2750,10 +2764,12 @@ 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;
@@ -2862,10 +2878,12 @@ 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;
@@ -3131,22 +3149,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 */
@@ -3388,7 +3406,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 * const slave_vols_vt1616[] = {
+static const char * const follower_vols_vt1616[] = {
"Front Playback Volume",
"Surround Playback Volume",
"Center Playback Volume",
@@ -3396,7 +3414,7 @@ static const char * const slave_vols_vt1616[] = {
NULL
};
-static const char * const slave_sws_vt1616[] = {
+static const char * const follower_sws_vt1616[] = {
"Front Playback Switch",
"Surround Playback Switch",
"Center Playback Switch",
@@ -3408,20 +3426,15 @@ static const char * const slave_sws_vt1616[] = {
static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
const char *name)
{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, name);
- return snd_ctl_find_id(ac97->bus->card, &id);
+ return snd_ctl_find_id_mixer(ac97->bus->card, name);
}
-/* 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 * const *slaves)
+ const char * const *followers)
{
struct snd_kcontrol *kctl;
- const char * const *s;
int err;
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -3431,20 +3444,7 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
if (err < 0)
return err;
- for (s = slaves; *s; s++) {
- struct snd_kcontrol *sctl;
-
- sctl = snd_ac97_find_mixer_ctl(ac97, *s);
- if (!sctl) {
- dev_dbg(ac97->bus->card->dev,
- "Cannot find slave %s, skipped\n", *s);
- continue;
- }
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
- }
- return 0;
+ return snd_ctl_add_followers(ac97->bus->card, kctl, followers);
}
static int patch_vt1616_specific(struct snd_ac97 * ac97)
@@ -3452,10 +3452,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. */
@@ -3466,12 +3469,12 @@ 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;
@@ -3650,7 +3653,7 @@ struct vt1618_uaj_item {
/* 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,
@@ -3694,7 +3697,7 @@ static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
unsigned short datpag, uaj;
struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol);
- mutex_lock(&pac97->page_mutex);
+ guard(mutex)(&pac97->page_mutex);
datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0);
@@ -3703,7 +3706,6 @@ static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
vt1618_uaj[kcontrol->private_value].mask;
snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag);
- mutex_unlock(&pac97->page_mutex);
ucontrol->value.enumerated.item[0] = uaj >>
vt1618_uaj[kcontrol->private_value].shift;
@@ -3825,9 +3827,11 @@ 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;
}
@@ -3860,9 +3864,11 @@ 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;
}
@@ -3886,7 +3892,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 },
@@ -3907,41 +3913,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 const 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 d1ce151fe722..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) \
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index d15297a68801..4715d88ff8f4 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,22 +6,6 @@
* 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>
@@ -41,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 */
{
@@ -144,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 */
@@ -207,7 +192,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
mask = AC97_SC_SPSR_MASK;
}
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
old = snd_ac97_read(ac97, reg) & mask;
if (old != bits) {
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
@@ -232,7 +217,6 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
ac97->spdif_status = sbits;
}
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
- mutex_unlock(&ac97->reg_mutex);
return 0;
}
@@ -246,7 +230,7 @@ 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.
@@ -586,33 +570,31 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
return err;
}
}
- spin_lock_irq(&pcm->bus->bus_lock);
- for (i = 3; i < 12; i++) {
- if (!(slots & (1 << i)))
- continue;
- ok_flag = 0;
- for (cidx = 0; cidx < 4; cidx++) {
- if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
- spin_unlock_irq(&pcm->bus->bus_lock);
- err = -EBUSY;
- goto error;
+ scoped_guard(spinlock_irq, &pcm->bus->bus_lock) {
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ ok_flag = 0;
+ for (cidx = 0; cidx < 4; cidx++) {
+ if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
+ err = -EBUSY;
+ goto error;
+ }
+ if (pcm->r[r].rslots[cidx] & (1 << i)) {
+ bus->used_slots[pcm->stream][cidx] |= (1 << i);
+ ok_flag++;
+ }
}
- if (pcm->r[r].rslots[cidx] & (1 << i)) {
- bus->used_slots[pcm->stream][cidx] |= (1 << i);
- ok_flag++;
+ if (!ok_flag) {
+ dev_err(bus->card->dev,
+ "cannot find configuration for AC97 slot %i\n",
+ i);
+ err = -EAGAIN;
+ goto error;
}
}
- if (!ok_flag) {
- spin_unlock_irq(&pcm->bus->bus_lock);
- dev_err(bus->card->dev,
- "cannot find configuration for AC97 slot %i\n",
- i);
- err = -EAGAIN;
- goto error;
- }
+ pcm->cur_dbl = r;
}
- pcm->cur_dbl = r;
- spin_unlock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
continue;
@@ -680,7 +662,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
#endif
bus = pcm->bus;
- spin_lock_irq(&pcm->bus->bus_lock);
+ guard(spinlock_irq)(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
continue;
@@ -689,7 +671,6 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
}
pcm->aslots = 0;
pcm->cur_dbl = 0;
- spin_unlock_irq(&pcm->bus->bus_lock);
return 0;
}
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 6320bf084e47..1c9d76994b3a 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>
@@ -113,7 +98,7 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe
static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" };
- snd_ac97_get_name(NULL, ac97->id, name, 0);
+ snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), 0);
snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
@@ -176,12 +161,12 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe
"Mic select : %s\n"
"ADC/DAC loopback : %s\n",
val & 0x8000 ? "post" : "pre",
- val & 0x4000 ? "on" : "off",
- val & 0x2000 ? "on" : "off",
- val & 0x1000 ? "on" : "off",
+ str_on_off(val & 0x4000),
+ str_on_off(val & 0x2000),
+ str_on_off(val & 0x1000),
val & 0x0200 ? "Mic" : "MIX",
val & 0x0100 ? "Mic2" : "Mic1",
- val & 0x0080 ? "on" : "off");
+ str_on_off(val & 0x0080));
if (ac97->ext_id & AC97_EI_DRA)
snd_iprintf(buffer, "Double rate slots: %s\n",
double_rate_slots[(val >> 10) & 3]);
@@ -344,7 +329,7 @@ static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buf
{
struct snd_ac97 *ac97 = entry->private_data;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
int idx;
for (idx = 0; idx < 3; idx++)
@@ -370,7 +355,6 @@ static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buf
} else {
snd_ac97_proc_read_main(ac97, buffer, 0);
}
- mutex_unlock(&ac97->page_mutex);
}
#ifdef CONFIG_SND_DEBUG
@@ -380,7 +364,8 @@ static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_in
struct snd_ac97 *ac97 = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ac97->page_mutex);
+
+ guard(mutex)(&ac97->page_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
@@ -388,7 +373,6 @@ static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_in
if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff)
snd_ac97_write_cache(ac97, reg, val);
}
- mutex_unlock(&ac97->page_mutex);
}
#endif
@@ -407,7 +391,7 @@ static void snd_ac97_proc_regs_read(struct snd_info_entry *entry,
{
struct snd_ac97 *ac97 = entry->private_data;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
int idx;
@@ -423,7 +407,6 @@ static void snd_ac97_proc_regs_read(struct snd_info_entry *entry,
} else {
snd_ac97_proc_regs_read_main(ac97, buffer, 0);
}
- mutex_unlock(&ac97->page_mutex);
}
void snd_ac97_proc_init(struct snd_ac97 * ac97)
@@ -436,25 +419,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 +451,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 0bf2c04eeada..f4ec404c0d15 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)?
@@ -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);
@@ -258,7 +245,7 @@ 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) {
dev_err(chip->card->dev, "[%s] Link is not ready.\n",
__func__);
@@ -269,20 +256,6 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
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 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,
@@ -380,7 +353,7 @@ snd_ad1889_playback_prepare(struct snd_pcm_substream *ss)
reg |= AD_DS_WSMC_WAST;
/* let's make sure we don't clobber ourselves */
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
chip->wave.size = size;
chip->wave.reg = reg;
@@ -399,8 +372,6 @@ snd_ad1889_playback_prepare(struct snd_pcm_substream *ss)
/* writes flush */
ad1889_readw(chip, AD_DS_WSMC);
- spin_unlock_irq(&chip->lock);
-
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);
@@ -430,7 +401,7 @@ snd_ad1889_capture_prepare(struct snd_pcm_substream *ss)
reg |= AD_DS_RAMC_ADST;
/* let's make sure we don't clobber ourselves */
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
chip->ramc.size = size;
chip->ramc.reg = reg;
@@ -446,8 +417,6 @@ snd_ad1889_capture_prepare(struct snd_pcm_substream *ss)
/* writes flush */
ad1889_readw(chip, AD_DS_RAMC);
- spin_unlock_irq(&chip->lock);
-
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);
@@ -574,9 +543,6 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)
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,
@@ -585,9 +551,6 @@ static const struct snd_pcm_ops snd_ad1889_playback_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,
@@ -638,22 +601,15 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
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) {
- dev_err(chip->card->dev, "buffer allocation error: %d\n", err);
- return err;
- }
-
return 0;
}
@@ -666,7 +622,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_WSMC);
snd_iprintf(buffer, "Wave output: %s\n",
- (reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_WSMC_WAEN));
snd_iprintf(buffer, "Wave Channels: %s\n",
(reg & AD_DS_WSMC_WAST) ? "stereo" : "mono");
snd_iprintf(buffer, "Wave Quality: %d-bit linear\n",
@@ -682,7 +638,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "Synthesis output: %s\n",
- reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_WSMC_SYEN));
/* SYRQ is at offset 4 */
tmp = (reg & AD_DS_WSMC_SYRQ) ?
@@ -694,7 +650,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_RAMC);
snd_iprintf(buffer, "ADC input: %s\n",
- (reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_RAMC_ADEN));
snd_iprintf(buffer, "ADC Channels: %s\n",
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
snd_iprintf(buffer, "ADC Quality: %d-bit linear\n",
@@ -709,7 +665,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
snd_iprintf(buffer, "Resampler input: %s\n",
- reg & AD_DS_RAMC_REEN ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_RAMC_REEN));
/* RERQ is at offset 12 */
tmp = (reg & AD_DS_RAMC_RERQ) ?
@@ -741,10 +697,8 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
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 const struct ac97_quirk ac97_quirks[] = {
@@ -782,26 +736,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
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,
};
@@ -813,11 +753,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);
@@ -829,13 +766,12 @@ 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);
+ guard(spinlock_irq)(&chip->lock);
ad1889_mute(chip);
@@ -845,128 +781,64 @@ snd_ad1889_free(struct snd_ad1889 *chip)
/* clear DISR. If we don't, we'd better jump off the Eiffel Tower */
ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PTAI | AD_DMA_DISR_PMAI);
ad1889_readl(chip, AD_DMA_DISR); /* flush, dammit! */
-
- spin_unlock_irq(&chip->lock);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
-skip_hw:
- 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
-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
-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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
dev_err(card->dev, "error setting 32-bit DMA mask.\n");
- pci_disable_device(pci);
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;
+ chip->iobase = pcim_iomap_region(pci, 0, card->driver);
+ if (IS_ERR(chip->iobase))
+ return PTR_ERR(chip->iobase);
chip->bar = pci_resource_start(pci, 0);
- chip->iobase = pci_ioremap_bar(pci, 0);
- if (chip->iobase == NULL) {
- dev_err(card->dev, "unable to reserve region.\n");
- err = -EBUSY;
- goto free_and_ret;
- }
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, KBUILD_MODNAME, 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);
- snd_ad1889_free(chip);
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;
- }
+ ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
+ ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1889_free(chip);
- return err;
- }
+ 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
-snd_ad1889_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+__snd_ad1889_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
int err;
static int devno;
@@ -982,19 +854,19 @@ snd_ad1889_probe(struct pci_dev *pci,
}
/* (2) */
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "AD1889");
+ strscpy(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",
@@ -1004,11 +876,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);
if (err < 0)
- goto free_and_ret;
+ return err;
/* register proc interface */
snd_ad1889_proc_init(chip);
@@ -1016,23 +888,19 @@ 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
-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));
+ return snd_card_free_on_error(&pci->dev, __snd_ad1889_probe(pci, pci_id));
}
static const struct pci_device_id snd_ad1889_ids[] = {
@@ -1045,7 +913,6 @@ static struct pci_driver ad1889_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ad1889_ids,
.probe = snd_ad1889_probe,
- .remove = snd_ad1889_remove,
};
module_pci_driver(ad1889_pci_driver);
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index 2fb1fbba3e5e..cdad47e4098d 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -1,22 +1,7 @@
+// 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>
@@ -92,9 +77,8 @@ static int snd_ak4531_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
int val;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val = (ak4531->regs[reg] >> shift) & mask;
- mutex_unlock(&ak4531->reg_mutex);
if (invert) {
val = mask - val;
}
@@ -117,11 +101,10 @@ static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val = mask - val;
}
val <<= shift;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val = (ak4531->regs[reg] & ~(mask << shift)) | val;
change = val != ak4531->regs[reg];
ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -161,10 +144,9 @@ static int snd_ak4531_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
int left, right;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
left = (ak4531->regs[left_reg] >> left_shift) & mask;
right = (ak4531->regs[right_reg] >> right_shift) & mask;
- mutex_unlock(&ak4531->reg_mutex);
if (invert) {
left = mask - left;
right = mask - right;
@@ -194,7 +176,7 @@ static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
left <<= left_shift;
right <<= right_shift;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
if (left_reg == right_reg) {
left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
change = left != ak4531->regs[left_reg];
@@ -206,7 +188,6 @@ static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
}
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -233,12 +214,11 @@ static int snd_ak4531_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
int right_shift = (kcontrol->private_value >> 24) & 0x0f;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
- mutex_unlock(&ak4531->reg_mutex);
return 0;
}
@@ -252,7 +232,7 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
int change;
int val1, val2;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
@@ -262,7 +242,6 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -270,7 +249,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
-static struct snd_kcontrol_new snd_ak4531_controls[] = {
+static const struct snd_kcontrol_new snd_ak4531_controls[] = {
AK4531_DOUBLE_TLV("Master Playback Switch", 0,
AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -350,7 +329,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 */
@@ -386,7 +365,7 @@ int snd_ak4531_mixer(struct snd_card *card,
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,
};
@@ -399,11 +378,12 @@ int 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;
}
- strcpy(card->mixername, "Asahi Kasei AK4531");
+ strscpy(card->mixername, "Asahi Kasei AK4531");
ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */
udelay(100);
ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
@@ -413,13 +393,15 @@ int 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;
}
@@ -481,8 +463,5 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry,
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);
}
diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile
index 713459c12d22..e319a4c1d6b2 100644
--- a/sound/pci/ali5451/Makefile
+++ b/sound/pci/ali5451/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ali5451-objs := ali5451.o
+snd-ali5451-y := ali5451.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 39547e32e584..571d89a6a8da 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,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 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 <linux/io.h>
@@ -43,7 +29,6 @@
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 */
@@ -258,9 +243,7 @@ struct snd_ali {
spinlock_t reg_lock;
spinlock_t voice_alloc;
-#ifdef CONFIG_PM_SLEEP
- struct snd_ali_image *image;
-#endif
+ struct snd_ali_image image;
};
static const struct pci_device_id snd_ali_ids[] = {
@@ -309,7 +292,7 @@ static int snd_ali_codec_ready(struct snd_ali *codec,
}
snd_ali_5451_poke(codec, port, res & ~0x8000);
- dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n ");
+ dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n");
return -EIO;
}
@@ -928,13 +911,12 @@ static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec);
- spin_lock_irq(&codec->voice_alloc);
+ guard(spinlock_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) {
dev_err(codec->card->dev, "ali_alloc_voice: err.\n");
- spin_unlock_irq(&codec->voice_alloc);
return NULL;
}
pvoice = &(codec->synth.voices[idx]);
@@ -942,10 +924,8 @@ static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
pvoice->use = 1;
pvoice->pcm = 1;
pvoice->mode = rec;
- spin_unlock_irq(&codec->voice_alloc);
return pvoice;
}
- spin_unlock_irq(&codec->voice_alloc);
return NULL;
}
@@ -960,16 +940,16 @@ static void snd_ali_free_voice(struct snd_ali * codec,
if (!pvoice->use)
return;
snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
- spin_lock_irq(&codec->voice_alloc);
- private_free = pvoice->private_free;
- private_data = pvoice->private_data;
- pvoice->private_free = NULL;
- pvoice->private_data = NULL;
- if (pvoice->pcm)
- snd_ali_free_channel_pcm(codec, pvoice->number);
- pvoice->use = pvoice->pcm = pvoice->synth = 0;
- pvoice->substream = NULL;
- spin_unlock_irq(&codec->voice_alloc);
+ scoped_guard(spinlock_irq, &codec->voice_alloc) {
+ private_free = pvoice->private_free;
+ private_data = pvoice->private_data;
+ pvoice->private_free = NULL;
+ pvoice->private_data = NULL;
+ if (pvoice->pcm)
+ snd_ali_free_channel_pcm(codec, pvoice->number);
+ pvoice->use = pvoice->pcm = pvoice->synth = 0;
+ pvoice->substream = NULL;
+ }
if (private_free)
private_free(private_data);
}
@@ -1084,7 +1064,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;
@@ -1102,7 +1082,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;
@@ -1124,11 +1104,9 @@ 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);
+ guard(spinlock)(&codec->reg_lock);
if (!do_start)
outl(what, ALI_REG(codec, ALI_STOP));
val = inl(ALI_REG(codec, ALI_AINTEN));
@@ -1140,7 +1118,6 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
if (do_start)
outl(what, ALI_REG(codec, ALI_START));
dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati);
- spin_unlock(&codec->reg_lock);
return 0;
}
@@ -1152,13 +1129,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 !=
@@ -1189,7 +1160,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;
@@ -1197,18 +1167,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);
@@ -1227,8 +1185,8 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
dev_dbg(codec->card->dev, "playback_prepare ...\n");
- spin_lock_irq(&codec->reg_lock);
-
+ guard(spinlock_irq)(&codec->reg_lock);
+
/* set Delta (rate) value */
Delta = snd_ali_convert_rate(runtime->rate, 0);
@@ -1297,7 +1255,6 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
CTRL,
EC);
}
- spin_unlock_irq(&codec->reg_lock);
return 0;
}
@@ -1398,14 +1355,11 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
unsigned int cso;
- spin_lock(&codec->reg_lock);
- if (!pvoice->running) {
- spin_unlock(&codec->reg_lock);
+ guard(spinlock)(&codec->reg_lock);
+ if (!pvoice->running)
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);
dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
cso %= runtime->buffer_size;
@@ -1420,20 +1374,17 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
unsigned int cso;
- spin_lock(&codec->reg_lock);
- if (!pvoice->running) {
- spin_unlock(&codec->reg_lock);
+ guard(spinlock)(&codec->reg_lock);
+ if (!pvoice->running)
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 |
@@ -1459,7 +1410,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 |
@@ -1484,16 +1435,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;
@@ -1543,7 +1491,6 @@ static int snd_ali_close(struct snd_pcm_substream *substream)
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,
@@ -1554,9 +1501,6 @@ static const struct snd_pcm_ops snd_ali_playback_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,
@@ -1574,10 +1518,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 |
@@ -1629,9 +1573,7 @@ static int snd_ali_modem_capture_open(struct snd_pcm_substream *substream)
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,
@@ -1640,9 +1582,7 @@ static const struct snd_pcm_ops snd_ali_modem_playback_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,
@@ -1688,14 +1628,13 @@ static int 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;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, desc->name);
+ strscpy(pcm->name, desc->name);
codec->pcm[0] = pcm;
return 0;
}
@@ -1738,12 +1677,12 @@ static int snd_ali_build_pcms(struct snd_ali *codec)
static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_ali *codec = kcontrol->private_data;
+ struct snd_ali *codec = snd_kcontrol_chip(kcontrol);
unsigned int spdif_enable;
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&codec->reg_lock);
+ guard(spinlock_irq)(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0;
@@ -1759,19 +1698,18 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
break;
}
ucontrol->value.integer.value[0] = spdif_enable;
- spin_unlock_irq(&codec->reg_lock);
return 0;
}
static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_ali *codec = kcontrol->private_data;
+ struct snd_ali *codec = snd_kcontrol_chip(kcontrol);
unsigned int change = 0, spdif_enable = 0;
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&codec->reg_lock);
+ guard(spinlock_irq)(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
change = (codec->spdif_mask & 0x02) ? 1 : 0;
@@ -1816,12 +1754,11 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
default:
break;
}
- spin_unlock_irq(&codec->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = {
+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),
@@ -1836,7 +1773,7 @@ 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,
};
@@ -1872,25 +1809,18 @@ static int snd_ali_mixer(struct snd_ali *codec)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int ali_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
- struct snd_ali_image *im;
+ struct snd_ali_image *im = &chip->image;
int i, j;
- im = chip->image;
- if (!im)
- 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);
+ guard(spinlock_irq)(&chip->reg_lock);
im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
/* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */
@@ -1914,7 +1844,6 @@ static int ali_suspend(struct device *dev)
/* stop all HW channel */
outl(0xffffffff, ALI_REG(chip, ALI_STOP));
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1922,34 +1851,28 @@ static int ali_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
- struct snd_ali_image *im;
+ struct snd_ali_image *im = &chip->image;
int i, j;
- im = chip->image;
- if (!im)
- return 0;
-
- spin_lock_irq(&chip->reg_lock);
-
- for (i = 0; i < ALI_CHANNELS; i++) {
- outb(i, ALI_REG(chip, ALI_GC_CIR));
- for (j = 0; j < ALI_CHANNEL_REGS; j++)
- outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
- }
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ for (i = 0; i < ALI_CHANNELS; i++) {
+ outb(i, ALI_REG(chip, ALI_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+ }
- for (i = 0; i < ALI_GLOBAL_REGS; i++) {
- if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
- (i*4 == ALI_START))
- continue;
- outl(im->regs[i], ALI_REG(chip, i*4));
+ for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+ if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
+ (i*4 == ALI_START))
+ continue;
+ outl(im->regs[i], ALI_REG(chip, i*4));
+ }
+
+ /* start HW channel */
+ outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+ /* restore IRQ enable bits */
+ outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
}
-
- /* start HW channel */
- outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
- /* restore IRQ enable bits */
- outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
-
- spin_unlock_irq(&chip->reg_lock);
for (i = 0 ; i < chip->num_of_codecs; i++)
snd_ac97_resume(chip->ac97[i]);
@@ -1958,28 +1881,16 @@ static int ali_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
-static int snd_ali_free(struct snd_ali * codec)
+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_SLEEP
- 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)
@@ -2054,9 +1965,7 @@ static void snd_ali_proc_read(struct snd_info_entry *entry,
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 snd_ali_resources(struct snd_ali *codec)
@@ -2064,63 +1973,45 @@ static int snd_ali_resources(struct snd_ali *codec)
int err;
dev_dbg(codec->card->dev, "resources allocation ...\n");
- err = pci_request_regions(codec->pci, "ALI 5451");
+ err = pcim_request_all_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, KBUILD_MODNAME, codec)) {
+ 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;
+ codec->card->sync_irq = codec->irq;
dev_dbg(codec->card->dev, "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);
- return 0;
-}
static int snd_ali_create(struct snd_card *card,
struct pci_dev *pci,
int pcm_streams,
- int spdif_support,
- struct snd_ali **r_ali)
+ 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;
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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(31)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(31)) < 0) {
+ 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");
- pci_disable_device(pci);
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);
@@ -2141,14 +2032,10 @@ static int 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;
@@ -2175,24 +2062,15 @@ static int snd_ali_create(struct snd_card *card,
codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL);
if (!codec->pci_m1533) {
dev_err(card->dev, "cannot find ALi 1533 chip.\n");
- snd_ali_free(codec);
return -ENODEV;
}
/* M7101: power management */
codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL);
if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) {
dev_err(card->dev, "cannot find ALi 7101 chip.\n");
- snd_ali_free(codec);
return -ENODEV;
}
- dev_dbg(card->dev, "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;
- }
-
/* initialise synth voices*/
for (i = 0; i < ALI_CHANNELS; i++)
codec->synth.voices[i].number = i;
@@ -2203,22 +2081,13 @@ static int snd_ali_create(struct snd_card *card,
return err;
}
-#ifdef CONFIG_PM_SLEEP
- codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
- if (!codec->image)
- dev_warn(card->dev, "can't allocate apm buffer\n");
-#endif
-
snd_ali_enable_address_interrupt(codec);
codec->hw_initialized = 1;
-
- *r_ali = codec;
- dev_dbg(card->dev, "created.\n");
return 0;
}
-static int 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;
@@ -2226,29 +2095,30 @@ static int snd_ali_probe(struct pci_dev *pci,
dev_dbg(&pci->dev, "probe ...\n");
- err = snd_card_new(&pci->dev, 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;
dev_dbg(&pci->dev, "mixer building ...\n");
err = snd_ali_mixer(codec);
if (err < 0)
- goto error;
+ return err;
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);
- strcpy(card->driver, "ALI5451");
- strcpy(card->shortname, "ALI 5451");
+ strscpy(card->driver, "ALI5451");
+ strscpy(card->shortname, "ALI 5451");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, codec->port, codec->irq);
@@ -2256,28 +2126,24 @@ static int snd_ali_probe(struct pci_dev *pci,
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_ali_probe(pci, pci_id));
}
static struct pci_driver ali5451_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ali_ids,
.probe = snd_ali_probe,
- .remove = snd_ali_remove,
.driver = {
- .pm = ALI_PM_OPS,
+ .pm = &ali_pm,
},
};
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index eaa2d853d922..733e84def5a7 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
@@ -99,7 +86,6 @@ 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;
@@ -177,21 +163,11 @@ static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
snd_als300_gcr_write(chip->port, MISC_CONTROL, tmp);
}
-static int snd_als300_free(struct snd_als300 *chip)
+static void snd_als300_free(struct snd_card *card)
{
- 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);
- 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)
@@ -262,11 +238,6 @@ static irqreturn_t snd_als300plus_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void snd_als300_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
static unsigned short snd_als300_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
@@ -307,12 +278,13 @@ 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,
};
- 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));
@@ -391,7 +363,6 @@ static int snd_als300_playback_close(struct snd_pcm_substream *substream)
data = substream->runtime->private_data;
kfree(data);
chip->playback_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -420,22 +391,9 @@ static int snd_als300_capture_close(struct snd_pcm_substream *substream)
data = substream->runtime->private_data;
kfree(data);
chip->capture_substream = NULL;
- snd_pcm_lib_free_pages(substream);
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;
@@ -444,7 +402,7 @@ 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);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
tmp &= ~TRANSFER_START;
@@ -461,7 +419,6 @@ static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
runtime->dma_addr);
snd_als300_gcr_write(chip->port, PLAYBACK_END,
runtime->dma_addr + buffer_bytes - 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -473,7 +430,7 @@ 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);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, RECORD_CONTROL);
tmp &= ~TRANSFER_START;
@@ -490,7 +447,6 @@ static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
runtime->dma_addr);
snd_als300_gcr_write(chip->port, RECORD_END,
runtime->dma_addr + buffer_bytes - 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -505,7 +461,7 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
data = substream->runtime->private_data;
reg = data->control_register;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -534,7 +490,6 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
snd_als300_dbgplay("TRIGGER INVALID\n");
ret = -EINVAL;
}
- spin_unlock(&chip->reg_lock);
return ret;
}
@@ -548,10 +503,10 @@ 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);
- spin_lock(&chip->reg_lock);
- current_ptr = (u16) snd_als300_gcr_read(chip->port,
- data->block_counter_register) + 4;
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ current_ptr = (u16) snd_als300_gcr_read(chip->port,
+ data->block_counter_register) + 4;
+ }
if (current_ptr > period_bytes)
current_ptr = 0;
else
@@ -566,9 +521,6 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
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,
@@ -577,9 +529,6 @@ static const struct snd_pcm_ops snd_als300_playback_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,
@@ -594,7 +543,7 @@ static int snd_als300_new_pcm(struct snd_als300 *chip)
if (err < 0)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "ALS300");
+ strscpy(pcm->name, "ALS300");
chip->pcm = pcm;
/* set operators */
@@ -604,17 +553,16 @@ static int 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_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
static void snd_als300_init(struct snd_als300 *chip)
{
- unsigned long flags;
u32 tmp;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->revision = (snd_als300_gcr_read(chip->port, MISC_CONTROL) >> 16)
& 0x0000000F;
/* Setup DRAM */
@@ -639,50 +587,35 @@ static void snd_als300_init(struct snd_als300 *chip)
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL,
tmp & ~TRANSFER_START);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static int snd_als300_create(struct snd_card *card,
- struct pci_dev *pci, int chip_type,
- struct snd_als300 **rchip)
+ 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;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
dev_err(card->dev, "error setting 28bit DMA mask\n");
- pci_disable_device(pci);
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 = pcim_request_all_regions(pci, "ALS300");
+ if (err < 0)
return err;
- }
+
chip->port = pci_resource_start(pci, 0);
if (chip->chip_type == DEVICE_ALS300_PLUS)
@@ -690,48 +623,38 @@ static int snd_als300_create(struct snd_card *card,
else
irq_handler = snd_als300_interrupt;
- if (request_irq(pci->irq, irq_handler, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_als300_free(chip);
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) {
dev_err(card->dev, "Could not create ac97\n");
- snd_als300_free(chip);
return err;
}
- if ((err = snd_als300_new_pcm(chip)) < 0) {
+ err = snd_als300_new_pcm(chip);
+ if (err < 0) {
dev_err(card->dev, "Could not create PCM\n");
- snd_als300_free(chip);
return err;
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
- snd_als300_free(chip);
- return err;
- }
-
- *rchip = chip;
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int snd_als300_suspend(struct device *dev)
{
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);
return 0;
}
@@ -748,11 +671,7 @@ static int snd_als300_resume(struct device *dev)
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 DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume);
static int snd_als300_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
@@ -769,21 +688,19 @@ static int snd_als300_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "ALS300");
if (chip->chip_type == DEVICE_ALS300_PLUS)
/* don't know much about ALS300+ yet
* print revision number for now */
@@ -794,22 +711,25 @@ static int 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 als300_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
- .remove = snd_als300_remove,
.driver = {
- .pm = SND_ALS300_PM_OPS,
+ .pm = &snd_als300_pm,
},
};
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 26b097edec8c..33034e07b3d6 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
@@ -82,7 +68,6 @@
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 IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -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);
@@ -396,14 +369,14 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
count >>= 1;
count--;
- spin_lock_irq(&chip->reg_lock);
- snd_als4000_set_rate(chip, runtime->rate);
- snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
- spin_unlock_irq(&chip->reg_lock);
- spin_lock_irq(&chip->mixer_lock);
- snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
- snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
- spin_unlock_irq(&chip->mixer_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_als4000_set_rate(chip, runtime->rate);
+ snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
+ }
+ scoped_guard(spinlock_irq, &chip->mixer_lock) {
+ snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
+ snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
+ }
return 0;
}
@@ -429,7 +402,7 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
* reordering, ...). Something seems to get enabled on playback
* that I haven't found out how to disable again, which then causes
* the switching pops to reach the speakers the next time here. */
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_als4000_set_rate(chip, runtime->rate);
snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
@@ -440,7 +413,6 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count & 0xff);
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -456,7 +428,7 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int
Probably need to take reg_lock as outer (or inner??) lock, too.
(or serialize both lock operations? probably not, though... - racy?)
*/
- spin_lock(&chip->mixer_lock);
+ guard(spinlock)(&chip->mixer_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -474,7 +446,6 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int
result = -EINVAL;
break;
}
- spin_unlock(&chip->mixer_lock);
return result;
}
@@ -483,7 +454,7 @@ static int snd_als4000_playback_trigger(struct snd_pcm_substream *substream, int
struct snd_sb *chip = snd_pcm_substream_chip(substream);
int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -499,7 +470,6 @@ static int snd_als4000_playback_trigger(struct snd_pcm_substream *substream, int
result = -EINVAL;
break;
}
- spin_unlock(&chip->reg_lock);
return result;
}
@@ -508,9 +478,9 @@ static snd_pcm_uframes_t snd_als4000_capture_pointer(struct snd_pcm_substream *s
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int result;
- spin_lock(&chip->reg_lock);
- result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
+ }
result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -520,9 +490,9 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned result;
- spin_lock(&chip->reg_lock);
- result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
+ }
result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -563,10 +533,10 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
snd_als4k_iobase_writeb(chip->alt_port,
ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);
- spin_lock(&chip->mixer_lock);
- /* SPECS_PAGE: 20 */
- sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* SPECS_PAGE: 20 */
+ sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ }
if (sb_irqstatus & SB_IRQTYPE_8BIT)
snd_sb_ack_8bit(chip);
@@ -647,7 +617,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,7 +635,6 @@ 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;
}
@@ -675,9 +643,6 @@ static int snd_als4000_capture_close(struct snd_pcm_substream *substream)
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
@@ -686,9 +651,6 @@ static const struct snd_pcm_ops snd_als4000_playback_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
@@ -707,8 +669,8 @@ static int 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;
@@ -744,18 +706,18 @@ static void snd_als4000_configure(struct snd_sb *chip)
int i;
/* do some more configuration */
- spin_lock_irq(&chip->mixer_lock);
- tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
- snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
- tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
- /* always select DMA channel 0, since we do not actually use DMA
- * SPECS_PAGE: 19/20 */
- snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
- snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
- tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
- spin_unlock_irq(&chip->mixer_lock);
+ scoped_guard(spinlock_irq, &chip->mixer_lock) {
+ tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+ /* always select DMA channel 0, since we do not actually use DMA
+ * SPECS_PAGE: 19/20 */
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+ }
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* enable interrupts */
snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL,
ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE);
@@ -766,7 +728,6 @@ static void snd_als4000_configure(struct snd_sb *chip)
/* enable burst mode to prevent dropouts during high PCI bus usage */
snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
(snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL) & ~0x07) | 0x04);
- spin_unlock_irq(&chip->reg_lock);
}
#ifdef SUPPORT_JOYSTICK
@@ -781,13 +742,15 @@ static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
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) {
@@ -798,7 +761,6 @@ static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
acard->gameport = gp = gameport_allocate_port();
if (!gp) {
dev_err(&acard->pci->dev, "cannot allocate memory for gameport\n");
- release_and_free_resource(r);
return -ENOMEM;
}
@@ -806,7 +768,6 @@ static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
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 +780,11 @@ static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
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 +800,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 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 +822,30 @@ static int 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
+ 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");
- pci_disable_device(pci);
return -ENXIO;
}
- if ((err = pci_request_regions(pci, "ALS4000")) < 0) {
- pci_disable_device(pci);
+ err = pcim_request_all_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_new(&pci->dev, 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,17 +855,17 @@ static int 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;
@@ -923,19 +873,20 @@ static int snd_card_als4000_probe(struct pci_dev *pci,
snd_als4000_configure(chip);
- strcpy(card->driver, "ALS4000");
- strcpy(card->shortname, "Avance Logic ALS4000");
+ strscpy(card->driver, "ALS4000");
+ strscpy(card->shortname, "Avance Logic ALS4000");
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 |
- MPU401_INFO_IRQ_HOOK,
- -1, &chip->rmidi)) < 0) {
+ 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,12 +894,13 @@ static int 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,
@@ -958,34 +910,28 @@ static int snd_card_als4000_probe(struct pci_dev *pci,
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id));
}
-#ifdef CONFIG_PM_SLEEP
static int snd_als4000_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -994,7 +940,6 @@ static int snd_als4000_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -1018,19 +963,14 @@ static int snd_als4000_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
static struct pci_driver als4000_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_als4000_ids,
.probe = snd_card_als4000_probe,
- .remove = snd_card_als4000_remove,
.driver = {
- .pm = SND_ALS4000_PM_OPS,
+ .pm = &snd_als4000_pm,
},
};
diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile
index 391830a4556c..d558a974fa7e 100644
--- a/sound/pci/asihpi/Makefile
+++ b/sound/pci/asihpi/Makefile
@@ -1,4 +1,5 @@
-snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
+# SPDX-License-Identifier: GPL-2.0-only
+snd-asihpi-y := 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 720361455c60..fd0a67b772d1 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Asihpi soundcard
* Copyright (c) by 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
- *
- *
* 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
@@ -49,19 +36,10 @@ MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
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)
+#ifdef ASIHPI_VERBOSE_DEBUG
+#define asihpi_dbg(format, args...) pr_debug(format, ##args)
#else
-#define snd_printddd(format, args...) do { } while (0)
+#define asihpi_dbg(format, args...) do { } while (0)
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
@@ -69,27 +47,27 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
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_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_param(build_info, charp, 0444);
MODULE_PARM_DESC(build_info, "Built within ALSA source");
#endif
@@ -130,7 +108,6 @@ struct snd_card_asihpi {
* snd_card_asihpi_timer_function().
*/
struct snd_card_asihpi_pcm *llmode_streampriv;
- struct tasklet_struct t;
void (*pcm_start)(struct snd_pcm_substream *substream);
void (*pcm_stop)(struct snd_pcm_substream *substream);
@@ -271,20 +248,10 @@ static inline u16 hpi_stream_group_reset(u32 h_stream)
return hpi_instream_group_reset(h_stream);
}
-static inline u16 hpi_stream_group_get_map(
- u32 h_stream, u32 *mo, u32 *mi)
-{
- if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_get_map(h_stream, mo, mi);
- else
- return hpi_instream_group_get_map(h_stream, mo, mi);
-}
-
static u16 handle_error(u16 err, int line, char *filename)
{
if (err)
- printk(KERN_WARNING
- "in file %s, line %d: HPI error %d\n",
+ pr_warn("in file %s, line %d: HPI error %d\n",
filename, line, err);
return err;
}
@@ -296,49 +263,54 @@ static u16 handle_error(u16 err, int line, char *filename)
static void print_hwparams(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *p)
{
+ struct device *dev = substream->pcm->card->dev;
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",
+ dev_dbg(dev, "%s HWPARAMS\n", name);
+ dev_dbg(dev, " 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",
+ dev_dbg(dev, " 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",
+ dev_dbg(dev, " 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);
}
-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 */
#endif
};
-static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
+static int snd_card_asihpi_format_alsa2hpi(struct snd_card_asihpi *asihpi,
+ snd_pcm_format_t alsa_format,
u16 *hpi_format)
{
u16 format;
@@ -351,8 +323,8 @@ static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
}
}
- snd_printd(KERN_WARNING "failed match for alsa format %d\n",
- alsa_format);
+ dev_dbg(asihpi->card->dev, "failed match for alsa format %d\n",
+ alsa_format);
*hpi_format = 0;
return -EINVAL;
}
@@ -460,10 +432,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int bytes_per_sec;
print_hwparams(substream, params);
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
- err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
+ err = snd_card_asihpi_format_alsa2hpi(card, params_format(params), &format);
if (err)
return err;
@@ -485,17 +454,17 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
- snd_printdd(
+ dev_dbg(card->card->dev,
"stream_host_buffer_attach success %u %lu\n",
params_buffer_bytes(params),
(unsigned long)runtime->dma_addr);
} else {
- snd_printd("stream_host_buffer_attach error %d\n",
- err);
+ dev_dbg(card->card->dev,
+ "stream_host_buffer_attach error %d\n", err);
return -ENOMEM;
}
- err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
+ 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);
@@ -520,7 +489,6 @@ snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
if (dpcm->hpi_buffer_attached)
hpi_stream_host_buffer_detach(dpcm->h_stream);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -550,7 +518,7 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
dpcm->respawn_timer = 0;
- del_timer(&dpcm->timer);
+ timer_delete(&dpcm->timer);
}
static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
@@ -562,9 +530,7 @@ static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
card = snd_pcm_substream_chip(substream);
WARN_ON(in_interrupt());
- tasklet_disable(&card->t);
card->llmode_streampriv = dpcm;
- tasklet_enable(&card->t);
hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
HPI_ADAPTER_PROPERTY_IRQ_RATE,
@@ -580,13 +546,7 @@ static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
- if (in_interrupt())
- card->llmode_streampriv = NULL;
- else {
- tasklet_disable(&card->t);
- card->llmode_streampriv = NULL;
- tasklet_enable(&card->t);
- }
+ card->llmode_streampriv = NULL;
}
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
@@ -602,7 +562,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_printdd("%s trigger start\n", name);
+ dev_dbg(card->card->dev, "%s trigger start\n", name);
snd_pcm_group_for_each_entry(s, substream) {
struct snd_pcm_runtime *runtime = s->runtime;
struct snd_card_asihpi_pcm *ds = runtime->private_data;
@@ -623,7 +583,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
* data??
*/
unsigned int preload = ds->period_bytes * 1;
- snd_printddd("%d preload %d\n", s->number, preload);
+ asihpi_dbg("%d preload %d\n", s->number, preload);
hpi_handle_error(hpi_outstream_write_buf(
ds->h_stream,
&runtime->dma_area[0],
@@ -633,7 +593,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
}
if (card->support_grouping) {
- snd_printdd("%d group\n", s->number);
+ dev_dbg(card->card->dev, "%d group\n", s->number);
e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
@@ -654,7 +614,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_printdd("%s trigger stop\n", name);
+ dev_dbg(card->card->dev, "%s trigger stop\n", name);
card->pcm_stop(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) != card)
@@ -665,10 +625,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
/*? 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) {
- snd_printdd("%d group\n", s->number);
+ dev_dbg(card->card->dev, "%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
@@ -685,17 +645,17 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printdd("%s trigger pause release\n", name);
+ dev_dbg(card->card->dev, "%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_printdd("%s trigger pause push\n", name);
+ dev_dbg(card->card->dev, "%s trigger pause push\n", name);
card->pcm_stop(substream);
hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
break;
default:
- snd_printd(KERN_ERR "\tINVALID\n");
+ dev_dbg(card->card->dev, "\tINVALID\n");
return -EINVAL;
}
@@ -749,7 +709,7 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b,
*/
static void snd_card_asihpi_timer_function(struct timer_list *t)
{
- struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer);
+ struct snd_card_asihpi_pcm *dpcm = timer_container_of(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;
@@ -758,7 +718,6 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
unsigned int remdata, xfercount, next_jiffies;
int first = 1;
- int loops = 0;
u16 state;
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
char name[16];
@@ -794,12 +753,13 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
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);
+ dev_dbg(card->card->dev,
+ "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);
+ dev_dbg(card->card->dev,
+ "P%d drained\n", s->number);
ds->drained_count++;
if (ds->drained_count > 20) {
snd_pcm_stop_xrun(s);
@@ -824,7 +784,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
newdata);
}
- snd_printddd(
+ asihpi_dbg(
"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,
@@ -839,7 +799,6 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
(unsigned long)frames_to_bytes(runtime,
runtime->control->appl_ptr)
);
- loops++;
}
pcm_buf_dma_ofs = min_buf_pos;
@@ -856,7 +815,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
+ asihpi_dbg("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, substream) {
@@ -889,7 +848,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
}
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n",
+ asihpi_dbg("write1, P=%d, xfer=%d, buf_ofs=%d\n",
s->number, xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
@@ -899,7 +858,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
if (xfer2) {
pd = s->runtime->dma_area;
- snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n",
+ asihpi_dbg("write2, P=%d, xfer=%d, buf_ofs=%d\n",
s->number,
xfercount - xfer1, buf_ofs);
hpi_handle_error(
@@ -909,7 +868,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
&ds->format));
}
} else {
- snd_printddd("read1, C=%d, xfer=%d\n",
+ asihpi_dbg("read1, C=%d, xfer=%d\n",
s->number, xfer1);
hpi_handle_error(
hpi_instream_read_buf(
@@ -917,7 +876,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
pd, xfer1));
if (xfer2) {
pd = s->runtime->dma_area;
- snd_printddd("read2, C=%d, xfer=%d\n",
+ asihpi_dbg("read2, C=%d, xfer=%d\n",
s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
@@ -936,9 +895,8 @@ static void snd_card_asihpi_timer_function(struct timer_list *t)
add_timer(&dpcm->timer);
}
-static void snd_card_asihpi_int_task(unsigned long data)
+static void snd_card_asihpi_isr(struct hpi_adapter *a)
{
- struct hpi_adapter *a = (struct hpi_adapter *)data;
struct snd_card_asihpi *asihpi;
WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
@@ -948,33 +906,13 @@ static void snd_card_asihpi_int_task(unsigned long data)
&asihpi->llmode_streampriv->timer);
}
-static void snd_card_asihpi_isr(struct hpi_adapter *a)
-{
- 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;
- tasklet_schedule(&asihpi->t);
-}
-
/***************************** PLAYBACK OPS ****************/
-static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- char name[16];
- snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printddd(KERN_INFO "%s ioctl %d\n", name, cmd);
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
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_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;
@@ -992,7 +930,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_debug_name(substream, name, sizeof(name));
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
- snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr);
+ asihpi_dbg("%s, pointer=%ld\n", name, (unsigned long)ptr);
return ptr;
}
@@ -1023,7 +961,7 @@ static u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi,
format, sample_rate, 128000, 0);
if (!err)
err = hpi_outstream_query_format(h_stream, &hpi_format);
- if (!err && (hpi_to_alsa_formats[format] != -1))
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
return formats;
@@ -1044,12 +982,12 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
err = hpi_outstream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream);
hpi_handle_error(err);
- if (err)
+ if (err) {
kfree(dpcm);
- if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
- return -EBUSY;
- if (err)
+ if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
+ return -EBUSY;
return -EIO;
+ }
/*? also check ASI5000 samplerate source
If external, only support external rate.
@@ -1114,8 +1052,6 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames, UINT_MAX);
- snd_printdd("playback open\n");
-
return 0;
}
@@ -1125,15 +1061,12 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
- snd_printdd("playback close\n");
-
return 0;
}
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,
@@ -1150,7 +1083,7 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
+ asihpi_dbg("%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.
@@ -1158,12 +1091,6 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
}
-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);
-}
-
static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1174,14 +1101,13 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
dpcm->pcm_buf_dma_ofs = 0;
dpcm->pcm_buf_elapsed_dma_ofs = 0;
- snd_printdd("Capture Prepare %d\n", substream->number);
return 0;
}
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;
@@ -1205,7 +1131,7 @@ static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
format, sample_rate, 128000, 0);
if (!err)
err = hpi_instream_query_format(h_stream, &hpi_format);
- if (!err && (hpi_to_alsa_formats[format] != -1))
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
return formats;
@@ -1223,18 +1149,19 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
if (dpcm == NULL)
return -ENOMEM;
- snd_printdd("capture open adapter %d stream %d\n",
- card->hpi->adapter->index, substream->number);
+
+ dev_dbg(card->card->dev, "capture open adapter %d stream %d\n",
+ card->hpi->adapter->index, substream->number);
err = hpi_handle_error(
hpi_instream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream));
- if (err)
+ if (err) {
kfree(dpcm);
- if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
- return -EBUSY;
- if (err)
+ if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
+ return -EBUSY;
return -EIO;
+ }
timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
dpcm->substream = substream;
@@ -1299,7 +1226,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
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,
@@ -1331,13 +1257,13 @@ static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
pcm->private_data = asihpi;
pcm->info_flags = 0;
- strcpy(pcm->name, "Asihpi PCM");
+ strscpy(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;
}
@@ -1475,8 +1401,6 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
hpi_ctl->src_node_index,
dir, name);
}
- /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name,
- hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */
}
/*------------------------------------------------------------
@@ -1530,7 +1454,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];
@@ -1541,9 +1464,8 @@ 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(h_control, an_gain_mB));
- return change;
+ return 1;
}
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
@@ -1566,13 +1488,12 @@ static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
- int change = 1;
/* 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 change;
+ return 1;
}
static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
@@ -1939,7 +1860,7 @@ 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);
@@ -1966,7 +1887,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
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);
@@ -2108,7 +2029,7 @@ static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
}
/* linear values for 10dB steps */
-static int log2lin[] = {
+static const int log2lin[] = {
0x7FFFFFFF, /* 0dB */
679093956,
214748365,
@@ -2196,7 +2117,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;
@@ -2209,10 +2129,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(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],
@@ -2242,9 +2161,8 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
return 0;
}
}
- snd_printd(KERN_WARNING
- "Control %x failed to match mux source %hu %hu\n",
- h_control, source_type, source_index);
+ pr_warn("%s: Control %x failed to match mux source %hu %hu\n",
+ __func__, h_control, source_type, source_index);
ucontrol->value.enumerated.item[0] = 0;
return 0;
}
@@ -2382,8 +2300,7 @@ static const char * const sampleclock_sources[] = {
static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
@@ -2393,7 +2310,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
clkcache->s[uinfo->value.enumerated.item].name);
return 0;
}
@@ -2401,8 +2318,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
u32 h_control = kcontrol->private_value;
u16 source, srcindex = 0;
@@ -2429,8 +2345,7 @@ static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
unsigned int item;
int change;
@@ -2615,7 +2530,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (snd_BUG_ON(!asihpi))
return -EINVAL;
card = asihpi->card;
- strcpy(card->mixername, "Asihpi Mixer");
+ strscpy(card->mixername, "Asihpi Mixer");
err =
hpi_mixer_open(asihpi->hpi->adapter->index,
@@ -2780,10 +2695,8 @@ snd_asihpi_proc_read(struct snd_info_entry *entry,
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);
}
/*------------------------------------------------------------
@@ -2828,7 +2741,7 @@ static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device)
err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
if (err < 0)
return err;
- strcpy(hw->name, "asihpi (HPI)");
+ strscpy(hw->name, "asihpi (HPI)");
hw->iface = SNDRV_HWDEP_IFACE_LAST;
hw->ops.open = snd_asihpi_hpi_open;
hw->ops.ioctl = snd_asihpi_hpi_ioctl;
@@ -2910,8 +2823,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
if (hpi->interrupt_mode) {
asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
- tasklet_init(&asihpi->t, snd_card_asihpi_int_task,
- (unsigned long)hpi);
hpi->interrupt_callback = snd_card_asihpi_isr;
} else {
asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
@@ -2978,7 +2889,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
by enable_hwdep module param*/
snd_asihpi_hpi_new(asihpi, 0);
- strcpy(card->driver, "ASIHPI");
+ strscpy(card->driver, "ASIHPI");
sprintf(card->shortname, "AudioScience ASI%4X",
asihpi->hpi->adapter->type);
@@ -3000,14 +2911,12 @@ __nodev:
static void snd_asihpi_remove(struct pci_dev *pci_dev)
{
struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
- struct snd_card_asihpi *asihpi = hpi->snd_card->private_data;
/* 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));
- tasklet_kill(&asihpi->t);
}
snd_card_free(hpi->snd_card);
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 4466bd2c5272..04a5cf6572cd 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-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
@@ -1202,19 +1191,6 @@ u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode);
-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_test_assert(u16 adapter_index, u16 assert_id);
-
-u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key);
-
-u16 hpi_adapter_self_test(u16 adapter_index);
-
-u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes,
- int *count_bytes);
-
u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1,
u16 paramter2);
@@ -1499,9 +1475,6 @@ u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY);
u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI);
-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 */
/****************************/
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 2d6364825d4d..b08578c93c6a 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-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.
@@ -399,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
*/
@@ -619,7 +608,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
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");
+ strscpy(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;
@@ -1264,7 +1253,6 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
int local_count = count;
int xfer_size;
u32 *pdata = dest;
- u32 loop_count = 0;
while (local_count) {
if (local_count > c6711_burst_size)
@@ -1284,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)
diff --git a/sound/pci/asihpi/hpi6000.h b/sound/pci/asihpi/hpi6000.h
index 7e0deeff5e7c..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-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 8d5abfa4e24b..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-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.
@@ -441,7 +430,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
pao = hpi_find_adapter(phm->adapter_index);
} else {
/* subsys messages don't address an adapter */
- _HPI_6205(NULL, phm, phr);
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
}
@@ -456,7 +445,7 @@ void HPI_6205(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 ***
*/
@@ -635,7 +624,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
{
struct hpi_message hm;
struct hpi_response hr;
- u32 max_streams;
HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
memset(&hm, 0, sizeof(hm));
@@ -660,10 +648,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
pao->type = hr.u.ax.info.adapter_type;
pao->index = hr.u.ax.info.adapter_index;
- max_streams =
- hr.u.ax.info.num_outstreams +
- hr.u.ax.info.num_instreams;
-
HPI_DEBUG_LOG(VERBOSE,
"got adapter info type %x index %d serial %d\n",
hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index,
diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h
index ec0827b633a6..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-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.
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index aeea679b2281..e569e3b33b8e 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-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
@@ -64,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
*/
@@ -1405,17 +1394,12 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
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_resource *p_resource,
- u16 *pw_adapter_index);
-
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(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
-u16 hpi_adapter_restart(u16 adapter_index);
-
/*
The following 3 functions were last declared in header files for
driver 3.10. HPI_ControlQuery() used to be the recommended way
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index c7751243dc42..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-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
@@ -39,10 +28,12 @@ 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)
{
if (phr->type != HPI_TYPE_RESPONSE) {
@@ -117,10 +108,11 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao)
}
/**
-* 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;
@@ -148,10 +140,9 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
}
/**
-*
-* wipe an HPI_ADAPTERS_LIST structure.
-*
-**/
+ * wipe_adapter_list - wipe an HPI_ADAPTERS_LIST structure.
+ *
+ */
static void wipe_adapter_list(void)
{
memset(&adapters, 0, sizeof(adapters));
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index 46629c2d101b..8ec656cf8848 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -1,20 +1,9 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
AudioScience HPI driver
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
*/
diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c
index 9e122327dc05..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-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.
@@ -63,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;
diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h
index 2c9af2329d36..c6dfc229213d 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-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.
@@ -40,16 +29,15 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
the start of each message, eg see linux kernel hpios.h */
#ifdef SOURCEFILE_NAME
+#undef FILE_LINE
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
-#else
-#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
#endif
#define HPI_DEBUG_ASSERT(expression) \
do { \
if (!(expression)) { \
printk(KERN_ERR FILE_LINE \
- "ASSERT " __stringify(expression)); \
+ " ASSERT " __stringify(expression)); \
} \
} while (0)
@@ -57,7 +45,7 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
printk(HPI_DEBUG_FLAG_##level \
- FILE_LINE __VA_ARGS__); \
+ FILE_LINE " " __VA_ARGS__); \
} \
} while (0)
@@ -81,7 +69,7 @@ void hpi_debug_data(u16 *pdata, u32 len);
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \
- FILE_LINE __stringify(level)); \
+ FILE_LINE " " __stringify(level)); \
} \
} while (0)
diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c
index 3603c24f34d2..9acc0ac75eca 100644
--- a/sound/pci/asihpi/hpidspcd.c
+++ b/sound/pci/asihpi/hpidspcd.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/***********************************************************************
AudioScience HPI driver
@@ -5,18 +6,6 @@
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
***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h
index 659d19ca6d42..9f1468ed7096 100644
--- a/sound/pci/asihpi/hpidspcd.h
+++ b/sound/pci/asihpi/hpidspcd.h
@@ -1,21 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/***********************************************************************/
-/**
+/*
AudioScience HPI driver
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
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 1de05383126a..24047fafef51 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -2020,7 +2020,6 @@ u16 hpi_meter_get_peak(u32 h_control, short an_peakdB[HPI_MAX_CHANNELS]
HPI_CONTROL_GET_STATE);
if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
return HPI_ERROR_INVALID_HANDLE;
- hm.obj_index = hm.obj_index;
hm.u.c.attribute = HPI_METER_PEAK;
hpi_send_recv(&hm, &hr);
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 7eb617175fde..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-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;
@@ -39,10 +29,12 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
{
u16 size;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
size = msg_size[object];
- else
+ } else {
size = sizeof(*phm);
+ }
memset(phm, 0, size);
phm->size = size;
@@ -66,10 +58,12 @@ void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
{
u16 size;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
size = res_size[object];
- else
+ } else {
size = sizeof(*phr);
+ }
memset(phr, 0, sizeof(*phr));
phr->size = size;
diff --git a/sound/pci/asihpi/hpimsginit.h b/sound/pci/asihpi/hpimsginit.h
index 5b48708c7d1e..c64126b30f8d 100644
--- a/sound/pci/asihpi/hpimsginit.h
+++ b/sound/pci/asihpi/hpimsginit.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
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
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index 736f45337fc7..b68e6bfbbfba 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
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 Caching
@@ -28,7 +17,7 @@ Extended Message Function With Response Caching
#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"
};
@@ -104,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;
@@ -724,7 +708,7 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
return phr->error;
}
- if (hr.error == 0) {
+ if (hr.error == 0 && hr.u.s.adapter_index < HPI_MAX_ADAPTERS) {
/* the adapter was created successfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
diff --git a/sound/pci/asihpi/hpimsgx.h b/sound/pci/asihpi/hpimsgx.h
index 37f3efd95a70..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-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 5badd08e1d69..9fb0c8e503df 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -1,17 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*******************************************************************************
AudioScience HPI driver
Common Linux HPI ioctl and module probe/remove functions
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.
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
@@ -33,6 +26,7 @@
#include <linux/stringify.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
+#include <linux/nospec.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
@@ -45,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");
/* 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 */
@@ -186,7 +180,8 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct hpi_adapter *pa = NULL;
if (hm->h.adapter_index < ARRAY_SIZE(adapters))
- pa = &adapters[hm->h.adapter_index];
+ 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,
@@ -334,11 +329,20 @@ static irqreturn_t asihpi_isr(int irq, void *dev_id)
asihpi_irq_count, a->adapter->type, a->adapter->index); */
if (a->interrupt_callback)
- a->interrupt_callback(a);
+ 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)
{
@@ -348,16 +352,16 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
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));
- dev_printk(KERN_DEBUG, &pci_dev->dev,
+ dev_dbg(&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 (pci_enable_device(pci_dev) < 0) {
+ if (pcim_enable_device(pci_dev) < 0) {
dev_err(&pci_dev->dev,
"pci_enable_device failed, disabling device\n");
return -EIO;
@@ -483,8 +487,9 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
}
/* Note: request_irq calls asihpi_isr here */
- if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED,
- "asihpi", &adapters[adapter_index])) {
+ 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;
@@ -504,7 +509,7 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
return 0;
err:
- for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
+ while (--idx >= 0) {
if (pci.ap_mem_base[idx]) {
iounmap(pci.ap_mem_base[idx]);
pci.ap_mem_base[idx] = NULL;
diff --git a/sound/pci/asihpi/hpioctl.h b/sound/pci/asihpi/hpioctl.h
index 0d767e10ac48..f2ee172da56c 100644
--- a/sound/pci/asihpi/hpioctl.h
+++ b/sound/pci/asihpi/hpioctl.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*******************************************************************************
AudioScience HPI driver
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
*******************************************************************************/
diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c
index 5ef4fe964366..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-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
@@ -49,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",
diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h
index 4e383601b9cf..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-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
@@ -78,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)
diff --git a/sound/pci/asihpi/hpipcida.h b/sound/pci/asihpi/hpipcida.h
index db570ddf64b3..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-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 7ae63d452bba..2a0c59d5afa5 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1,22 +1,8 @@
+// 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 <linux/io.h>
@@ -37,7 +23,6 @@
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 */
@@ -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 */
};
/*
@@ -296,7 +281,7 @@ static const struct pci_device_id snd_atiixp_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
-static struct snd_pci_quirk atiixp_quirks[] = {
+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 */
@@ -360,14 +345,13 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
{
unsigned int i;
u32 addr, desc_addr;
- unsigned long flags;
if (periods > ATI_MAX_DESCRIPTORS)
return -ENOMEM;
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;
@@ -378,11 +362,11 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
return 0;
/* reset DMA before changing the descriptor table */
- spin_lock_irqsave(&chip->reg_lock, flags);
- writel(0, chip->remap_addr + dma->ops->llp_offset);
- dma->ops->enable_dma(chip, 0);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ }
/* fill the entries */
addr = (u32)substream->runtime->dma_addr;
@@ -535,7 +519,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -545,7 +528,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_CMD_POWERDOWN);
return 0;
}
-#endif
/*
* auto-detection of codecs
@@ -728,11 +710,15 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
!dma->ops->flush_dma))
return -EINVAL;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (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 +726,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;
@@ -755,7 +744,6 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_atiixp_check_bus_busy(chip);
}
}
- spin_unlock(&chip->reg_lock);
return err;
}
@@ -869,7 +857,7 @@ static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
if (chip->spdif_over_aclink) {
unsigned int data;
/* enable slots 10/11 */
@@ -887,7 +875,6 @@ static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream)
atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0);
atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -897,21 +884,21 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
struct atiixp *chip = snd_pcm_substream_chip(substream);
unsigned int data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
switch (substream->runtime->channels) {
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);
@@ -932,7 +919,6 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN,
substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -941,11 +927,10 @@ static int snd_atiixp_capture_prepare(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN,
substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
ATI_REG_CMD_INTERLEAVE_IN : 0);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -959,9 +944,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 +983,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;
}
@@ -1051,14 +1032,15 @@ 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;
/* enable DMA bits */
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 1);
+ }
dma->opened = 1;
return 0;
@@ -1071,9 +1053,9 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
/* disable DMA bits */
if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 0);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 0);
+ }
dma->substream = NULL;
dma->opened = 0;
return 0;
@@ -1086,9 +1068,8 @@ static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
struct atiixp *chip = snd_pcm_substream_chip(substream);
int err;
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
- mutex_unlock(&chip->open_mutex);
if (err < 0)
return err;
substream->runtime->hw.channels_max = chip->max_channels;
@@ -1102,11 +1083,9 @@ static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
- mutex_unlock(&chip->open_mutex);
- return err;
+
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
}
static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
@@ -1124,34 +1103,29 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
static int snd_atiixp_spdif_open(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
+
+ guard(mutex)(&chip->open_mutex);
if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
else
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
- mutex_unlock(&chip->open_mutex);
- return err;
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
}
static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
+
+ guard(mutex)(&chip->open_mutex);
if (chip->spdif_over_aclink)
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
else
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
- mutex_unlock(&chip->open_mutex);
- return err;
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
}
/* AC97 playback */
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,
@@ -1163,7 +1137,6 @@ static const struct snd_pcm_ops snd_atiixp_playback_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,
@@ -1175,7 +1148,6 @@ static const struct snd_pcm_ops snd_atiixp_capture_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,
@@ -1287,12 +1259,11 @@ static int snd_atiixp_pcm_new(struct atiixp *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, "ATI IXP AC97");
+ strscpy(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,
@@ -1318,14 +1289,13 @@ static int snd_atiixp_pcm_new(struct atiixp *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops);
pcm->private_data = chip;
if (chip->spdif_over_aclink)
- strcpy(pcm->name, "ATI IXP IEC958 (AC97)");
+ strscpy(pcm->name, "ATI IXP IEC958 (AC97)");
else
- strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
+ strscpy(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++) {
@@ -1373,10 +1343,9 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
if (status & CODEC_CHECK_BITS) {
unsigned int detected;
detected = status & CODEC_CHECK_BITS;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->codec_not_ready_bits |= detected;
atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
- spin_unlock(&chip->reg_lock);
}
/* ack */
@@ -1419,11 +1388,11 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock,
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,
@@ -1432,7 +1401,8 @@ static int 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;
@@ -1448,7 +1418,8 @@ static int 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 */
dev_dbg(chip->card->dev,
"codec %d not available for audio\n", i);
@@ -1468,7 +1439,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock,
}
-#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
@@ -1479,14 +1449,6 @@ static int snd_atiixp_suspend(struct device *dev)
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);
@@ -1514,8 +1476,6 @@ static int snd_atiixp_resume(struct device *dev)
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);
}
}
@@ -1523,12 +1483,7 @@ static int snd_atiixp_resume(struct device *dev)
return 0;
}
-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 */
-
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
/*
* proc interface for register dump
@@ -1546,10 +1501,7 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
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);
}
@@ -1557,115 +1509,79 @@ static void snd_atiixp_proc_init(struct atiixp *chip)
* destructor
*/
-static int snd_atiixp_free(struct atiixp *chip)
+static void snd_atiixp_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- 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)
-{
- struct atiixp *chip = device->device_data;
- return snd_atiixp_free(chip);
+ snd_atiixp_chip_stop(card->private_data);
}
/*
* constructor for chip instance
*/
-static int 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);
- return err;
- }
+ chip->remap_addr = pcim_iomap_region(pci, 0, "ATI IXP AC97");
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- dev_err(card->dev, "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_atiixp_free(chip);
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;
- }
- *r_chip = chip;
return 0;
}
-static int 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_new(&pci->dev, 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;
+ strscpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
+ strscpy(card->shortname, "ATI IXP");
+ 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);
@@ -1677,29 +1593,26 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
static struct pci_driver atiixp_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = snd_atiixp_remove,
.driver = {
- .pm = SND_ATIIXP_PM_OPS,
+ .pm = &snd_atiixp_pm,
},
};
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index a586635664e0..91f31e2ad3d3 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1,22 +1,8 @@
+// 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 <linux/io.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 */
@@ -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 */
};
/*
@@ -329,13 +314,12 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
{
unsigned int i;
u32 addr, desc_addr;
- unsigned long flags;
if (periods > ATI_MAX_DESCRIPTORS)
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 */
@@ -345,11 +329,11 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
return 0;
/* reset DMA before changing the descriptor table */
- spin_lock_irqsave(&chip->reg_lock, flags);
- writel(0, chip->remap_addr + dma->ops->llp_offset);
- dma->ops->enable_dma(chip, 0);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ }
/* fill the entries */
addr = (u32)substream->runtime->dma_addr;
@@ -511,7 +495,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -521,7 +504,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
ATI_REG_CMD_POWERDOWN);
return 0;
}
-#endif
/*
* auto-detection of codecs
@@ -678,7 +660,7 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
!dma->ops->flush_dma))
return -EINVAL;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch(cmd) {
case SNDRV_PCM_TRIGGER_START:
dma->ops->enable_transfer(chip, 1);
@@ -699,7 +681,6 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_atiixp_check_bus_busy(chip);
}
}
- spin_unlock(&chip->reg_lock);
return err;
}
@@ -770,13 +751,12 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
unsigned int data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* set output threshold */
data = atiixp_read(chip, MODEM_OUT_FIFO);
data &= ~ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK;
data |= 0x04 << ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT;
atiixp_write(chip, MODEM_OUT_FIFO, data);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -797,9 +777,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 +803,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;
}
@@ -875,19 +851,19 @@ 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;
/* enable DMA bits */
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 1);
+ }
dma->opened = 1;
return 0;
@@ -900,9 +876,9 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
/* disable DMA bits */
if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 0);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 0);
+ }
dma->substream = NULL;
dma->opened = 0;
return 0;
@@ -913,24 +889,17 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
{
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
- mutex_unlock(&chip->open_mutex);
- if (err < 0)
- return err;
- return 0;
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
}
static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
{
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
- mutex_unlock(&chip->open_mutex);
- return err;
+
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
}
static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
@@ -950,7 +919,6 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
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,
@@ -962,7 +930,6 @@ static const struct snd_pcm_ops snd_atiixp_playback_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,
@@ -1005,12 +972,11 @@ static int snd_atiixp_pcm_new(struct atiixp_modem *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
pcm->private_data = chip;
- strcpy(pcm->name, "ATI IXP MC97");
+ strscpy(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;
}
@@ -1044,10 +1010,9 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
if (status & CODEC_CHECK_BITS) {
unsigned int detected;
detected = status & CODEC_CHECK_BITS;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->codec_not_ready_bits |= detected;
atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
- spin_unlock(&chip->reg_lock);
}
/* ack */
@@ -1067,11 +1032,11 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
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 +1045,8 @@ static int 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,7 +1060,8 @@ static int 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 */
dev_dbg(chip->card->dev,
"codec %d not available for modem\n", i);
@@ -1114,7 +1081,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
}
-#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
@@ -1125,8 +1091,6 @@ static int snd_atiixp_suspend(struct device *dev)
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);
@@ -1150,11 +1114,7 @@ static int snd_atiixp_resume(struct device *dev)
return 0;
}
-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 */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
/*
* proc interface for register dump
@@ -1172,10 +1132,8 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
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);
}
@@ -1183,113 +1141,77 @@ static void snd_atiixp_proc_init(struct atiixp_modem *chip)
* destructor
*/
-static int snd_atiixp_free(struct atiixp_modem *chip)
+static void snd_atiixp_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- 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)
-{
- 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 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);
- return err;
- }
+ chip->remap_addr = pcim_iomap_region(pci, 0, "ATI IXP MC97");
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- dev_err(card->dev, "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_atiixp_free(chip);
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;
- }
-
- *r_chip = chip;
return 0;
}
-static int 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_new(&pci->dev, 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;
+ strscpy(card->driver, "ATIIXP-MODEM");
+ strscpy(card->shortname, "ATI IXP Modem");
+ 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);
@@ -1298,29 +1220,26 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
static struct pci_driver atiixp_modem_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = snd_atiixp_remove,
.driver = {
- .pm = SND_ATIIXP_PM_OPS,
+ .pm = &snd_atiixp_pm,
},
};
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
index 78ab11562f4d..5ec5abdee28d 100644
--- a/sound/pci/au88x0/Makefile
+++ b/sound/pci/au88x0/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-snd-au8810-objs := au8810.o
-snd-au8820-objs := au8820.o
-snd-au8830-objs := au8830.o
+snd-au8810-y := au8810.o
+snd-au8820-y := au8820.o
+snd-au8830-y := au8830.o
obj-$(CONFIG_SND_AU8810) += snd-au8810.o
obj-$(CONFIG_SND_AU8820) += snd-au8820.o
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 32092184bbf2..de56e83d8e10 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)
@@ -9,7 +10,7 @@
* Thanks to the ALSA developers, they helped a lot working out
* the ALSA part.
* Thanks also to Sourceforge for maintaining the old binary drivers,
- * and the forum, where developers could comunicate.
+ * and the forum, where developers could communicate.
*
* Now at least i can play Legacy DOOM with MIDI music :-)
*/
@@ -40,15 +41,14 @@ 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))) {
- dev_info(&vortex->dev, "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 {
dev_warn(&vortex->dev,
"could not set vortex latency: pci error 0x%x\n", rc);
@@ -66,9 +66,12 @@ 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)))) {
+ 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 {
dev_warn(&via->dev,
@@ -103,14 +106,16 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
} 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);
@@ -118,56 +123,35 @@ static void 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
-snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
+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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
dev_err(card->dev, "error to set DMA mask\n");
- pci_disable_device(pci);
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;
@@ -176,63 +160,39 @@ 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) {
- dev_err(card->dev, "MMIO area remap failed.\n");
- err = -ENOMEM;
- goto ioremap_out;
- }
+ chip->mmio = pcim_iomap_region(pci, 0, KBUILD_MODNAME);
+ if (IS_ERR(chip->mmio))
+ return PTR_ERR(chip->mmio);
+
+ chip->io = pci_resource_start(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) {
+ err = vortex_core_init(chip);
+ if (err) {
dev_err(card->dev, "hw core init failed\n");
- goto core_out;
+ return err;
}
- if ((err = request_irq(pci->irq, vortex_interrupt,
- IRQF_SHARED, KBUILD_MODNAME,
- chip)) != 0) {
+ 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");
- goto irq_out;
+ 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;
- }
-
- *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
-snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+__snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -247,66 +207,57 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
// (2)
- err = snd_card_new(&pci->dev, 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
- strcpy(card->driver, CARD_NAME_SHORT);
+ strscpy(card->driver, CARD_NAME_SHORT);
sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT);
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->io, chip->irq);
// (4) Alloc components.
err = snd_vortex_mixer(chip);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
// ADB pcm.
err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM);
- if (err < 0) {
- snd_card_free(card);
+ 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
- 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);
@@ -319,7 +270,7 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
snd_vortex_synth_arg_t *arg;
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
- strcpy(wave->name, "Aureal Synth");
+ strscpy(wave->name, "Aureal Synth");
arg->hwptr = vortex;
arg->index = 1;
arg->seq_ports = seq_ports[dev];
@@ -328,16 +279,12 @@ 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) {
@@ -346,17 +293,14 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
chip->rev);
dev_alert(card->dev,
"Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
- snd_card_free(card);
- err = -ENODEV;
- return err;
+ 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++;
@@ -365,10 +309,10 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return 0;
}
-// destructor -- see "Destructor" sub-section
-static void 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));
+ return snd_card_free_on_error(&pci->dev, __snd_vortex_probe(pci, pci_id));
}
// pci_driver definition
@@ -376,7 +320,6 @@ static struct pci_driver vortex_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vortex_ids,
.probe = snd_vortex_probe,
- .remove = snd_vortex_remove,
};
module_pci_driver(vortex_driver);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index bcc648bf6478..6cbb2bc4a048 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -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.
*/
#ifndef __SOUND_AU88X0_H
@@ -153,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 */
@@ -241,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, vortex_t *v);
+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, int subdev);
-static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
- int restype);
+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);
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index 7a4558a70fb9..d5cafaa229f1 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"
@@ -766,7 +754,7 @@ snd_vortex_a3d_filter_info(struct snd_kcontrol *kcontrol,
static int
snd_vortex_a3d_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- //a3dsrc_t *a = kcontrol->private_data;
+ //a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
/* No read yet. Would this be really useable/needed ? */
return 0;
@@ -776,8 +764,8 @@ static int
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;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
+ int i;
int coord[6];
for (i = 0; i < 6; i++)
coord[i] = ucontrol->value.integer.value[i];
@@ -786,16 +774,16 @@ 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
snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
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. */
@@ -805,15 +793,14 @@ 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
snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
- int changed = 1;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
int l, r;
/* There may be some scale tranlation needed here. */
l = ucontrol->value.integer.value[0];
@@ -822,15 +809,15 @@ 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
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;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
+ int i;
int params[6];
for (i = 0; i < 6; i++)
params[i] = ucontrol->value.integer.value[i];
@@ -843,7 +830,7 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
a3dsrc_SetAtmosCurrent(a, a->filter[0],
a->filter[1], a->filter[2],
a->filter[3], a->filter[4]);
- return changed;
+ return 1;
}
static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
@@ -862,46 +849,50 @@ static int vortex_a3d_register_controls(vortex_t *vortex)
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 4083c8b01619..e5d867637336 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.
*/
/*
@@ -1115,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;
@@ -1122,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);
@@ -1204,7 +1195,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
(((adbdma << 2) + pp) << 2));
}
@@ -1246,7 +1237,7 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
}
}
@@ -1390,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);
@@ -1472,7 +1466,7 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
(((wtdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
(((wtdma << 2) + pp) << 2));
}
@@ -1860,7 +1854,7 @@ vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
vortex_mix_disableinput(vortex, mix, mixin, a);
}
-// Connect absolut address to mixin.
+// Connect absolute address to mixin.
static void
vortex_connection_adb_mixin(vortex_t * vortex, int en,
unsigned char channel, unsigned char source,
@@ -1886,7 +1880,7 @@ vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
ADB_DMA(adbdma));
}
-// mix to absolut address.
+// mix to absolute address.
static void
vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
unsigned char mix, unsigned char dest)
@@ -1995,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.
@@ -2004,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;
@@ -2126,9 +2120,9 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
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;
}
@@ -2146,18 +2140,20 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
/* 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(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(stream->resources));
@@ -2168,10 +2164,10 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
}
#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(stream->resources));
dev_err(vortex->card->dev,
@@ -2284,19 +2280,18 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
/* 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(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(stream->resources));
return -EBUSY;
@@ -2770,7 +2765,7 @@ static int vortex_core_shutdown(vortex_t * vortex)
/* Alsa support. */
-static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v)
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v)
{
int fmt;
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
index b566b44e4da7..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;
@@ -864,7 +852,7 @@ static const struct snd_kcontrol_new vortex_levels_kcontrol = {
};
/* EQ band gain labels. */
-static char *EqBandLabels[10] = {
+static const char * const EqBandLabels[10] = {
"EQ0 31Hz\0",
"EQ1 63Hz\0",
"EQ2 125Hz\0",
@@ -885,29 +873,33 @@ static int vortex_eq_init(vortex_t *vortex)
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;
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_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
index 49a52d298b1a..a74f266f0bd0 100644
--- a/sound/pci/au88x0/au88x0_eqdata.c
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -1,7 +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,
@@ -14,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,
@@ -33,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,
@@ -66,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,
@@ -74,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,
@@ -105,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 53abcd3eccbd..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
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index 60dd8a091bc3..00781a7fd28c 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -15,7 +15,7 @@ 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);
+ strscpy(id.name, name);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_remove_id(card, &id);
}
@@ -25,12 +25,13 @@ 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.
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 1025e55ca854..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>
@@ -82,9 +68,9 @@ static int 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,
- MPU401_INFO_IRQ_HOOK, -1, &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 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 |
- MPU401_INFO_IRQ_HOOK, -1, &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 53714e0bf598..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.
*/
/*
@@ -142,14 +130,14 @@ 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,
@@ -221,15 +209,7 @@ 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) {
- dev_err(chip->card->dev, "Vortex: pcm page alloc failed!\n");
- return err;
- }
/*
pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
params_period_bytes(hw_params), params_channels(hw_params));
@@ -316,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 */
@@ -442,27 +422,25 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
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] = {
+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",
@@ -519,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[] = {
+static const struct snd_kcontrol_new snd_vortex_mixer_spdif[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -649,9 +627,8 @@ static int 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:
@@ -681,7 +658,8 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
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;
}
}
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
index 78e12f4796f3..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.
*/
/*
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
index b278e285fd40..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,35 +17,35 @@
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 = {
+static __maybe_unused xtalk_gains_t const asXtalkGainsDefault = {
0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
0x4000, 0x4000, 0x4000, 0x4000, 0x4000
};
-static xtalk_gains_t const asXtalkGainsTest = {
+static __maybe_unused xtalk_gains_t const asXtalkGainsTest = {
0x7fff, 0x8000, 0x0000, 0x0000, 0x0001,
0xffff, 0x4000, 0xc000, 0x0002, 0xfffe
};
-static xtalk_gains_t const asXtalkGains1Chan = {
+static __maybe_unused xtalk_gains_t const asXtalkGains1Chan = {
0x7FFF, 0, 0, 0, 0,
0x7FFF, 0, 0, 0, 0,
};
@@ -76,7 +64,7 @@ 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 xtalk_dline_t const alXtalkDlineTest = {
+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
@@ -86,7 +74,7 @@ static xtalk_instate_t const asXtalkInStateZeros = {
0, 0, 0, 0
};
-static xtalk_instate_t const asXtalkInStateTest = {
+static __maybe_unused xtalk_instate_t const asXtalkInStateTest = {
0x0080, 0xff80, 0x0001, 0xffff
};
@@ -101,11 +89,11 @@ static xtalk_state_t const asXtalkOutStateZeros = {
static short const sDiamondKLeftEq = 0x401d;
static short const sDiamondKRightEq = 0x401d;
static short const sDiamondKLeftXt = 0xF90E;
-static short const sDiamondKRightXt = 0xF90E;
+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;
@@ -130,7 +118,7 @@ static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
{0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+static __maybe_unused xtalk_coefs_t const asXtalkWideCoefsRightXt = {
{0x55c6, 0xc97b, 0x005b, 0x0047, 0},
{0x6a60, 0xca20, 0xffc6, 0x0040, 0},
{0x6411, 0xd711, 0xfca1, 0x0190, 0},
@@ -161,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},
@@ -184,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},
@@ -192,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},
@@ -200,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},
@@ -208,7 +196,7 @@ 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},
{0x0200, 0x0010, 0xFFF0, 0xC000},
@@ -240,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},
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..c246f7c7f2bf 100644
--- a/sound/pci/aw2/Makefile
+++ b/sound/pci/aw2/Makefile
@@ -1,3 +1,4 @@
-snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-aw2-y := 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 9a49e4243a9c..e2c501f4394c 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>
@@ -112,19 +99,13 @@ struct aw2 {
/*********************************
* FUNCTION DECLARATIONS
********************************/
-static int snd_aw2_dev_free(struct snd_device *device);
-static int 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);
static int snd_aw2_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id);
-static void snd_aw2_remove(struct pci_dev *pci);
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,
@@ -173,7 +154,6 @@ static struct pci_driver aw2_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
- .remove = snd_aw2_remove,
};
module_pci_driver(aw2_driver);
@@ -182,9 +162,6 @@ module_pci_driver(aw2_driver);
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,
@@ -194,9 +171,6 @@ static const struct snd_pcm_ops snd_aw2_playback_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,
@@ -218,57 +192,32 @@ static const struct snd_kcontrol_new aw2_control = {
********************************/
/* 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 */
- 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 snd_aw2_create(struct snd_card *card,
- struct pci_dev *pci, struct aw2 **rchip)
+ 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 ((dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) ||
- (dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0)) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
dev_err(card->dev, "Impossible to set 32bit mask DMA\n");
- pci_disable_device(pci);
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
/* initialize the stuff */
chip->card = card;
@@ -276,51 +225,22 @@ static int 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);
- return err;
- }
+ chip->iobase_virt = pcim_iomap_region(pci, 0, "Audiowerk2");
+ if (IS_ERR(chip->iobase_virt))
+ return PTR_ERR(chip->iobase_virt);
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) {
- dev_err(card->dev, "unable to remap memory region");
- pci_release_regions(pci);
- pci_disable_device(pci);
- kfree(chip);
- return -ENOMEM;
- }
/* (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, KBUILD_MODNAME, 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);
-
- iounmap(chip->iobase_virt);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
return -EBUSY;
}
chip->irq = pci->irq;
-
- 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;
- }
-
- *rchip = chip;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_aw2_free;
dev_info(card->dev,
"Audiowerk 2 sound card (saa7146 chipset) detected and managed\n");
@@ -345,25 +265,24 @@ static int snd_aw2_probe(struct pci_dev *pci,
}
/* (2) Create card instance */
- err = snd_card_new(&pci->dev, 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);
/* init spinlock */
spin_lock_init(&chip->reg_lock);
/* (4) Define driver ID and name string */
- strcpy(card->driver, "aw2");
- strcpy(card->shortname, "Audiowerk2");
+ strscpy(card->driver, "aw2");
+ strscpy(card->shortname, "Audiowerk2");
sprintf(card->longname, "%s with SAA7146 irq %i",
card->shortname, chip->irq);
@@ -373,22 +292,18 @@ static int 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 snd_aw2_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
+ error:
+ snd_card_free(card);
+ return err;
}
/* open callback */
@@ -424,20 +339,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)
{
@@ -446,7 +347,7 @@ static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
- mutex_lock(&chip->mtx);
+ guard(mutex)(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
@@ -462,8 +363,6 @@ static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
snd_pcm_period_elapsed,
(void *)substream);
- mutex_unlock(&chip->mtx);
-
return 0;
}
@@ -475,7 +374,7 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
- mutex_lock(&chip->mtx);
+ guard(mutex)(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
@@ -491,8 +390,6 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
snd_pcm_period_elapsed,
(void *)substream);
- mutex_unlock(&chip->mtx);
-
return 0;
}
@@ -500,10 +397,10 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
int cmd)
{
- int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
- spin_lock(&chip->reg_lock);
+
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146,
@@ -516,20 +413,19 @@ static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
stream_number);
break;
default:
- status = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return status;
+ return 0;
}
/* capture trigger callback */
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
int cmd)
{
- int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
- spin_lock(&chip->reg_lock);
+
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146,
@@ -542,10 +438,9 @@ static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
stream_number);
break;
default:
- status = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return status;
+ return 0;
}
/* playback pointer callback */
@@ -608,7 +503,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA];
/* Set PCM device name */
- strcpy(pcm_playback_ana->name, "Analog playback");
+ strscpy(pcm_playback_ana->name, "Analog playback");
/* Associate private data to PCM device */
pcm_playback_ana->private_data = pcm_device;
/* set operators of PCM device */
@@ -624,15 +519,10 @@ static int 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)
- dev_err(chip->card->dev,
- "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);
@@ -645,7 +535,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG];
/* Set PCM device name */
- strcpy(pcm_playback_num->name, "Digital playback");
+ strscpy(pcm_playback_num->name, "Digital playback");
/* Associate private data to PCM device */
pcm_playback_num->private_data = pcm_device;
/* set operators of PCM device */
@@ -661,15 +551,10 @@ static int 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)
- dev_err(chip->card->dev,
- "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);
@@ -683,7 +568,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA];
/* Set PCM device name */
- strcpy(pcm_capture->name, "Capture");
+ strscpy(pcm_capture->name, "Capture");
/* Associate private data to PCM device */
pcm_capture->private_data = pcm_device;
/* set operators of PCM device */
@@ -699,16 +584,10 @@ static int 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)
- dev_err(chip->card->dev,
- "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));
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c
index 1d7890459334..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
@@ -343,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..3a3de56b9b07 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
@@ -32,11 +19,12 @@
#define NUM_STREAM_CAPTURE_ANA 0
-typedef void (*snd_aw2_saa7146_it_cb) (void *);
+struct snd_pcm_substream;
+typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *);
struct snd_aw2_saa7146_cb_param {
snd_aw2_saa7146_it_cb p_it_callback;
- void *p_callback_param;
+ struct snd_pcm_substream *p_callback_param;
};
/* definition of the chip-specific record */
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 fc18c29a8173..6cdf76e2b7d2 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,3 +1,4 @@
+// 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>
*
@@ -16,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,
@@ -210,7 +196,6 @@
MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_GAMEPORT 1
@@ -310,7 +295,6 @@ struct snd_azf3328 {
* CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_ctrl_6AH;
-#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];
@@ -318,7 +302,6 @@ struct snd_azf3328 {
u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
-#endif
};
static const struct pci_device_id snd_azf3328_ids[] = {
@@ -379,15 +362,6 @@ 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
-)
-{
- outl(value, codec->io_base + reg);
-}
-
-static inline void
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
unsigned reg, const void *buffer, int count
)
@@ -438,25 +412,25 @@ snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
outl(value, chip->ctrl_io + reg);
}
-static inline void
+static inline void __maybe_unused
snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
{
outb(value, chip->game_io + reg);
}
-static inline void
+static inline void __maybe_unused
snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
outw(value, chip->game_io + reg);
}
-static inline u8
+static inline u8 __maybe_unused
snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
{
return inb(chip->game_io + reg);
}
-static inline u16
+static inline u16 __maybe_unused
snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
{
return inw(chip->game_io + reg);
@@ -771,7 +745,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
{
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 = snd_azf3328_mixer_ac97_write,
.read = snd_azf3328_mixer_ac97_read,
};
@@ -1108,7 +1082,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
return (nreg != oreg);
}
-static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
+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),
@@ -1166,7 +1140,7 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
#endif
};
-static u16 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 },
@@ -1209,30 +1183,17 @@ 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");
+ strscpy(card->mixername, "AZF3328 mixer");
return 0;
}
#endif /* AZF_USE_AC97_LAYER */
-static int
-snd_azf3328_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_azf3328_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
static void
snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
enum azf_freq_t bitrate,
@@ -1240,7 +1201,6 @@ snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
unsigned int channels
)
{
- unsigned long flags;
u16 val = 0xff00;
u8 freq = 0;
@@ -1259,8 +1219,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
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 */
+ pr_warn("azf3328: unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
+ 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;
@@ -1283,7 +1243,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
if (format_width == 16)
val |= SOUNDFORMAT_FLAG_16BIT;
- spin_lock_irqsave(codec->lock, flags);
+ guard(spinlock_irqsave)(codec->lock);
/* set bitrate/format */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
@@ -1305,8 +1265,6 @@ snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE
);
-
- spin_unlock_irqrestore(codec->lock, flags);
}
static inline void
@@ -1412,15 +1370,13 @@ snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */
- unsigned long flags;
-
/* width 32bit (prevent overflow): */
u32 area_length;
struct codec_setup_io {
u32 dma_start_1;
u32 dma_start_2;
u32 dma_lengths;
- } __attribute__((packed)) setup_io;
+ } __packed setup_io;
area_length = buffer_bytes/2;
@@ -1444,11 +1400,10 @@ snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
/* build combined I/O buffer length word */
setup_io.dma_lengths = (area_length << 16) | (area_length);
- spin_lock_irqsave(codec->lock, flags);
+ guard(spinlock_irqsave)(codec->lock);
snd_azf3328_codec_outl_multi(
codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
);
- spin_unlock_irqrestore(codec->lock, flags);
}
}
@@ -1503,48 +1458,48 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_format_width(runtime->format),
runtime->channels);
- spin_lock(codec->lock);
- /* first, remember current value: */
- flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
+ scoped_guard(spinlock, codec->lock) {
+ /* first, remember current value: */
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop transfer */
- flags1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- /* FIXME: clear interrupts or what??? */
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
- spin_unlock(codec->lock);
+ /* FIXME: clear interrupts or what??? */
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
+ }
snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream)
);
- spin_lock(codec->lock);
+ scoped_guard(spinlock, codec->lock) {
#ifdef WIN9X
- /* FIXME: enable playback/recording??? */
- flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
-
- /* start transfer again */
- /* FIXME: what is this value (0x0010)??? */
- flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* FIXME: enable playback/recording??? */
+ flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+
+ /* start transfer again */
+ /* FIXME: what is this value (0x0010)??? */
+ flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
#else /* NT4 */
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- 0x0000);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RUN_SOMETHING1);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RUN_SOMETHING1 |
- DMA_RUN_SOMETHING2);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RESUME |
- SOMETHING_ALMOST_ALWAYS_SET |
- DMA_EPILOGUE_SOMETHING |
- DMA_SOMETHING_ELSE);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ 0x0000);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1 |
+ DMA_RUN_SOMETHING2);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RESUME |
+ SOMETHING_ALMOST_ALWAYS_SET |
+ DMA_EPILOGUE_SOMETHING |
+ DMA_SOMETHING_ELSE);
#endif
- spin_unlock(codec->lock);
+ }
snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
if (is_main_mixer_playback_codec) {
@@ -1560,14 +1515,14 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
/* resume codec if we were active */
- 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(codec->lock);
+ scoped_guard(spinlock, 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
+ );
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
@@ -1580,22 +1535,22 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
);
}
- spin_lock(codec->lock);
- /* first, remember current value: */
- flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
+ scoped_guard(spinlock, codec->lock) {
+ /* first, remember current value: */
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop transfer */
- flags1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- /* hmm, is this really required? we're resetting the same bit
- * immediately thereafter... */
- flags1 |= DMA_RUN_SOMETHING1;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* hmm, is this really required? we're resetting the same bit
+ * immediately thereafter... */
+ flags1 |= DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- flags1 &= ~DMA_RUN_SOMETHING1;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- spin_unlock(codec->lock);
+ flags1 &= ~DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ }
snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
if (is_main_mixer_playback_codec) {
@@ -1763,12 +1718,11 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
int i;
u8 val;
- unsigned long flags;
if (snd_BUG_ON(!chip))
return 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
*buttons = (~(val) >> 4) & 0xf;
@@ -1805,7 +1759,6 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
axes[i] = chip->axes[i];
@@ -1902,11 +1855,11 @@ snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
if (!(status & (1 << codec_type)))
continue;
- 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(codec->lock);
+ scoped_guard(spinlock, 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);
+ }
if (codec->substream) {
snd_pcm_period_elapsed(codec->substream);
@@ -1951,9 +1904,9 @@ snd_azf3328_interrupt(int irq, void *dev_id)
if (chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */
- spin_lock(&chip->reg_lock);
- snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
+ }
dev_dbg(chip->card->dev, "timer IRQ\n");
}
@@ -2093,9 +2046,6 @@ snd_azf3328_pcm_close(struct snd_pcm_substream *substream
static const struct snd_pcm_ops snd_azf3328_playback_ops = {
.open = snd_azf3328_pcm_playback_open,
.close = snd_azf3328_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
@@ -2104,9 +2054,6 @@ static const struct snd_pcm_ops snd_azf3328_playback_ops = {
static const struct snd_pcm_ops snd_azf3328_capture_ops = {
.open = snd_azf3328_pcm_capture_open,
.close = snd_azf3328_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
@@ -2115,9 +2062,6 @@ static const struct snd_pcm_ops snd_azf3328_capture_ops = {
static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
.open = snd_azf3328_pcm_i2s_out_open,
.close = snd_azf3328_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
@@ -2143,14 +2087,13 @@ snd_azf3328_pcm(struct snd_azf3328 *chip)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
/* same pcm object for playback/capture (see snd_pcm_new() above) */
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);
@@ -2161,12 +2104,11 @@ snd_azf3328_pcm(struct snd_azf3328 *chip)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(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);
return 0;
}
@@ -2187,7 +2129,6 @@ static int
snd_azf3328_timer_start(struct snd_timer *timer)
{
struct snd_azf3328 *chip;
- unsigned long flags;
unsigned int delay;
chip = snd_timer_chip(timer);
@@ -2202,9 +2143,8 @@ snd_azf3328_timer_start(struct snd_timer *timer)
}
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);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -2212,10 +2152,9 @@ static int
snd_azf3328_timer_stop(struct snd_timer *timer)
{
struct snd_azf3328 *chip;
- unsigned long flags;
chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* disable timer countdown and interrupt */
/* Hmm, should we write TIMER_IRQ_ACK here?
YES indeed, otherwise a rogue timer operation - which prompts
@@ -2224,7 +2163,6 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
Simply manually poking 0x04 _once_ immediately successfully stops
the hardware/ALSA interrupt activity. */
snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -2267,7 +2205,7 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
if (err < 0)
goto out;
- strcpy(timer->name, "AZF3328 timer");
+ strscpy(timer->name, "AZF3328 timer");
timer->private_data = chip;
timer->hw = snd_azf3328_timer_hw;
@@ -2283,32 +2221,15 @@ out:
/******************************************************************/
-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;
snd_azf3328_mixer_reset(chip);
snd_azf3328_timer_stop(chip->timer);
snd_azf3328_gameport_free(chip);
-
-__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
@@ -2389,47 +2310,34 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
+ 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");
+ err = pcim_request_all_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);
@@ -2455,26 +2363,22 @@ snd_azf3328_create(struct snd_card *card,
codec_setup->type = AZF_CODEC_I2S_OUT;
codec_setup->name = "I2S_OUT";
- if (request_irq(pci->irq, snd_azf3328_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ 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);
- err = -EBUSY;
- goto out_err;
+ 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 */
@@ -2487,31 +2391,19 @@ snd_azf3328_create(struct snd_card *card,
/* 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(codec->lock);
+ guard(spinlock_irq)(codec->lock);
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init);
- spin_unlock_irq(codec->lock);
}
- *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
-snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+__snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2519,29 +2411,25 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_opl3 *opl3;
int err;
- if (dev >= SNDRV_CARDS) {
- err = -ENODEV;
- goto out;
- }
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
if (!enable[dev]) {
dev++;
- err = -ENOENT;
- goto out;
+ return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)
- goto out;
+ return err;
+ chip = card->private_data;
- strcpy(card->driver, "AZF3328");
- strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
+ strscpy(card->driver, "AZF3328");
+ strscpy(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. */
@@ -2555,16 +2443,16 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
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) {
@@ -2575,10 +2463,10 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
/* 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;
}
@@ -2587,7 +2475,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
err = snd_card_register(card);
if (err < 0)
- goto out_err;
+ return err;
#ifdef MODULE
dev_info(card->dev,
@@ -2605,25 +2493,15 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
pci_set_drvdata(pci, card);
dev++;
-
- err = 0;
- goto out;
-
-out_err:
- dev_err(card->dev, "something failed, exiting\n");
- snd_card_free(card);
-
-out:
- return err;
+ return 0;
}
-static void
-snd_azf3328_remove(struct pci_dev *pci)
+static int
+snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
+ return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id));
}
-#ifdef CONFIG_PM_SLEEP
static inline void
snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
unsigned long io_addr, unsigned count, u32 *saved_regs)
@@ -2699,10 +2577,6 @@ snd_azf3328_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- /* same pcm object for playback/capture */
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
-
snd_azf3328_suspend_ac97(chip);
snd_azf3328_suspend_regs(chip, chip->ctrl_io,
@@ -2743,19 +2617,14 @@ snd_azf3328_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
static struct pci_driver azf3328_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
- .remove = snd_azf3328_remove,
.driver = {
- .pm = SND_AZF3328_PM_OPS,
+ .pm = &snd_azf3328_pm,
},
};
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index d8ade8771a32..383def1f2af7 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -1,24 +1,10 @@
+// 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>
@@ -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 bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int digital_rate[SNDRV_CARDS]; /* digital input rate */
-static bool 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 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,13 +269,8 @@ 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)
dev_err(chip->card->dev,
"Aieee - PCI error! status %#08x, PCI status %#04x\n",
@@ -346,7 +325,8 @@ 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);
}
@@ -451,10 +431,10 @@ static int snd_bt87x_close(struct snd_pcm_substream *substream)
{
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
- chip->reg_control |= CTL_A_PWRDN;
- snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->reg_control |= CTL_A_PWRDN;
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ }
chip->substream = NULL;
clear_bit(0, &chip->opened);
@@ -466,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));
@@ -482,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;
}
@@ -492,20 +466,19 @@ static int snd_bt87x_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int decimation;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR);
decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate;
chip->reg_control |= decimation << CTL_DA_SDR_SHIFT;
if (runtime->format == SNDRV_PCM_FORMAT_S8)
chip->reg_control |= CTL_DA_SBR;
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int snd_bt87x_start(struct snd_bt87x *chip)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->current_line = 0;
chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN;
snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr);
@@ -513,18 +486,16 @@ static int snd_bt87x_start(struct snd_bt87x *chip)
chip->line_bytes | (chip->lines << 16));
snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock(&chip->reg_lock);
return 0;
}
static int snd_bt87x_stop(struct snd_bt87x *chip)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
snd_bt87x_writel(chip, REG_INT_MASK, 0);
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
- spin_unlock(&chip->reg_lock);
return 0;
}
@@ -553,13 +524,11 @@ static snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream)
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,
@@ -588,13 +557,12 @@ static int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK)
| (value->value.integer.value[0] << CTL_A_GAIN_SHIFT);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = old_control != chip->reg_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
@@ -624,13 +592,12 @@ static int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_G2X)
| (value->value.integer.value[0] ? CTL_A_G2X : 0);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = chip->reg_control != old_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
@@ -666,13 +633,12 @@ static int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK)
| (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = chip->reg_control != old_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
@@ -684,23 +650,11 @@ static const struct snd_kcontrol_new snd_bt87x_capture_source = {
.put = snd_bt87x_capture_source_put,
};
-static int snd_bt87x_free(struct snd_bt87x *chip)
+static void snd_bt87x_free(struct snd_card *card)
{
- if (chip->mmio)
- snd_bt87x_stop(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- iounmap(chip->mmio);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
+ struct snd_bt87x *chip = card->private_data;
-static int snd_bt87x_dev_free(struct snd_device *device)
-{
- struct snd_bt87x *chip = device->device_data;
- return snd_bt87x_free(chip);
+ snd_bt87x_stop(chip);
}
static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
@@ -712,52 +666,33 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
if (err < 0)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, name);
+ strscpy(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 snd_bt87x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_bt87x **rchip)
+ 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);
- return err;
- }
- chip->mmio = pci_ioremap_bar(pci, 0);
- if (!chip->mmio) {
- dev_err(card->dev, "cannot remap io memory\n");
- err = -ENOMEM;
- goto fail;
- }
+ chip->mmio = pcim_iomap_region(pci, 0, "Bt87x audio");
+ if (IS_ERR(chip->mmio))
+ return PTR_ERR(chip->mmio);
chip->reg_control = CTL_A_PWRDN | CTL_DA_ES2 |
CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT);
@@ -766,26 +701,18 @@ static int 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,
- KBUILD_MODNAME, chip);
+ err = devm_request_irq(&pci->dev, pci->irq, snd_bt87x_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
if (err < 0) {
dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
- goto fail;
+ 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;
- *rchip = chip;
return 0;
-
-fail:
- snd_bt87x_free(chip);
- return err;
}
#define BT_DEVICE(chip, subvend, subdev, id) \
@@ -828,7 +755,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[] = {
+} denylist[] = {
{0x0071, 0x0101}, /* Nebula Electronics DigiTV */
{0x11bd, 0x001c}, /* Pinnacle PCTV Sat */
{0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */
@@ -844,7 +771,7 @@ static struct {
static struct pci_driver driver;
-/* return the id of the card, or a negative value if it's blacklisted */
+/* 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;
@@ -854,9 +781,9 @@ static int 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) {
+ 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);
@@ -871,8 +798,8 @@ static int snd_bt87x_detect_card(struct pci_dev *pci)
return SND_BT87X_BOARD_UNKNOWN;
}
-static int 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;
@@ -895,14 +822,15 @@ static int snd_bt87x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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));
@@ -914,53 +842,50 @@ static int 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;
}
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);
- strcpy(card->driver, "Bt87x");
+ strscpy(card->driver, "Bt87x");
sprintf(card->shortname, "Brooktree Bt%x", pci->device);
sprintf(card->longname, "%s at %#llx, irq %i",
card->shortname, (unsigned long long)pci_resource_start(pci, 0),
chip->irq);
- strcpy(card->mixername, "Bt87x");
+ strscpy(card->mixername, "Bt87x");
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_bt87x_probe(pci, pci_id));
}
/* default entries for all Bt87x cards - it's not exported */
@@ -975,7 +900,6 @@ static struct pci_driver driver = {
.name = KBUILD_MODNAME,
.id_table = snd_bt87x_ids,
.probe = snd_bt87x_probe,
- .remove = snd_bt87x_remove,
};
static int __init alsa_card_bt87x_init(void)
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
index c1455fc5588c..693dc4d80925 100644
--- a/sound/pci/ca0106/Makefile
+++ b/sound/pci/ca0106/Makefile
@@ -1,4 +1,5 @@
-snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-ca0106-y := 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 04402c14cb23..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 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,9 +152,9 @@
* 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 */
@@ -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,7 +687,7 @@ 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;
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index cd27b5536654..41774e2ef53f 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
@@ -119,21 +120,6 @@
*
* 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>
@@ -151,7 +137,6 @@
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;
@@ -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:
@@ -347,16 +332,13 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + CA0106_PTR);
+ return inl(emu->port + CA0106_DATA);
}
void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
@@ -365,14 +347,12 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + CA0106_PTR);
+ outl(data, emu->port + CA0106_DATA);
}
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
@@ -466,24 +446,20 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) | intrenb;
- outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
}
static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) & ~intrenb;
- outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
}
@@ -517,7 +493,7 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
}
static int snd_ca0106_channel_dac(struct snd_ca0106 *chip,
- struct snd_ca0106_details *details,
+ const struct snd_ca0106_details *details,
int channel_id)
{
switch (channel_id) {
@@ -551,7 +527,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;
}
@@ -589,9 +566,11 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
*/
//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);
@@ -682,10 +661,12 @@ static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substre
*/
//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 +702,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 +709,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;
@@ -784,7 +737,7 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
runtime->dma_addr, runtime->dma_area, table_base);
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);
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* Rate can be set per channel. */
/* reg40 control host to fifo */
@@ -824,9 +777,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);
@@ -834,13 +787,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);
@@ -891,7 +844,7 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
runtime->dma_addr, runtime->dma_area, table_base);
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);
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* reg71 controls ADC rate. */
switch (runtime->rate) {
@@ -926,9 +879,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);
@@ -1110,9 +1063,6 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
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,
@@ -1121,9 +1071,6 @@ static const struct snd_pcm_ops snd_ca0106_playback_front_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,
@@ -1132,9 +1079,6 @@ static const struct snd_pcm_ops snd_ca0106_capture_0_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,
@@ -1143,9 +1087,6 @@ static const struct snd_pcm_ops snd_ca0106_capture_1_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,
@@ -1154,9 +1095,6 @@ static const struct snd_pcm_ops snd_ca0106_capture_2_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,
@@ -1165,9 +1103,6 @@ static const struct snd_pcm_ops snd_ca0106_capture_3_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,
@@ -1176,9 +1111,6 @@ static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_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,
@@ -1187,9 +1119,6 @@ static const struct snd_pcm_ops snd_ca0106_playback_unknown_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,26 +1129,20 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct snd_ca0106 *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ return inw(emu->port + CA0106_AC97DATA);
}
static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct snd_ca0106 *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- outw(val, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ outw(val, emu->port + CA0106_AC97DATA);
}
static int snd_ca0106_ac97(struct snd_ca0106 *chip)
@@ -1227,12 +1150,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 */
@@ -1244,32 +1168,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)
@@ -1282,7 +1185,7 @@ 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;
@@ -1337,7 +1240,7 @@ 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;
}
@@ -1397,26 +1300,22 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
}
pcm->info_flags = 0;
- strcpy(pcm->name, "CA0106");
+ strscpy(pcm->name, "CA0106");
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,
@@ -1430,7 +1329,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
}
#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),
@@ -1448,7 +1347,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 */
@@ -1469,7 +1368,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 :
@@ -1506,8 +1405,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);
@@ -1581,30 +1480,30 @@ 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. */
@@ -1646,12 +1545,12 @@ 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
@@ -1659,62 +1558,43 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip)
}
static int snd_ca0106_create(int dev, struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ca0106 **rchip)
+ 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
dev_err(card->dev, "error to set 32bit mask DMA\n");
- pci_disable_device(pci);
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 = pcim_request_all_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);
- dev_err(card->dev, "cannot allocate the port\n");
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_ca0106_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, chip)) {
- snd_ca0106_free(chip);
+ 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 */
@@ -1722,8 +1602,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card,
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
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");
+ strscpy(card->driver, "CA0106");
+ strscpy(card->shortname, "CA0106");
for (c = ca0106_chip_details; c->serial; c++) {
if (subsystem[dev]) {
@@ -1743,13 +1623,6 @@ static int 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;
}
@@ -1829,15 +1702,16 @@ static int snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
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 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;
@@ -1851,36 +1725,37 @@ static int snd_ca0106_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
dev_dbg(card->dev, "probe for MIDI channel A ...");
err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
if (err < 0)
- goto error;
+ return err;
dev_dbg(card->dev, " done.\n");
#ifdef CONFIG_SND_PROC_FS
@@ -1889,20 +1764,17 @@ static int snd_ca0106_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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_ca0106_probe(pci, pci_id));
}
#ifdef CONFIG_PM_SLEEP
@@ -1910,11 +1782,8 @@ static int snd_ca0106_suspend(struct device *dev)
{
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);
@@ -1961,7 +1830,6 @@ static struct pci_driver ca0106_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
- .remove = snd_ca0106_remove,
.driver = {
.pm = SND_CA0106_PM_OPS,
},
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index b4d3415331f6..f7b6b2db889b 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
@@ -44,21 +45,6 @@
*
* 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>
@@ -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); */
}
}
@@ -564,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[] = {
+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",
@@ -645,7 +631,7 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] = {
.private_value = chid \
}
-static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = {
+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),
@@ -668,7 +654,7 @@ static const int spi_dmute_bit[] = {
};
static struct snd_kcontrol_new
-snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
+snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details,
int channel_id)
{
struct snd_kcontrol_new spi_switch = {0};
@@ -715,26 +701,16 @@ 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);
+ strscpy(id.name, name);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_remove_id(card, &id);
}
-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);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
@@ -753,7 +729,7 @@ static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
static
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
-static char *slave_vols[] = {
+static const char * const follower_vols[] = {
"Analog Front Playback Volume",
"Analog Rear Playback Volume",
"Analog Center/LFE Playback Volume",
@@ -766,7 +742,7 @@ static char *slave_vols[] = {
NULL
};
-static char *slave_sws[] = {
+static const char * const follower_sws[] = {
"Analog Front Playback Switch",
"Analog Rear Playback Switch",
"Analog Center/LFE Playback Switch",
@@ -775,23 +751,13 @@ static char *slave_sws[] = {
NULL
};
-static void add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
- }
-}
-
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",
@@ -815,7 +781,7 @@ int 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",
@@ -866,7 +832,9 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_vols);
+ err = snd_ctl_add_followers(card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
if (emu->details->spi_dac) {
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
@@ -876,10 +844,12 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_sws);
+ err = snd_ctl_add_followers(card, vmaster, follower_sws);
+ if (err < 0)
+ return err;
}
- strcpy(card->mixername, "CA0106");
+ strscpy(card->mixername, "CA0106");
return 0;
}
@@ -889,7 +859,7 @@ struct ca0106_vol_tbl {
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 },
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index 9b2b8b38122f..c181e4954579 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
@@ -44,21 +45,6 @@
*
* 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>
@@ -80,7 +66,7 @@ struct snd_ca0106_category_str {
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" },
@@ -295,16 +281,14 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ca0106 *emu = entry->private_data;
- unsigned long flags;
char line[64];
u32 reg, val;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg < 0x40 && val <= 0xffffffff) {
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(val, emu->port + (reg & 0xfffffffc));
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
}
}
@@ -314,13 +298,13 @@ static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned long value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inl(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
}
}
@@ -330,13 +314,13 @@ static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned int value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=2) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inw(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inw(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
}
}
@@ -346,13 +330,13 @@ static void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned int value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=1) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inb(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inb(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
}
}
@@ -424,30 +408,20 @@ static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry,
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;
}
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index 4d4d385205eb..6efd93abddb5 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>
@@ -60,58 +45,54 @@ static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- ca_midi_clear_rx(midi);
- } else {
- byte = ca_midi_read_data(midi);
- if(midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
-
-
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
+ if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
+ ca_midi_clear_rx(midi);
+ } else {
+ byte = ca_midi_read_data(midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- ca_midi_write_data(midi, byte);
- } else {
- midi->interrupt_disable(midi,midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ ca_midi_write_data(midi, byte);
+ } else {
+ midi->interrupt_disable(midi, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
-
}
static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- ca_midi_write_data(midi, 0x00);
- /* ca_midi_clear_rx(midi); */
-
- ca_midi_write_cmd(midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (ca_midi_input_avail(midi)) {
- if (ca_midi_read_data(midi) == midi->ack)
- ok = 1;
+ scoped_guard(spinlock_irqsave, &midi->input_lock) {
+ ca_midi_write_data(midi, 0x00);
+ /* ca_midi_clear_rx(midi); */
+
+ ca_midi_write_cmd(midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (ca_midi_input_avail(midi)) {
+ if (ca_midi_read_data(midi) == midi->ack)
+ ok = 1;
+ }
}
- }
- if (!ok && ca_midi_read_data(midi) == midi->ack)
+ if (!ok && ca_midi_read_data(midi) == midi->ack)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok)
pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
cmd,
@@ -123,83 +104,69 @@ static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= CA_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 1);
- ca_midi_cmd(midi, midi->enter_uart, 1);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= CA_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 1);
+ ca_midi_cmd(midi, midi->enter_uart, 1);
return 0;
}
static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 1);
- ca_midi_cmd(midi, midi->enter_uart, 1);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & CA_MIDI_MODE_INPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 1);
+ ca_midi_cmd(midi, midi->enter_uart, 1);
return 0;
}
static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->interrupt_disable(midi,midi->rx_enable);
- midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->interrupt_disable(midi, midi->rx_enable);
+ midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 0);
return 0;
}
static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
-
- midi->interrupt_disable(midi,midi->tx_enable);
- midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
-
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->interrupt_disable(midi, midi->tx_enable);
+ midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & CA_MIDI_MODE_INPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 0);
return 0;
}
@@ -220,7 +187,6 @@ static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int u
static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return;
@@ -229,25 +195,23 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int
int max = 4;
unsigned char byte;
- spin_lock_irqsave(&midi->output_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->output_lock) {
- /* try to send some amount of bytes here before interrupts */
- while (max > 0) {
- if (ca_midi_output_ready(midi)) {
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ /* try to send some amount of bytes here before interrupts */
+ while (max > 0) {
+ if (ca_midi_output_ready(midi)) {
+ if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ ca_midi_write_data(midi, byte);
+ max--;
+ } else {
+ break;
}
- ca_midi_write_data(midi, byte);
- max--;
- } else {
- break;
}
}
-
- spin_unlock_irqrestore(&midi->output_lock, flags);
midi->interrupt_enable(midi,midi->tx_enable);
} else {
@@ -291,7 +255,8 @@ 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;
@@ -301,7 +266,7 @@ int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
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 26a657870664..0666be543474 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -1,20 +1,7 @@
+// 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 */
@@ -43,10 +30,6 @@
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 IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -55,7 +38,7 @@ MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738},"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static long mpu_port[SNDRV_CARDS];
+static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
static bool soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
#ifdef SUPPORT_JOYSTICK
@@ -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,10 +486,8 @@ struct cmipci {
spinlock_t reg_lock;
-#ifdef CONFIG_PM_SLEEP
unsigned int saved_regs[0x20];
unsigned char saved_mixers[0x20];
-#endif
};
@@ -599,7 +579,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)
{
@@ -679,27 +659,18 @@ 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)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
if (params_channels(hw_params) > 2) {
- mutex_lock(&cm->open_mutex);
- if (cm->opened[CM_CH_PLAY]) {
- mutex_unlock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
+ if (cm->opened[CM_CH_PLAY])
return -EBUSY;
- }
/* reserve the channel A */
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,11 +681,6 @@ 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);
-}
-
/*
*/
@@ -746,7 +712,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
}
if (cm->can_multi_ch) {
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (channels > 2) {
snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
@@ -769,7 +735,6 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
else
snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
- spin_unlock_irq(&cm->reg_lock);
}
return 0;
}
@@ -810,7 +775,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
period_size = (period_size * runtime->channels) / 2;
}
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
/* set buffer address */
reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
@@ -876,7 +841,6 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
}
rec->running = 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -888,14 +852,13 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
int cmd)
{
unsigned int inthld, chen, reset, pause;
- int result = 0;
inthld = CM_CH0_INT_EN << rec->ch;
chen = CM_CHEN0 << rec->ch;
reset = CM_RST_CH0 << rec->ch;
pause = CM_PAUSE0 << rec->ch;
- spin_lock(&cm->reg_lock);
+ guard(spinlock)(&cm->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
rec->running = 1;
@@ -927,11 +890,9 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&cm->reg_lock);
- return result;
+ return 0;
}
/*
@@ -1021,10 +982,9 @@ static int snd_cmipci_spdif_default_get(struct snd_kcontrol *kcontrol,
struct cmipci *chip = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1036,12 +996,11 @@ static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val;
val = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
change = val != chip->dig_status;
chip->dig_status = val;
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1095,10 +1054,9 @@ static int snd_cmipci_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct cmipci *chip = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1110,12 +1068,11 @@ static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
unsigned int val;
val = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
change = val != chip->dig_pcm_status;
chip->dig_pcm_status = val;
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1139,7 +1096,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,11 +1210,13 @@ 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);
+ guard(spinlock_irq)(&cm->reg_lock);
cm->spdif_playback_avail = up;
if (up) {
/* they are controlled via "IEC958 Output Switch" */
@@ -1283,7 +1242,6 @@ static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *sub
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
setup_ac3(cm, subs, 0, 0);
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -1304,7 +1262,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 +1278,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);
}
@@ -1347,32 +1307,32 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
/* configure for 16 bits, 2 channels, 8 kHz */
if (runtime->channels > 2)
set_dac_channels(cm, rec, 2);
- spin_lock_irq(&cm->reg_lock);
- val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
- val &= ~(CM_ASFC_MASK << (rec->ch * 3));
- val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3);
- snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
- val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
- val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
- val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
- if (cm->can_96k)
- val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
- snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+ val &= ~(CM_ASFC_MASK << (rec->ch * 3));
+ val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
+ val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+ val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
+ val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
+ if (cm->can_96k)
+ val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
+ snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
- /* start stream (we don't need interrupts) */
- cm->ctrl |= CM_CHEN0 << rec->ch;
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- spin_unlock_irq(&cm->reg_lock);
+ /* start stream (we don't need interrupts) */
+ cm->ctrl |= CM_CHEN0 << rec->ch;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ }
msleep(1);
/* stop and reset stream */
- spin_lock_irq(&cm->reg_lock);
- cm->ctrl &= ~(CM_CHEN0 << rec->ch);
- val = CM_RST_CH0 << rec->ch;
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val);
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val);
- spin_unlock_irq(&cm->reg_lock);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ cm->ctrl &= ~(CM_CHEN0 << rec->ch);
+ val = CM_RST_CH0 << rec->ch;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val);
+ }
rec->needs_silencing = 0;
}
@@ -1384,14 +1344,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 */
@@ -1406,20 +1366,19 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
- spin_lock_irq(&cm->reg_lock);
- snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
- if (cm->can_96k) {
- if (substream->runtime->rate > 48000)
- snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ if (cm->can_96k) {
+ if (substream->runtime->rate > 48000)
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ }
+ if (snd_pcm_format_width(substream->runtime->format) > 16)
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
else
- snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
}
- if (snd_pcm_format_width(substream->runtime->format) > 16)
- snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
- else
- snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
-
- spin_unlock_irq(&cm->reg_lock);
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
}
@@ -1428,12 +1387,11 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
{
struct cmipci *cm = snd_pcm_substream_chip(subs);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
- spin_unlock_irq(&cm->reg_lock);
- return snd_cmipci_hw_free(subs);
+ return 0;
}
@@ -1451,14 +1409,14 @@ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id)
return IRQ_NONE;
/* acknowledge interrupt */
- spin_lock(&cm->reg_lock);
- if (status & CM_CHINT0)
- mask |= CM_CH0_INT_EN;
- if (status & CM_CHINT1)
- mask |= CM_CH1_INT_EN;
- snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
- snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
- spin_unlock(&cm->reg_lock);
+ scoped_guard(spinlock, &cm->reg_lock) {
+ if (status & CM_CHINT0)
+ mask |= CM_CH0_INT_EN;
+ if (status & CM_CHINT1)
+ mask |= CM_CH1_INT_EN;
+ snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
+ snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
+ }
if (cm->rmidi && (status & CM_UARTINT))
snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data);
@@ -1597,14 +1555,6 @@ static const struct snd_pcm_hardware snd_cmipci_capture_spdif =
.fifo_size = 0,
};
-static const unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
- 32000, 44100, 48000, 88200, 96000, 128000 };
-static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rate_constraints),
- .list = rate_constraints,
- .mask = 0,
-};
-
/*
* check device open/close
*/
@@ -1617,21 +1567,17 @@ static int open_device_check(struct cmipci *cm, int mode, struct snd_pcm_substre
* pcm framework doesn't pass file pointer before actually opened,
* we can't know whether blocking mode or not in open callback..
*/
- mutex_lock(&cm->open_mutex);
- if (cm->opened[ch]) {
- mutex_unlock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
+ if (cm->opened[ch])
return -EBUSY;
- }
cm->opened[ch] = mode;
cm->channel[ch].substream = subs;
if (! (mode & CM_OPEN_DAC)) {
/* disable dual DAC mode */
cm->channel[ch].is_dac = 0;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
- spin_unlock_irq(&cm->reg_lock);
}
- mutex_unlock(&cm->open_mutex);
return 0;
}
@@ -1639,7 +1585,7 @@ static void close_device_check(struct cmipci *cm, int mode)
{
int ch = mode & CM_OPEN_CH_MASK;
- mutex_lock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
if (cm->opened[ch] == mode) {
if (cm->channel[ch].substream) {
snd_cmipci_ch_reset(cm, ch);
@@ -1650,12 +1596,10 @@ static void close_device_check(struct cmipci *cm, int mode)
if (! cm->channel[ch].is_dac) {
/* enable dual DAC mode again */
cm->channel[ch].is_dac = 1;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
- spin_unlock_irq(&cm->reg_lock);
}
}
- mutex_unlock(&cm->open_mutex);
}
/*
@@ -1667,7 +1611,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) {
@@ -1675,11 +1620,9 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1693,18 +1636,17 @@ 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
runtime->hw.rate_min = 41000;
runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1717,10 +1659,12 @@ 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);
+ guard(mutex)(&cm->open_mutex);
if (! cm->opened[CM_CH_PLAY]) {
if (cm->can_multi_ch) {
runtime->hw.channels_max = cm->max_channels;
@@ -1732,17 +1676,14 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8);
}
}
- mutex_unlock(&cm->open_mutex);
if (cm->chip_version == 68) {
runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1755,7 +1696,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 +1725,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)) {
@@ -1841,8 +1786,6 @@ static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream)
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,
@@ -1852,9 +1795,6 @@ static const struct snd_pcm_ops snd_cmipci_playback_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,
@@ -1863,7 +1803,6 @@ static const struct snd_pcm_ops snd_cmipci_capture_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 */
@@ -1874,8 +1813,6 @@ static const struct snd_pcm_ops snd_cmipci_playback2_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,
@@ -1885,8 +1822,6 @@ static const struct snd_pcm_ops snd_cmipci_playback_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,
@@ -1911,11 +1846,11 @@ static int snd_cmipci_pcm_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI DAC/ADC");
+ strscpy(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;
}
@@ -1933,11 +1868,11 @@ static int snd_cmipci_pcm2_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI 2nd DAC");
+ strscpy(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;
}
@@ -1956,11 +1891,11 @@ static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI IEC958");
+ strscpy(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,
@@ -2053,7 +1988,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
int val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
@@ -2064,7 +1999,6 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2088,7 +2022,7 @@ static int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol,
right <<= reg.right_shift;
} else
right = 0;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oleft = snd_cmipci_mixer_read(cm, reg.left_reg);
left |= oleft & ~(reg.mask << reg.left_shift);
change = left != oleft;
@@ -2103,7 +2037,6 @@ static int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol,
snd_cmipci_mixer_write(cm, reg.right_reg, right);
} else
snd_cmipci_mixer_write(cm, reg.left_reg, left);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2135,10 +2068,9 @@ static int snd_cmipci_get_input_sw(struct snd_kcontrol *kcontrol,
int val1, val2;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
val1 = snd_cmipci_mixer_read(cm, reg.left_reg);
val2 = snd_cmipci_mixer_read(cm, reg.right_reg);
- spin_unlock_irq(&cm->reg_lock);
ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1;
ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1;
ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1;
@@ -2155,7 +2087,7 @@ static int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol,
int val1, val2, oval1, oval2;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oval1 = snd_cmipci_mixer_read(cm, reg.left_reg);
oval2 = snd_cmipci_mixer_read(cm, reg.right_reg);
val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
@@ -2167,7 +2099,6 @@ static int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol,
change = val1 != oval1 || val2 != oval2;
snd_cmipci_mixer_write(cm, reg.left_reg, val1);
snd_cmipci_mixer_write(cm, reg.right_reg, val2);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2225,7 +2156,7 @@ static int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol,
unsigned char oreg, val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oreg = inb(cm->iobase + reg.left_reg);
val = (oreg >> reg.left_shift) & reg.mask;
if (reg.invert)
@@ -2237,7 +2168,6 @@ static int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol,
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2249,7 +2179,7 @@ static int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol,
unsigned char oreg, nreg, val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oreg = inb(cm->iobase + reg.left_reg);
val = ucontrol->value.integer.value[0] & reg.mask;
if (reg.invert)
@@ -2264,7 +2194,6 @@ static int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol,
nreg |= (val << reg.right_shift);
}
outb(nreg, cm->iobase + reg.left_reg);
- spin_unlock_irq(&cm->reg_lock);
return (nreg != oreg);
}
@@ -2290,7 +2219,7 @@ static int snd_cmipci_put_native_mixer_sensitive(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_cmipci_mixers[] = {
+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),
@@ -2351,10 +2280,9 @@ static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
unsigned int val;
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (args->ac3_sensitive && cm->mixer_insensitive) {
ucontrol->value.integer.value[0] = 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
if (args->is_byte)
@@ -2362,7 +2290,6 @@ static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
else
val = snd_cmipci_read(cm, args->reg);
ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2384,10 +2311,9 @@ static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
int change;
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (args->ac3_sensitive && cm->mixer_insensitive) {
/* ignored */
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
if (args->is_byte)
@@ -2407,7 +2333,6 @@ static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
else
snd_cmipci_write(cm, args->reg, val);
}
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2540,9 +2465,8 @@ static int snd_cmipci_line_in_mode_get(struct snd_kcontrol *kcontrol,
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2552,7 +2476,7 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (ucontrol->value.enumerated.item[0] == 2)
change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CENTR2LIN | CM_BASE2LIN);
else
@@ -2561,7 +2485,6 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN);
else
change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2577,11 +2500,11 @@ static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+
/* same bit as spdi_phase */
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
ucontrol->value.enumerated.item[0] =
(snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2591,17 +2514,16 @@ static int snd_cmipci_mic_in_mode_put(struct snd_kcontrol *kcontrol,
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (ucontrol->value.enumerated.item[0])
change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
else
change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
/* both for CM8338/8738 */
-static struct snd_kcontrol_new snd_cmipci_mixer_switches[] = {
+static const struct snd_kcontrol_new snd_cmipci_mixer_switches[] = {
DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
{
.name = "Line-In Mode",
@@ -2613,11 +2535,11 @@ static struct snd_kcontrol_new snd_cmipci_mixer_switches[] = {
};
/* for non-multichannel chips */
-static struct snd_kcontrol_new snd_cmipci_nomulti_switch =
+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[] = {
+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),
@@ -2639,14 +2561,14 @@ static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] = {
};
/* only for model 033/037 */
-static struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] = {
+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[] = {
+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),
{
@@ -2659,14 +2581,14 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] = {
};
/* card control switches */
-static struct snd_kcontrol_new snd_cmipci_modem_switch =
+static const struct snd_kcontrol_new snd_cmipci_modem_switch =
DEFINE_CARD_SWITCH("Modem", modem);
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;
@@ -2676,11 +2598,11 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
card = cm->card;
- strcpy(card->mixername, "CMedia PCI");
+ strscpy(card->mixername, "CMedia PCI");
- spin_lock_irq(&cm->reg_lock);
- snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */
- spin_unlock_irq(&cm->reg_lock);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */
+ }
for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) {
if (cm->chip_version == 68) { // 8768 has no PCM volume
@@ -2688,7 +2610,8 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
"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;
}
@@ -2713,15 +2636,21 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
return err;
}
if (cm->can_ac3_hw) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
- return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm);
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm);
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm);
kctl->id.device = pcm_spdif_device;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
}
if (cm->chip_version <= 37) {
sw = snd_cmipci_old_mixer_switches;
@@ -2754,12 +2683,8 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
}
for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
- struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *ctl;
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, cm_saved_mixer[idx].name);
- ctl = snd_ctl_find_id(cm->card, &elem_id);
+ ctl = snd_ctl_find_id_mixer(cm->card, cm_saved_mixer[idx].name);
if (ctl)
cm->mixer_res_ctl[idx] = ctl;
}
@@ -2792,10 +2717,7 @@ static void snd_cmipci_proc_read(struct snd_info_entry *entry,
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);
}
static const struct pci_device_id snd_cmipci_ids[] = {
@@ -2863,7 +2785,7 @@ static void query_chip(struct cmipci *cm)
#ifdef SUPPORT_JOYSTICK
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;
@@ -2874,13 +2796,15 @@ static int 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) {
@@ -2891,14 +2815,12 @@ static int snd_cmipci_create_gameport(struct cmipci *cm, int dev)
cm->gameport = gp = gameport_allocate_port();
if (!gp) {
dev_err(cm->card->dev, "cannot allocate memory for gameport\n");
- release_and_free_resource(r);
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);
@@ -2910,13 +2832,10 @@ static int 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
@@ -2924,34 +2843,22 @@ 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 snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
@@ -2996,7 +2903,8 @@ static int snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
goto disable_fm;
}
}
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0) {
dev_err(cm->card->dev, "cannot create OPL3 hwdep\n");
return err;
}
@@ -3009,13 +2917,10 @@ static int snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
}
static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
- int dev, struct cmipci **rcmipci)
+ 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;
@@ -3026,17 +2931,10 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
{ },
};
- *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;
@@ -3047,20 +2945,19 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
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 = pcim_request_all_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, KBUILD_MODNAME, 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);
- snd_cmipci_free(cm);
return -EBUSY;
}
cm->irq = pci->irq;
+ card->sync_irq = cm->irq;
+ card->private_free = snd_cmipci_free;
pci_set_master(cm->pci);
@@ -3076,11 +2973,12 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B)
query_chip(cm);
/* added -MCx suffix for chip supporting multi-channels */
- if (cm->can_multi_ch)
- sprintf(cm->card->driver + strlen(cm->card->driver),
- "-MC%d", cm->max_channels);
- else if (cm->can_ac3_sw)
- strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC");
+ if (cm->can_multi_ch) {
+ int l = strlen(cm->card->driver);
+ scnprintf(cm->card->driver + l, sizeof(cm->card->driver) - l,
+ "-MC%d", cm->max_channels);
+ } else if (cm->can_ac3_sw)
+ strlcat(cm->card->driver, "-SWIEC", sizeof(cm->card->driver));
cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
@@ -3152,23 +3050,21 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
}
}
}
- sprintf(card->shortname, "C-Media CMI%d", val);
+ sprintf(card->shortname, "C-Media CMI%u", val);
if (cm->chip_version < 68)
- sprintf(modelstr, " (model %d)", cm->chip_version);
+ scnprintf(modelstr, sizeof(modelstr),
+ " (model %d)", cm->chip_version);
else
modelstr[0] = '\0';
- 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;
- }
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s%s at %#lx, irq %i",
+ card->shortname, modelstr, cm->iobase, cm->irq);
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;
}
}
@@ -3211,32 +3107,36 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
/* 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) |
- MPU401_INFO_IRQ_HOOK,
- -1, &cm->rmidi)) < 0) {
+ 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
@@ -3252,7 +3152,6 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
if (snd_cmipci_create_gameport(cm, dev) < 0)
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
- *rcmipci = cm;
return 0;
}
@@ -3266,7 +3165,6 @@ static int snd_cmipci_probe(struct pci_dev *pci,
{
static int dev;
struct snd_card *card;
- struct cmipci *cm;
int err;
if (dev >= SNDRV_CARDS)
@@ -3276,63 +3174,54 @@ static int snd_cmipci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
switch (pci->device) {
case PCI_DEVICE_ID_CMEDIA_CM8738:
case PCI_DEVICE_ID_CMEDIA_CM8738B:
- strcpy(card->driver, "CMI8738");
+ strscpy(card->driver, "CMI8738");
break;
case PCI_DEVICE_ID_CMEDIA_CM8338A:
case PCI_DEVICE_ID_CMEDIA_CM8338B:
- strcpy(card->driver, "CMI8338");
+ strscpy(card->driver, "CMI8338");
break;
default:
- strcpy(card->driver, "CMIPCI");
+ strscpy(card->driver, "CMIPCI");
break;
}
- err = snd_cmipci_create(card, pci, dev, &cm);
+ err = snd_cmipci_create(card, pci, dev);
if (err < 0)
- goto free_card;
-
- card->private_data = cm;
+ goto error;
err = snd_card_register(card);
if (err < 0)
- goto free_card;
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-free_card:
+ error:
snd_card_free(card);
return err;
}
-static void snd_cmipci_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
-
-#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,
@@ -3351,10 +3240,6 @@ static int snd_cmipci_suspend(struct device *dev)
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]);
@@ -3388,19 +3273,14 @@ static int snd_cmipci_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume);
static struct pci_driver cmipci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
- .remove = snd_cmipci_remove,
.driver = {
- .pm = SND_CMIPCI_PM_OPS,
+ .pm = &snd_cmipci_pm,
},
};
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index ec4247638fa1..d00b2c9fb1e3 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1,22 +1,7 @@
+// 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 <linux/io.h>
@@ -40,7 +25,6 @@
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 */
@@ -486,10 +470,7 @@ struct cs4281 {
struct gameport *gameport;
-#ifdef CONFIG_PM_SLEEP
u32 suspend_regs[SUSPEND_REGISTERS];
-#endif
-
};
static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);
@@ -670,7 +651,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
struct cs4281_dma *dma = substream->runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dma->valDCR |= BA0_DCR_MSK;
@@ -697,19 +678,17 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
dma->valFCR &= ~BA0_FCR_FEN;
break;
default:
- spin_unlock(&chip->reg_lock);
return -EINVAL;
}
snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR);
snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR);
snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR);
- spin_unlock(&chip->reg_lock);
return 0;
}
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;
@@ -722,9 +701,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;
@@ -796,26 +774,14 @@ 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;
struct cs4281_dma *dma = runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_cs4281_mode(chip, dma, runtime, 0, 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -825,9 +791,8 @@ static int snd_cs4281_capture_prepare(struct snd_pcm_substream *substream)
struct cs4281_dma *dma = runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_cs4281_mode(chip, dma, runtime, 1, 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -954,9 +919,6 @@ static int snd_cs4281_capture_close(struct snd_pcm_substream *substream)
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,
@@ -965,9 +927,6 @@ static const struct snd_pcm_ops snd_cs4281_playback_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,
@@ -987,11 +946,11 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "CS4281");
+ strscpy(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);
return 0;
}
@@ -1097,28 +1056,33 @@ 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;
}
@@ -1162,11 +1126,11 @@ 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,
};
@@ -1174,8 +1138,7 @@ 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;
@@ -1298,12 +1261,11 @@ static inline int snd_cs4281_create_gameport(struct cs4281 *chip) { return -ENOS
static inline void snd_cs4281_free_gameport(struct cs4281 *chip) { }
#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,48 +1273,20 @@ 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, PCI_D3hot);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- iounmap(chip->ba0);
- 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 snd_cs4281_create(struct snd_card *card,
struct pci_dev *pci,
- struct cs4281 **rchip,
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;
@@ -1364,43 +1298,30 @@ static int snd_cs4281_create(struct snd_card *card,
}
chip->dual_codec = dual_codec;
- if ((err = pci_request_regions(pci, "CS4281")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
+ chip->ba0 = pcim_iomap_region(pci, 0, "CS4281");
+ if (IS_ERR(chip->ba0))
+ return PTR_ERR(chip->ba0);
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->ba1 = pcim_iomap_region(pci, 1, "CS4281");
+ if (IS_ERR(chip->ba1))
+ return PTR_ERR(chip->ba1);
+ chip->ba1_addr = pci_resource_start(pci, 1);
- if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_cs4281_free(chip);
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);
-
- *rchip = chip;
return 0;
}
@@ -1432,12 +1353,14 @@ 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)) {
+ 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)) {
+ 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;
@@ -1634,7 +1557,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;
}
@@ -1654,7 +1576,7 @@ static int snd_cs4281_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr |= BA0_MIDCR_RXE;
chip->midi_input = substream;
if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
@@ -1662,7 +1584,6 @@ static int snd_cs4281_midi_input_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1670,7 +1591,7 @@ static int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE);
chip->midi_input = NULL;
if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
@@ -1679,7 +1600,6 @@ static int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream)
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
chip->uartm &= ~CS4281_MODE_INPUT;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1687,7 +1607,7 @@ static int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS4281_MODE_OUTPUT;
chip->midcr |= BA0_MIDCR_TXE;
chip->midi_output = substream;
@@ -1696,7 +1616,6 @@ static int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1704,7 +1623,7 @@ static int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE);
chip->midi_output = NULL;
if (!(chip->uartm & CS4281_MODE_INPUT)) {
@@ -1713,16 +1632,14 @@ static int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream)
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
chip->uartm &= ~CS4281_MODE_OUTPUT;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
static void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & BA0_MIDCR_RIE) == 0) {
chip->midcr |= BA0_MIDCR_RIE;
@@ -1734,16 +1651,14 @@ static void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substrea
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct cs4281 *chip = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & BA0_MIDCR_TIE) == 0) {
chip->midcr |= BA0_MIDCR_TIE;
@@ -1764,7 +1679,6 @@ static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substre
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static const struct snd_rawmidi_ops snd_cs4281_midi_output =
@@ -1786,9 +1700,10 @@ static int snd_cs4281_midi(struct cs4281 *chip, int device)
struct snd_rawmidi *rmidi;
int err;
- 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");
+ strscpy(rmidi->name, "CS4281");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -1816,10 +1731,12 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
}
if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) {
- for (dma = 0; dma < 4; dma++)
+ for (dma = 0; dma < 4; dma++) {
+ bool period_elapsed = false;
+ cdma = &chip->dma[dma];
+
if (status & BA0_HISR_DMA(dma)) {
- cdma = &chip->dma[dma];
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
/* ack DMA IRQ */
val = snd_cs4281_peekBA0(chip, cdma->regHDSR);
/* workaround, sometimes CS4281 acknowledges */
@@ -1828,24 +1745,24 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) {
cdma->frag--;
chip->spurious_dhtc_irq++;
- spin_unlock(&chip->reg_lock);
continue;
}
if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) {
cdma->frag--;
chip->spurious_dtc_irq++;
- spin_unlock(&chip->reg_lock);
continue;
}
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(cdma->substream);
+ period_elapsed = true;
}
+ if (period_elapsed)
+ snd_pcm_period_elapsed(cdma->substream);
+ }
}
if ((status & BA0_HISR_MIDI) && chip->rmidi) {
unsigned char c;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) {
c = snd_cs4281_peekBA0(chip, BA0_MIDRP);
if ((chip->midcr & BA0_MIDCR_RIE) == 0)
@@ -1862,7 +1779,6 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
}
snd_cs4281_pokeBA0(chip, BA0_MIDWP, c);
}
- spin_unlock(&chip->reg_lock);
}
/* EOI to the PCI part... reenables interrupts */
@@ -1878,7 +1794,6 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd,
unsigned char val)
{
- unsigned long flags;
struct cs4281 *chip = opl3->private_data;
void __iomem *port;
@@ -1887,19 +1802,17 @@ static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd,
else
port = chip->ba0 + BA0_B0AP; /* left port */
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
writel((unsigned int)cmd, port);
udelay(10);
writel((unsigned int)val, port + 4);
udelay(30);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
-static int 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;
@@ -1914,69 +1827,61 @@ static int snd_cs4281_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_cs4281_midi(chip, 0)) < 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");
+ strscpy(card->driver, "CS4281");
+ strscpy(card->shortname, "Cirrus Logic CS4281");
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname,
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_cs4281_probe(pci, pci_id));
}
/*
* Power Management
*/
-#ifdef CONFIG_PM_SLEEP
-
-static int saved_regs[SUSPEND_REGISTERS] = {
+static const int saved_regs[SUSPEND_REGISTERS] = {
BA0_JSCTL,
BA0_GPIOR,
BA0_SSCR,
@@ -2002,8 +1907,6 @@ static int cs4281_suspend(struct device *dev)
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);
@@ -2066,19 +1969,14 @@ static int cs4281_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume);
static struct pci_driver cs4281_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs4281_ids,
.probe = snd_cs4281_probe,
- .remove = snd_cs4281_remove,
.driver = {
- .pm = CS4281_PM_OPS,
+ .pm = &cs4281_pm,
},
};
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 655fbea1692c..9c1995737eb7 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
- *
*/
/*
@@ -36,13 +21,6 @@
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 */
@@ -58,7 +36,7 @@ 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);
@@ -88,84 +66,72 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)) < 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)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_cs46xx_pcm_iec958(chip, 2)) < 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)) < 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)) < 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);
- strcpy(card->driver, "CS46xx");
- strcpy(card->shortname, "Sound Fusion CS46xx");
+ strscpy(card->driver, "CS46xx");
+ strscpy(card->shortname, "Sound Fusion CS46xx");
sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i",
card->shortname,
chip->ba0_addr,
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 snd_card_cs46xx_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
+ error:
+ snd_card_free(card);
+ return err;
}
static struct pci_driver cs46xx_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
- .remove = snd_card_cs46xx_remove,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_cs46xx_pm,
diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h
index 9c9f89a8be5f..c4f0a0b94270 100644
--- a/sound/pci/cs46xx/cs46xx.h
+++ b/sound/pci/cs46xx/cs46xx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_CS46XX_H
#define __SOUND_CS46XX_H
@@ -5,22 +6,6 @@
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Cirrus Logic, Inc.
* Definitions for Cirrus Logic CS46xx 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 <sound/pcm.h>
@@ -1650,7 +1635,6 @@ struct snd_cs46xx_region {
unsigned long base;
void __iomem *remap_addr;
unsigned long size;
- struct resource *resource;
};
struct snd_cs46xx {
@@ -1733,8 +1717,7 @@ struct snd_cs46xx {
int snd_cs46xx_create(struct snd_card *card,
struct pci_dev *pci,
- int external_amp, int thinkpad,
- struct snd_cs46xx **rcodec);
+ int external_amp, int thinkpad);
extern const struct dev_pm_ops snd_cs46xx_pm;
int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device);
diff --git a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
index 080857ad0ca2..7339c38570be 100644
--- a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
+++ b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
@@ -1,23 +1,8 @@
+/* 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
- *
- *
* NOTE: comments are copy/paste from cwcemb80.lst
* provided by Tom Woller at Cirrus (my only
* documentation about the SP OS running inside
diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h
index 8008c59288a6..2fa9c7d6acc3 100644
--- a/sound/pci/cs46xx/cs46xx_dsp_spos.h
+++ b/sound/pci/cs46xx/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
- *
*/
#ifndef __CS46XX_DSP_SPOS_H__
@@ -177,22 +162,16 @@ struct dsp_spos_instance {
/* proc fs */
struct snd_card *snd_card;
struct snd_info_entry * proc_dsp_dir;
- struct snd_info_entry * proc_sym_info_entry;
- struct snd_info_entry * proc_modules_info_entry;
- struct snd_info_entry * proc_parameter_dump_info_entry;
- struct snd_info_entry * proc_sample_dump_info_entry;
/* SCB's descriptors */
int nscb;
int scb_highest_frag_index;
struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC];
- struct snd_info_entry * proc_scb_info_entry;
struct dsp_scb_descriptor * the_null_scb;
/* Task's descriptors */
int ntask;
struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC];
- struct snd_info_entry * proc_task_info_entry;
/* SPDIF status */
int spdif_status_out;
diff --git a/sound/pci/cs46xx/cs46xx_dsp_task_types.h b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
index be5694718546..fcbd31e40c5a 100644
--- a/sound/pci/cs46xx/cs46xx_dsp_task_types.h
+++ b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
@@ -1,23 +1,8 @@
+/* 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
- *
- *
* NOTE: comments are copy/paste from cwcemb80.lst
* provided by Tom Woller at Cirrus (my only
* documentation about the SP OS running inside
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 0020fd0efc46..b96ab7fd464c 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>
@@ -460,7 +446,7 @@ static int load_firmware(struct snd_cs46xx *chip,
entry->size = le32_to_cpu(fwdat[fwlen++]);
if (fwlen + entry->size > fwsize)
goto error_inval;
- entry->data = kmalloc(entry->size * 4, GFP_KERNEL);
+ entry->data = kmalloc_array(entry->size, 4, GFP_KERNEL);
if (!entry->data)
goto error;
memcpy_le32(entry->data, &fwdat[fwlen], entry->size * 4);
@@ -545,7 +531,7 @@ static int load_firmware(struct snd_cs46xx *chip)
return err;
}
-int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+static __maybe_unused int snd_cs46xx_download_image(struct snd_cs46xx *chip)
{
int idx, err;
unsigned int offset = 0;
@@ -721,7 +707,6 @@ static void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
static void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
{
- unsigned long flags;
unsigned int tmp1, tmp2;
unsigned int phiIncr;
unsigned int correctionPerGOF, correctionPerSec;
@@ -758,16 +743,14 @@ static void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned in
/*
* Fill in the SampleRateConverter control block.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_cs46xx_poke(chip, BA1_PSRC,
((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
{
- unsigned long flags;
unsigned int phiIncr, coeffIncr, tmp1, tmp2;
unsigned int correctionPerGOF, correctionPerSec, initialDelay;
unsigned int frameGroupLength, cnt;
@@ -780,7 +763,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)
@@ -827,19 +810,19 @@ 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.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs46xx_poke(chip, BA1_CSRC,
- ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
- snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
- snd_cs46xx_poke(chip, BA1_CD,
- (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
- snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_cs46xx_poke(chip, BA1_CSRC,
+ ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+ snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
+ snd_cs46xx_poke(chip, BA1_CD,
+ (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+ snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
+ }
/*
* Figure out the frame group length for the write back task. Basically,
@@ -862,13 +845,12 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
/*
* Fill in the WriteBack control block.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/*
@@ -983,15 +965,14 @@ static int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
if (substream->runtime->periods != CS46XX_FRAGS)
snd_cs46xx_playback_transfer(substream);
#else
- spin_lock(&chip->reg_lock);
- if (substream->runtime->periods != CS46XX_FRAGS)
- snd_cs46xx_playback_transfer(substream);
- { unsigned int tmp;
- tmp = snd_cs46xx_peek(chip, BA1_PCTL);
- tmp &= 0x0000ffff;
- snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ unsigned int tmp;
+ if (substream->runtime->periods != CS46XX_FRAGS)
+ snd_cs46xx_playback_transfer(substream);
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
}
- spin_unlock(&chip->reg_lock);
#endif
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -1004,13 +985,12 @@ static int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
if (!cpcm->pcm_channel->unlinked)
cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
#else
- spin_lock(&chip->reg_lock);
- { unsigned int tmp;
- tmp = snd_cs46xx_peek(chip, BA1_PCTL);
- tmp &= 0x0000ffff;
- snd_cs46xx_poke(chip, BA1_PCTL, tmp);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ unsigned int tmp;
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, tmp);
}
- spin_unlock(&chip->reg_lock);
#endif
break;
default:
@@ -1026,9 +1006,8 @@ static int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
unsigned int tmp;
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -1043,12 +1022,9 @@ static int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
snd_cs46xx_poke(chip, BA1_CCTL, tmp);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
-
- return result;
+ return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -1072,9 +1048,10 @@ 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) {
+ 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;
@@ -1106,24 +1083,17 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
if (snd_BUG_ON(!sample_rate))
return -ENXIO;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
- if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
- mutex_unlock(&chip->spos_mutex);
+ if (_cs46xx_adjust_sample_rate(chip, cpcm, sample_rate))
return -ENXIO;
- }
snd_BUG_ON(!cpcm->pcm_channel);
- if (!cpcm->pcm_channel) {
- mutex_unlock(&chip->spos_mutex);
+ if (!cpcm->pcm_channel)
return -ENXIO;
- }
-
- if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) {
- mutex_unlock(&chip->spos_mutex);
+ if (cs46xx_dsp_pcm_channel_set_period(chip, cpcm->pcm_channel, period_size))
return -EINVAL;
- }
dev_dbg(chip->card->dev,
"period_size (%d), periods (%d) buffer_size(%d)\n",
@@ -1134,9 +1104,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
@@ -1156,17 +1124,11 @@ 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) {
-#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_unlock(&chip->spos_mutex);
-#endif
+ 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)
return err;
- }
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
@@ -1186,10 +1148,6 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
}
-#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_unlock(&chip->spos_mutex);
-#endif
-
return 0;
}
@@ -1208,9 +1166,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;
}
@@ -1299,17 +1255,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;
}
@@ -1324,9 +1276,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;
}
@@ -1411,7 +1361,7 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
if ((status1 & HISR_MIDI) && chip->rmidi) {
unsigned char c;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
if ((chip->midcr & MIDCR_RIE) == 0)
@@ -1428,7 +1378,6 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
}
snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
}
- spin_unlock(&chip->reg_lock);
}
/*
* EOI to the PCI part....reenables interrupts
@@ -1443,7 +1392,8 @@ 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),
@@ -1465,7 +1415,8 @@ 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,
@@ -1506,7 +1457,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;
@@ -1518,16 +1469,14 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
cpcm->substream = substream;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_lock(&chip->spos_mutex);
- cpcm->pcm_channel = NULL;
- cpcm->pcm_channel_id = pcm_channel_id;
-
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cpcm->pcm_channel = NULL;
+ cpcm->pcm_channel_id = pcm_channel_id;
+ }
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_sizes);
-
- mutex_unlock(&chip->spos_mutex);
#else
chip->playback_pcm = cpcm; /* HACK */
#endif
@@ -1564,9 +1513,9 @@ static int snd_cs46xx_playback_open_iec958(struct snd_pcm_substream *substream)
dev_dbg(chip->card->dev, "open raw iec958 channel\n");
- mutex_lock(&chip->spos_mutex);
- cs46xx_iec958_pre_open (chip);
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cs46xx_iec958_pre_open(chip);
+ }
return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
}
@@ -1582,9 +1531,9 @@ static int snd_cs46xx_playback_close_iec958(struct snd_pcm_substream *substream)
err = snd_cs46xx_playback_close(substream);
- mutex_lock(&chip->spos_mutex);
- cs46xx_iec958_post_close (chip);
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cs46xx_iec958_post_close(chip);
+ }
return err;
}
@@ -1594,7 +1543,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;
@@ -1625,12 +1574,12 @@ static int snd_cs46xx_playback_close(struct snd_pcm_substream *substream)
if (!cpcm) return -ENXIO;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_lock(&chip->spos_mutex);
- if (cpcm->pcm_channel) {
- cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
- cpcm->pcm_channel = NULL;
+ scoped_guard(mutex, &chip->spos_mutex) {
+ if (cpcm->pcm_channel) {
+ cs46xx_dsp_destroy_pcm_channel(chip, cpcm->pcm_channel);
+ cpcm->pcm_channel = NULL;
+ }
}
- mutex_unlock(&chip->spos_mutex);
#else
chip->playback_pcm = NULL;
#endif
@@ -1657,7 +1606,6 @@ static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream)
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,
@@ -1668,7 +1616,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_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,
@@ -1680,7 +1627,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_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,
@@ -1691,7 +1637,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_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,
@@ -1703,7 +1648,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_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,
@@ -1714,7 +1658,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_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,
@@ -1728,7 +1671,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_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,
@@ -1739,7 +1681,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_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,
@@ -1751,7 +1692,6 @@ static const struct snd_pcm_ops snd_cs46xx_playback_indirect_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,
@@ -1762,7 +1702,6 @@ static const struct snd_pcm_ops snd_cs46xx_capture_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,
@@ -1782,7 +1721,8 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1792,11 +1732,12 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx");
+ strscpy(pcm->name, "CS46xx");
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);
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1808,7 +1749,8 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1817,11 +1759,12 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - Rear");
+ strscpy(pcm->name, "CS46xx - Rear");
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);
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1831,7 +1774,8 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1840,11 +1784,12 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - Center LFE");
+ strscpy(pcm->name, "CS46xx - Center LFE");
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);
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1854,7 +1799,8 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1863,11 +1809,12 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - IEC958");
+ strscpy(pcm->name, "CS46xx - IEC958");
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);
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1876,13 +1823,6 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
/*
* 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;
@@ -2014,15 +1954,15 @@ static int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol,
switch (kcontrol->private_value) {
case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
- mutex_lock(&chip->spos_mutex);
- change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
- if (ucontrol->value.integer.value[0] && !change)
- cs46xx_dsp_enable_spdif_out(chip);
- else if (change && !ucontrol->value.integer.value[0])
- cs46xx_dsp_disable_spdif_out(chip);
-
- res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+ if (ucontrol->value.integer.value[0] && !change)
+ cs46xx_dsp_enable_spdif_out(chip);
+ else if (change && !ucontrol->value.integer.value[0])
+ cs46xx_dsp_disable_spdif_out(chip);
+
+ res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
+ }
break;
case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
change = chip->dsp_spos_instance->spdif_status_in;
@@ -2163,12 +2103,11 @@ static int snd_cs46xx_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff);
ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff);
ucontrol->value.iec958.status[2] = 0;
ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff);
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -2181,7 +2120,7 @@ static int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val;
int change;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[2]) << 16) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
@@ -2195,8 +2134,6 @@ static int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) )
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
- mutex_unlock(&chip->spos_mutex);
-
return change;
}
@@ -2216,12 +2153,11 @@ static int snd_cs46xx_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff);
ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff);
ucontrol->value.iec958.status[2] = 0;
ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff);
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -2234,7 +2170,7 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
unsigned int val;
int change;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[1]) << 16) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
@@ -2248,15 +2184,13 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN )
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
- mutex_unlock(&chip->spos_mutex);
-
return change;
}
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
-static struct snd_kcontrol_new snd_cs46xx_controls[] = {
+static const struct snd_kcontrol_new snd_cs46xx_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
@@ -2382,7 +2316,7 @@ static const 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",
@@ -2432,7 +2366,8 @@ 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);
@@ -2480,10 +2415,9 @@ static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
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
@@ -2494,9 +2428,9 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
/* detect primary codec */
chip->nr_ac97_codecs = 0;
dev_dbg(chip->card->dev, "detecting primary codec\n");
- 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_cs46xx_mixer_free_ac97_bus;
if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
return -ENXIO;
@@ -2515,15 +2449,14 @@ int 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;
}
/* get EAPD mixer switch (for voyetra hack) */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "External Amplifier");
- chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+ chip->eapd_switch = snd_ctl_find_id_mixer(chip->card,
+ "External Amplifier");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
@@ -2565,7 +2498,7 @@ static int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
struct snd_cs46xx *chip = substream->rmidi->private_data;
chip->active_ctrl(chip, 1);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS46XX_MODE_INPUT;
chip->midcr |= MIDCR_RXE;
chip->midi_input = substream;
@@ -2574,7 +2507,6 @@ static int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -2582,16 +2514,16 @@ static int snd_cs46xx_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
- chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
- chip->midi_input = NULL;
- if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
- snd_cs46xx_midi_reset(chip);
- } else {
- snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
+ chip->midi_input = NULL;
+ if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_INPUT;
}
- chip->uartm &= ~CS46XX_MODE_INPUT;
- spin_unlock_irq(&chip->reg_lock);
chip->active_ctrl(chip, -1);
return 0;
}
@@ -2602,7 +2534,7 @@ static int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
chip->active_ctrl(chip, 1);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS46XX_MODE_OUTPUT;
chip->midcr |= MIDCR_TXE;
chip->midi_output = substream;
@@ -2611,7 +2543,6 @@ static int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -2619,26 +2550,25 @@ static int snd_cs46xx_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
- chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
- chip->midi_output = NULL;
- if (!(chip->uartm & CS46XX_MODE_INPUT)) {
- snd_cs46xx_midi_reset(chip);
- } else {
- snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
+ chip->midi_output = NULL;
+ if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_OUTPUT;
}
- chip->uartm &= ~CS46XX_MODE_OUTPUT;
- spin_unlock_irq(&chip->reg_lock);
chip->active_ctrl(chip, -1);
return 0;
}
static void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & MIDCR_RIE) == 0) {
chip->midcr |= MIDCR_RIE;
@@ -2650,16 +2580,14 @@ static void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substrea
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_cs46xx *chip = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & MIDCR_TIE) == 0) {
chip->midcr |= MIDCR_TIE;
@@ -2680,7 +2608,6 @@ static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substre
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static const struct snd_rawmidi_ops snd_cs46xx_midi_output =
@@ -2702,9 +2629,10 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device)
struct snd_rawmidi *rmidi;
int err;
- 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");
+ strscpy(rmidi->name, "CS46XX");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -2833,7 +2761,7 @@ 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,
};
@@ -2849,7 +2777,7 @@ static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
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
@@ -2920,12 +2848,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);
@@ -2937,22 +2865,11 @@ 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];
-
- 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);
@@ -2963,20 +2880,6 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
#else
vfree(chip->ba1);
#endif
-
-#ifdef CONFIG_PM_SLEEP
- 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);
}
/*
@@ -3544,7 +3447,8 @@ 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) {
+ err = snd_ctl_add(card, kctl);
+ if (err < 0) {
dev_err(card->dev,
"failed to initialize Hercules mixer (%d)\n",
err);
@@ -3763,7 +3667,7 @@ static struct cs_card_type cards[] = {
* APM support
*/
#ifdef CONFIG_PM_SLEEP
-static unsigned int saved_regs[] = {
+static const unsigned int saved_regs[] = {
BA0_ACOSV,
/*BA0_ASER_FADDR,*/
BA0_ASER_MASTER,
@@ -3779,12 +3683,6 @@ static int snd_cs46xx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
- snd_pcm_suspend_all(chip->pcm);
-#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_pcm_suspend_all(chip->pcm_rear);
- snd_pcm_suspend_all(chip->pcm_center_lfe);
- snd_pcm_suspend_all(chip->pcm_iec958);
-#endif
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
@@ -3880,29 +3778,19 @@ SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume);
int snd_cs46xx_create(struct snd_card *card,
struct pci_dev *pci,
- int external_amp, int thinkpad,
- struct snd_cs46xx **rchip)
+ 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);
@@ -3910,6 +3798,10 @@ int snd_cs46xx_create(struct snd_card *card,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+
+ err = pcim_request_all_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 ||
@@ -3917,32 +3809,31 @@ int snd_cs46xx_create(struct snd_card *card,
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;
}
region = &chip->region.name.ba0;
- strcpy(region->name, "CS46xx_BA0");
+ strscpy(region->name, "CS46xx_BA0");
region->base = chip->ba0_addr;
region->size = CS46XX_BA0_SIZE;
region = &chip->region.name.data0;
- strcpy(region->name, "CS46xx_BA1_data0");
+ strscpy(region->name, "CS46xx_BA1_data0");
region->base = chip->ba1_addr + BA1_SP_DMEM0;
region->size = CS46XX_BA1_DATA0_SIZE;
region = &chip->region.name.data1;
- strcpy(region->name, "CS46xx_BA1_data1");
+ strscpy(region->name, "CS46xx_BA1_data1");
region->base = chip->ba1_addr + BA1_SP_DMEM1;
region->size = CS46XX_BA1_DATA1_SIZE;
region = &chip->region.name.pmem;
- strcpy(region->name, "CS46xx_BA1_pmem");
+ strscpy(region->name, "CS46xx_BA1_pmem");
region->base = chip->ba1_addr + BA1_SP_PMEM;
region->size = CS46XX_BA1_PRG_SIZE;
region = &chip->region.name.reg;
- strcpy(region->name, "CS46xx_BA1_reg");
+ strscpy(region->name, "CS46xx_BA1_reg");
region->base = chip->ba1_addr + BA1_SP_REG;
region->size = CS46XX_BA1_REG_SIZE;
@@ -3989,63 +3880,45 @@ int 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) {
- dev_err(chip->card->dev,
- "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) {
dev_err(chip->card->dev,
"%s ioremap problem\n", region->name);
- snd_cs46xx_free(chip);
return -ENOMEM;
}
}
- if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_cs46xx_free(chip);
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_SLEEP
- chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
- ARRAY_SIZE(saved_regs), GFP_KERNEL);
- if (!chip->saved_regs) {
- snd_cs46xx_free(chip);
+ 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 */
-
- *rchip = chip;
return 0;
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index bdf4114167ea..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__
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index aa61615288ff..3d34575a0e8f 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
- *
*/
/*
@@ -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,
@@ -216,7 +203,7 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type)
index = find_free_symbol_index (ins);
- strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
+ strscpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
ins->symbol_table.symbols[index].address = address;
ins->symbol_table.symbols[index].symbol_type = type;
ins->symbol_table.symbols[index].module = NULL;
@@ -240,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;
@@ -293,7 +283,7 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
if (snd_BUG_ON(!ins))
return;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue;
@@ -307,7 +297,6 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
vfree(ins->symbol_table.symbols);
kfree(ins->modules);
kfree(ins);
- mutex_unlock(&chip->spos_mutex);
}
static int dsp_load_parameter(struct snd_cs46xx *chip,
@@ -535,7 +524,7 @@ static void cs46xx_dsp_proc_modules_read (struct snd_info_entry *entry,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i,j;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "MODULES:\n");
for ( i = 0; i < ins->nmodules; ++i ) {
snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
@@ -548,7 +537,6 @@ static void cs46xx_dsp_proc_modules_read (struct snd_info_entry *entry,
desc->segment_type,desc->offset, desc->size);
}
}
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
@@ -559,7 +547,7 @@ static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
int i, j, col;
void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "TASK TREES:\n");
for ( i = 0; i < ins->ntask; ++i) {
snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
@@ -576,7 +564,6 @@ static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
}
snd_iprintf(buffer,"\n");
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
@@ -586,7 +573,7 @@ static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "SCB's:\n");
for ( i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted)
@@ -609,7 +596,6 @@ static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
}
snd_iprintf(buffer,"\n");
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_parameter_dump_read (struct snd_info_entry *entry,
@@ -627,7 +613,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);
}
@@ -796,101 +783,57 @@ 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;
-
- mutex_lock(&chip->spos_mutex);
+ 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);
+
+ guard(mutex)(&chip->spos_mutex);
/* register/update SCB's entries on proc */
for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
}
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -900,30 +843,16 @@ 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) {
- if (ins->scbs[i].deleted) continue;
- cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+ scoped_guard(mutex, &chip->spos_mutex) {
+ for (i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted)
+ continue;
+ cs46xx_dsp_proc_free_scb_desc((ins->scbs + i));
+ }
}
- mutex_unlock(&chip->spos_mutex);
snd_info_free_entry(ins->proc_dsp_dir);
ins->proc_dsp_dir = NULL;
@@ -990,7 +919,7 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam
index = find_free_scb_index (ins);
memset(&ins->scbs[index], 0, sizeof(ins->scbs[index]));
- strcpy(ins->scbs[index].scb_name, name);
+ strscpy(ins->scbs[index].scb_name, name);
ins->scbs[index].address = dest;
ins->scbs[index].index = index;
ins->scbs[index].ref_count = 1;
@@ -1020,9 +949,9 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
}
if (name)
- strcpy(ins->tasks[ins->ntask].task_name, name);
+ strscpy(ins->tasks[ins->ntask].task_name, name);
else
- strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
+ strscpy(ins->tasks[ins->ntask].task_name, "(NULL)");
ins->tasks[ins->ntask].address = dest;
ins->tasks[ins->ntask].size = size;
@@ -1106,7 +1035,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,
@@ -1744,7 +1673,7 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->spdif_in_src))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED) ) {
/* time countdown enable */
@@ -1767,7 +1696,7 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
ins->spdif_in_src,
SCB_ON_PARENT_SUBLIST_SCB);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* reset SPDIF input sample buffer pointer */
/*snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
@@ -1780,15 +1709,12 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
/* unmute SRC volume */
cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,0x7fff,0x7fff);
- spin_unlock_irq(&chip->reg_lock);
-
/* set SPDIF input sample rate and unmute
NOTE: only 48khz support for SPDIF input this time */
/* cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000); */
/* monitor state */
ins->spdif_status_in = 1;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1802,17 +1728,16 @@ int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->spdif_in_src))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
-
- /* Remove the asynchronous receiver SCB */
- cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
- ins->asynch_rx_scb = NULL;
+ scoped_guard(mutex, &chip->spos_mutex) {
+ /* Remove the asynchronous receiver SCB */
+ cs46xx_dsp_remove_scb(chip, ins->asynch_rx_scb);
+ ins->asynch_rx_scb = NULL;
- cs46xx_src_unlink(chip,ins->spdif_in_src);
+ cs46xx_src_unlink(chip, ins->spdif_in_src);
- /* monitor state */
- ins->spdif_status_in = 0;
- mutex_unlock(&chip->spos_mutex);
+ /* monitor state */
+ ins->spdif_status_in = 0;
+ }
/* restore amplifier */
chip->active_ctrl(chip, -1);
@@ -1830,10 +1755,9 @@ int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->ref_snoop_scb))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
"PCMSerialInput_Wave");
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1845,10 +1769,9 @@ int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->pcm_input))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->pcm_input);
ins->pcm_input = NULL;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1862,10 +1785,9 @@ int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->codec_in_scb))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
"PCMSerialInput_ADC");
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1877,10 +1799,9 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->adc_input))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->adc_input);
ins->adc_input = NULL;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1928,7 +1849,7 @@ int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * scb;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
/* main output */
scb = ins->master_mix_scb->sub_list_ptr;
@@ -1947,8 +1868,6 @@ int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right)
ins->dac_volume_left = left;
ins->dac_volume_right = right;
- mutex_unlock(&chip->spos_mutex);
-
return 0;
}
@@ -1956,7 +1875,7 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
if (ins->asynch_rx_scb != NULL)
cs46xx_dsp_scb_set_volume (chip,ins->asynch_rx_scb,
@@ -1965,8 +1884,6 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
ins->spdif_input_volume_left = left;
ins->spdif_input_volume_right = right;
- mutex_unlock(&chip->spos_mutex);
-
return 0;
}
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 7488e1b7a770..32ed415bf427 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
- *
*/
/*
@@ -73,14 +59,11 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
{
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);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
for (col = 0,j = 0;j < 0x10; j++,col++) {
@@ -108,7 +91,6 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
scb->task_entry->address);
snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);
- mutex_unlock(&chip->spos_mutex);
}
#endif
@@ -177,7 +159,6 @@ static void _dsp_clear_sample_buffer (struct snd_cs46xx *chip, u32 sample_buffer
void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- unsigned long flags;
/* check integrety */
if (snd_BUG_ON(scb->index < 0 ||
@@ -193,9 +174,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
goto _end;
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- _dsp_unlink_scb (chip,scb);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ _dsp_unlink_scb(chip, scb);
+ }
cs46xx_dsp_proc_free_scb_desc(scb);
if (snd_BUG_ON(!scb->scb_symbol))
@@ -218,13 +199,6 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
if (ins->scb_highest_frag_index > ins->nscb) {
ins->scb_highest_frag_index = ins->nscb;
}
-
-#if 0
- /* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */
- for(i = scb->index + 1;i < ins->nscb; ++i) {
- ins->scbs[i - 1].index = i - 1;
- }
-#endif
}
@@ -257,8 +231,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);
@@ -268,18 +243,8 @@ 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;
@@ -296,8 +261,6 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * scb;
- unsigned long flags;
-
if (snd_BUG_ON(!ins->the_null_scb))
return NULL;
@@ -351,12 +314,10 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
snd_BUG();
}
- spin_lock_irqsave(&chip->reg_lock, flags);
-
- /* update entry in DSP RAM */
- cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
-
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ /* update entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip, scb->parent_scb_ptr);
+ }
}
@@ -1171,7 +1132,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 */
@@ -1206,7 +1167,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,
@@ -1223,7 +1184,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,
@@ -1253,7 +1214,6 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
/* struct dsp_scb_descriptor * pcm_parent_scb; */
char scb_name[DSP_MAX_SCB_NAME];
int i, pcm_index = -1, insert_point, src_index = -1, pass_through = 0;
- unsigned long flags;
switch (pcm_channel_id) {
case DSP_PCM_MAIN_CHANNEL:
@@ -1319,7 +1279,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
if (src_scb == NULL) {
if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
dev_err(chip->card->dev,
- "dsp_spos: to many SRC instances\n!");
+ "dsp_spos: too many SRC instances\n!");
return NULL;
}
@@ -1390,7 +1350,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
return NULL;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ins->pcm_channels[pcm_index].sample_rate = sample_rate;
ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
ins->pcm_channels[pcm_index].src_scb = src_scb;
@@ -1401,7 +1361,6 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
ins->npcm_channels ++;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return (ins->pcm_channels + pcm_index);
}
@@ -1489,20 +1448,19 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
struct dsp_pcm_channel_descriptor * pcm_channel)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- unsigned long flags;
if (snd_BUG_ON(!pcm_channel->active ||
ins->npcm_channels <= 0 ||
pcm_channel->src_scb->ref_count <= 0))
return;
- spin_lock_irqsave(&chip->reg_lock, flags);
- pcm_channel->unlinked = 1;
- pcm_channel->active = 0;
- pcm_channel->private_data = NULL;
- pcm_channel->src_scb->ref_count --;
- ins->npcm_channels --;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ pcm_channel->unlinked = 1;
+ pcm_channel->active = 0;
+ pcm_channel->private_data = NULL;
+ pcm_channel->src_scb->ref_count--;
+ ins->npcm_channels--;
+ }
cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb);
@@ -1521,22 +1479,17 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
struct dsp_pcm_channel_descriptor * pcm_channel)
{
- unsigned long flags;
-
if (snd_BUG_ON(!pcm_channel->active ||
chip->dsp_spos_instance->npcm_channels <= 0))
return -EIO;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (pcm_channel->unlinked) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ if (pcm_channel->unlinked)
return -EIO;
- }
pcm_channel->unlinked = 1;
_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1547,14 +1500,11 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * parent_scb;
struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
- if (pcm_channel->unlinked == 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (pcm_channel->unlinked == 0)
return -EIO;
- }
parent_scb = src_scb;
@@ -1575,7 +1525,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
cs46xx_dsp_spos_update_scb(chip,parent_scb);
pcm_channel->unlinked = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1608,17 +1557,14 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
{
- unsigned long flags;
-
if (snd_BUG_ON(!src->parent_scb_ptr))
return -EINVAL;
/* mute SCB */
cs46xx_dsp_scb_set_volume (chip,src,0,0);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
_dsp_unlink_scb (chip,src);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1742,7 +1688,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/cs5530.c b/sound/pci/cs5530.c
index 0a8cf94c4858..292b65aa758a 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,16 +22,6 @@
* 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.
@@ -74,25 +65,6 @@ static const struct pci_device_id 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 snd_cs5530_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg)
{
outb(reg, io + 4);
@@ -103,50 +75,27 @@ static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg)
}
static int snd_cs5530_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_cs5530 **rchip)
+ 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);
- return err;
- }
+ mem = pcim_iomap_region(pci, 0, "CS5530");
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
chip->pci_base = pci_resource_start(pci, 0);
-
- mem = pci_ioremap_bar(pci, 0);
- if (mem == NULL) {
- snd_cs5530_free(chip);
- return -EBUSY;
- }
-
map = readw(mem + 0x18);
- iounmap(mem);
/* Map bits
0:1 * 0x20 + 0x200 = sb base
@@ -163,7 +112,6 @@ static int snd_cs5530_create(struct snd_card *card,
dev_info(card->dev, "XpressAudio at 0x%lx\n", sb_base);
else {
dev_err(card->dev, "Could not find XpressAudio!\n");
- snd_cs5530_free(chip);
return -ENODEV;
}
@@ -183,7 +131,6 @@ static int snd_cs5530_create(struct snd_card *card,
dma16 = 7;
else {
dev_err(card->dev, "No 16bit DMA enabled\n");
- snd_cs5530_free(chip);
return -ENODEV;
}
@@ -195,7 +142,6 @@ static int snd_cs5530_create(struct snd_card *card,
dma8 = 3;
else {
dev_err(card->dev, "No 8bit DMA enabled\n");
- snd_cs5530_free(chip);
return -ENODEV;
}
@@ -209,7 +155,6 @@ static int snd_cs5530_create(struct snd_card *card,
irq = 10;
else {
dev_err(card->dev, "SoundBlaster IRQ not set\n");
- snd_cs5530_free(chip);
return -ENODEV;
}
@@ -219,31 +164,21 @@ static int snd_cs5530_create(struct snd_card *card,
dma16, SB_HW_CS5530, &chip->sb);
if (err < 0) {
dev_err(card->dev, "Could not create SoundBlaster\n");
- snd_cs5530_free(chip);
return err;
}
err = snd_sb16dsp_pcm(chip->sb, 0);
if (err < 0) {
dev_err(card->dev, "Could not create PCM\n");
- snd_cs5530_free(chip);
return err;
}
err = snd_sbmixer_new(chip->sb);
if (err < 0) {
dev_err(card->dev, "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);
return err;
}
- *rchip = chip;
return 0;
}
@@ -252,7 +187,7 @@ static int snd_cs5530_probe(struct pci_dev *pci,
{
static int dev;
struct snd_card *card;
- struct snd_cs5530 *chip = NULL;
+ struct snd_cs5530 *chip;
int err;
if (dev >= SNDRV_CARDS)
@@ -262,27 +197,23 @@ static int snd_cs5530_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "CS5530");
+ strscpy(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;
@@ -292,7 +223,6 @@ static struct pci_driver cs5530_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
- .remove = snd_cs5530_remove,
};
module_pci_driver(cs5530_driver);
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index a8f75f8dfda9..447e628751a2 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for cs5535audio
#
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index de409cda50aa..0ebf6c02b1ef 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>
@@ -152,12 +138,13 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
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));
@@ -169,7 +156,8 @@ static int 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) {
+ err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97);
+ if (err < 0) {
dev_err(card->dev, "mixer failed\n");
return err;
}
@@ -188,12 +176,11 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
static void process_bm0_irq(struct cs5535audio *cs5535au)
{
u8 bm_stat;
- spin_lock(&cs5535au->reg_lock);
- bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
- spin_unlock(&cs5535au->reg_lock);
+
+ scoped_guard(spinlock, &cs5535au->reg_lock) {
+ bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
+ }
if (bm_stat & EOP) {
- struct cs5535audio_dma *dma;
- dma = cs5535au->playback_substream->runtime->private_data;
snd_pcm_period_elapsed(cs5535au->playback_substream);
} else {
dev_err(cs5535au->card->dev,
@@ -205,14 +192,12 @@ static void process_bm0_irq(struct cs5535audio *cs5535au)
static void process_bm1_irq(struct cs5535audio *cs5535au)
{
u8 bm_stat;
- 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;
- snd_pcm_period_elapsed(cs5535au->capture_substream);
+
+ scoped_guard(spinlock, &cs5535au->reg_lock) {
+ bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
}
+ if (bm_stat & EOP)
+ snd_pcm_period_elapsed(cs5535au->capture_substream);
}
static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
@@ -254,52 +239,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, PCI_D3hot);
-
- 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 snd_cs5535audio_create(struct snd_card *card,
- struct pci_dev *pci,
- struct cs5535audio **rcs5535au)
+ 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
dev_warn(card->dev, "unable to get 32bit dma\n");
- err = -ENXIO;
- goto pcifail;
- }
-
- cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
- if (cs5535au == NULL) {
- err = -ENOMEM;
- goto pcifail;
+ return -ENXIO;
}
spin_lock_init(&cs5535au->reg_lock);
@@ -307,41 +264,27 @@ static int 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 = pcim_request_all_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, KBUILD_MODNAME, cs5535au)) {
+ 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);
- err = -EBUSY;
- goto sndfail;
+ 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;
-
- *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 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;
@@ -355,52 +298,51 @@ static int snd_cs5535audio_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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);
+ strscpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "CS5535 Audio");
+ strscpy(card->shortname, "CS5535 Audio");
sprintf(card->longname, "%s %s at 0x%lx, irq %i",
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_cs5535audio_probe(pci, pci_id));
}
static struct pci_driver cs5535audio_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
- .remove = snd_cs5535audio_remove,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_cs5535audio_pm,
@@ -413,4 +355,3 @@ 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 f4fcdf93f3c8..d84620a0c26c 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -67,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 {
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
index 3b0fdaca8dc7..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>
@@ -115,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[] = {
+static const struct snd_kcontrol_new olpc_cs5535audio_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DC Mode Enable",
@@ -162,23 +158,21 @@ int 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;
- strlcpy(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;
- strlcpy(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 */
@@ -188,5 +182,6 @@ int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
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 ee7065f6e162..48b99a07e3bc 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
*/
@@ -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,16 +129,14 @@ 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_le16(period_bytes);
desc->ctlreserved = cpu_to_le16(PRD_EOP);
- desc_addr += sizeof(struct cs5535audio_dma_desc);
addr += period_bytes;
}
/* we reserved one dummy descriptor at the end to do the PRD jump */
@@ -158,16 +144,15 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
lastdesc->size = 0;
lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
- jmpprd_addr = cpu_to_le32(lastdesc->addr +
- (sizeof(struct cs5535audio_dma_desc)*periods));
+ jmpprd_addr = (u32)dma->desc_buf.addr +
+ sizeof(struct cs5535audio_dma_desc) * periods;
dma->substream = substream;
dma->period_bytes = period_bytes;
dma->periods = periods;
- spin_lock_irq(&cs5535au->reg_lock);
+ guard(spinlock_irq)(&cs5535au->reg_lock);
dma->ops->disable_dma(cs5535au);
dma->ops->setup_prd(cs5535au, jmpprd_addr);
- spin_unlock_irq(&cs5535au->reg_lock);
return 0;
}
@@ -249,10 +234,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 +261,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)
@@ -294,9 +275,8 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
struct cs5535audio_dma *dma = substream->runtime->private_data;
- int err = 0;
- spin_lock(&cs5535au->reg_lock);
+ guard(spinlock)(&cs5535au->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dma->ops->pause_dma(cs5535au);
@@ -318,11 +298,9 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
break;
default:
dev_err(cs5535au->card->dev, "unhandled trigger\n");
- err = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&cs5535au->reg_lock);
- return err;
+ return 0;
}
static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
@@ -359,8 +337,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;
@@ -383,7 +362,6 @@ static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
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,
@@ -394,7 +372,6 @@ static const struct snd_pcm_ops snd_cs5535audio_playback_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,
@@ -442,11 +419,11 @@ int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
pcm->private_data = cs5535au;
pcm->info_flags = 0;
- strcpy(pcm->name, "CS5535 Audio");
+ strscpy(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 82bd10b68a77..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>
@@ -62,7 +48,6 @@ static int __maybe_unused snd_cs5535audio_suspend(struct device *dev)
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];
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
index 15075f89e98a..ff2b1cba3a3c 100644
--- a/sound/pci/ctxfi/Makefile
+++ b/sound/pci/ctxfi/Makefile
@@ -1,4 +1,5 @@
-snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-ctxfi-y := 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 5851249f11d9..05bb006c0f4c 100644
--- a/sound/pci/ctxfi/ct20k1reg.h
+++ b/sound/pci/ctxfi/ct20k1reg.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 CT20K1REG_H
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
index ca501ba03d64..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_
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
index 5fcbb065d870..bb4658592636 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)
@@ -236,7 +231,6 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
int err, i;
unsigned int idx;
struct amixer *amixer;
- unsigned long flags;
*ramixer = NULL;
@@ -248,15 +242,15 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
/* Check whether there are sufficient
* amixer resources to meet request. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- amixer->idx[i] = idx;
+ amixer->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
dev_err(mgr->card->dev,
"Can't meet AMIXER resource request!\n");
@@ -272,32 +266,30 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
return 0;
error:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ }
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
kfree(amixer);
return err;
}
static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < amixer->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < amixer->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ }
amixer_rsc_uninit(amixer);
kfree(amixer);
return 0;
}
-int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr)
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr)
{
int err;
struct amixer_mgr *amixer_mgr;
@@ -326,8 +318,9 @@ error:
return err;
}
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+int amixer_mgr_destroy(void *ptr)
{
+ struct amixer_mgr *amixer_mgr = ptr;
rsc_mgr_uninit(&amixer_mgr->mgr);
kfree(amixer_mgr);
return 0;
@@ -335,16 +328,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)
@@ -392,7 +384,6 @@ static int get_sum_rsc(struct sum_mgr *mgr,
int err, i;
unsigned int idx;
struct sum *sum;
- unsigned long flags;
*rsum = NULL;
@@ -403,15 +394,15 @@ static int get_sum_rsc(struct sum_mgr *mgr,
/* Check whether there are sufficient sum resources to meet request. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- sum->idx[i] = idx;
+ sum->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
dev_err(mgr->card->dev,
"Can't meet SUM resource request!\n");
@@ -427,32 +418,29 @@ static int get_sum_rsc(struct sum_mgr *mgr,
return 0;
error:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+ }
kfree(sum);
return err;
}
static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < sum->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < sum->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+ }
sum_rsc_uninit(sum);
kfree(sum);
return 0;
}
-int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr)
+int sum_mgr_create(struct hw *hw, void **rsum_mgr)
{
int err;
struct sum_mgr *sum_mgr;
@@ -481,8 +469,9 @@ error:
return err;
}
-int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+int sum_mgr_destroy(void *ptr)
{
+ struct sum_mgr *sum_mgr = ptr;
rsc_mgr_uninit(&sum_mgr->mgr);
kfree(sum_mgr);
return 0;
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
index 2de18aa6508d..8fc017da6bda 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
@@ -47,8 +43,8 @@ struct sum_mgr {
};
/* Constructor and destructor of daio resource manager */
-int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr);
-int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+int sum_mgr_create(struct hw *hw, void **ptr);
+int sum_mgr_destroy(void *ptr);
/* Define the descriptor of a amixer resource */
struct amixer_rsc_ops;
@@ -93,7 +89,7 @@ struct amixer_mgr {
};
/* Constructor and destructor of amixer resource manager */
-int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr);
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr);
+int amixer_mgr_destroy(void *amixer_mgr);
#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 908658a00377..14779b383d9e 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
@@ -38,7 +35,8 @@
| (0x10 << 16) \
| ((IEC958_AES3_CON_FS_48000) << 24))
-static struct snd_pci_quirk 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),
@@ -48,7 +46,7 @@ static struct snd_pci_quirk subsys_20k1_list[] = {
{ } /* terminator */
};
-static struct snd_pci_quirk 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,
@@ -67,6 +65,7 @@ static struct snd_pci_quirk subsys_20k2_list[] = {
static const char *ct_subsys_name[NUM_CTCARDS] = {
/* 20k1 models */
+ [CTSB046X] = "SB046x",
[CTSB055X] = "SB055x",
[CTSB073X] = "SB073x",
[CTUAA] = "UAA",
@@ -106,23 +105,20 @@ static struct {
.public_name = "Mixer"}
};
-typedef int (*create_t)(struct hw *, void **);
-typedef int (*destroy_t)(void *);
-
static struct {
int (*create)(struct hw *hw, void **rmgr);
int (*destroy)(void *mgr);
} rsc_mgr_funcs[NUM_RSCTYP] = {
- [SRC] = { .create = (create_t)src_mgr_create,
- .destroy = (destroy_t)src_mgr_destroy },
- [SRCIMP] = { .create = (create_t)srcimp_mgr_create,
- .destroy = (destroy_t)srcimp_mgr_destroy },
- [AMIXER] = { .create = (create_t)amixer_mgr_create,
- .destroy = (destroy_t)amixer_mgr_destroy },
- [SUM] = { .create = (create_t)sum_mgr_create,
- .destroy = (destroy_t)sum_mgr_destroy },
- [DAIO] = { .create = (create_t)daio_mgr_create,
- .destroy = (destroy_t)daio_mgr_destroy }
+ [SRC] = { .create = src_mgr_create,
+ .destroy = src_mgr_destroy },
+ [SRCIMP] = { .create = srcimp_mgr_create,
+ .destroy = srcimp_mgr_destroy },
+ [AMIXER] = { .create = amixer_mgr_create,
+ .destroy = amixer_mgr_destroy },
+ [SUM] = { .create = sum_mgr_create,
+ .destroy = sum_mgr_destroy },
+ [DAIO] = { .create = daio_mgr_create,
+ .destroy = daio_mgr_destroy }
};
static int
@@ -275,7 +271,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
/* 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;
@@ -299,10 +295,10 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
for (i = 0; i < n_amixer; i++) {
amixer = apcm->amixers[i];
- mutex_lock(&atc->atc_mutex);
- amixer->ops->setup(amixer, &src->rsc,
- INIT_VOL, atc->pcm[i+device*2]);
- mutex_unlock(&atc->atc_mutex);
+ scoped_guard(mutex, &atc->atc_mutex) {
+ amixer->ops->setup(amixer, &src->rsc,
+ INIT_VOL, atc->pcm[i+device*2]);
+ }
src = src->ops->next_interleave(src);
if (!src)
src = apcm->src;
@@ -543,18 +539,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;
@@ -819,7 +815,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
/* 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;
@@ -878,7 +874,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
return -ENOENT;
}
- mutex_lock(&atc->atc_mutex);
+ guard(mutex)(&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);
@@ -888,7 +884,6 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
}
if ((rate != atc->pll_rate) && (32000 != rate))
err = atc_pll_init(atc, rate);
- mutex_unlock(&atc->atc_mutex);
return err;
}
@@ -925,13 +920,13 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
}
/* Connect to SPDIFOO */
- mutex_lock(&atc->atc_mutex);
- dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
- amixer = apcm->amixers[0];
- dao->ops->set_left_input(dao, &amixer->rsc);
- amixer = apcm->amixers[1];
- dao->ops->set_right_input(dao, &amixer->rsc);
- mutex_unlock(&atc->atc_mutex);
+ scoped_guard(mutex, &atc->atc_mutex) {
+ dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+ amixer = apcm->amixers[0];
+ dao->ops->set_left_input(dao, &amixer->rsc);
+ amixer = apcm->amixers[1];
+ dao->ops->set_right_input(dao, &amixer->rsc);
+ }
ct_timer_prepare(apcm->timer);
@@ -1119,7 +1114,7 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
struct rsc *rscs[2] = {NULL};
unsigned int spos = 0;
- mutex_lock(&atc->atc_mutex);
+ guard(mutex)(&atc->atc_mutex);
dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
da_dsc.msr = state ? 1 : atc->msr;
da_dsc.passthru = state ? 1 : 0;
@@ -1137,7 +1132,6 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
}
dao->ops->set_spos(dao, spos);
dao->ops->commit_write(dao);
- mutex_unlock(&atc->atc_mutex);
return err;
}
@@ -1285,7 +1279,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
if (p) {
if (p->value < 0) {
dev_err(atc->card->dev,
- "Device %04x:%04x is black-listed\n",
+ "Device %04x:%04x is on the denylist\n",
vendor_id, device_id);
return -ENOENT;
}
@@ -1378,19 +1372,19 @@ static int atc_get_resources(struct ct_atc *atc)
num_daios = ((atc->model == CTSB1270) ? 8 : 7);
num_srcs = ((atc->model == CTSB1270) ? 6 : 4);
- atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL);
+ atc->daios = kcalloc(num_daios, sizeof(void *), GFP_KERNEL);
if (!atc->daios)
return -ENOMEM;
- atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
+ atc->srcs = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL);
if (!atc->srcs)
return -ENOMEM;
- atc->srcimps = kzalloc(sizeof(void *)*num_srcs, 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;
@@ -1548,18 +1542,10 @@ static void atc_connect_resources(struct ct_atc *atc)
#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);
@@ -1666,6 +1652,10 @@ static const struct ct_atc atc_preset = {
* 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.
@@ -1680,7 +1670,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
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;
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 56413343a9e8..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
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index 7f089cb433e1..c0c3f8ab8467 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"
@@ -33,7 +29,7 @@ struct daio_rsc_idx {
unsigned short right;
};
-static 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},
@@ -44,7 +40,7 @@ static struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
};
-static 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},
@@ -55,12 +51,12 @@ static struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[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)
@@ -68,19 +64,19 @@ 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 const struct rsc_ops daio_out_rsc_ops = {
@@ -163,7 +159,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
struct daio *daio = &dao->daio;
int i;
- entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
+ entry = kcalloc(daio->rscl.msr, sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -192,7 +188,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
struct daio *daio = &dao->daio;
int i;
- entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
+ entry = kcalloc(daio->rscr.msr, sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -215,52 +211,30 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
return 0;
}
-static int dao_clear_left_input(struct dao *dao)
+static int dao_clear_input(struct dao *dao, unsigned int start, unsigned int end)
{
- struct imapper *entry;
- struct daio *daio = &dao->daio;
- int i;
+ unsigned int i;
- if (!dao->imappers[0])
+ if (!dao->imappers[start])
return 0;
-
- entry = dao->imappers[0];
- dao->mgr->imap_delete(dao->mgr, entry);
- /* Program conjugate resources */
- for (i = 1; i < daio->rscl.msr; i++) {
- entry = dao->imappers[i];
- dao->mgr->imap_delete(dao->mgr, entry);
+ for (i = start; i < end; i++) {
+ dao->mgr->imap_delete(dao->mgr, dao->imappers[i]);
dao->imappers[i] = NULL;
}
- kfree(dao->imappers[0]);
- dao->imappers[0] = NULL;
-
return 0;
}
-static int dao_clear_right_input(struct dao *dao)
-{
- struct imapper *entry;
- struct daio *daio = &dao->daio;
- int i;
-
- if (!dao->imappers[daio->rscl.msr])
- return 0;
-
- entry = dao->imappers[daio->rscl.msr];
- dao->mgr->imap_delete(dao->mgr, entry);
- /* Program conjugate resources */
- for (i = 1; i < daio->rscr.msr; i++) {
- entry = dao->imappers[daio->rscl.msr + i];
- dao->mgr->imap_delete(dao->mgr, entry);
- dao->imappers[daio->rscl.msr + i] = NULL;
- }
- kfree(dao->imappers[daio->rscl.msr]);
- dao->imappers[daio->rscl.msr] = NULL;
+static int dao_clear_left_input(struct dao *dao)
+{
+ return dao_clear_input(dao, 0, dao->daio.rscl.msr);
+}
- return 0;
+static int dao_clear_right_input(struct dao *dao)
+{
+ return dao_clear_input(dao, dao->daio.rscl.msr,
+ dao->daio.rscl.msr + dao->daio.rscr.msr);
}
static const struct dao_rsc_ops dao_ops = {
@@ -398,7 +372,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;
@@ -528,14 +503,13 @@ static int get_daio_rsc(struct daio_mgr *mgr,
struct daio **rdaio)
{
int err;
- unsigned long flags;
*rdaio = NULL;
/* Check whether there are sufficient daio resources to meet request. */
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
+ }
if (err) {
dev_err(mgr->card->dev,
"Can't meet DAIO resource request!\n");
@@ -576,22 +550,20 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return 0;
error:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- daio_mgr_put_rsc(&mgr->mgr, desc->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ daio_mgr_put_rsc(&mgr->mgr, desc->type);
+ }
return err;
}
static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
{
- unsigned long flags;
-
mgr->daio_disable(mgr, daio);
mgr->commit_write(mgr);
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- daio_mgr_put_rsc(&mgr->mgr, daio->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ daio_mgr_put_rsc(&mgr->mgr, daio->type);
+ }
if (daio->type <= DAIO_OUT_MAX) {
dao_rsc_uninit(container_of(daio, struct dao, daio));
@@ -647,34 +619,26 @@ static int daio_map_op(void *data, struct imapper *entry)
static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
if (!entry->addr && mgr->init_imap_added) {
input_mapper_delete(&mgr->imappers, mgr->init_imap,
daio_map_op, mgr);
mgr->init_imap_added = 0;
}
- err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
-
- return err;
+ return input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
}
static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
if (list_empty(&mgr->imappers)) {
input_mapper_add(&mgr->imappers, mgr->init_imap,
daio_map_op, mgr);
mgr->init_imap_added = 1;
}
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
return err;
}
@@ -687,7 +651,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr)
return 0;
}
-int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr)
+int daio_mgr_create(struct hw *hw, void **rdaio_mgr)
{
int err, i;
struct daio_mgr *daio_mgr;
@@ -741,14 +705,14 @@ error1:
return err;
}
-int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+int daio_mgr_destroy(void *ptr)
{
- unsigned long flags;
+ struct daio_mgr *daio_mgr = ptr;
/* free daio input mapper list */
- spin_lock_irqsave(&daio_mgr->imap_lock, flags);
- free_input_mapper_list(&daio_mgr->imappers);
- spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
+ scoped_guard(spinlock_irqsave, &daio_mgr->imap_lock) {
+ free_input_mapper_list(&daio_mgr->imappers);
+ }
rsc_mgr_uninit(&daio_mgr->mgr);
kfree(daio_mgr);
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
index a30be73b08ea..15147fe5f74a 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
@@ -119,7 +115,7 @@ struct daio_mgr {
};
/* Constructor and destructor of daio resource manager */
-int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr);
-int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+int daio_mgr_create(struct hw *hw, void **ptr);
+int daio_mgr_destroy(void *ptr);
#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
index a689f2552706..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"
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index 54cc9cb75f00..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
@@ -30,8 +26,9 @@ enum CHIPTYP {
enum CTCARDS {
/* 20k1 models */
+ CTSB046X,
+ CT20K1_MODEL_FIRST = CTSB046X,
CTSB055X,
- CT20K1_MODEL_FIRST = CTSB055X,
CTSB073X,
CTUAA,
CT20K1_UNKNOWN,
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 8e6eb9d7984b..42b90c9b2ee9 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>
@@ -172,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;
}
@@ -498,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;
}
@@ -519,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;
}
@@ -706,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;
}
@@ -913,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;
}
@@ -962,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;
}
@@ -1160,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;
}
@@ -1319,7 +1315,7 @@ 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) {
dev_alert(hw->card->dev, "PLL initialization failed!!!\n");
@@ -1407,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);
@@ -1905,12 +1901,8 @@ static int hw_card_start(struct hw *hw)
return err;
/* Set DMA transfer mask */
- if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
- } else {
- dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(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));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -1924,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)
@@ -1941,6 +1933,7 @@ static int hw_card_start(struct hw *hw)
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
@@ -1966,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;
}
@@ -2030,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) {
@@ -2101,57 +2091,30 @@ static int hw_resume(struct hw *hw, struct card_conf *info)
static u32 hw_read_20kx(struct hw *hw, u32 reg)
{
- u32 value;
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_20k1_lock);
outl(reg, hw->io_base + 0x0);
- value = inl(hw->io_base + 0x4);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
-
- return value;
+ return inl(hw->io_base + 0x4);
}
static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
{
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_20k1_lock);
outl(reg, hw->io_base + 0x0);
outl(data, hw->io_base + 0x4);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
-
}
static u32 hw_read_pci(struct hw *hw, u32 reg)
{
- u32 value;
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_pci_lock);
outl(reg, hw->io_base + 0x10);
- value = inl(hw->io_base + 0x14);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
-
- return value;
+ return inl(hw->io_base + 0x14);
}
static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
{
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_pci_lock);
outl(reg, hw->io_base + 0x10);
outl(data, hw->io_base + 0x14);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
}
static const struct hw ct20k1_preset = {
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 b866d6b2c923..55af8ef29838 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.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 cthw20k2.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 14 2008
- *
*/
#include <linux/types.h>
@@ -995,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;
@@ -1316,12 +1312,12 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
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_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);
@@ -1584,7 +1580,7 @@ static void hw_dac_stop(struct hw *hw)
data = hw_read_20kx(hw, GPIO_DATA);
data &= 0xFFFFFFFD;
hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
+ usleep_range(10000, 11000);
}
static void hw_dac_start(struct hw *hw)
@@ -1593,7 +1589,7 @@ static void hw_dac_start(struct hw *hw)
data = hw_read_20kx(hw, GPIO_DATA);
data |= 0x2;
hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ msleep(50);
}
static void hw_dac_reset(struct hw *hw)
@@ -1864,11 +1860,11 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
hw_write_20kx(hw, GPIO_DATA, data);
}
- mdelay(10);
+ 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, */
@@ -2030,12 +2026,8 @@ static int hw_card_start(struct hw *hw)
return err;
/* Set DMA transfer mask */
- if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
- } else {
- dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(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));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -2065,6 +2057,7 @@ static int hw_card_start(struct hw *hw)
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
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 4f4a2a5dedb8..496682613db5 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
- *
*/
@@ -910,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;
@@ -937,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);
}
@@ -971,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);
}
@@ -1221,7 +1219,7 @@ int ct_alsa_mix_create(struct ct_atc *atc,
if (err)
return err;
- strcpy(atc->card->mixername, device_name);
+ strscpy(atc->card->mixername, device_name);
return 0;
}
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
index be881c639fee..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
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index b36e37ae2b51..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"
@@ -182,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)
@@ -200,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;
}
@@ -377,26 +367,22 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
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 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[] = {
@@ -447,7 +433,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
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);
@@ -455,8 +441,8 @@ 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) {
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 80c4d84f9667..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,18 +109,17 @@ 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 const struct rsc_ops rsc_generic_ops = {
@@ -213,7 +208,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
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;
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
index 736d9f7e9e16..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
@@ -43,8 +39,8 @@ struct rsc {
};
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);
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
index bb4c9c3c89ae..46afc9604c08 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,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#include "ctsrc.h"
@@ -418,18 +414,16 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
unsigned int idx = SRC_RESOURCE_NUM;
int err;
struct src *src;
- unsigned long flags;
*rsrc = NULL;
/* Check whether there are sufficient src resources to meet request. */
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- if (MEMRD == desc->mode)
- err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
- else
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ if (MEMRD == desc->mode)
+ err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
+ else
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ }
if (err) {
dev_err(mgr->card->dev,
"Can't meet SRC resource request!\n");
@@ -458,29 +452,25 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
error2:
kfree(src);
error1:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- if (MEMRD == desc->mode)
- mgr_put_resource(&mgr->mgr, desc->multi, idx);
- else
- mgr_put_resource(&mgr->mgr, 1, idx);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ if (MEMRD == desc->mode)
+ mgr_put_resource(&mgr->mgr, desc->multi, idx);
+ else
+ mgr_put_resource(&mgr->mgr, 1, idx);
+ }
return err;
}
static int put_src_rsc(struct src_mgr *mgr, struct src *src)
{
- unsigned long flags;
-
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- src->rsc.ops->master(&src->rsc);
- if (MEMRD == src->mode)
- mgr_put_resource(&mgr->mgr, src->multi,
- src->rsc.ops->index(&src->rsc));
- else
- mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ src->rsc.ops->master(&src->rsc);
+ if (MEMRD == src->mode)
+ mgr_put_resource(&mgr->mgr, src->multi,
+ src->rsc.ops->index(&src->rsc));
+ else
+ mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
+ }
src_rsc_uninit(src, mgr);
kfree(src);
@@ -544,7 +534,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr)
return 0;
}
-int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr)
+int src_mgr_create(struct hw *hw, void **rsrc_mgr)
{
int err, i;
struct src_mgr *src_mgr;
@@ -584,8 +574,9 @@ error1:
return err;
}
-int src_mgr_destroy(struct src_mgr *src_mgr)
+int src_mgr_destroy(void *ptr)
{
+ struct src_mgr *src_mgr = ptr;
rsc_mgr_uninit(&src_mgr->mgr);
kfree(src_mgr);
@@ -594,16 +585,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)
@@ -679,7 +669,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;
@@ -718,7 +708,6 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
int err, i;
unsigned int idx;
struct srcimp *srcimp;
- unsigned long flags;
*rsrcimp = NULL;
@@ -729,15 +718,15 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
/* Check whether there are sufficient SRCIMP resources. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- srcimp->idx[i] = idx;
+ srcimp->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
dev_err(mgr->card->dev,
"Can't meet SRCIMP resource request!\n");
@@ -753,25 +742,22 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
return 0;
error1:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+ }
kfree(srcimp);
return err;
}
static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < srcimp->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < srcimp->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+ }
srcimp_rsc_uninit(srcimp);
kfree(srcimp);
@@ -794,39 +780,31 @@ static int srcimp_map_op(void *data, struct imapper *entry)
static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
if ((0 == entry->addr) && (mgr->init_imap_added)) {
input_mapper_delete(&mgr->imappers,
mgr->init_imap, srcimp_map_op, mgr);
mgr->init_imap_added = 0;
}
- err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
-
- return err;
+ return input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
}
static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
if (list_empty(&mgr->imappers)) {
input_mapper_add(&mgr->imappers, mgr->init_imap,
srcimp_map_op, mgr);
mgr->init_imap_added = 1;
}
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
return err;
}
-int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr)
+int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr)
{
int err;
struct srcimp_mgr *srcimp_mgr;
@@ -871,14 +849,14 @@ error1:
return err;
}
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+int srcimp_mgr_destroy(void *ptr)
{
- unsigned long flags;
+ struct srcimp_mgr *srcimp_mgr = ptr;
/* free src input mapper list */
- spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
- free_input_mapper_list(&srcimp_mgr->imappers);
- spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
+ scoped_guard(spinlock_irqsave, &srcimp_mgr->imap_lock) {
+ free_input_mapper_list(&srcimp_mgr->imappers);
+ }
rsc_mgr_uninit(&srcimp_mgr->mgr);
kfree(srcimp_mgr);
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
index 92944a012bc4..e6366cc6a7ae 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
@@ -143,10 +139,10 @@ struct srcimp_mgr {
};
/* Constructor and destructor of SRC resource manager */
-int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr);
-int src_mgr_destroy(struct src_mgr *src_mgr);
+int src_mgr_create(struct hw *hw, void **ptr);
+int src_mgr_destroy(void *ptr);
/* Constructor and destructor of SRCIMP resource manager */
-int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr);
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+int srcimp_mgr_create(struct hw *hw, void **ptr);
+int srcimp_mgr_destroy(void *ptr);
#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index 08e874e9a7f6..609b10320ff7 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>
@@ -17,7 +14,7 @@
static bool use_system_timer;
MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
-module_param(use_system_timer, bool, S_IRUGO);
+module_param(use_system_timer, bool, 0444);
struct ct_timer_ops {
void (*init)(struct ct_timer_instance *);
@@ -65,13 +62,12 @@ struct ct_timer {
static void ct_systimer_callback(struct timer_list *t)
{
- struct ct_timer_instance *ti = from_timer(ti, t, timer);
+ struct ct_timer_instance *ti = timer_container_of(ti, t, timer);
struct snd_pcm_substream *substream = ti->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ct_atc_pcm *apcm = ti->apcm;
unsigned int period_size = runtime->period_size;
unsigned int buffer_size = runtime->buffer_size;
- unsigned long flags;
unsigned int position, dist, interval;
position = substream->ops->pointer(substream);
@@ -85,10 +81,9 @@ static void ct_systimer_callback(struct timer_list *t)
* at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
interval = ((period_size - (position % period_size))
* HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
if (ti->running)
mod_timer(&ti->timer, jiffies + interval);
- spin_unlock_irqrestore(&ti->lock, flags);
}
static void ct_systimer_init(struct ct_timer_instance *ti)
@@ -99,30 +94,25 @@ static void ct_systimer_init(struct ct_timer_instance *ti)
static void ct_systimer_start(struct ct_timer_instance *ti)
{
struct snd_pcm_runtime *runtime = ti->substream->runtime;
- unsigned long flags;
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
ti->running = 1;
mod_timer(&ti->timer,
jiffies + (runtime->period_size * HZ +
(runtime->rate - 1)) / runtime->rate);
- spin_unlock_irqrestore(&ti->lock, flags);
}
static void ct_systimer_stop(struct ct_timer_instance *ti)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
ti->running = 0;
- del_timer(&ti->timer);
- spin_unlock_irqrestore(&ti->lock, flags);
+ timer_delete(&ti->timer);
}
static void ct_systimer_prepare(struct ct_timer_instance *ti)
{
ct_systimer_stop(ti);
- try_to_del_timer_sync(&ti->timer);
+ timer_delete_sync_try(&ti->timer);
}
#define ct_systimer_free ct_systimer_prepare
@@ -232,25 +222,22 @@ static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
static void ct_xfitimer_check_period(struct ct_timer *atimer)
{
struct ct_timer_instance *ti;
- unsigned long flags;
- spin_lock_irqsave(&atimer->list_lock, flags);
+ guard(spinlock_irqsave)(&atimer->list_lock);
list_for_each_entry(ti, &atimer->instance_head, instance_list) {
if (ti->running && ti->need_update) {
ti->need_update = 0;
ti->apcm->interrupt(ti->apcm);
}
}
- spin_unlock_irqrestore(&atimer->list_lock, flags);
}
/* Handle timer-interrupt */
static void ct_xfitimer_callback(struct ct_timer *atimer)
{
int update;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
+ guard(spinlock_irqsave)(&atimer->lock);
atimer->irq_handling = 1;
do {
update = ct_xfitimer_reprogram(atimer, 1);
@@ -260,7 +247,6 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
spin_lock(&atimer->lock);
} while (atimer->reprogram);
atimer->irq_handling = 0;
- spin_unlock_irqrestore(&atimer->lock, flags);
}
static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
@@ -274,45 +260,39 @@ static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
/* start/stop the timer */
static void ct_xfitimer_update(struct ct_timer *atimer)
{
- unsigned long flags;
-
- spin_lock_irqsave(&atimer->lock, flags);
+ guard(spinlock_irqsave)(&atimer->lock);
if (atimer->irq_handling) {
/* reached from IRQ handler; let it handle later */
atimer->reprogram = 1;
- spin_unlock_irqrestore(&atimer->lock, flags);
return;
}
ct_xfitimer_irq_stop(atimer);
ct_xfitimer_reprogram(atimer, 0);
- spin_unlock_irqrestore(&atimer->lock, flags);
}
static void ct_xfitimer_start(struct ct_timer_instance *ti)
{
struct ct_timer *atimer = ti->timer_base;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
- if (list_empty(&ti->running_list))
- atimer->wc = ct_xfitimer_get_wc(atimer);
- ti->running = 1;
- ti->need_update = 0;
- list_add(&ti->running_list, &atimer->running_head);
- spin_unlock_irqrestore(&atimer->lock, flags);
+ scoped_guard(spinlock_irqsave, &atimer->lock) {
+ if (list_empty(&ti->running_list))
+ atimer->wc = ct_xfitimer_get_wc(atimer);
+ ti->running = 1;
+ ti->need_update = 0;
+ list_add(&ti->running_list, &atimer->running_head);
+ }
ct_xfitimer_update(atimer);
}
static void ct_xfitimer_stop(struct ct_timer_instance *ti)
{
struct ct_timer *atimer = ti->timer_base;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
- list_del_init(&ti->running_list);
- ti->running = 0;
- spin_unlock_irqrestore(&atimer->lock, flags);
+ scoped_guard(spinlock_irqsave, &atimer->lock) {
+ list_del_init(&ti->running_list);
+ ti->running = 0;
+ }
ct_xfitimer_update(atimer);
}
@@ -350,9 +330,9 @@ ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
if (atimer->ops->init)
atimer->ops->init(ti);
- spin_lock_irq(&atimer->list_lock);
- list_add(&ti->instance_list, &atimer->instance_head);
- spin_unlock_irq(&atimer->list_lock);
+ scoped_guard(spinlock_irq, &atimer->list_lock) {
+ list_add(&ti->instance_list, &atimer->instance_head);
+ }
return ti;
}
@@ -385,9 +365,9 @@ void ct_timer_instance_free(struct ct_timer_instance *ti)
if (atimer->ops->free_instance)
atimer->ops->free_instance(ti);
- spin_lock_irq(&atimer->list_lock);
- list_del(&ti->instance_list);
- spin_unlock_irq(&atimer->list_lock);
+ scoped_guard(spinlock_irq, &atimer->list_lock) {
+ list_del(&ti->instance_list);
+ }
kfree(ti);
}
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
index 520e19bc649f..823d6e240a07 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
@@ -32,7 +29,7 @@
static struct ct_vm_block *
get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
{
- struct ct_vm_block *block = NULL, *entry;
+ struct ct_vm_block *block, *entry;
struct list_head *pos;
size = CT_PAGE_ALIGN(size);
@@ -42,26 +39,25 @@ get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
return NULL;
}
- mutex_lock(&vm->lock);
+ guard(mutex)(&vm->lock);
list_for_each(pos, &vm->unused) {
entry = list_entry(pos, struct ct_vm_block, list);
if (entry->size >= size)
break; /* found a block that is big enough */
}
if (pos == &vm->unused)
- goto out;
+ return NULL;
if (entry->size == size) {
/* Move the vm node from unused list to used list directly */
list_move(&entry->list, &vm->used);
vm->size -= size;
- block = entry;
- goto out;
+ return entry;
}
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
- goto out;
+ return NULL;
block->addr = entry->addr;
block->size = size;
@@ -70,8 +66,6 @@ get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
entry->size -= size;
vm->size -= size;
- out:
- mutex_unlock(&vm->lock);
return block;
}
@@ -82,7 +76,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
block->size = CT_PAGE_ALIGN(block->size);
- mutex_lock(&vm->lock);
+ guard(mutex)(&vm->lock);
list_del(&block->list);
vm->size += block->size;
@@ -119,7 +113,6 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
pos = pre;
pre = pos->prev;
}
- mutex_unlock(&vm->lock);
}
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
@@ -186,7 +179,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 e6da60eb19ce..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
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index f2f32779de98..d8dd84d41c87 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -1,11 +1,8 @@
+// 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>
@@ -21,14 +18,13 @@
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;
@@ -102,8 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (err < 0)
goto error;
- strcpy(card->driver, "SB-XFi");
- strcpy(card->shortname, "Creative X-Fi");
+ strscpy(card->driver, "SB-XFi");
+ strscpy(card->shortname, "Creative X-Fi");
snprintf(card->longname, sizeof(card->longname), "%s %s %s",
card->shortname, atc->chip_name, atc->model_name);
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
index 4865b8fe7434..96667641c7cf 100644
--- a/sound/pci/echoaudio/Makefile
+++ b/sound/pci/echoaudio/Makefile
@@ -4,20 +4,20 @@
# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
#
-snd-darla20-objs := darla20.o
-snd-gina20-objs := gina20.o
-snd-layla20-objs := layla20.o
-snd-darla24-objs := darla24.o
-snd-gina24-objs := gina24.o
-snd-layla24-objs := layla24.o
-snd-mona-objs := mona.o
-snd-mia-objs := mia.o
-snd-echo3g-objs := echo3g.o
-snd-indigo-objs := indigo.o
-snd-indigoio-objs := indigoio.o
-snd-indigodj-objs := indigodj.o
-snd-indigoiox-objs := indigoiox.o
-snd-indigodjx-objs := indigodjx.o
+snd-darla20-y := darla20.o
+snd-gina20-y := gina20.o
+snd-layla20-y := layla20.o
+snd-darla24-y := darla24.o
+snd-gina24-y := gina24.o
+snd-layla24-y := layla24.o
+snd-mona-y := mona.o
+snd-mia-y := mia.o
+snd-echo3g-y := echo3g.o
+snd-indigo-y := indigo.o
+snd-indigoio-y := indigoio.o
+snd-indigodj-y := indigodj.o
+snd-indigoiox-y := indigoiox.o
+snd-indigodjx-y := indigodjx.o
obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
obj-$(CONFIG_SND_GINA20) += snd-gina20.o
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index c95da6301677..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
@@ -68,7 +56,7 @@ static const struct pci_device_id 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/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
index 320837ba7bab..0356efad7528 100644
--- a/sound/pci/echoaudio/darla20_dsp.c
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -36,7 +36,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw: could not initialize DSP comm page\n");
return err;
@@ -53,7 +54,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index 3013b4daa19e..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
@@ -73,7 +61,7 @@ static const struct pci_device_id 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/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
index 8736b5e81ca3..b96300772aee 100644
--- a/sound/pci/echoaudio/darla24_dsp.c
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -36,7 +36,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw: could not initialize DSP comm page\n");
return err;
@@ -52,7 +53,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 1f34a07b0b19..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
@@ -86,7 +74,7 @@ static const struct pci_device_id 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/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 6deb80c42f11..9e1f2cad0909 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -49,7 +49,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index d68f99e076a8..f2c8602a1ad7 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1,27 +1,16 @@
+// 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>
+#include <linux/string.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;
@@ -35,7 +24,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);
@@ -46,7 +35,6 @@ static int get_firmware(const struct firmware **fw_entry,
int err;
char name[30];
-#ifdef CONFIG_PM_SLEEP
if (chip->fw_cache[fw_index]) {
dev_dbg(chip->card->dev,
"firmware requested: %s is cached\n",
@@ -54,19 +42,16 @@ static int get_firmware(const struct firmware **fw_entry,
*fw_entry = chip->fw_cache[fw_index];
return 0;
}
-#endif
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)
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
return err;
}
@@ -75,18 +60,13 @@ static int get_firmware(const struct firmware **fw_entry,
static void free_firmware(const struct firmware *fw_entry,
struct echoaudio *chip)
{
-#ifdef CONFIG_PM_SLEEP
dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
-#else
- release_firmware(fw_entry);
-#endif
}
static void free_firmware_cache(struct echoaudio *chip)
{
-#ifdef CONFIG_PM_SLEEP
int i;
for (i = 0; i < 8 ; i++)
@@ -94,8 +74,6 @@ static void free_firmware_cache(struct echoaudio *chip)
release_firmware(chip->fw_cache[i]);
dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
}
-
-#endif
}
@@ -257,13 +235,19 @@ 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;
+
+ guard(mutex)(&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;
+
+ return err;
}
@@ -306,42 +290,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) {
+ /* 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;
}
@@ -352,25 +351,23 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err;
- 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;
- dev_dbg(chip->card->dev, "pcm_analog_in_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate);
+
return 0;
}
@@ -386,26 +383,24 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream)
#else
max_channels = num_analog_busses_out(chip);
#endif
- 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;
- dev_dbg(chip->card->dev, "pcm_analog_out_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate);
+
return 0;
}
@@ -419,7 +414,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
int err, max_channels;
max_channels = num_digital_busses_in(chip) - substream->number;
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else /* If the card has ADAT, subtract the 6 channels
@@ -428,26 +423,22 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
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)
- 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)
- 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;
+ return err;
+
+ 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;
+ 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;
+
+ return 0;
}
@@ -460,7 +451,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
int err, max_channels;
max_channels = num_digital_busses_out(chip) - substream->number;
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else /* If the card has ADAT, subtract the 6 channels
@@ -469,26 +460,24 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
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)
- 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)
- 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;
+ return err;
+
+ 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;
+ 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;
+
+ return 0;
}
#endif /* !ECHOCARD_HAS_VMIXER */
@@ -500,22 +489,27 @@ 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
*/
- atomic_dec(&chip->opencount);
- oc = atomic_read(&chip->opencount);
- dev_dbg(chip->card->dev, "pcm_close oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate, chip->rate_set);
- if (oc < 2)
+ guard(mutex)(&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;
- dev_dbg(chip->card->dev, "pcm_close2 oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate, chip->rate_set);
+ break;
+ }
return 0;
}
@@ -537,38 +531,27 @@ static int init_engine(struct snd_pcm_substream *substream,
/* Sets up che hardware. If it's already initialized, reset and
* redo with the new parameters
*/
- spin_lock_irq(&chip->lock);
- if (pipe->index >= 0) {
- 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;
- }
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (pipe->index >= 0) {
+ 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;
+ }
- err = allocate_pipes(chip, pipe, pipe_index, interleave);
- if (err < 0) {
- spin_unlock_irq(&chip->lock);
- dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
- pipe_index, err);
- return err;
+ err = allocate_pipes(chip, pipe, pipe_index, interleave);
+ if (err < 0) {
+ dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
+ pipe_index, err);
+ return err;
+ }
}
- spin_unlock_irq(&chip->lock);
dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index);
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) {
- dev_err(chip->card->dev, "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;
- }
sglist_init(chip, pipe);
edge = PAGE_SIZE;
@@ -604,15 +587,14 @@ 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();
chip->substream[pipe_index] = substream;
chip->rate_set = 1;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
- spin_unlock_irq(&chip->lock);
return 0;
}
@@ -674,16 +656,14 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
chip = snd_pcm_substream_chip(substream);
pipe = (struct audiopipe *) substream->runtime->private_data;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (pipe->index >= 0) {
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);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -713,6 +693,7 @@ 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;
@@ -725,9 +706,19 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
if (snd_BUG_ON(pipe_index >= px_num(chip)))
return -EINVAL;
+
+ /*
+ * We passed checks we can do independently; now take
+ * exclusive control
+ */
+
+ guard(spinlock_irq)(&chip->lock);
+
if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index)))
return -EINVAL;
+
set_audio_format(chip, pipe_index, &format);
+
return 0;
}
@@ -736,8 +727,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;
@@ -751,7 +741,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
}
- spin_lock(&chip->lock);
+ guard(spinlock)(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
@@ -761,10 +751,11 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
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;
@@ -798,7 +789,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
default:
err = -EINVAL;
}
- spin_unlock(&chip->lock);
return err;
}
@@ -808,19 +798,26 @@ 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);
}
@@ -829,49 +826,41 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
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 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 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 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 */
@@ -880,21 +869,17 @@ static const 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);
}
@@ -913,28 +898,28 @@ static int 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);
+ strscpy(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;
+ 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);
+ strscpy(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;
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#else /* ECHOCARD_HAS_VMIXER */
@@ -945,31 +930,31 @@ static int 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);
+ strscpy(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;
+ 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);
+ strscpy(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;
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#endif /* ECHOCARD_HAS_VMIXER */
@@ -1020,7 +1005,7 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_busses_out(chip); c++) {
gain = ucontrol->value.integer.value[c];
/* Ignore out of range values */
@@ -1033,7 +1018,6 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
@@ -1101,7 +1085,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
gain = ucontrol->value.integer.value[c];
/* Ignore out of range values */
@@ -1114,7 +1098,6 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_input_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
@@ -1170,7 +1153,7 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_out(chip); c++) {
if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
set_nominal_level(chip, c,
@@ -1180,7 +1163,6 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
@@ -1233,7 +1215,7 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
if (chip->nominal_level[bx_analog_in(chip) + c] !=
ucontrol->value.integer.value[c]) {
@@ -1246,7 +1228,6 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
update_output_line_level(chip); /* "Output" is not a mistake
* here.
*/
- spin_unlock_irq(&chip->lock);
return changed;
}
@@ -1268,15 +1249,10 @@ static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
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;
}
@@ -1311,10 +1287,9 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
if (chip->monitor_gain[out][in] != gain) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_monitor_gain(chip, out, in, gain);
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
changed = 1;
}
return changed;
@@ -1340,15 +1315,10 @@ static struct snd_kcontrol_new snd_echo_monitor_mixer = {
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;
}
@@ -1379,10 +1349,9 @@ static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
update_vmixer_level(chip);
- spin_unlock_irq(&chip->lock);
changed = 1;
}
return changed;
@@ -1452,12 +1421,12 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
if (dmode != chip->digital_mode) {
/* mode_mutex is required to make this operation atomic wrt
pcm_digital_*_open() and set_input_clock() functions. */
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
/* Do not allow the user to change the digital mode when a pcm
device is open because it also changes the number of channels
and the allowed sample rates */
- if (atomic_read(&chip->opencount)) {
+ if (chip->opencount) {
changed = -EAGAIN;
} else {
changed = set_digital_mode(chip, dmode);
@@ -1472,7 +1441,6 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
if (changed >= 0)
changed = 1; /* No errors */
}
- mutex_unlock(&chip->mode_mutex);
}
return changed;
}
@@ -1519,9 +1487,8 @@ static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
chip = snd_kcontrol_chip(kcontrol);
mode = !!ucontrol->value.enumerated.item[0];
if (mode != chip->professional_spdif) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_professional_spdif(chip, mode);
- spin_unlock_irq(&chip->lock);
return 1;
}
return 0;
@@ -1585,12 +1552,11 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
dclock = chip->clock_source_list[eclock];
if (chip->input_clock != dclock) {
- mutex_lock(&chip->mode_mutex);
- spin_lock_irq(&chip->lock);
- if ((changed = set_input_clock(chip, dclock)) == 0)
+ guard(mutex)(&chip->mode_mutex);
+ guard(spinlock_irq)(&chip->lock);
+ 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)
@@ -1634,9 +1600,8 @@ static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
power = !!ucontrol->value.integer.value[0];
if (chip->phantom_power != power) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
changed = set_phantom_power(chip, power);
- spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1; /* no errors */
}
@@ -1677,9 +1642,8 @@ static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
automute = !!ucontrol->value.integer.value[0];
if (chip->digital_in_automute != automute) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
changed = set_input_auto_mute(chip, automute);
- spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1; /* no errors */
}
@@ -1707,9 +1671,8 @@ static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_meters_on(chip, ucontrol->value.integer.value[0]);
- spin_unlock_irq(&chip->lock);
return 1;
}
@@ -1731,13 +1694,6 @@ static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
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;
}
@@ -1816,14 +1772,43 @@ static const struct snd_kcontrol_new snd_echo_channels_info = {
/******************************************************************************
- 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);
@@ -1834,17 +1819,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);
@@ -1865,146 +1846,102 @@ 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)
{
+ struct echoaudio *chip = card->private_data;
+
if (chip->comm_page)
rest_in_peace(chip);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->comm_page)
- snd_dma_free_pages(&chip->commpage_dma_buf);
-
- iounmap(chip->dsp_registers);
- release_and_free_resource(chip->iores);
- pci_disable_device(chip->pci);
-
/* release chip data */
free_firmware_cache(chip);
- kfree(chip);
- return 0;
-}
-
-
-
-static int snd_echo_dev_free(struct snd_device *device)
-{
- struct echoaudio *chip = device->device_data;
-
- return snd_echo_free(chip);
}
-
-
/* <--snd_echo_probe() */
static int snd_echo_create(struct snd_card *card,
- struct pci_dev *pci,
- struct echoaudio **rchip)
+ 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;
- }
- dev_dbg(card->dev, "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 = pcim_request_all_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) {
- dev_err(chip->card->dev, "cannot get memory region\n");
- snd_echo_free(chip);
- 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,
KBUILD_MODNAME, chip)) {
dev_err(chip->card->dev, "cannot grab irq\n");
- snd_echo_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
+ 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) {
- dev_err(chip->card->dev, "cannot allocate the comm page\n");
- snd_echo_free(chip);
+ 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) {
dev_err(card->dev, "init_hw err=%d\n", err);
- snd_echo_free(chip);
return err;
}
- 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 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;
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2013,20 +1950,18 @@ static int snd_echo_probe(struct pci_dev *pci,
return -ENOENT;
}
- i = 0;
- err = snd_card_new(&pci->dev, 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 = 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);
+ strscpy(card->driver, "Echo_" ECHOCARD_NAME);
+ strscpy(card->shortname, chip->card_name);
dsp = "56301";
if (pci_id->device == 0x3410)
@@ -2036,17 +1971,17 @@ static int 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) {
+ err = snd_echo_new_pcm(chip);
+ if (err < 0) {
dev_err(chip->card->dev, "new pcm error %d\n", err);
- snd_card_free(card);
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) {
+ err = snd_echo_midi_create(card, chip);
+ if (err < 0) {
dev_err(chip->card->dev, "new midi error %d\n", err);
- snd_card_free(card);
return err;
}
}
@@ -2054,139 +1989,146 @@ static int 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 */
chip->num_digital_modes = 0;
- for (i = 0; i < 6; i++)
+ for (int i = 0; i < 6; i++)
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
/* Creates a list of available clock sources */
chip->num_clock_sources = 0;
- for (i = 0; i < 10; i++)
+ for (int i = 0; i < 10; i++)
if (chip->input_clock_types & (1 << i))
chip->clock_source_list[chip->num_clock_sources++] = i;
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;
+ return err;
dev_info(card->dev, "Card registered: %s\n", card->longname);
pci_set_drvdata(pci, chip);
dev++;
return 0;
-
-ctl_error:
- dev_err(card->dev, "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_SLEEP)
-
static int snd_echo_suspend(struct device *dev)
{
struct echoaudio *chip = dev_get_drvdata(dev);
- snd_pcm_suspend_all(chip->analog_pcm);
- snd_pcm_suspend_all(chip->digital_pcm);
-
#ifdef ECHOCARD_HAS_MIDI
/* This call can sleep */
if (chip->midi_out)
snd_echo_midi_output_trigger(chip->midi_out, 0);
#endif
- spin_lock_irq(&chip->lock);
- if (wait_handshake(chip)) {
- spin_unlock_irq(&chip->lock);
- return -EIO;
- }
- clear_handshake(chip);
- if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
- spin_unlock_irq(&chip->lock);
- return -EIO;
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0)
+ return -EIO;
}
- spin_unlock_irq(&chip->lock);
chip->dsp_code = NULL;
free_irq(chip->irq, chip);
chip->irq = -1;
+ chip->card->sync_irq = -1;
return 0;
}
@@ -2200,17 +2142,15 @@ static int snd_echo_resume(struct device *dev)
u32 pipe_alloc_mask;
int err;
- commpage_bak = kmalloc(sizeof(*commpage), 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(*commpage));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) {
kfree(commpage_bak);
dev_err(dev, "resume init_hw err=%d\n", err);
- snd_echo_free(chip);
return err;
}
@@ -2237,10 +2177,10 @@ static int snd_echo_resume(struct device *dev)
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
KBUILD_MODNAME, chip)) {
dev_err(chip->card->dev, "cannot grab irq\n");
- snd_echo_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
+ chip->card->sync_irq = chip->irq;
dev_dbg(dev, "resume irq=%d\n", chip->irq);
#ifdef ECHOCARD_HAS_MIDI
@@ -2253,23 +2193,7 @@ static int snd_echo_resume(struct device *dev)
return 0;
}
-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 */
-
-
-static void snd_echo_remove(struct pci_dev *pci)
-{
- struct echoaudio *chip;
-
- chip = pci_get_drvdata(pci);
- if (chip)
- snd_card_free(chip->card);
-}
-
-
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
/******************************************************************************
Everything starts and ends here
@@ -2280,9 +2204,8 @@ static struct pci_driver echo_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_echo_ids,
.probe = snd_echo_probe,
- .remove = snd_echo_remove,
.driver = {
- .pm = SND_ECHO_PM_OPS,
+ .pm = &snd_echo_pm,
},
};
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 152ce158583c..511f2fcc0fb9 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -294,11 +294,16 @@
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
@@ -332,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;
@@ -344,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;
@@ -353,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
@@ -415,12 +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_SLEEP
const struct firmware *fw_cache[8]; /* Cached firmwares */
-#endif
#ifdef ECHOCARD_HAS_MIDI
u16 mtc_state; /* State for MIDI input parsing state machine */
@@ -559,10 +561,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 22c786b8a889..c9ee98ea3c71 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -73,19 +73,21 @@ 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;
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);
}
@@ -117,7 +119,7 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
* updated by the DSP comm object. */
if (err >= 0 && previous_mode != mode &&
(previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (o = 0; o < num_busses_out(chip); o++)
for (i = 0; i < num_busses_in(chip); i++)
set_monitor_gain(chip, o, i,
@@ -132,7 +134,6 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
for (o = 0; o < num_busses_out(chip); o++)
set_output_gain(chip, o, chip->output_gain[o]);
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
}
return err;
@@ -272,7 +273,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->digital_mode == DIGITAL_MODE_ADAT))
return -EINVAL;
- clock = 0;
control_reg = le32_to_cpu(chip->comm_page->control_register);
control_reg &= E3G_CLOCK_CLEAR_MASK;
@@ -395,7 +395,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) {
chip->sample_rate = 48000;
@@ -421,7 +421,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
}
err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
- spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index 15aae2fad8e4..2a40091d472c 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -349,7 +349,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
/* 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
@@ -495,7 +496,8 @@ static int load_firmware(struct echoaudio *chip)
/* 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;
@@ -509,7 +511,8 @@ static int load_firmware(struct echoaudio *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;
@@ -635,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;
}
@@ -673,13 +670,14 @@ static int restore_dsp_rettings(struct echoaudio *chip)
{
int i, o, err;
- 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++) {
@@ -904,7 +902,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- dev_warn(chip->card->dev, "pause_transport: No pipes to stop!\n");
+ dev_dbg(chip->card->dev, "pause_transport: No pipes to stop!\n");
return 0;
}
@@ -930,7 +928,7 @@ static int stop_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- dev_warn(chip->card->dev, "stop_transport: No pipes to stop!\n");
+ dev_dbg(chip->card->dev, "stop_transport: No pipes to stop!\n");
return 0;
}
@@ -989,7 +987,7 @@ static int init_dsp_comm_page(struct echoaudio *chip)
/* 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);
@@ -1058,7 +1056,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
{
int i;
u32 channel_mask;
- char is_cyclic;
dev_dbg(chip->card->dev,
"allocate_pipes: ch=%d int=%d\n", pipe_index, interleave);
@@ -1066,8 +1063,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
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) {
@@ -1078,8 +1073,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
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;
@@ -1087,7 +1082,7 @@ 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;
return pipe_index;
}
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 834b39e97db7..248983fa2959 100644
--- a/sound/pci/echoaudio/echoaudio_gml.c
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -63,6 +63,8 @@ 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;
@@ -72,11 +74,11 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force)
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);
}
@@ -192,7 +194,8 @@ 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;
dev_dbg(chip->card->dev, "set_professional_spdif to %s\n",
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index 67bd0c9fc342..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
@@ -72,7 +60,7 @@ static const struct pci_device_id 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/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index b2377573de09..c93939850357 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -40,7 +40,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -58,7 +59,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index b1bcacaef257..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
@@ -93,7 +81,7 @@ static const struct pci_device_id 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 8eff2b4f5ceb..78fbac9f3eac 100644
--- a/sound/pci/echoaudio/gina24_dsp.c
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -44,7 +44,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -74,7 +75,8 @@ 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;
@@ -303,7 +305,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
@@ -334,7 +336,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
}
err = write_control_reg(chip, control_reg, true);
- spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 175af9b1435f..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
@@ -73,7 +61,7 @@ static const struct pci_device_id 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/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index c97dc83bbbdf..16eb082df56a 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -41,7 +41,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -56,7 +57,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 8c60314e4901..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
@@ -73,7 +61,7 @@ static const struct pci_device_id 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/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 2428b35f45d6..17a1d888d0b9 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -41,7 +41,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -56,7 +57,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c
index 201688ee50fa..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
@@ -73,7 +61,7 @@ static const struct pci_device_id 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/indigoio.c b/sound/pci/echoaudio/indigoio.c
index f7618edfd79c..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
@@ -74,7 +62,7 @@ static const struct pci_device_id 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/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index 79b68ba70936..791787aa0744 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -41,7 +41,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -56,7 +57,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c
index e145b688148a..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
@@ -74,7 +62,7 @@ static const struct pci_device_id 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/layla20.c b/sound/pci/echoaudio/layla20.c
index fc8468dabdb3..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
@@ -82,7 +70,7 @@ static const struct pci_device_id 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/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index 5e5b6e288a2d..5fb5c4a4598b 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -60,7 +61,8 @@ 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;
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 6e4023728ef5..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
@@ -92,7 +80,7 @@ static const struct pci_device_id 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/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
index c02bc1dcc170..decfccb1e803 100644
--- a/sound/pci/echoaudio/layla24_dsp.c
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -62,11 +63,13 @@ 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;
- if ((err = init_line_levels(chip)) < 0)
+ err = init_line_levels(chip);
+ if (err < 0)
return err;
return err;
@@ -355,16 +358,15 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_input_clock(chip, ECHO_CLOCK_INTERNAL);
- spin_unlock_irq(&chip->lock);
}
/* switch_asic() can sleep */
if (switch_asic(chip, asic) < 0)
return -EIO;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
/* Tweak the control register */
control_reg = le32_to_cpu(chip->comm_page->control_register);
@@ -384,7 +386,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
}
err = write_control_reg(chip, control_reg, true);
- spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 62b5240f53b9..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
@@ -83,7 +71,7 @@ static const struct pci_device_id 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/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 8f612a09c5d0..8a4dffc68889 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -44,7 +44,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -62,7 +63,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
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;
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index 6045a115cffe..dd5212644844 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -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 */
@@ -168,9 +167,8 @@ static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
struct echoaudio *chip = substream->rmidi->private_data;
if (up != chip->midi_input_enabled) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
enable_midi_input(chip, up);
- spin_unlock_irq(&chip->lock);
chip->midi_input_enabled = up;
}
}
@@ -201,15 +199,14 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
static void snd_echo_midi_output_write(struct timer_list *t)
{
- struct echoaudio *chip = from_timer(chip, t, timer);
- unsigned long flags;
+ struct echoaudio *chip = timer_container_of(chip, t, timer);
int bytes, sent, time;
unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
/* 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;
- spin_lock_irqsave(&chip->lock, flags);
+ sent = 0;
+ guard(spinlock_irqsave)(&chip->lock);
chip->midi_full = 0;
if (!snd_rawmidi_transmit_empty(chip->midi_out)) {
bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
@@ -243,7 +240,6 @@ static void snd_echo_midi_output_write(struct timer_list *t)
dev_dbg(chip->card->dev,
"Timer armed(%d)\n", ((time * HZ + 999) / 1000));
}
- spin_unlock_irqrestore(&chip->lock, flags);
}
@@ -252,25 +248,29 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream
int up)
{
struct echoaudio *chip = substream->rmidi->private_data;
+ bool remove_timer = false;
dev_dbg(chip->card->dev, "snd_echo_midi_output_trigger(%d)\n", up);
- spin_lock_irq(&chip->lock);
- if (up) {
- if (!chip->tinuse) {
- timer_setup(&chip->timer, snd_echo_midi_output_write,
- 0);
- chip->tinuse = 1;
- }
- } else {
- if (chip->tinuse) {
- chip->tinuse = 0;
- spin_unlock_irq(&chip->lock);
- del_timer_sync(&chip->timer);
- dev_dbg(chip->card->dev, "Timer removed\n");
- return;
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (up) {
+ if (!chip->tinuse) {
+ timer_setup(&chip->timer, snd_echo_midi_output_write,
+ 0);
+ chip->tinuse = 1;
+ }
+ } else {
+ if (chip->tinuse) {
+ chip->tinuse = 0;
+ remove_timer = true;
+ }
}
}
- spin_unlock_irq(&chip->lock);
+
+ if (remove_timer) {
+ timer_delete_sync(&chip->timer);
+ dev_dbg(chip->card->dev, "Timer removed\n");
+ return;
+ }
if (up && !chip->midi_full)
snd_echo_midi_output_write(&chip->timer);
@@ -308,11 +308,11 @@ static int snd_echo_midi_create(struct snd_card *card,
{
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);
+ strscpy(chip->rmidi->name, card->shortname);
chip->rmidi->private_data = chip;
snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 34d499466393..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
@@ -102,7 +90,7 @@ static const struct pci_device_id 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 dce9e57d01c4..9bb6a174745c 100644
--- a/sound/pci/echoaudio/mona_dsp.c
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -44,7 +44,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
+ err = init_dsp_comm_page(chip);
+ if (err) {
dev_err(chip->card->dev,
"init_hw - could not initialize DSP comm page\n");
return err;
@@ -67,7 +68,8 @@ 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;
@@ -300,11 +302,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
u32 control_reg, clocks_from_dsp;
int err;
-
- /* 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;
@@ -384,7 +381,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
@@ -416,7 +413,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
}
err = write_control_reg(chip, control_reg, false);
- spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index 17d5527be319..1f325abcb3ef 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -4,12 +4,12 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+snd-emu10k1-y := emu10k1.o emu10k1_main.o \
irq.o memory.o voice.o emumpu401.o emupcm.o io.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
+snd-emu10k1-synth-y := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+snd-emu10k1x-y := emu10k1x.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index d3203df50a1a..548e7d049901 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -1,30 +1,13 @@
+// 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
- *
- *
+ * James Courtier-Dutton <James@superbug.co.uk>
*/
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -34,8 +17,6 @@
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 IS_ENABLED(CONFIG_SND_SEQUENCER)
#define ENABLE_SYNTH
@@ -52,7 +33,6 @@ 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 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};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
@@ -74,8 +54,6 @@ module_param_array(enable_ir, bool, NULL, 0444);
MODULE_PARM_DESC(enable_ir, "Enable IR.");
module_param_array(subsystem, uint, NULL, 0444);
MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
-module_param_array(delay_pcm_irq, uint, NULL, 0444);
-MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
/*
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
*/
@@ -86,17 +64,6 @@ static const struct pci_device_id snd_emu10k1_ids[] = {
{ 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 snd_card_emu10k1_probe(struct pci_dev *pci,
@@ -117,55 +84,68 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
- emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
- if ((err = snd_emu10k1_pcm(emu, 0)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_mic(emu, 1)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_efx(emu, 2)) < 0)
- goto error;
+ 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;
+ err = snd_emu10k1_pcm(emu, 0);
+ if (err < 0)
+ return err;
+ if (emu->card_capabilities->ac97_chip) {
+ 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)) < 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)) < 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)) < 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 ||
@@ -175,7 +155,7 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
} else {
struct snd_emu10k1_synth_arg *arg;
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
- strcpy(wave->name, "Emu-10k1 Synth");
+ strscpy(wave->name, "Emu-10k1 Synth");
arg->hwptr = emu;
arg->index = 1;
arg->seq_ports = seq_ports[dev];
@@ -183,35 +163,23 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
}
#endif
- strlcpy(card->driver, emu->card_capabilities->driver,
+ strscpy(card->driver, emu->card_capabilities->driver,
sizeof(card->driver));
- strlcpy(card->shortname, emu->card_capabilities->name,
+ 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;
-
- if (emu->card_capabilities->emu_model)
- schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
-}
-
-static void snd_card_emu10k1_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
}
-
#ifdef CONFIG_PM_SLEEP
static int snd_emu10k1_suspend(struct device *dev)
{
@@ -222,13 +190,7 @@ static int snd_emu10k1_suspend(struct device *dev)
emu->suspend = 1;
- cancel_delayed_work_sync(&emu->emu1010.firmware_work);
-
- 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);
+ cancel_work_sync(&emu->emu1010.work);
snd_ac97_suspend(emu->ac97);
@@ -258,9 +220,6 @@ static int snd_emu10k1_resume(struct device *dev)
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;
}
@@ -274,7 +233,6 @@ static struct pci_driver emu10k1_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_emu10k1_ids,
.probe = snd_card_emu10k1_probe,
- .remove = snd_card_emu10k1_remove,
.driver = {
.pm = SND_EMU10K1_PM_OPS,
},
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index aa2cc27b8491..ef26e4d3e2a3 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -1,21 +1,8 @@
+// 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>
@@ -46,9 +33,9 @@ static void release_voice(struct snd_emux_voice *vp);
static void update_voice(struct snd_emux_voice *vp, int update);
static void terminate_voice(struct snd_emux_voice *vp);
static void free_voice(struct snd_emux_voice *vp);
-static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
-static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
-static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static u32 make_fmmod(struct snd_emux_voice *vp);
+static u32 make_fm2frq2(struct snd_emux_voice *vp);
+static int get_pitch_shift(struct snd_emux *emu);
/*
* Ensure a value is between two points
@@ -72,6 +59,7 @@ static const struct snd_emux_operators emu10k1_ops = {
.free_voice = free_voice,
.sample_new = snd_emu10k1_sample_new,
.sample_free = snd_emu10k1_sample_free,
+ .get_pitch_shift = get_pitch_shift,
};
void
@@ -103,7 +91,8 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
if (best[i].voice >= 0) {
int ch;
vp = &emu->voices[best[i].voice];
- if ((ch = vp->ch) < 0) {
+ ch = vp->ch;
+ if (ch < 0) {
/*
dev_warn(emu->card->dev,
"synth_get_voice: ch < 0 (%d) ??", i);
@@ -128,14 +117,13 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
static void
release_voice(struct snd_emux_voice *vp)
{
- int dcysusv;
struct snd_emu10k1 *hw;
hw = vp->hw;
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
- snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+ snd_emu10k1_ptr_write_multiple(hw, vp->ch,
+ DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK,
+ DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK,
+ REGLIST_END);
}
@@ -150,7 +138,13 @@ 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_multiple(hw, vp->ch,
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+ REGLIST_END);
if (vp->block) {
struct snd_emu10k1_memblk *emem;
emem = (struct snd_emu10k1_memblk *)vp->block;
@@ -173,11 +167,6 @@ free_voice(struct snd_emux_voice *vp)
/* Problem apparent on plug, unplug then plug */
/* on the Audigy 2 ZS Notebook. */
if (hw && (vp->ch >= 0)) {
- snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
- // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
- snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
- snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
vp->emu->num_voices--;
vp->ch = -1;
@@ -203,13 +192,13 @@ update_voice(struct snd_emux_voice *vp, int update)
snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
}
if (update & SNDRV_EMUX_UPDATE_FMMOD)
- set_fmmod(hw, vp);
+ snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, make_fmmod(vp));
if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
- set_fm2frq2(hw, vp);
+ snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, make_fm2frq2(vp));
if (update & SNDRV_EMUX_UPDATE_Q)
- set_filterQ(hw, vp);
+ snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
}
@@ -266,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
/* check if sample is finished playing (non-looping only) */
if (bp != best + V_OFF && bp != best + V_FREE &&
(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
- val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+ val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3;
if (val >= vp->reg.loopstart)
bp = best + V_OFF;
}
@@ -300,7 +289,7 @@ get_voice(struct snd_emux *emu, struct snd_emux_port *port)
if (vp->ch < 0) {
/* allocate a voice */
struct snd_emu10k1_voice *hwvoice;
- if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
+ if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0)
continue;
vp->ch = hwvoice->number;
emu->num_voices++;
@@ -321,6 +310,8 @@ start_voice(struct snd_emux_voice *vp)
{
unsigned int temp;
int ch;
+ bool w_16;
+ u32 psst, dsl, map, ccca, vtarget;
unsigned int addr, mapped_offset;
struct snd_midi_channel *chan;
struct snd_emu10k1 *hw;
@@ -331,6 +322,7 @@ start_voice(struct snd_emux_voice *vp)
if (snd_BUG_ON(ch < 0))
return -EINVAL;
chan = vp->chan;
+ w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS);
emem = (struct snd_emu10k1_memblk *)vp->block;
if (emem == NULL)
@@ -340,7 +332,7 @@ start_voice(struct snd_emux_voice *vp)
/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
return -ENOMEM;
}
- mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+ mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16;
vp->reg.start += mapped_offset;
vp->reg.end += mapped_offset;
vp->reg.loopstart += mapped_offset;
@@ -358,114 +350,98 @@ start_voice(struct snd_emux_voice *vp)
snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
}
- /* 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, PTRX, ch, 0);
- snd_emu10k1_ptr_write(hw, CPF, ch, 0);
-
- /* set pitch offset */
- snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
-
- /* set envelope parameters */
- snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
- snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
- snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
- snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
- snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
- /* decay/sustain parameter for volume envelope is used
- for triggerg the voice */
-
- /* cutoff and volume */
- temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
- snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
-
- /* modulation envelope heights */
- snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
-
- /* lfo1/2 delay */
- snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
- snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
-
- /* lfo1 pitch & cutoff shift */
- set_fmmod(hw, vp);
- /* lfo1 volume & freq */
- snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
- /* lfo2 pitch & freq */
- set_fm2frq2(hw, vp);
-
- /* reverb and loop start (reverb 8bit, MSB) */
temp = vp->reg.parm.reverb;
temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
LIMITMAX(temp, 255);
addr = vp->reg.loopstart;
- snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+ psst = (temp << 24) | addr;
- /* chorus & loop end (chorus 8bit, MSB) */
addr = vp->reg.loopend;
temp = vp->reg.parm.chorus;
temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
LIMITMAX(temp, 255);
- temp = (temp <<24) | addr;
- snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+ dsl = (temp << 24) | addr;
- /* clear filter delay memory */
- snd_emu10k1_ptr_write(hw, Z1, ch, 0);
- snd_emu10k1_ptr_write(hw, Z2, ch, 0);
+ map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
- /* invalidate maps */
- 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
- /* cache */
- {
- unsigned int val, sample;
- val = 32;
- if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
- sample = 0x80808080;
- else {
- sample = 0;
- val *= 2;
- }
-
- /* cache */
- snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
- snd_emu10k1_ptr_write(hw, CDE, ch, sample);
- snd_emu10k1_ptr_write(hw, CDF, ch, sample);
-
- /* invalidate maps */
- 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);
-
- /* fill cache */
- val -= 4;
- val <<= 25;
- val |= 0x1c << 16;
- snd_emu10k1_ptr_write(hw, CCR, ch, val);
- }
-#endif
-
- /* Q & current address (Q 4bit value, MSB) */
- addr = vp->reg.start;
+ addr = vp->reg.start + 64 - 3;
temp = vp->reg.parm.filterQ;
- temp = (temp<<28) | addr;
+ ccca = (temp << 28) | addr;
if (vp->apitch < 0xe400)
- temp |= CCCA_INTERPROM_0;
+ ccca |= CCCA_INTERPROM_0;
else {
unsigned int shift = (vp->apitch - 0xe000) >> 10;
- temp |= shift << 25;
+ ccca |= shift << 25;
}
- if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
- temp |= CCCA_8BITSELECT;
- snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
-
- /* 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);
+ if (!w_16)
+ ccca |= CCCA_8BITSELECT;
+
+ vtarget = (unsigned int)vp->vtarget << 16;
+
+ snd_emu10k1_ptr_write_multiple(hw, ch,
+ /* channel to be silent and idle */
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+
+ /* set pitch offset */
+ IP, vp->apitch,
+
+ /* set envelope parameters */
+ ENVVAL, vp->reg.parm.moddelay,
+ ATKHLDM, vp->reg.parm.modatkhld,
+ DCYSUSM, vp->reg.parm.moddcysus,
+ ENVVOL, vp->reg.parm.voldelay,
+ ATKHLDV, vp->reg.parm.volatkhld,
+ /* decay/sustain parameter for volume envelope is used
+ for triggerg the voice */
+
+ /* cutoff and volume */
+ IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol,
+
+ /* modulation envelope heights */
+ PEFE, vp->reg.parm.pefe,
+
+ /* lfo1/2 delay */
+ LFOVAL1, vp->reg.parm.lfo1delay,
+ LFOVAL2, vp->reg.parm.lfo2delay,
+
+ /* lfo1 pitch & cutoff shift */
+ FMMOD, make_fmmod(vp),
+ /* lfo1 volume & freq */
+ TREMFRQ, vp->reg.parm.tremfrq,
+ /* lfo2 pitch & freq */
+ FM2FRQ2, make_fm2frq2(vp),
+
+ /* reverb and loop start (reverb 8bit, MSB) */
+ PSST, psst,
+
+ /* chorus & loop end (chorus 8bit, MSB) */
+ DSL, dsl,
+
+ /* clear filter delay memory */
+ Z1, 0,
+ Z2, 0,
+
+ /* invalidate maps */
+ MAPA, map,
+ MAPB, map,
+
+ /* Q & current address (Q 4bit value, MSB) */
+ CCCA, ccca,
+
+ /* cache */
+ CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64),
+
+ /* reset volume */
+ VTFT, vtarget | vp->ftarget,
+ CVCF, vtarget | CVCF_CURRENTFILTER_MASK,
+
+ REGLIST_END);
+
+ hw->voices[ch].dirty = 1;
return 0;
}
@@ -475,7 +451,7 @@ start_voice(struct snd_emux_voice *vp)
static void
trigger_voice(struct snd_emux_voice *vp)
{
- unsigned int temp, ptarget;
+ unsigned int ptarget;
struct snd_emu10k1 *hw;
struct snd_emu10k1_memblk *emem;
@@ -490,24 +466,25 @@ trigger_voice(struct snd_emux_voice *vp)
#else
ptarget = IP_TO_CP(vp->apitch);
#endif
- /* set pitch target and pan (volume) */
- temp = ptarget | (vp->apan << 8) | vp->aaux;
- snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+ snd_emu10k1_ptr_write_multiple(hw, vp->ch,
+ /* set pitch target and pan (volume) */
+ PTRX, ptarget | (vp->apan << 8) | vp->aaux,
+
+ /* current pitch and fractional address */
+ CPF, ptarget,
- /* pitch target */
- snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+ /* enable envelope engine */
+ DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK,
- /* trigger voice */
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+ REGLIST_END);
}
#define MOD_SENSE 18
-/* set lfo1 modulation height and cutoff */
-static void
-set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+/* calculate lfo1 modulation height and cutoff register */
+static u32
+make_fmmod(struct snd_emux_voice *vp)
{
- unsigned short fmmod;
short pitch;
unsigned char cutoff;
int modulation;
@@ -517,15 +494,13 @@ set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
pitch += (MOD_SENSE * modulation) / 1200;
LIMITVALUE(pitch, -128, 127);
- fmmod = ((unsigned char)pitch<<8) | cutoff;
- snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+ return ((unsigned char)pitch << 8) | cutoff;
}
-/* set lfo2 pitch & frequency */
-static void
-set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+/* calculate set lfo2 pitch & frequency register */
+static u32
+make_fm2frq2(struct snd_emux_voice *vp)
{
- unsigned short fm2frq2;
short pitch;
unsigned char freq;
int modulation;
@@ -535,16 +510,13 @@ set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
pitch += (MOD_SENSE * modulation) / 1200;
LIMITVALUE(pitch, -128, 127);
- fm2frq2 = ((unsigned char)pitch<<8) | freq;
- snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+ return ((unsigned char)pitch << 8) | freq;
}
-/* set filterQ */
-static void
-set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+static int get_pitch_shift(struct snd_emux *emu)
{
- 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);
+ struct snd_emu10k1 *hw = emu->hw;
+
+ return (hw->card_capabilities->emu_model &&
+ hw->emu1010.word_clock == 44100) ? 0 : -501;
}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index ccf4415a1c7b..b2fe2d164ba8 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1,34 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added support for Audigy 2 Value.
- * 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
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/sched.h>
@@ -36,6 +13,7 @@
#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>
@@ -71,50 +49,53 @@ MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
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, PTRX, ch, 0);
- snd_emu10k1_ptr_write(emu, CPF, ch, 0);
- snd_emu10k1_ptr_write(emu, CCR, ch, 0);
-
- snd_emu10k1_ptr_write(emu, PSST, ch, 0);
- snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
- snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
- snd_emu10k1_ptr_write(emu, Z1, ch, 0);
- snd_emu10k1_ptr_write(emu, Z2, ch, 0);
- snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
-
- 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, PEFE, ch, 0);
- snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
- snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */
- snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */
- snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
-
- /*** these are last so OFF prevents writing ***/
- snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
- snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
- snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
- snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
- snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+ CCR, 0,
+
+ PSST, 0,
+ DSL, 0x10,
+ CCCA, 0,
+ Z1, 0,
+ Z2, 0,
+ FXRT, 0x32100000,
+
+ // The rest is meaningless as long as DCYSUSV_CHANNELENABLE_MASK is zero
+ DCYSUSM, 0,
+ ATKHLDV, 0,
+ ATKHLDM, 0,
+ IP, 0,
+ IFATN, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK,
+ PEFE, 0,
+ FMMOD, 0,
+ TREMFRQ, 24, /* 1 Hz */
+ FM2FRQ2, 24, /* 1 Hz */
+ LFOVAL2, 0,
+ LFOVAL1, 0,
+ ENVVOL, 0,
+ ENVVAL, 0,
+
+ REGLIST_END);
/* 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_FXRT1, ch, 0x03020100);
- snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ A_CSBA, 0,
+ A_CSDC, 0,
+ A_CSFE, 0,
+ A_CSHG, 0,
+ A_FXRT1, 0x03020100,
+ A_FXRT2, 0x07060504,
+ A_SENDAMOUNTS, 0,
+ REGLIST_END);
}
}
-static unsigned int spi_dac_init[] = {
+static const unsigned int spi_dac_init[] = {
0x00ff,
0x02ff,
0x0400,
@@ -138,7 +119,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 +135,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;
@@ -164,20 +145,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
- /* reset recording buffers */
- snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
- snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
-
- /* disable channel interrupt */
outl(0, emu->port + INTE);
- snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
- snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ /* reset recording buffers */
+ MICBS, ADCBS_BUFSIZE_NONE,
+ MICBA, 0,
+ FXBS, ADCBS_BUFSIZE_NONE,
+ FXBA, 0,
+ ADCBS, ADCBS_BUFSIZE_NONE,
+ ADCBA, 0,
+
+ /* disable channel interrupt */
+ CLIEL, 0,
+ CLIEH, 0,
+
+ /* disable stop on loop end */
+ SOLEL, 0,
+ SOLEH, 0,
+
+ REGLIST_END);
if (emu->audigy) {
/* set SPDIF bypass mode */
@@ -191,17 +178,17 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
for (ch = 0; ch < NUM_G; ch++)
snd_emu10k1_voice_init(emu, ch);
- snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]);
- snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
- snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ SPCS0, emu->spdif_bits[0],
+ SPCS1, emu->spdif_bits[1],
+ SPCS2, emu->spdif_bits[2],
+ REGLIST_END);
- 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 +200,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 */
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 +239,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,7 +260,7 @@ 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 << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
for (ch = 0; ch < NUM_G; ch++) {
@@ -322,12 +303,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 +324,8 @@ 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) {
@@ -368,19 +349,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);
}
}
@@ -402,7 +383,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
}
#endif
- snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+ if (emu->card_capabilities->emu_model)
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE | INTE_A_GPIOENABLE);
+ else
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
}
int snd_emu10k1_done(struct snd_emu10k1 *emu)
@@ -412,41 +396,48 @@ int snd_emu10k1_done(struct snd_emu10k1 *emu)
outl(0, emu->port + INTE);
/*
- * Shutdown the chip
+ * Shutdown the voices
*/
- for (ch = 0; ch < NUM_G; ch++)
- snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
for (ch = 0; ch < NUM_G; ch++) {
- snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
- snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
- snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
- snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ DCYSUSV, 0,
+ VTFT, 0,
+ CVCF, 0,
+ PTRX, 0,
+ CPF, 0,
+ REGLIST_END);
}
- /* reset recording buffers */
- snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
- snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
- snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
- snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
- snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+ // stop the DSP
if (emu->audigy)
snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
else
snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
- /* disable channel interrupt */
- snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
- snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ /* reset recording buffers */
+ MICBS, 0,
+ MICBA, 0,
+ FXBS, 0,
+ FXBA, 0,
+ FXWC, 0,
+ ADCBS, ADCBS_BUFSIZE_NONE,
+ ADCBA, 0,
+ TCBS, TCBS_BUFFSIZE_16K,
+ TCB, 0,
+
+ /* disable channel interrupt */
+ CLIEL, 0,
+ CLIEH, 0,
+ SOLEL, 0,
+ SOLEH, 0,
+
+ PTB, 0,
+
+ REGLIST_END);
/* disable audio and lock cache */
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
- snd_emu10k1_ptr_write(emu, PTB, 0, 0);
return 0;
}
@@ -637,7 +628,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.
@@ -661,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
return 0;
}
-static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
- const struct firmware *fw_entry)
-{
- int n, i;
- int reg;
- int value;
- unsigned int write_post;
- unsigned long flags;
-
- if (!fw_entry)
- return -EIO;
-
- /* The FPGA is a Xilinx Spartan IIE XC2S50E */
- /* 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);
- udelay(100);
- outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
- write_post = inl(emu->port + A_IOCFG);
- udelay(100); /* Allow FPGA memory to clean */
- for (n = 0; n < fw_entry->size; n++) {
- value = fw_entry->data[n];
- for (i = 0; i < 8; i++) {
- reg = 0x80;
- 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);
- }
- }
- /* After programming, set GPIO bit 4 high again. */
- outl(0x10, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return 0;
-}
-
/* firmware file names, per model, init-fw and dock-fw (optional) */
static const char * const firmware_names[5][2] = {
[EMU_MODEL_EMU1010] = {
@@ -738,99 +683,117 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
return err;
}
- return snd_emu1010_load_firmware_entry(emu, *fw);
+ snd_emu1010_load_firmware_entry(emu, dock, *fw);
+ return 0;
}
-static void emu1010_firmware_work(struct work_struct *work)
+static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
{
- struct snd_emu10k1 *emu;
- u32 tmp, tmp2, reg;
+ u32 tmp, tmp2;
int err;
- emu = container_of(work, struct snd_emu10k1,
- emu1010.firmware_work.work);
- if (emu->card->shutdown)
+ // The docking events clearly arrive prematurely - while the
+ // Dock's FPGA seems to be successfully programmed, the Dock
+ // fails to initialize subsequently if we don't give it some
+ // time to "warm up" here.
+ msleep(200);
+
+ dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n");
+ err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+ if (err < 0)
return;
-#ifdef CONFIG_PM_SLEEP
- if (emu->suspend)
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+ dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+ if ((tmp & 0x1f) != 0x15) {
+ /* FPGA failed to be programmed */
+ dev_err(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n",
+ tmp);
return;
-#endif
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+ }
+ 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);
+
+ /* Allow DLL to settle, to sync clocking between 1010 and Dock */
+ msleep(10);
+}
+
+static void emu1010_dock_event(struct snd_emu10k1 *emu)
+{
+ u32 reg;
+
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);
+ snd_emu1010_load_dock_firmware(emu);
/* 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) {
+ } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) {
/* Audio Dock removed */
dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
- /* Unmute all */
+ /* The hardware auto-mutes all, so we unmute again */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
}
+}
+
+static void emu1010_clock_event(struct snd_emu10k1 *emu)
+{
+ struct snd_ctl_elem_id id;
+
+ scoped_guard(spinlock_irq, &emu->reg_lock) {
+ // This is the only thing that can actually happen.
+ emu->emu1010.clock_source = emu->emu1010.clock_fallback;
+ emu->emu1010.wclock = 1 - emu->emu1010.clock_source;
+ snd_emu1010_update_clock(emu);
+ }
+ snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0);
+ snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
+}
+
+static void emu1010_work(struct work_struct *work)
+{
+ struct snd_emu10k1 *emu;
+ u32 sts;
+
+ emu = container_of(work, struct snd_emu10k1, emu1010.work);
+ if (emu->card->shutdown)
+ return;
+#ifdef CONFIG_PM_SLEEP
+ if (emu->suspend)
+ return;
+#endif
+
+ guard(snd_emu1010_fpga_lock)(emu);
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts);
+
+ // The distinction of the IRQ status bits is unreliable,
+ // so we dispatch later based on option card status.
+ if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST))
+ emu1010_dock_event(emu);
- next:
- emu->emu1010.last_reg = reg;
- if (!emu->card->shutdown)
- schedule_delayed_work(&emu->emu1010.firmware_work,
- msecs_to_jiffies(1000));
+ if (sts & EMU_HANA_IRQ_WCLK_CHANGED)
+ emu1010_clock_event(emu);
+}
+
+static void emu1010_interrupt(struct snd_emu10k1 *emu)
+{
+ // We get an interrupt on each GPIO input pin change, but we
+ // care only about the ones triggered by the dedicated pin.
+ u16 sts = inw(emu->port + A_GPIO);
+ u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000;
+ if (!(sts & bit))
+ return;
+
+ schedule_work(&emu->emu1010.work);
}
/*
- * 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)
@@ -840,52 +803,18 @@ static void emu1010_firmware_work(struct work_struct *work)
*/
static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
{
- unsigned int i;
u32 tmp, tmp2, reg;
int err;
dev_info(emu->card->dev, "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);
- /* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+ /* 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);
- /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &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_read(emu, EMU_HANA_ID, &reg);
- dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg);
- if ((reg & 0x3f) == 0x15) {
- /* FPGA failed to return to programming mode */
- dev_info(emu->card->dev,
- "emu1010: FPGA failed to return to programming mode\n");
- return -ENODEV;
- }
- dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
+ guard(snd_emu1010_fpga_lock)(emu);
+ dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n");
err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
if (err < 0) {
dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
@@ -911,339 +840,53 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
- /* 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);
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE)
+ snd_emu1010_load_dock_firmware(emu);
+ if (emu->card_capabilities->no_adat) {
+ emu->emu1010.optical_in = 0; /* IN_SPDIF */
+ emu->emu1010.optical_out = 0; /* OUT_SPDIF */
+ } else {
+ /* Optical -> ADAT I/O */
+ emu->emu1010.optical_in = 1; /* IN_ADAT */
+ 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: All on */
- /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
- /* IRQ Enable: All off */
- snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
+ 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);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- dev_info(emu->card->dev, "emu1010: Card options3 = 0x%x\n", reg);
+ emu->gpio_interrupt = emu1010_interrupt;
+ // Note: The Audigy INTE is set later
+ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE,
+ EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST | EMU_HANA_IRQ_WCLK_CHANGED);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg); // Clear pending IRQs
+
+ emu->emu1010.clock_source = 1; /* 48000 */
+ emu->emu1010.clock_fallback = 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 */
+ emu->emu1010.wclock = EMU_HANA_WCLOCK_INT_48K;
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);
-
-#if 0
- /* For 96kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
-#endif
-#if 0
- /* For 192kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
-#endif
-#if 1
- /* For 48kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
- /* Pavel Hofman - setting defaults for 8 more capture channels
- * Defaults only, users will set their own values anyways, let's
- * just copy/paste.
- */
-
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
-#endif
-#if 0
- /* Original */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
-#endif
- for (i = 0; i < 0x20; i++) {
- /* AudioDock Elink <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0100 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 4; i++) {
- /* Hana SPDIF Out <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0200 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 7; i++) {
- /* Hamoa DAC <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0300 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 7; i++) {
- /* Hana ADAT Out <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
- }
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(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) */
-
-#if 0
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
-#endif
- /* Default outputs */
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- /* 1616(M) cardbus default outputs */
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[0] = 17;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[1] = 18;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[2] = 19;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[3] = 20;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[4] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[5] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[16] = 17;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[17] = 18;
- } else {
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[0] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[1] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[2] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[3] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[4] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[5] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[6] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[7] = 28;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[8] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[9] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[10] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[11] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[12] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[13] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[14] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[15] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[16] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[17] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[18] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[19] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[20] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[21] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[22] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[23] = 28;
- }
- /* TEMP: Select SPDIF in/out */
- /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
+ snd_emu1010_update_clock(emu);
- /* 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 */
+ // The routes are all set to EMU_SRC_SILENCE due to the reset,
+ // so it is safe to simply enable the outputs.
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
return 0;
}
@@ -1256,8 +899,10 @@ 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);
@@ -1265,19 +910,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
}
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
/* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0);
}
- cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.work);
+ mutex_destroy(&emu->emu1010.lock);
release_firmware(emu->firmware);
release_firmware(emu->dock_fw);
- 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;
- }
snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
snd_dma_free_pages(&emu->silent_page);
@@ -1288,22 +926,9 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
#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
@@ -1376,6 +1001,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",
@@ -1424,9 +1058,12 @@ 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]",
+ .driver = "Audigy2", .name = "E-MU 02 CardBus [MAEM8950]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1434,9 +1071,12 @@ 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]",
+ .driver = "Audigy2", .name = "E-MU 1010b PCI [MAEM8960]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1444,47 +1084,62 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.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]",
+ .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]",
+ .driver = "Audigy2", .name = "E-MU 1010 [MAEM8810]",
.id = "EMU1010",
.emu10k2_chip = 1,
.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]",
+ .driver = "Audigy2", .name = "E-MU 0404b PCI [MAEM8852]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0108_chip = 1,
- .spk71 = 1,
+ .spk20 = 1,
+ .no_adat = 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]",
+ .driver = "Audigy2", .name = "E-MU 0404 [MAEM8850]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0102_chip = 1,
- .spk71 = 1,
+ .spk20 = 1,
+ .no_adat = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
/* EMU0404 PCIe */
+ /* Does NOT support sync daughter card. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102,
- .driver = "Audigy2", .name = "E-mu 0404 PCIe [MAEM8984]",
+ .driver = "Audigy2", .name = "E-MU 0404 PCIe [MAEM8984]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0108_chip = 1,
- .spk71 = 1,
+ .spk20 = 1,
+ .no_adat = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */
- /* Note that all E-mu cards require kernel 2.6 or newer. */
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
.id = "Audigy2",
@@ -1565,6 +1220,8 @@ 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 = "Audigy 2 Platinum EX [SB0280]",
.id = "Audigy2",
@@ -1585,6 +1242,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",
@@ -1690,6 +1349,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",
@@ -1739,7 +1401,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
- .driver = "EMU10K1", .name = "E-mu APS [PC545]",
+ .driver = "EMU10K1", .name = "E-MU APS [PC545]",
.id = "APS",
.emu10k1_chip = 1,
.ecard = 1} ,
@@ -1764,36 +1426,57 @@ static struct snd_emu_chip_details emu_chip_details[] = {
{ } /* terminator */
};
+/*
+ * 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);
@@ -1809,7 +1492,8 @@ int 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);
+ INIT_WORK(&emu->emu1010.work, emu1010_work);
+ mutex_init(&emu->emu1010.lock);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
@@ -1836,8 +1520,6 @@ int snd_emu10k1_create(struct snd_card *card,
}
if (c->vendor == 0) {
dev_err(card->dev, "emu10k1: Card not recognised\n");
- kfree(emu);
- pci_disable_device(pci);
return -ENOENT;
}
emu->card_capabilities = c;
@@ -1854,36 +1536,21 @@ int snd_emu10k1_create(struct snd_card *card,
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 = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
- if (dma_set_mask(&pci->dev, emu->dma_mask) < 0 ||
- dma_set_coherent_mask(&pci->dev, emu->dma_mask) < 0) {
+ 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);
- kfree(emu);
- pci_disable_device(pci);
return -ENXIO;
}
if (is_audigy)
@@ -1891,49 +1558,51 @@ int snd_emu10k1_create(struct snd_card *card,
else
emu->gpr_base = FXGPREGBASE;
- err = pci_request_regions(pci, "EMU10K1");
- if (err < 0) {
- kfree(emu);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "EMU10K1");
+ 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),
- (emu->address_mode ? 32 : 16) * 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;
@@ -1941,18 +1610,16 @@ int 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 */
@@ -1966,12 +1633,11 @@ int 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,
- KBUILD_MODNAME, 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 :
@@ -1993,56 +1659,40 @@ int 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);
+ 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++)
- ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | 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;
+ 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_SND_PROC_FS
snd_emu10k1_proc_init(emu);
#endif
-
- *remu = emu;
return 0;
-
- error:
- snd_emu10k1_free(emu);
- return err;
}
#ifdef CONFIG_PM_SLEEP
-static unsigned char saved_regs[] = {
+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,
@@ -2051,8 +1701,8 @@ 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 */
};
@@ -2064,7 +1714,7 @@ static int alloc_pm_buffer(struct snd_emu10k1 *emu)
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)
@@ -2086,7 +1736,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;
@@ -2099,7 +1749,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);
}
@@ -2113,20 +1763,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 0e069aeab86d..806b4f95cad1 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
@@ -29,7 +16,7 @@
#define BLANK_LOOP_START 4
#define BLANK_LOOP_END 8
#define BLANK_LOOP_SIZE 12
-#define BLANK_HEAD_SIZE 32
+#define BLANK_HEAD_SIZE 3
/*
* allocate a sample block and copy data from userspace
@@ -39,58 +26,77 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_util_memhdr *hdr,
const void __user *data, long count)
{
+ u8 fill;
+ u32 xor;
+ int shift;
int offset;
- int truesize, size, loopsize, blocksize;
- int loopend, sampleend;
- unsigned int start_addr;
+ int truesize, size, blocksize;
+ int loop_start, loop_end, loop_size, data_end, unroll;
struct snd_emu10k1 *emu;
emu = rec->hw;
if (snd_BUG_ON(!sp || !hdr))
return -EINVAL;
- if (sp->v.size == 0) {
- dev_dbg(emu->card->dev,
- "emu: rom font for sample %d\n", sp->v.sample);
- return 0;
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+ /* should instead return -ENOTSUPP; but compatibility */
+ dev_warn(emu->card->dev,
+ "Emu10k1 wavetable patch %d with unsupported loop feature\n",
+ sp->v.sample);
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
- /* some samples have invalid data. the addresses are corrected in voice info */
- sampleend = sp->v.end;
- if (sampleend > sp->v.size)
- sampleend = sp->v.size;
- loopend = sp->v.loopend;
- if (loopend > sampleend)
- 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.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+ shift = 0;
+ fill = 0x80;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080;
+ } else {
+ shift = 1;
+ fill = 0;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0;
}
/* compute true data size to be loaded */
truesize = sp->v.size + BLANK_HEAD_SIZE;
- loopsize = 0;
-#if 0 /* not supported */
- if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
- loopsize = sp->v.loopend - sp->v.loopstart;
- truesize += loopsize;
-#endif
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
truesize += BLANK_LOOP_SIZE;
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+ sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+ sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+ }
+ }
+
+ loop_start = sp->v.loopstart;
+ loop_end = sp->v.loopend;
+ loop_size = loop_end - loop_start;
+ if (!loop_size)
+ return -EINVAL;
+ data_end = sp->v.end;
+
+ /* recalculate offset */
+ sp->v.start += BLANK_HEAD_SIZE;
+ sp->v.end += BLANK_HEAD_SIZE;
+ sp->v.loopstart += BLANK_HEAD_SIZE;
+ sp->v.loopend += BLANK_HEAD_SIZE;
+
+ // Automatic pre-filling of the cache does not work in the presence
+ // of loops (*), and we don't want to fill it manually, as that is
+ // fiddly and slow. So we unroll the loop until the loop end is
+ // beyond the cache size.
+ // (*) Strictly speaking, a single iteration is supported (that's
+ // how it works when the playback engine runs), but handling this
+ // special case is not worth it.
+ unroll = 0;
+ while (sp->v.loopend < 64) {
+ truesize += loop_size;
+ sp->v.loopstart += loop_size;
+ sp->v.loopend += loop_size;
+ sp->v.end += loop_size;
+ unroll++;
+ }
/* try to allocate a memory block */
- blocksize = truesize;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- blocksize *= 2;
+ blocksize = truesize << shift;
sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
if (sp->block == NULL) {
dev_dbg(emu->card->dev,
@@ -103,110 +109,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
/* write blank samples at head */
offset = 0;
- size = BLANK_HEAD_SIZE;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
- offset += size;
-
- /* copy start->loopend */
- size = loopend;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
- }
+ size = BLANK_HEAD_SIZE << shift;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill);
offset += size;
- data += size;
-
-#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 */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- int woffset;
- unsigned short *wblock = (unsigned short*)block;
- woffset = offset / 2;
- if (offset + loopsize * 2 > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- wblock[woffset + i] = wblock[woffset - i -1];
- offset += loopsize * 2;
- } else {
- if (offset + loopsize > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- block[offset + i] = block[offset - i -1];
- offset += loopsize;
- }
- /* modify loop pointers */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
- sp->v.loopend += loopsize;
- } else {
- sp->v.loopstart += loopsize;
- sp->v.loopend += loopsize;
+ /* copy provided samples */
+ if (unroll && loop_end <= data_end) {
+ size = loop_end << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
+
+ data += loop_start << shift;
+ while (--unroll > 0) {
+ size = loop_size << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
}
- /* add sample pointer */
- sp->v.end += loopsize;
- }
-#endif
- /* loopend -> sample end */
- size = sp->v.size - loopend;
- if (size < 0)
- return -EINVAL;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
+ size = (data_end - loop_start) << shift;
+ } else {
+ size = data_end << shift;
}
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
offset += size;
/* clear rest of samples (if any) */
if (offset < blocksize)
- snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
-
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
- /* if no blank loop is attached in the sample, add it */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
- sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
- sp->v.loopend = sp->v.end + BLANK_LOOP_END;
- }
- }
-
-#if 0 /* not supported yet */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
- /* unsigned -> signed */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- unsigned short *wblock = (unsigned short*)block;
- for (i = 0; i < truesize; i++)
- wblock[i] ^= 0x8000;
- } else {
- for (i = 0; i < truesize; i++)
- block[i] ^= 0x80;
- }
- }
-#endif
-
- /* recalculate offset */
- start_addr = BLANK_HEAD_SIZE * 2;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- start_addr >>= 1;
- sp->v.start += start_addr;
- sp->v.end += start_addr;
- sp->v.loopstart += start_addr;
- sp->v.loopend += start_addr;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill);
return 0;
+
+faulty:
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ return -EFAULT;
}
/*
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 5457d5613f6b..662d20eb9689 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -1,21 +1,8 @@
+// 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"
@@ -35,7 +22,6 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
- unsigned long flags;
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (arg == NULL)
@@ -56,7 +42,6 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
emux->hw = hw;
emux->max_voices = arg->max_voices;
emux->num_ports = arg->seq_ports;
- emux->pitch_shift = -501;
emux->memhdr = hw->memhdr;
/* maximum two ports */
emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
@@ -70,10 +55,9 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
return -ENOMEM;
}
- spin_lock_irqsave(&hw->voice_lock, flags);
+ guard(spinlock_irq)(&hw->voice_lock);
hw->synth = emux;
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
dev->driver_data = emux;
@@ -85,7 +69,6 @@ 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;
if (dev->driver_data == NULL)
return 0; /* not registered actually */
@@ -93,10 +76,10 @@ static int snd_emu10k1_synth_remove(struct device *_dev)
emux = dev->driver_data;
hw = emux->hw;
- spin_lock_irqsave(&hw->voice_lock, flags);
- hw->synth = NULL;
- hw->get_synth_voice = NULL;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
+ scoped_guard(spinlock_irq, &hw->voice_lock) {
+ hw->synth = NULL;
+ hw->get_synth_voice = NULL;
+ }
snd_emux_free(emux);
return 0;
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
index 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 2c2b12a06177..9607a0f7174b 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,27 +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/string.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -45,7 +32,6 @@
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;
@@ -231,7 +217,6 @@ struct emu10k1x {
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
int irq;
unsigned char revision; /* chip revision */
@@ -248,7 +233,7 @@ 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;
};
@@ -296,16 +281,13 @@ static unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inl(emu->port + DATA);
}
static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
@@ -314,45 +296,36 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(regptr, emu->port + PTR);
outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
intr_enable = inl(emu->port + INTE) | intrenb;
outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
intr_enable = inl(emu->port + INTE) & ~intrenb;
outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(value, emu->port + GPIO);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime)
@@ -364,7 +337,8 @@ 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;
@@ -386,10 +360,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);
@@ -425,8 +400,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 */
@@ -446,7 +420,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 */
@@ -456,7 +430,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;
@@ -465,7 +439,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);
@@ -551,7 +525,6 @@ snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
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,
@@ -567,10 +540,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)
@@ -608,8 +583,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 */
@@ -629,7 +603,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 */
@@ -697,7 +671,6 @@ snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
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,
@@ -709,26 +682,20 @@ static unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct emu10k1x *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inw(emu->port + AC97DATA);
}
static void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct emu10k1x *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
outw(val, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static int snd_emu10k1x_ac97(struct emu10k1x *chip)
@@ -736,12 +703,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 */
@@ -751,37 +719,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)
@@ -857,7 +803,8 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
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;
@@ -876,56 +823,43 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
pcm->info_flags = 0;
switch(device) {
case 0:
- strcpy(pcm->name, "EMU10K1X Front");
+ strscpy(pcm->name, "EMU10K1X Front");
map = snd_pcm_std_chmaps;
break;
case 1:
- strcpy(pcm->name, "EMU10K1X Rear");
+ strscpy(pcm->name, "EMU10K1X Rear");
map = surround_map;
break;
case 2:
- strcpy(pcm->name, "EMU10K1X Center/LFE");
+ strscpy(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 32*1024, 32*1024);
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
1 << 2, NULL);
}
static int snd_emu10k1x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct emu10k1x **rchip)
+ 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) {
+
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28)) < 0) {
dev_err(card->dev, "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;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
@@ -933,28 +867,24 @@ static int snd_emu10k1x_create(struct snd_card *card,
spin_lock_init(&chip->emu_lock);
spin_lock_init(&chip->voice_lock);
+ err = pcim_request_all_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) {
- dev_err(card->dev, "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, KBUILD_MODNAME, 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);
- snd_emu10k1x_free(chip);
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 */
@@ -1010,12 +940,6 @@ static int 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;
}
@@ -1024,14 +948,12 @@ static void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry,
{
struct emu10k1x *emu = entry->private_data;
unsigned long value,value1,value2;
- unsigned long flags;
int i;
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
}
snd_iprintf(buffer, "\nRegisters\n\n");
@@ -1058,22 +980,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 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;
}
@@ -1094,7 +1010,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] ;
@@ -1109,7 +1024,7 @@ 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 const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
@@ -1197,17 +1112,23 @@ static int snd_emu10k1x_mixer(struct emu10k1x *emu)
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;
@@ -1265,28 +1186,28 @@ static void do_emu10k1x_midi_interrupt(struct emu10k1x *emu,
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- mpu401_clear_rx(emu, midi);
- } else {
- byte = mpu401_read_data(emu, midi);
- if (midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- mpu401_write_data(emu, midi, byte);
- } else {
- snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
}
static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status)
@@ -1297,29 +1218,28 @@ static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int statu
static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
struct emu10k1x_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- mpu401_write_data(emu, midi, 0x00);
- /* mpu401_clear_rx(emu, midi); */
-
- mpu401_write_cmd(emu, midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (mpu401_input_avail(emu, midi)) {
- if (mpu401_read_data(emu, midi) == MPU401_ACK)
- ok = 1;
+ scoped_guard(spinlock_irqsave, &midi->input_lock) {
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
}
- }
- if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
dev_err(emu->card->dev,
"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
@@ -1335,100 +1255,78 @@ static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)
+ return 0;
}
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)
+ return 0;
}
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1x_intr_disable(emu, midi->rx_enable);
- midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1x_intr_disable(emu, midi->tx_enable);
- midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1449,7 +1347,6 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
@@ -1460,22 +1357,21 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
- spin_lock_irqsave(&midi->output_lock, flags);
- while (max > 0) {
- if (mpu401_output_ready(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ scoped_guard(spinlock_irqsave, &midi->output_lock) {
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
}
- mpu401_write_data(emu, midi, byte);
- max--;
- } else {
- break;
}
}
- spin_unlock_irqrestore(&midi->output_lock, flags);
snd_emu10k1x_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1x_intr_disable(emu, midi->tx_enable);
@@ -1514,13 +1410,14 @@ static int emu10k1x_midi_init(struct emu10k1x *emu,
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);
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -1537,7 +1434,8 @@ 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;
@@ -1549,8 +1447,8 @@ static int snd_emu10k1x_midi(struct emu10k1x *emu)
return 0;
}
-static int 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;
@@ -1564,64 +1462,58 @@ static int snd_emu10k1x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 1)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 2)) < 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);
- strcpy(card->driver, "EMU10K1X");
- strcpy(card->shortname, "Dell Sound Blaster Live!");
+ strscpy(card->driver, "EMU10K1X");
+ strscpy(card->shortname, "Dell Sound Blaster Live!");
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);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void 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));
+ return snd_card_free_on_error(&pci->dev, __snd_emu10k1x_probe(pci, pci_id));
}
// PCI IDs
@@ -1636,7 +1528,6 @@ static struct pci_driver emu10k1x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
- .remove = snd_emu10k1x_remove,
};
module_pci_driver(emu10k1x_driver);
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index a2b56b188be4..37af7bf76347 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1,41 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for effect processor FX8010
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * 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
*
+ * Routines for effect processor FX8010
*/
#include <linux/pci.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/vmalloc.h>
#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,26 +41,45 @@ MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");
* Tables
*/
-static char *fxbuses[16] = {
+// Playback channel labels; corresponds with the public FXBUS_* defines.
+// Unlike the tables below, this is not determined by the hardware.
+const char * const snd_emu10k1_fxbus[32] = {
/* 0x00 */ "PCM Left",
/* 0x01 */ "PCM Right",
- /* 0x02 */ "PCM Surround Left",
- /* 0x03 */ "PCM Surround Right",
+ /* 0x02 */ "PCM Rear Left",
+ /* 0x03 */ "PCM Rear Right",
/* 0x04 */ "MIDI Left",
/* 0x05 */ "MIDI Right",
- /* 0x06 */ "Center",
- /* 0x07 */ "LFE",
- /* 0x08 */ NULL,
- /* 0x09 */ NULL,
+ /* 0x06 */ "PCM Center",
+ /* 0x07 */ "PCM LFE",
+ /* 0x08 */ "PCM Front Left",
+ /* 0x09 */ "PCM Front Right",
/* 0x0a */ NULL,
/* 0x0b */ NULL,
/* 0x0c */ "MIDI Reverb",
/* 0x0d */ "MIDI Chorus",
- /* 0x0e */ NULL,
- /* 0x0f */ NULL
+ /* 0x0e */ "PCM Side Left",
+ /* 0x0f */ "PCM Side Right",
+ /* 0x10 */ NULL,
+ /* 0x11 */ NULL,
+ /* 0x12 */ NULL,
+ /* 0x13 */ NULL,
+ /* 0x14 */ "Passthrough Left",
+ /* 0x15 */ "Passthrough Right",
+ /* 0x16 */ NULL,
+ /* 0x17 */ NULL,
+ /* 0x18 */ NULL,
+ /* 0x19 */ NULL,
+ /* 0x1a */ NULL,
+ /* 0x1b */ NULL,
+ /* 0x1c */ NULL,
+ /* 0x1d */ NULL,
+ /* 0x1e */ NULL,
+ /* 0x1f */ NULL
};
-static char *creative_ins[16] = {
+// Physical inputs; corresponds with the public EXTIN_* defines.
+const char * const snd_emu10k1_sblive_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "TTL IEC958 Left",
@@ -97,7 +98,8 @@ static char *creative_ins[16] = {
/* 0x0f */ NULL
};
-static char *audigy_ins[16] = {
+// Physical inputs; corresponds with the public A_EXTIN_* defines.
+const char * const snd_emu10k1_audigy_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Audigy CD Left",
@@ -116,7 +118,8 @@ static char *audigy_ins[16] = {
/* 0x0f */ NULL
};
-static char *creative_outs[32] = {
+// Physical outputs; corresponds with the public EXTOUT_* defines.
+const char * const snd_emu10k1_sblive_outs[32] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Optical IEC958 Left",
@@ -133,6 +136,7 @@ static char *creative_outs[32] = {
/* 0x0d */ "AC97 Surround Left",
/* 0x0e */ "AC97 Surround Right",
/* 0x0f */ NULL,
+ // This is actually the FXBUS2 range; SB Live! 5.1 only.
/* 0x10 */ NULL,
/* 0x11 */ "Analog Center",
/* 0x12 */ "Analog LFE",
@@ -151,7 +155,8 @@ static char *creative_outs[32] = {
/* 0x1f */ NULL,
};
-static char *audigy_outs[32] = {
+// Physical outputs; corresponds with the public A_EXTOUT_* defines.
+const char * const snd_emu10k1_audigy_outs[32] = {
/* 0x00 */ "Digital Front Left",
/* 0x01 */ "Digital Front Right",
/* 0x02 */ "Digital Center",
@@ -170,7 +175,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,
@@ -186,6 +191,18 @@ static char *audigy_outs[32] = {
/* 0x1f */ NULL,
};
+// On the SB Live! 5.1, FXBUS2[1] and FXBUS2[2] are occupied by EXTOUT_ACENTER
+// and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+//
+// Since only 14 of the 16 EXTINs are used, this is not a big problem.
+// We route AC97 to FX capture 14 and 15, SPDIF_CD to FX capture 0 and 3,
+// and the rest of the EXTINs to the corresponding FX capture channel.
+// Multitrack recorders will still see the center/LFE output signal
+// on the second and third "input" channel.
+const s8 snd_emu10k1_sblive51_fxbus2_map[16] = {
+ 2, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1
+};
+
static const u32 bass_table[41][5] = {
{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
@@ -331,16 +348,12 @@ static int snd_emu10k1_gpr_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu10k1_gpr_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_fx8010_ctl *ctl =
(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
- unsigned long flags;
unsigned int i;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (i = 0; i < ctl->vcount; i++)
ucontrol->value.integer.value[i] = ctl->value[i];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -349,12 +362,10 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_fx8010_ctl *ctl =
(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
- unsigned long flags;
- unsigned int nval, val;
+ int nval, val;
unsigned int i, j;
int change = 0;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (i = 0; i < ctl->vcount; i++) {
nval = ucontrol->value.integer.value[i];
if (nval < ctl->min)
@@ -368,9 +379,16 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
case EMU10K1_GPR_TRANSLATION_NONE:
snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
break;
+ case EMU10K1_GPR_TRANSLATION_NEGATE:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, ~val);
+ break;
case EMU10K1_GPR_TRANSLATION_TABLE100:
snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
break;
+ case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0,
+ val == 100 ? 0x80000000 : -(int)db_table[val]);
+ break;
case EMU10K1_GPR_TRANSLATION_BASS:
if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
change = -EIO;
@@ -393,7 +411,6 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
}
}
__error:
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -421,19 +438,13 @@ 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;
irq->next = NULL;
- spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+ guard(spinlock_irqsave)(&emu->fx8010.irq_lock);
if (emu->fx8010.irq_handlers == NULL) {
emu->fx8010.irq_handlers = irq;
emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
@@ -442,9 +453,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
irq->next = emu->fx8010.irq_handlers;
emu->fx8010.irq_handlers = irq;
}
- spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- if (r_irq)
- *r_irq = irq;
return 0;
}
@@ -452,10 +460,10 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_irq *irq)
{
struct snd_emu10k1_fx8010_irq *tmp;
- unsigned long flags;
- spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
- if ((tmp = emu->fx8010.irq_handlers) == irq) {
+ guard(spinlock_irqsave)(&emu->fx8010.irq_lock);
+ 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);
@@ -467,8 +475,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
if (tmp)
tmp->next = tmp->next->next;
}
- spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- kfree(irq);
return 0;
}
@@ -483,7 +489,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);
@@ -500,7 +506,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);
@@ -533,8 +539,8 @@ static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
if (!test_bit(gpr, icode->gpr_valid))
continue;
if (in_kernel)
- val = *(u32 *)&icode->gpr_map[gpr];
- else if (get_user(val, &icode->gpr_map[gpr]))
+ 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);
}
@@ -550,7 +556,7 @@ 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;
@@ -567,11 +573,11 @@ static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
if (!test_bit(tram, icode->tram_valid))
continue;
if (in_kernel) {
- val = *(u32 *)&icode->tram_data_map[tram];
- addr = *(u32 *)&icode->tram_addr_map[tram];
+ val = icode->tram_data_map[tram];
+ addr = icode->tram_addr_map[tram];
} else {
- if (get_user(val, &icode->tram_data_map[tram]) ||
- get_user(addr, &icode->tram_addr_map[tram]))
+ 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);
@@ -601,8 +607,8 @@ 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;
@@ -618,11 +624,11 @@ static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
if (!test_bit(pc / 2, icode->code_valid))
continue;
if (in_kernel) {
- lo = *(u32 *)&icode->code[pc + 0];
- hi = *(u32 *)&icode->code[pc + 1];
+ lo = icode->code[pc + 0];
+ hi = icode->code[pc + 1];
} else {
- if (get_user(lo, &icode->code[pc + 0]) ||
- get_user(hi, &icode->code[pc + 1]))
+ 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);
@@ -639,25 +645,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;
@@ -673,7 +683,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
if (!_tlv)
return NULL;
if (in_kernel)
- memcpy(data, (void *)_tlv, sizeof(data));
+ memcpy(data, (__force void *)_tlv, sizeof(data));
else if (copy_from_user(data, _tlv, sizeof(data)))
return NULL;
if (data[1] >= MAX_TLV_SIZE)
@@ -683,7 +693,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
return NULL;
memcpy(tlv, data, sizeof(data));
if (in_kernel) {
- memcpy(tlv + 2, (void *)(_tlv + 2), data[1]);
+ memcpy(tlv + 2, (__force void *)(_tlv + 2), data[1]);
} else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
kfree(tlv);
return NULL;
@@ -692,41 +702,60 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
}
static int copy_gctl(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_control_gpr *gctl,
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+ 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)
- memcpy(gctl, (void *)&_gctl[idx], sizeof(*gctl));
- else if (copy_from_user(gctl, &_gctl[idx], sizeof(*gctl)))
+ *dst = src[idx];
+ else if (copy_from_user(dst, &_src[idx], sizeof(*src)))
return -EFAULT;
return 0;
}
- octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr *)src;
+ _octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)octl;
if (in_kernel)
- memcpy(gctl, (void *)&octl[idx], sizeof(*octl));
- else if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+ 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,
@@ -734,17 +763,16 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
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 (in_kernel)
- id = *(struct snd_ctl_elem_id *)_id;
- else 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;
}
@@ -760,15 +788,41 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
}
if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
continue;
- down_read(&emu->card->controls_rwsem);
- if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
- up_read(&emu->card->controls_rwsem);
+ gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
+ if (snd_ctl_find_id(emu->card, gctl_id)) {
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;
+ }
+ switch (gctl->translation) {
+ case EMU10K1_GPR_TRANSLATION_NONE:
+ case EMU10K1_GPR_TRANSLATION_NEGATE:
+ break;
+ case EMU10K1_GPR_TRANSLATION_TABLE100:
+ case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
+ if (gctl->min != 0 || gctl->max != 100) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ case EMU10K1_GPR_TRANSLATION_BASS:
+ case EMU10K1_GPR_TRANSLATION_TREBLE:
+ if (gctl->min != 0 || gctl->max != 40) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ case EMU10K1_GPR_TRANSLATION_ONOFF:
+ if (gctl->min != 0 || gctl->max != 1) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ default:
err = -EINVAL;
goto __error;
}
@@ -803,6 +857,7 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
{
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;
@@ -823,24 +878,25 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
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, in_kernel);
+ 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;
@@ -866,7 +922,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;
@@ -896,22 +954,19 @@ static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
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 (in_kernel)
- id = *(struct snd_ctl_elem_id *)_id;
- else if (copy_from_user(&id, _id, sizeof(id)))
- return -EFAULT;
- down_write(&card->controls_rwsem);
+ 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;
ctl = snd_emu10k1_look_for_ctl(emu, &id);
if (ctl)
snd_ctl_remove(card, ctl->kcontrol);
- up_write(&card->controls_rwsem);
}
return 0;
}
@@ -935,8 +990,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;
@@ -966,13 +1021,13 @@ static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_code *icode,
bool in_kernel)
{
- int err = 0;
+ int err;
- mutex_lock(&emu->fx8010.lock);
+ guard(mutex)(&emu->fx8010.lock);
err = snd_emu10k1_verify_controls(emu, icode, in_kernel);
if (err < 0)
- goto __error;
- strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+ return err;
+ 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)
@@ -982,27 +1037,25 @@ static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
/* ok, do the main job */
err = snd_emu10k1_del_controls(emu, icode, in_kernel);
if (err < 0)
- goto __error;
+ return err;
err = snd_emu10k1_gpr_poke(emu, icode, in_kernel);
if (err < 0)
- goto __error;
+ return err;
err = snd_emu10k1_tram_poke(emu, icode, in_kernel);
if (err < 0)
- goto __error;
+ return err;
err = snd_emu10k1_code_poke(emu, icode, in_kernel);
if (err < 0)
- goto __error;
+ return err;
err = snd_emu10k1_add_controls(emu, icode, in_kernel);
if (err < 0)
- goto __error;
+ return err;
/* start FX processor when the DSP code is updated */
if (emu->audigy)
snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
else
snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
- __error:
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
@@ -1010,8 +1063,8 @@ 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));
+ guard(mutex)(&emu->fx8010.lock);
+ strscpy(icode->name, emu->fx8010.name, sizeof(icode->name));
/* ok, do the main job */
err = snd_emu10k1_gpr_peek(emu, icode);
if (err >= 0)
@@ -1020,7 +1073,6 @@ static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
err = snd_emu10k1_code_peek(emu, icode);
if (err >= 0)
err = snd_emu10k1_list_controls(emu, icode);
- mutex_unlock(&emu->fx8010.lock);
return err;
}
@@ -1028,28 +1080,25 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
unsigned int i;
- int err = 0;
struct snd_emu10k1_fx8010_pcm *pcm;
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];
- mutex_lock(&emu->fx8010.lock);
- spin_lock_irq(&emu->reg_lock);
- if (pcm->opened) {
- err = -EBUSY;
- goto __error;
- }
+ guard(mutex)(&emu->fx8010.lock);
+ guard(spinlock_irq)(&emu->reg_lock);
+ if (pcm->opened)
+ return -EBUSY;
if (ipcm->channels == 0) { /* remove */
pcm->valid = 0;
} else {
/* FIXME: we need to add universal code to the PCM transfer routine */
- if (ipcm->channels != 2) {
- err = -EINVAL;
- goto __error;
- }
+ if (ipcm->channels != 2)
+ return -EINVAL;
pcm->valid = 1;
pcm->opened = 0;
pcm->channels = ipcm->channels;
@@ -1064,24 +1113,22 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
for (i = 0; i < pcm->channels; i++)
pcm->etram[i] = ipcm->etram[i];
}
- __error:
- spin_unlock_irq(&emu->reg_lock);
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
unsigned int i;
- int err = 0;
struct snd_emu10k1_fx8010_pcm *pcm;
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);
+ guard(mutex)(&emu->fx8010.lock);
+ guard(spinlock_irq)(&emu->reg_lock);
ipcm->channels = pcm->channels;
ipcm->tram_start = pcm->tram_start;
ipcm->buffer_size = pcm->buffer_size;
@@ -1095,9 +1142,7 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
ipcm->etram[i] = pcm->etram[i];
ipcm->res1 = ipcm->res2 = 0;
ipcm->pad = 0;
- spin_unlock_irq(&emu->reg_lock);
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
#define SND_EMU10K1_GPR_CONTROLS 44
@@ -1105,55 +1150,63 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
#define SND_EMU10K1_PLAYBACK_CHANNELS 8
#define SND_EMU10K1_CAPTURE_CHANNELS 4
+#define HR_VAL(v) ((v) * 0x80000000LL / 100 - 1)
+
static void
-snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
- const char *name, int gpr, int defval)
+snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
+ const char *name, int gpr, int defval, int defval_hr)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
if (high_res_gpr_volume) {
- ctl->min = 0;
+ ctl->min = -1;
ctl->max = 0x7fffffff;
ctl->tlv = snd_emu10k1_db_linear;
- ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+ defval = defval_hr;
} else {
ctl->min = 0;
ctl->max = 100;
ctl->tlv = snd_emu10k1_db_scale1;
- ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
}
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
}
+#define snd_emu10k1_init_mono_control(ctl, name, gpr, defval) \
+ snd_emu10k1_init_mono_control2(ctl, name, gpr, defval, HR_VAL(defval))
static void
-snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
- const char *name, int gpr, int defval)
+snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
+ const char *name, int gpr, int defval, int defval_hr)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
- ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
if (high_res_gpr_volume) {
- ctl->min = 0;
+ ctl->min = -1;
ctl->max = 0x7fffffff;
ctl->tlv = snd_emu10k1_db_linear;
- ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+ defval = defval_hr;
} else {
ctl->min = 0;
ctl->max = 100;
ctl->tlv = snd_emu10k1_db_scale1;
- ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
}
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
}
+#define snd_emu10k1_init_stereo_control(ctl, name, gpr, defval) \
+ snd_emu10k1_init_stereo_control2(ctl, name, gpr, defval, HR_VAL(defval))
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;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->min = 0;
@@ -1165,8 +1218,8 @@ 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;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
@@ -1176,39 +1229,54 @@ 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(
+static void snd_emu10k1_audigy_dsp_convert_32_to_2x16(
struct snd_emu10k1_fx8010_code *icode,
u32 *ptr, int tmp, int bit_shifter16,
int reg_in, int reg_out)
{
- A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000);
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000);
- A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2));
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000);
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000);
- A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000);
- A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2));
- A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000);
- return 1;
+ // This leaves the low word in place, which is fine,
+ // as the low bits are completely ignored subsequently.
+ // reg_out[1] = reg_in
+ A_OP(icode, ptr, iACC3, reg_out + 1, reg_in, A_C_00000000, A_C_00000000);
+ // It is fine to read reg_in multiple times.
+ // tmp = reg_in << 15
+ A_OP(icode, ptr, iMACINT1, A_GPR(tmp), A_C_00000000, reg_in, A_GPR(bit_shifter16));
+ // Left-shift once more. This is a separate step, as the
+ // signed multiplication would clobber the MSB.
+ // reg_out[0] = tmp + ((tmp << 31) >> 31)
+ A_OP(icode, ptr, iMAC3, reg_out, A_GPR(tmp), A_GPR(tmp), A_C_80000000);
}
+#define ENUM_GPR(name, size) name, name ## _dummy = name + (size) - 1
+
/*
* initial DSP configuration for Audigy
*/
static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
{
- int err, i, z, gpr, nctl;
- int bit_shifter16;
- const int playback = 10;
- const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
- const int stereo_mix = capture + 2;
- const int tmp = 0x88;
- u32 ptr;
+ int err, z, nctl;
+ enum {
+ ENUM_GPR(playback, SND_EMU10K1_PLAYBACK_CHANNELS),
+ ENUM_GPR(stereo_mix, 2),
+ ENUM_GPR(capture, 2),
+ ENUM_GPR(bit_shifter16, 1),
+ // The fixed allocation of these breaks the pattern, but why not.
+ // Splitting these into left/right is questionable, as it will break
+ // down for center/lfe. But it works for stereo/quadro, so whatever.
+ ENUM_GPR(bass_gpr, 2 * 5), // two sides, five coefficients
+ ENUM_GPR(treble_gpr, 2 * 5),
+ ENUM_GPR(bass_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4), // four delay stages
+ ENUM_GPR(treble_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4),
+ ENUM_GPR(tmp, 3),
+ num_static_gprs
+ };
+ int gpr = num_static_gprs;
+ u32 ptr, ptr_skip;
struct snd_emu10k1_fx8010_code *icode = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
u32 *gpr_map;
@@ -1218,8 +1286,8 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
if (!icode)
return err;
- icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024,
- sizeof(u_int32_t), GFP_KERNEL);
+ 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,
@@ -1227,64 +1295,55 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
if (!controls)
goto __err_ctrls;
- gpr_map = (u32 __force *)icode->gpr_map;
+ 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");
+ strscpy(icode->name, "Audigy DSP code for ALSA");
ptr = 0;
nctl = 0;
- gpr = stereo_mix + 10;
- gpr_map[gpr++] = 0x00007fff;
- gpr_map[gpr++] = 0x00008000;
- 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);
+ gpr_map[bit_shifter16] = 0x00008000;
#if 1
/* PCM front Playback Volume (independent from stereo mix)
- * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
- * where gpr contains attenuation from corresponding mixer control
+ * playback = -gpr * FXBUS_PCM_LEFT_FRONT >> 31
+ * where gpr contains negated attenuation from corresponding mixer control
* (snd_emu10k1_init_stereo_control)
*/
- A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
gpr += 2;
/* PCM Surround Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
gpr += 2;
/* PCM Side Playback (independent from stereo mix) */
if (emu->card_capabilities->spk71) {
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
gpr += 2;
}
/* PCM Center Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
gpr++;
/* PCM LFE Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
gpr++;
@@ -1292,159 +1351,174 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
* Stereo Mix
*/
/* Wave (PCM) Playback Volume (will be renamed later) */
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
gpr += 2;
/* Synth Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
gpr += 2;
/* Wave (PCM) Capture */
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
gpr += 2;
/* Synth Capture */
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
gpr += 2;
-
+
+ // We need to double the volume, as we configure the voices for half volume,
+ // which is necessary for bit-identical reproduction.
+ { static_assert(stereo_mix == playback + SND_EMU10K1_PLAYBACK_CHANNELS); }
+ for (z = 0; z < SND_EMU10K1_PLAYBACK_CHANNELS + 2; z++)
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + z), A_GPR(playback + z), A_GPR(playback + z), A_C_00000000);
+
/*
* inputs
*/
#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))
+ A_OP(icode, &ptr, iMAC1, 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);
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_GPR(tmp));
- A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x1), A_C_00000001);
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr), A_GPR(tmp));
+ // For unclear reasons, the EMU32IN cannot be the Y operand!
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A3_EMU32IN(0x0), A_GPR(gpr));
+ // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A3_EMU32IN(0x1), A_C_00000000, A_C_00000000);
} else {
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_P16VIN(0x0), A_GPR(gpr));
+ // A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
}
snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
- gpr += 2;
- }
- /* AC'97 Playback Volume - used only for mic (renamed later) */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
- gpr += 2;
- /* AC'97 Capture Volume - used only for mic */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
- gpr += 2;
+ gpr_map[gpr + 2] = 0x00000000;
+ gpr += 3;
+ } else {
+ if (emu->card_capabilities->ac97_chip) {
+ /* AC'97 Playback Volume - used only for mic (renamed later) */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
+ gpr += 2;
+ /* AC'97 Capture Volume - used only for mic */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* mic capture buffer */
+ 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));
+ }
- /* 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));
+ /* Audigy CD Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Audigy CD Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
+ gpr, 0);
+ gpr += 2;
- /* Audigy CD Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Audigy CD Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
- gpr, 0);
- gpr += 2;
+ /* Optical SPDIF Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
+ gpr += 2;
+ /* Optical SPDIF Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
+ gpr += 2;
- /* Optical SPDIF Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
- gpr += 2;
- /* Optical SPDIF Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
- gpr += 2;
+ /* Line2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Line2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
+ gpr, 0);
+ gpr += 2;
- /* Line2 Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Line2 Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
- gpr, 0);
- gpr += 2;
-
- /* Philips ADC Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
- gpr += 2;
- /* Philips ADC Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
- gpr += 2;
+ /* Philips ADC Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
+ gpr += 2;
+ /* Philips ADC Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
+ gpr += 2;
- /* Aux2 Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Aux2 Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
- gpr, 0);
- gpr += 2;
+ /* Aux2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Aux2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
+ gpr, 0);
+ gpr += 2;
+ }
/* Stereo Mix Front Playback Volume */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
gpr += 2;
/* Stereo Mix Surround Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
gpr += 2;
/* 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, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+ A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), A_C_40000000, A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, 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++;
/* Stereo Mix LFE Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
gpr++;
if (emu->card_capabilities->spk71) {
/* Stereo Mix Side Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
gpr += 2;
}
@@ -1469,21 +1543,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/*
* Process tone control
*/
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
- if (emu->card_capabilities->spk71) {
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
- }
-
-
ctl = &controls[nctl + 0];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
@@ -1491,44 +1553,47 @@ 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;
- strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(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->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
-
-#define BASS_GPR 0x8c
-#define TREBLE_GPR 0x96
-
for (z = 0; z < 5; z++) {
int j;
for (j = 0; j < 2; j++) {
- controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
- controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+ controls[nctl + 0].gpr[z * 2 + j] = bass_gpr + z * 2 + j;
+ controls[nctl + 1].gpr[z * 2 + j] = treble_gpr + z * 2 + j;
}
}
+ nctl += 2;
+
+ A_OP(icode, &ptr, iACC3, A_C_00000000, A_GPR(gpr), A_C_00000000, A_C_00000000);
+ snd_emu10k1_init_mono_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
+ gpr++;
+ A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_CC_REG_ZERO, A_GPR(gpr));
+ ptr_skip = ptr;
for (z = 0; z < 4; z++) { /* front/rear/center-lfe/side */
int j, k, l, d;
for (j = 0; j < 2; j++) { /* left/right */
- k = 0xb0 + (z * 8) + (j * 4);
- l = 0xe0 + (z * 8) + (j * 4);
- d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
-
- A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j));
- A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j));
+ k = bass_tmp + (z * 8) + (j * 4);
+ l = treble_tmp + (z * 8) + (j * 4);
+ d = playback + z * 2 + j;
+
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(bass_gpr + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(bass_gpr + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(bass_gpr + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(bass_gpr + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(bass_gpr + 6 + j));
A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);
- A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j));
- A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j));
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(treble_gpr + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(treble_gpr + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(treble_gpr + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(treble_gpr + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(treble_gpr + 6 + j));
A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);
A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);
@@ -1537,208 +1602,109 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
break;
}
}
- nctl += 2;
-
-#undef BASS_GPR
-#undef TREBLE_GPR
-
- for (z = 0; z < 8; z++) {
- A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
- A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
- A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
- }
- snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
- gpr += 2;
+ gpr_map[gpr++] = ptr - ptr_skip;
/* Master volume (will be renamed later) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS));
+ for (z = 0; z < 8; z++)
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+z), A_C_00000000, A_GPR(gpr), A_GPR(playback+z));
snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
- gpr += 2;
-
- /* analog speakers */
- A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
- if (emu->card_capabilities->spk71)
- A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
-
- /* headphone */
- A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+ gpr++;
- /* digital outputs */
- /* 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 */
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);
+ A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
} else {
- A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
}
}
- }
+ } else {
+ /* analog speakers */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback);
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2);
+ A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4);
+ A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5);
+ if (emu->card_capabilities->spk71)
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6);
- /* IEC958 Optical Raw Playback Switch */
- gpr_map[gpr++] = 0;
- gpr_map[gpr++] = 0x1008;
- gpr_map[gpr++] = 0xffff0000;
- for (z = 0; z < 2; z++) {
- A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
- A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
- A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
- A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
- A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
- A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
- 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 */
- 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 {
- A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ /* headphone */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback);
+
+ /* IEC958 Optical Raw Playback Switch */
+ gpr_map[gpr++] = 0;
+ gpr_map[gpr++] = 0x1008;
+ gpr_map[gpr++] = 0xffff0000;
+ for (z = 0; z < 2; z++) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
+ A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
+ A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
+ A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
+ A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+ A_SWITCH(icode, &ptr, tmp + 1, playback + 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 */
+ 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 {
+ A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ }
}
+ snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+ gpr += 2;
+
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2);
+ A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4);
+ A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5);
}
- snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
- gpr += 2;
-
- A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
/* ADC buffer */
#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
- A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback);
#else
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
#endif
if (emu->card_capabilities->emu_model) {
+ /* Capture 16 channels of S32_LE sound. */
if (emu->card_capabilities->ca0108_chip) {
dev_info(emu->card->dev, "EMU2 inputs on\n");
- for (z = 0; z < 0x10; z++) {
+ /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */
+
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+ icode, &ptr, tmp, bit_shifter16, A3_EMU32IN(0), A_FXBUS2(0));
+ // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ for (z = 1; z < 0x10; z++) {
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp,
bit_shifter16,
- A3_EMU32IN(z),
+ A_GPR(gpr),
A_FXBUS2(z*2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr), A3_EMU32IN(z), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
}
} else {
dev_info(emu->card->dev, "EMU inputs on\n");
- /* Capture 16 (originally 8) channels of S32_LE sound */
+ /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */
/*
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. */
- /* A_P16VIN(0) is delayed by one sample,
- * so all other A_P16VIN channels will need to also be delayed
- */
- /* Left ADC in. 1 of 2 */
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
- /* Right ADC in 1 of 2 */
- gpr_map[gpr++] = 0x00000000;
- /* Delaying by one sample: instead of copying the input
- * value A_P16VIN to output A_FXBUS2 as in the first channel,
- * we use an auxiliary register, delaying the value by one
- * sample
- */
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
- /* For 96kHz mode */
- /* Left ADC in. 2 of 2 */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
- /* Right ADC in 2 of 2 */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
- /* Pavel Hofman - we still have voices, A_FXBUS2s, and
- * A_P16VINs available -
- * let's add 8 more capture channels - total of 16
- */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x10));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x12));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x14));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x16));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x18));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1a));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1c));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1e));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
- A_C_00000000, A_C_00000000);
+ /* A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
+ * will need to also be delayed; we use an auxiliary register for that. */
+ for (z = 1; z < 0x10; z++) {
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr), A_FXBUS2(z * 2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr), A_P16VIN(z), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ }
}
#if 0
@@ -1762,17 +1728,18 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
* ok, set up done..
*/
- if (gpr > tmp) {
+ if (gpr > 512) {
snd_BUG();
err = -EIO;
goto __err;
}
+
/* clear remaining instruction memory */
while (ptr < 0x400)
A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
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, true);
emu->support_tlv = 0; /* clear again */
@@ -1780,7 +1747,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
__err:
kfree(controls);
__err_ctrls:
- kfree((void __force *)icode->gpr_map);
+ kfree(icode->gpr_map);
__err_gpr:
kfree(icode);
return err;
@@ -1791,30 +1758,14 @@ __err_gpr:
* initial DSP configuration for Emu10k1
*/
-/* when volume = max, then copy only to avoid volume modification */
-/* with iMAC0 (negative values) */
+/* Volumes are in the [-2^31, 0] range, zero being mute. */
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);
+ OP(icode, ptr, iMAC1, dst, C_00000000, src, 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);
- OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
- OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
- OP(icode, ptr, iMAC0, dst, dst, src, 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);
- OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
- OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
- OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+ OP(icode, ptr, iMAC1, dst, dst, src, vol);
}
#define VOLUME(icode, ptr, dst, src, vol) \
@@ -1826,7 +1777,7 @@ static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst
#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
_volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
#define VOLUME_OUT(icode, ptr, dst, src, vol) \
- _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+ _volume(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
#define _SWITCH(icode, ptr, dst, src, sw) \
OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
#define SWITCH(icode, ptr, dst, src, sw) \
@@ -1842,7 +1793,7 @@ static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst
static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
int err, i, z, gpr, tmp, playback, capture;
- u32 ptr;
+ u32 ptr, ptr_skip;
struct snd_emu10k1_fx8010_code *icode;
struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
@@ -1853,8 +1804,8 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
if (!icode)
return err;
- icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512,
- sizeof(u_int32_t), GFP_KERNEL);
+ icode->gpr_map = kcalloc(256 + 160 + 160 + 2 * 512,
+ sizeof(u_int32_t), GFP_KERNEL);
if (!icode->gpr_map)
goto __err_gpr;
@@ -1868,48 +1819,43 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
if (!ipcm)
goto __err_ipcm;
- gpr_map = (u32 __force *)icode->gpr_map;
+ 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");
+ strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
ptr = 0; i = 0;
/* we have 12 inputs */
playback = SND_EMU10K1_INPUTS;
/* we have 6 playback channels and tone control doubles */
- capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+ capture = playback + SND_EMU10K1_PLAYBACK_CHANNELS;
gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
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
*/
- OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000008);
OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */
OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */
- OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000008);
/* Raw S/PDIF PCM */
ipcm->substream = 0;
@@ -2003,7 +1949,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* Wave Center/LFE Playback Volume */
OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
- OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+ OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000004);
VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
@@ -2194,16 +2140,9 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/*
* Process tone control
*/
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
- 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;
- strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
@@ -2212,8 +2151,8 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
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;
- strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Treble");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
@@ -2232,12 +2171,19 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
}
}
+ i += 2;
+
+ OP(icode, &ptr, iACC3, C_00000000, GPR(gpr), C_00000000, C_00000000);
+ snd_emu10k1_init_mono_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+ gpr++;
+ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr));
+ ptr_skip = ptr;
for (z = 0; z < 3; z++) { /* front/rear/center-lfe */
int j, k, l, d;
for (j = 0; j < 2; j++) { /* left/right */
k = 0xa0 + (z * 8) + (j * 4);
l = 0xd0 + (z * 8) + (j * 4);
- d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+ d = playback + z * 2 + j;
OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
@@ -2259,20 +2205,11 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
break;
}
}
- i += 2;
+ gpr_map[gpr++] = ptr - ptr_skip;
#undef BASS_GPR
#undef TREBLE_GPR
- for (z = 0; z < 6; z++) {
- SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
- SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
- SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
- }
- snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
- gpr += 2;
-
/*
* Process outputs
*/
@@ -2280,7 +2217,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* AC'97 Playback Volume */
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + z), C_00000000, C_00000000);
}
if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
@@ -2289,7 +2226,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
for (z = 0; z < 2; z++) {
SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
- SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
@@ -2304,9 +2241,9 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* Headphone Playback Volume */
for (z = 0; z < 2; z++) {
- SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+ SWITCH(icode, &ptr, tmp + 0, playback + 4 + z, gpr + 2 + z);
SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
- SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
}
@@ -2323,29 +2260,29 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + 2 + z), C_00000000, C_00000000);
if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_REAR_L)|(1<<EXTOUT_AC97_REAR_R)))
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + 2 + z), C_00000000, C_00000000);
if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_CENTER)) {
#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + 4), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + 4), C_00000000, C_00000000);
#else
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + 0), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + 0), C_00000000, C_00000000);
#endif
}
if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_LFE)) {
#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + 5), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + 5), C_00000000, C_00000000);
#else
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + 1), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + 1), C_00000000, C_00000000);
#endif
}
@@ -2359,21 +2296,11 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* EFX capture - capture the 16 EXTINS */
if (emu->card_capabilities->sblive51) {
- /* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
- * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
- *
- * Since only 14 of the 16 EXTINs are used, this is not a big problem.
- * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture
- * 0 and 3, then the rest of the EXTINs to the corresponding FX capture
- * channel. Multitrack recorders will still see the center/lfe output signal
- * on the second and third channels.
- */
- OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
- OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
- OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
- OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
- for (z = 4; z < 14; z++)
- OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+ for (z = 0; z < 16; z++) {
+ s8 c = snd_emu10k1_sblive51_fxbus2_map[z];
+ if (c != -1)
+ OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(c));
+ }
} else {
for (z = 0; z < 16; z++)
OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
@@ -2395,10 +2322,11 @@ static int _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;
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, true);
emu->support_tlv = 0; /* clear again */
@@ -2409,7 +2337,7 @@ __err:
__err_ipcm:
kfree(controls);
__err_ctrls:
- kfree((void __force *)icode->gpr_map);
+ kfree(icode->gpr_map);
__err_gpr:
kfree(icode);
return err;
@@ -2468,11 +2396,11 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
}
if ((emu->fx8010.etram_pages.bytes / 2) == size)
return 0;
- spin_lock_irq(&emu->emu_lock);
- outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
- spin_unlock_irq(&emu->emu_lock);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+ }
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;
@@ -2480,15 +2408,15 @@ 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);
snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
- spin_lock_irq(&emu->emu_lock);
- outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
- spin_unlock_irq(&emu->emu_lock);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+ }
}
return 0;
@@ -2499,7 +2427,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);
@@ -2510,20 +2438,19 @@ 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;
info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
- 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;
+ fxbus = snd_emu10k1_fxbus;
+ extin = emu->audigy ? snd_emu10k1_audigy_ins : snd_emu10k1_sblive_ins;
+ extout = emu->audigy ? snd_emu10k1_audigy_outs : snd_emu10k1_sblive_outs;
+ 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);
}
@@ -2547,7 +2474,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);
@@ -2601,9 +2528,9 @@ 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;
- mutex_lock(&emu->fx8010.lock);
- res = snd_emu10k1_fx8010_tram_setup(emu, addr);
- mutex_unlock(&emu->fx8010.lock);
+ scoped_guard(mutex, &emu->fx8010.lock) {
+ res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+ }
return res;
case SNDRV_EMU10K1_IOCTL_STOP:
if (!capable(CAP_SYS_ADMIN))
@@ -2639,17 +2566,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)
@@ -2673,9 +2602,10 @@ int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device)
struct snd_hwdep *hw;
int err;
- 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)");
+ strscpy(hw->name, "EMU10K1 (FX8010)");
hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
hw->ops.open = snd_emu10k1_fx8010_open;
hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
@@ -2690,16 +2620,16 @@ 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 b2219a73c17c..f4906ab30c02 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1,37 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / mixer routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * 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
*
+ * Routines for control of EMU10K1 chips / mixer routines
*/
#include <linux/time.h>
#include <linux/init.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
@@ -43,6 +24,24 @@
static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
+
+static int add_ctls(struct snd_emu10k1 *emu, const struct snd_kcontrol_new *tpl,
+ const char * const *ctls, unsigned nctls)
+{
+ struct snd_kcontrol_new kctl = *tpl;
+ int err;
+
+ for (unsigned i = 0; i < nctls; i++) {
+ kctl.name = ctls[i];
+ kctl.private_value = i;
+ err = snd_ctl_add(emu->card, snd_ctl_new1(&kctl, emu));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
@@ -55,17 +54,14 @@ static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned long flags;
/* Limit: emu->spdif_bits */
if (idx >= 3)
return -EINVAL;
- spin_lock_irqsave(&emu->reg_lock, flags);
ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -79,292 +75,354 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
+#define PAIR_PS(base, one, two, sfx) base " " one sfx, base " " two sfx
+#define LR_PS(base, sfx) PAIR_PS(base, "Left", "Right", sfx)
+
+#define ADAT_PS(pfx, sfx) \
+ pfx "ADAT 0" sfx, pfx "ADAT 1" sfx, pfx "ADAT 2" sfx, pfx "ADAT 3" sfx, \
+ pfx "ADAT 4" sfx, pfx "ADAT 5" sfx, pfx "ADAT 6" sfx, pfx "ADAT 7" sfx
+
+#define PAIR_REGS(base, one, two) \
+ base ## one ## 1, \
+ base ## two ## 1
+
+#define LR_REGS(base) PAIR_REGS(base, _LEFT, _RIGHT)
+
+#define ADAT_REGS(base) \
+ base+0, base+1, base+2, base+3, base+4, base+5, base+6, base+7
+
/*
- * Items labels in enum mixer controls assigning source data to
- * each destination
+ * List of data sources available for each destination
*/
+
+#define DSP_TEXTS \
+ "DSP 0", "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", \
+ "DSP 8", "DSP 9", "DSP 10", "DSP 11", "DSP 12", "DSP 13", "DSP 14", "DSP 15", \
+ "DSP 16", "DSP 17", "DSP 18", "DSP 19", "DSP 20", "DSP 21", "DSP 22", "DSP 23", \
+ "DSP 24", "DSP 25", "DSP 26", "DSP 27", "DSP 28", "DSP 29", "DSP 30", "DSP 31"
+
+#define PAIR_TEXTS(base, one, two) PAIR_PS(base, one, two, "")
+#define LR_TEXTS(base) LR_PS(base, "")
+#define ADAT_TEXTS(pfx) ADAT_PS(pfx, "")
+
+#define EMU32_SRC_REGS \
+ EMU_SRC_ALICE_EMU32A, \
+ EMU_SRC_ALICE_EMU32A+1, \
+ EMU_SRC_ALICE_EMU32A+2, \
+ EMU_SRC_ALICE_EMU32A+3, \
+ EMU_SRC_ALICE_EMU32A+4, \
+ EMU_SRC_ALICE_EMU32A+5, \
+ EMU_SRC_ALICE_EMU32A+6, \
+ EMU_SRC_ALICE_EMU32A+7, \
+ EMU_SRC_ALICE_EMU32A+8, \
+ EMU_SRC_ALICE_EMU32A+9, \
+ EMU_SRC_ALICE_EMU32A+0xa, \
+ EMU_SRC_ALICE_EMU32A+0xb, \
+ EMU_SRC_ALICE_EMU32A+0xc, \
+ EMU_SRC_ALICE_EMU32A+0xd, \
+ EMU_SRC_ALICE_EMU32A+0xe, \
+ EMU_SRC_ALICE_EMU32A+0xf, \
+ EMU_SRC_ALICE_EMU32B, \
+ EMU_SRC_ALICE_EMU32B+1, \
+ EMU_SRC_ALICE_EMU32B+2, \
+ EMU_SRC_ALICE_EMU32B+3, \
+ EMU_SRC_ALICE_EMU32B+4, \
+ EMU_SRC_ALICE_EMU32B+5, \
+ EMU_SRC_ALICE_EMU32B+6, \
+ EMU_SRC_ALICE_EMU32B+7, \
+ EMU_SRC_ALICE_EMU32B+8, \
+ EMU_SRC_ALICE_EMU32B+9, \
+ EMU_SRC_ALICE_EMU32B+0xa, \
+ EMU_SRC_ALICE_EMU32B+0xb, \
+ EMU_SRC_ALICE_EMU32B+0xc, \
+ EMU_SRC_ALICE_EMU32B+0xd, \
+ EMU_SRC_ALICE_EMU32B+0xe, \
+ EMU_SRC_ALICE_EMU32B+0xf
+
+/* 1010 rev1 */
+
+#define EMU1010_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Dock Mic", "A", "B"), \
+ LR_TEXTS("Dock ADC1"), \
+ LR_TEXTS("Dock ADC2"), \
+ LR_TEXTS("Dock ADC3"), \
+ LR_TEXTS("0202 ADC"), \
+ LR_TEXTS("1010 SPDIF"), \
+ ADAT_TEXTS("1010 ")
+
static const char * const emu1010_src_texts[] = {
- "Silence",
- "Dock Mic A",
- "Dock Mic B",
- "Dock ADC1 Left",
- "Dock ADC1 Right",
- "Dock ADC2 Left",
- "Dock ADC2 Right",
- "Dock ADC3 Left",
- "Dock ADC3 Right",
- "0202 ADC Left",
- "0202 ADC Right",
- "0202 SPDIF Left",
- "0202 SPDIF Right",
- "ADAT 0",
- "ADAT 1",
- "ADAT 2",
- "ADAT 3",
- "ADAT 4",
- "ADAT 5",
- "ADAT 6",
- "ADAT 7",
- "DSP 0",
- "DSP 1",
- "DSP 2",
- "DSP 3",
- "DSP 4",
- "DSP 5",
- "DSP 6",
- "DSP 7",
- "DSP 8",
- "DSP 9",
- "DSP 10",
- "DSP 11",
- "DSP 12",
- "DSP 13",
- "DSP 14",
- "DSP 15",
- "DSP 16",
- "DSP 17",
- "DSP 18",
- "DSP 19",
- "DSP 20",
- "DSP 21",
- "DSP 22",
- "DSP 23",
- "DSP 24",
- "DSP 25",
- "DSP 26",
- "DSP 27",
- "DSP 28",
- "DSP 29",
- "DSP 30",
- "DSP 31",
+ EMU1010_COMMON_TEXTS,
+ DSP_TEXTS,
};
+static const unsigned short emu1010_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_DOCK_ADC3),
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ ADAT_REGS(EMU_SRC_HANA_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1010_src_regs) == ARRAY_SIZE(emu1010_src_texts));
+
+/* 1010 rev2 */
+
+#define EMU1010b_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Dock Mic", "A", "B"), \
+ LR_TEXTS("Dock ADC1"), \
+ LR_TEXTS("Dock ADC2"), \
+ LR_TEXTS("0202 ADC"), \
+ LR_TEXTS("Dock SPDIF"), \
+ LR_TEXTS("1010 SPDIF"), \
+ ADAT_TEXTS("Dock "), \
+ ADAT_TEXTS("1010 ")
+
+static const char * const emu1010b_src_texts[] = {
+ EMU1010b_COMMON_TEXTS,
+ DSP_TEXTS,
+};
+
+static const unsigned short emu1010b_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_MDOCK_SPDIF),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ ADAT_REGS(EMU_SRC_MDOCK_ADAT),
+ ADAT_REGS(EMU_SRC_HANA_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1010b_src_regs) == ARRAY_SIZE(emu1010b_src_texts));
+
/* 1616(m) cardbus */
+#define EMU1616_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Mic", "A", "B"), \
+ LR_TEXTS("ADC1"), \
+ LR_TEXTS("ADC2"), \
+ LR_TEXTS("SPDIF"), \
+ ADAT_TEXTS("")
+
static const char * const emu1616_src_texts[] = {
- "Silence",
- "Dock Mic A",
- "Dock Mic B",
- "Dock ADC1 Left",
- "Dock ADC1 Right",
- "Dock ADC2 Left",
- "Dock ADC2 Right",
- "Dock SPDIF Left",
- "Dock SPDIF Right",
- "ADAT 0",
- "ADAT 1",
- "ADAT 2",
- "ADAT 3",
- "ADAT 4",
- "ADAT 5",
- "ADAT 6",
- "ADAT 7",
- "DSP 0",
- "DSP 1",
- "DSP 2",
- "DSP 3",
- "DSP 4",
- "DSP 5",
- "DSP 6",
- "DSP 7",
- "DSP 8",
- "DSP 9",
- "DSP 10",
- "DSP 11",
- "DSP 12",
- "DSP 13",
- "DSP 14",
- "DSP 15",
- "DSP 16",
- "DSP 17",
- "DSP 18",
- "DSP 19",
- "DSP 20",
- "DSP 21",
- "DSP 22",
- "DSP 23",
- "DSP 24",
- "DSP 25",
- "DSP 26",
- "DSP 27",
- "DSP 28",
- "DSP 29",
- "DSP 30",
- "DSP 31",
+ EMU1616_COMMON_TEXTS,
+ DSP_TEXTS,
};
+static const unsigned short emu1616_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_MDOCK_SPDIF),
+ ADAT_REGS(EMU_SRC_MDOCK_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1616_src_regs) == ARRAY_SIZE(emu1616_src_texts));
-/*
- * List of data sources available for each destination
- */
-static unsigned int emu1010_src_regs[] = {
- EMU_SRC_SILENCE,/* 0 */
- EMU_SRC_DOCK_MIC_A1, /* 1 */
- EMU_SRC_DOCK_MIC_B1, /* 2 */
- EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
- EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
- EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
- EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
- EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
- EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
- EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
- EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
- EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
- EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
- EMU_SRC_HANA_ADAT, /* 13 */
- EMU_SRC_HANA_ADAT+1, /* 14 */
- EMU_SRC_HANA_ADAT+2, /* 15 */
- EMU_SRC_HANA_ADAT+3, /* 16 */
- EMU_SRC_HANA_ADAT+4, /* 17 */
- EMU_SRC_HANA_ADAT+5, /* 18 */
- EMU_SRC_HANA_ADAT+6, /* 19 */
- EMU_SRC_HANA_ADAT+7, /* 20 */
- EMU_SRC_ALICE_EMU32A, /* 21 */
- EMU_SRC_ALICE_EMU32A+1, /* 22 */
- EMU_SRC_ALICE_EMU32A+2, /* 23 */
- EMU_SRC_ALICE_EMU32A+3, /* 24 */
- EMU_SRC_ALICE_EMU32A+4, /* 25 */
- EMU_SRC_ALICE_EMU32A+5, /* 26 */
- EMU_SRC_ALICE_EMU32A+6, /* 27 */
- EMU_SRC_ALICE_EMU32A+7, /* 28 */
- EMU_SRC_ALICE_EMU32A+8, /* 29 */
- EMU_SRC_ALICE_EMU32A+9, /* 30 */
- EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
- EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
- EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
- EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
- EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
- EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
- EMU_SRC_ALICE_EMU32B, /* 37 */
- EMU_SRC_ALICE_EMU32B+1, /* 38 */
- EMU_SRC_ALICE_EMU32B+2, /* 39 */
- EMU_SRC_ALICE_EMU32B+3, /* 40 */
- EMU_SRC_ALICE_EMU32B+4, /* 41 */
- EMU_SRC_ALICE_EMU32B+5, /* 42 */
- EMU_SRC_ALICE_EMU32B+6, /* 43 */
- EMU_SRC_ALICE_EMU32B+7, /* 44 */
- EMU_SRC_ALICE_EMU32B+8, /* 45 */
- EMU_SRC_ALICE_EMU32B+9, /* 46 */
- EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
- EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
- EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
- EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
- EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
- EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+/* 0404 rev1 & rev2 */
+
+#define EMU0404_COMMON_TEXTS \
+ "Silence", \
+ LR_TEXTS("ADC"), \
+ LR_TEXTS("SPDIF")
+
+static const char * const emu0404_src_texts[] = {
+ EMU0404_COMMON_TEXTS,
+ DSP_TEXTS,
};
-/* 1616(m) cardbus */
-static unsigned int emu1616_src_regs[] = {
+static const unsigned short emu0404_src_regs[] = {
EMU_SRC_SILENCE,
- EMU_SRC_DOCK_MIC_A1,
- EMU_SRC_DOCK_MIC_B1,
- EMU_SRC_DOCK_ADC1_LEFT1,
- EMU_SRC_DOCK_ADC1_RIGHT1,
- EMU_SRC_DOCK_ADC2_LEFT1,
- EMU_SRC_DOCK_ADC2_RIGHT1,
- EMU_SRC_MDOCK_SPDIF_LEFT1,
- EMU_SRC_MDOCK_SPDIF_RIGHT1,
- EMU_SRC_MDOCK_ADAT,
- EMU_SRC_MDOCK_ADAT+1,
- EMU_SRC_MDOCK_ADAT+2,
- EMU_SRC_MDOCK_ADAT+3,
- EMU_SRC_MDOCK_ADAT+4,
- EMU_SRC_MDOCK_ADAT+5,
- EMU_SRC_MDOCK_ADAT+6,
- EMU_SRC_MDOCK_ADAT+7,
- EMU_SRC_ALICE_EMU32A,
- EMU_SRC_ALICE_EMU32A+1,
- EMU_SRC_ALICE_EMU32A+2,
- EMU_SRC_ALICE_EMU32A+3,
- EMU_SRC_ALICE_EMU32A+4,
- EMU_SRC_ALICE_EMU32A+5,
- EMU_SRC_ALICE_EMU32A+6,
- EMU_SRC_ALICE_EMU32A+7,
- EMU_SRC_ALICE_EMU32A+8,
- EMU_SRC_ALICE_EMU32A+9,
- EMU_SRC_ALICE_EMU32A+0xa,
- EMU_SRC_ALICE_EMU32A+0xb,
- EMU_SRC_ALICE_EMU32A+0xc,
- EMU_SRC_ALICE_EMU32A+0xd,
- EMU_SRC_ALICE_EMU32A+0xe,
- EMU_SRC_ALICE_EMU32A+0xf,
- EMU_SRC_ALICE_EMU32B,
- EMU_SRC_ALICE_EMU32B+1,
- EMU_SRC_ALICE_EMU32B+2,
- EMU_SRC_ALICE_EMU32B+3,
- EMU_SRC_ALICE_EMU32B+4,
- EMU_SRC_ALICE_EMU32B+5,
- EMU_SRC_ALICE_EMU32B+6,
- EMU_SRC_ALICE_EMU32B+7,
- EMU_SRC_ALICE_EMU32B+8,
- EMU_SRC_ALICE_EMU32B+9,
- EMU_SRC_ALICE_EMU32B+0xa,
- EMU_SRC_ALICE_EMU32B+0xb,
- EMU_SRC_ALICE_EMU32B+0xc,
- EMU_SRC_ALICE_EMU32B+0xd,
- EMU_SRC_ALICE_EMU32B+0xe,
- EMU_SRC_ALICE_EMU32B+0xf,
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ EMU32_SRC_REGS,
};
+static_assert(ARRAY_SIZE(emu0404_src_regs) == ARRAY_SIZE(emu0404_src_texts));
/*
* Data destinations - physical EMU outputs.
* Each destination has an enum mixer control to choose a data source
*/
-static unsigned int emu1010_output_dst[] = {
- EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
- EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
- EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
- EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
- EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
- EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
- EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
- EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
- EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
- EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
- EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
- EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
- EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
- EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
- EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
- EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
- EMU_DST_HANA_ADAT, /* 16 */
- EMU_DST_HANA_ADAT+1, /* 17 */
- EMU_DST_HANA_ADAT+2, /* 18 */
- EMU_DST_HANA_ADAT+3, /* 19 */
- EMU_DST_HANA_ADAT+4, /* 20 */
- EMU_DST_HANA_ADAT+5, /* 21 */
- EMU_DST_HANA_ADAT+6, /* 22 */
- EMU_DST_HANA_ADAT+7, /* 23 */
+
+#define LR_CTLS(base) LR_PS(base, " Playback Enum")
+#define ADAT_CTLS(pfx) ADAT_PS(pfx, " Playback Enum")
+
+/* 1010 rev1 */
+
+static const char * const emu1010_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock DAC4"),
+ LR_CTLS("Dock Phones"),
+ LR_CTLS("Dock SPDIF"),
+ LR_CTLS("0202 DAC"),
+ LR_CTLS("1010 SPDIF"),
+ ADAT_CTLS("1010 "),
+};
+static_assert(ARRAY_SIZE(emu1010_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1010_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_DOCK_DAC4),
+ LR_REGS(EMU_DST_DOCK_PHONES),
+ LR_REGS(EMU_DST_DOCK_SPDIF),
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+ ADAT_REGS(EMU_DST_HANA_ADAT),
+};
+static_assert(ARRAY_SIZE(emu1010_output_dst) == ARRAY_SIZE(emu1010_output_texts));
+
+static const unsigned short emu1010_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+};
+static_assert(ARRAY_SIZE(emu1010_output_dflt) == ARRAY_SIZE(emu1010_output_dst));
+
+/* 1010 rev2 */
+
+static const char * const snd_emu1010b_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock SPDIF"),
+ ADAT_CTLS("Dock "),
+ LR_CTLS("0202 DAC"),
+ LR_CTLS("1010 SPDIF"),
+ ADAT_CTLS("1010 "),
+};
+static_assert(ARRAY_SIZE(snd_emu1010b_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1010b_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_MDOCK_SPDIF),
+ ADAT_REGS(EMU_DST_MDOCK_ADAT),
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+ ADAT_REGS(EMU_DST_HANA_ADAT),
+};
+static_assert(ARRAY_SIZE(emu1010b_output_dst) == ARRAY_SIZE(snd_emu1010b_output_texts));
+
+static const unsigned short emu1010b_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
};
/* 1616(m) cardbus */
-static unsigned int emu1616_output_dst[] = {
- EMU_DST_DOCK_DAC1_LEFT1,
- EMU_DST_DOCK_DAC1_RIGHT1,
- EMU_DST_DOCK_DAC2_LEFT1,
- EMU_DST_DOCK_DAC2_RIGHT1,
- EMU_DST_DOCK_DAC3_LEFT1,
- EMU_DST_DOCK_DAC3_RIGHT1,
- EMU_DST_MDOCK_SPDIF_LEFT1,
- EMU_DST_MDOCK_SPDIF_RIGHT1,
- EMU_DST_MDOCK_ADAT,
- EMU_DST_MDOCK_ADAT+1,
- EMU_DST_MDOCK_ADAT+2,
- EMU_DST_MDOCK_ADAT+3,
- EMU_DST_MDOCK_ADAT+4,
- EMU_DST_MDOCK_ADAT+5,
- EMU_DST_MDOCK_ADAT+6,
- EMU_DST_MDOCK_ADAT+7,
- EMU_DST_MANA_DAC_LEFT,
- EMU_DST_MANA_DAC_RIGHT,
+
+static const char * const snd_emu1616_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock SPDIF"),
+ ADAT_CTLS("Dock "),
+ LR_CTLS("Mana DAC"),
+};
+static_assert(ARRAY_SIZE(snd_emu1616_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1616_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_MDOCK_SPDIF),
+ ADAT_REGS(EMU_DST_MDOCK_ADAT),
+ EMU_DST_MANA_DAC_LEFT, EMU_DST_MANA_DAC_RIGHT,
+};
+static_assert(ARRAY_SIZE(emu1616_output_dst) == ARRAY_SIZE(snd_emu1616_output_texts));
+
+static const unsigned short emu1616_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
};
+static_assert(ARRAY_SIZE(emu1616_output_dflt) == ARRAY_SIZE(emu1616_output_dst));
+
+/* 0404 rev1 & rev2 */
+
+static const char * const snd_emu0404_output_texts[] = {
+ LR_CTLS("DAC"),
+ LR_CTLS("SPDIF"),
+};
+static_assert(ARRAY_SIZE(snd_emu0404_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu0404_output_dst[] = {
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+};
+static_assert(ARRAY_SIZE(emu0404_output_dst) == ARRAY_SIZE(snd_emu0404_output_texts));
+
+static const unsigned short emu0404_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+};
+static_assert(ARRAY_SIZE(emu0404_output_dflt) == ARRAY_SIZE(emu0404_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 char * const emu1010_input_texts[] = {
+ "DSP 0 Capture Enum",
+ "DSP 1 Capture Enum",
+ "DSP 2 Capture Enum",
+ "DSP 3 Capture Enum",
+ "DSP 4 Capture Enum",
+ "DSP 5 Capture Enum",
+ "DSP 6 Capture Enum",
+ "DSP 7 Capture Enum",
+ "DSP 8 Capture Enum",
+ "DSP 9 Capture Enum",
+ "DSP A Capture Enum",
+ "DSP B Capture Enum",
+ "DSP C Capture Enum",
+ "DSP D Capture Enum",
+ "DSP E Capture Enum",
+ "DSP F Capture Enum",
+ /* These exist only on rev1 EMU1010 cards. */
+ "DSP 10 Capture Enum",
+ "DSP 11 Capture Enum",
+ "DSP 12 Capture Enum",
+ "DSP 13 Capture Enum",
+ "DSP 14 Capture Enum",
+ "DSP 15 Capture Enum",
+};
+static_assert(ARRAY_SIZE(emu1010_input_texts) <= NUM_INPUT_DESTS);
+
+static const unsigned short emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_0,
EMU_DST_ALICE2_EMU32_1,
EMU_DST_ALICE2_EMU32_2,
@@ -381,6 +439,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,
@@ -388,29 +447,199 @@ static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE_I2S2_LEFT,
EMU_DST_ALICE_I2S2_RIGHT,
};
+static_assert(ARRAY_SIZE(emu1010_input_dst) == ARRAY_SIZE(emu1010_input_texts));
+
+static const unsigned short emu1010_input_dflt[] = {
+ EMU_SRC_DOCK_MIC_A1,
+ EMU_SRC_DOCK_MIC_B1,
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+ /* Pavel Hofman - setting defaults for all capture channels.
+ * Defaults only, users will set their own values anyways, let's
+ * just copy/paste. */
+ EMU_SRC_DOCK_MIC_A1,
+ EMU_SRC_DOCK_MIC_B1,
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+ EMU_SRC_DOCK_ADC3_LEFT1,
+ EMU_SRC_DOCK_ADC3_RIGHT1,
+};
+static_assert(ARRAY_SIZE(emu1010_input_dflt) == ARRAY_SIZE(emu1010_input_dst));
+
+static const unsigned short emu0404_input_dflt[] = {
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_HANA_SPDIF_LEFT1,
+ EMU_SRC_HANA_SPDIF_RIGHT1,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+};
+
+struct snd_emu1010_routing_info {
+ const char * const *src_texts;
+ const char * const *out_texts;
+ const unsigned short *src_regs;
+ const unsigned short *out_regs;
+ const unsigned short *in_regs;
+ const unsigned short *out_dflts;
+ const unsigned short *in_dflts;
+ unsigned n_srcs;
+ unsigned n_outs;
+ unsigned n_ins;
+};
+
+static const struct snd_emu1010_routing_info emu1010_routing_info[] = {
+ {
+ /* rev1 1010 */
+ .src_regs = emu1010_src_regs,
+ .src_texts = emu1010_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1010_src_texts),
+
+ .out_dflts = emu1010_output_dflt,
+ .out_regs = emu1010_output_dst,
+ .out_texts = emu1010_output_texts,
+ .n_outs = ARRAY_SIZE(emu1010_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst),
+ },
+ {
+ /* rev2 1010 */
+ .src_regs = emu1010b_src_regs,
+ .src_texts = emu1010b_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1010b_src_texts),
+
+ .out_dflts = emu1010b_output_dflt,
+ .out_regs = emu1010b_output_dst,
+ .out_texts = snd_emu1010b_output_texts,
+ .n_outs = ARRAY_SIZE(emu1010b_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+ {
+ /* 1616(m) cardbus */
+ .src_regs = emu1616_src_regs,
+ .src_texts = emu1616_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1616_src_texts),
+
+ .out_dflts = emu1616_output_dflt,
+ .out_regs = emu1616_output_dst,
+ .out_texts = snd_emu1616_output_texts,
+ .n_outs = ARRAY_SIZE(emu1616_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+ {
+ /* 0404 */
+ .src_regs = emu0404_src_regs,
+ .src_texts = emu0404_src_texts,
+ .n_srcs = ARRAY_SIZE(emu0404_src_texts),
+
+ .out_dflts = emu0404_output_dflt,
+ .out_regs = emu0404_output_dst,
+ .out_texts = snd_emu0404_output_texts,
+ .n_outs = ARRAY_SIZE(emu0404_output_dflt),
+
+ .in_dflts = emu0404_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+};
+
+static unsigned emu1010_idx(struct snd_emu10k1 *emu)
+{
+ return emu->card_capabilities->emu_model - 1;
+}
+
+static void snd_emu1010_output_source_apply(struct snd_emu10k1 *emu,
+ int channel, int src)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu_ri->out_regs[channel], emu_ri->src_regs[src]);
+}
+
+static void snd_emu1010_input_source_apply(struct snd_emu10k1 *emu,
+ int channel, int src)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu_ri->in_regs[channel], emu_ri->src_regs[src]);
+}
+
+static void snd_emu1010_apply_sources(struct snd_emu10k1 *emu)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ for (unsigned i = 0; i < emu_ri->n_outs; i++)
+ snd_emu1010_output_source_apply(
+ emu, i, emu->emu1010.output_source[i]);
+ for (unsigned i = 0; i < emu_ri->n_ins; i++)
+ snd_emu1010_input_source_apply(
+ emu, i, emu->emu1010.input_source[i]);
+}
+
+static u8 emu1010_map_source(const struct snd_emu1010_routing_info *emu_ri,
+ unsigned val)
+{
+ for (unsigned i = 0; i < emu_ri->n_srcs; i++)
+ if (val == emu_ri->src_regs[i])
+ return i;
+ return 0;
+}
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);
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
- 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);
+ return snd_ctl_enum_info(uinfo, 1, emu_ri->n_srcs, emu_ri->src_texts);
}
static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned channel = kcontrol->private_value;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- channel >= 18))
+ if (channel >= emu_ri->n_outs)
return -EINVAL;
ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
return 0;
@@ -420,41 +649,42 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int val;
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned val = ucontrol->value.enumerated.item[0];
+ unsigned channel = kcontrol->private_value;
+ int change;
- val = ucontrol->value.enumerated.item[0];
- if (val >= 53 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- val >= 49))
+ if (val >= emu_ri->n_srcs)
return -EINVAL;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- channel >= 18))
+ if (channel >= emu_ri->n_outs)
return -EINVAL;
- if (emu->emu1010.output_source[channel] == val)
- return 0;
- emu->emu1010.output_source[channel] = val;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1616_output_dst[channel], emu1616_src_regs[val]);
- else
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_output_dst[channel], emu1010_src_regs[val]);
- return 1;
+ change = (emu->emu1010.output_source[channel] != val);
+ if (change) {
+ emu->emu1010.output_source[channel] = val;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_output_source_apply(emu, channel, val);
+ }
+ return change;
}
+static const struct snd_kcontrol_new emu1010_output_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_input_output_source_info,
+ .get = snd_emu1010_output_source_get,
+ .put = snd_emu1010_output_source_put
+};
+
static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned channel = kcontrol->private_value;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_input_dst, emu->emu1010.input_source */
- if (channel >= 22)
+ if (channel >= emu_ri->n_ins)
return -EINVAL;
ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
return 0;
@@ -464,134 +694,70 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int val;
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned val = ucontrol->value.enumerated.item[0];
+ unsigned channel = kcontrol->private_value;
+ int change;
- val = ucontrol->value.enumerated.item[0];
- if (val >= 53 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- val >= 49))
+ if (val >= emu_ri->n_srcs)
return -EINVAL;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_input_dst, emu->emu1010.input_source */
- if (channel >= 22)
+ if (channel >= emu_ri->n_ins)
return -EINVAL;
- if (emu->emu1010.input_source[channel] == val)
- return 0;
- emu->emu1010.input_source[channel] = val;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_input_dst[channel], emu1616_src_regs[val]);
- else
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_input_dst[channel], emu1010_src_regs[val]);
- return 1;
+ change = (emu->emu1010.input_source[channel] != val);
+ if (change) {
+ emu->emu1010.input_source[channel] = val;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_input_source_apply(emu, channel, val);
+ }
+ return change;
}
-#define EMU1010_SOURCE_OUTPUT(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_input_output_source_info, \
- .get = snd_emu1010_output_source_get, \
- .put = snd_emu1010_output_source_put, \
- .private_value = chid \
-}
-
-static 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),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
- EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6),
- EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7),
- EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8),
- EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb),
- EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc),
- EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd),
- EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe),
- EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
+static const struct snd_kcontrol_new emu1010_input_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_input_output_source_info,
+ .get = snd_emu1010_input_source_get,
+ .put = snd_emu1010_input_source_put
};
+static int add_emu1010_source_mixers(struct snd_emu10k1 *emu)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ int err;
-/* 1616(m) cardbus */
-static 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),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
- EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
- EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
-};
+ err = add_ctls(emu, &emu1010_output_source_ctl,
+ emu_ri->out_texts, emu_ri->n_outs);
+ if (err < 0)
+ return err;
+ err = add_ctls(emu, &emu1010_input_source_ctl,
+ emu1010_input_texts, emu_ri->n_ins);
+ return err;
+}
-#define EMU1010_SOURCE_INPUT(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_input_output_source_info, \
- .get = snd_emu1010_input_source_get, \
- .put = snd_emu1010_input_source_put, \
- .private_value = chid \
-}
-
-static 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),
- EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3),
- EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4),
- EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5),
- EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6),
- EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7),
- EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8),
- EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9),
- EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa),
- EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb),
- EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc),
- EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd),
- EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe),
- EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf),
- EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10),
- EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11),
- EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12),
- EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13),
- EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14),
- EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15),
+static const char * const snd_emu1010_adc_pads[] = {
+ "ADC1 14dB PAD 0202 Capture Switch",
+ "ADC1 14dB PAD Audio Dock Capture Switch",
+ "ADC2 14dB PAD Audio Dock Capture Switch",
+ "ADC3 14dB PAD Audio Dock Capture Switch",
};
-
+static const unsigned short snd_emu1010_adc_pad_regs[] = {
+ EMU_HANA_0202_ADC_PAD1,
+ EMU_HANA_DOCK_ADC_PAD1,
+ EMU_HANA_DOCK_ADC_PAD2,
+ EMU_HANA_DOCK_ADC_PAD3,
+};
#define snd_emu1010_adc_pads_info snd_ctl_boolean_mono_info
static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
+
ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
return 0;
}
@@ -599,39 +765,48 @@ static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
unsigned int val, cache;
+ int change;
+
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.adc_pads;
if (val == 1)
cache = cache | mask;
else
cache = cache & ~mask;
- if (cache != emu->emu1010.adc_pads) {
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+ change = (cache != emu->emu1010.adc_pads);
+ if (change) {
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache );
emu->emu1010.adc_pads = cache;
}
- return 0;
+ return change;
}
+static const struct snd_kcontrol_new emu1010_adc_pads_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_adc_pads_info,
+ .get = snd_emu1010_adc_pads_get,
+ .put = snd_emu1010_adc_pads_put
+};
-#define EMU1010_ADC_PADS(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_adc_pads_info, \
- .get = snd_emu1010_adc_pads_get, \
- .put = snd_emu1010_adc_pads_put, \
- .private_value = chid \
-}
+static const char * const snd_emu1010_dac_pads[] = {
+ "DAC1 0202 14dB PAD Playback Switch",
+ "DAC1 Audio Dock 14dB PAD Playback Switch",
+ "DAC2 Audio Dock 14dB PAD Playback Switch",
+ "DAC3 Audio Dock 14dB PAD Playback Switch",
+ "DAC4 Audio Dock 14dB PAD Playback Switch",
+};
-static 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),
- EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
+static const unsigned short snd_emu1010_dac_regs[] = {
+ EMU_HANA_0202_DAC_PAD1,
+ EMU_HANA_DOCK_DAC_PAD1,
+ EMU_HANA_DOCK_DAC_PAD2,
+ EMU_HANA_DOCK_DAC_PAD3,
+ EMU_HANA_DOCK_DAC_PAD4,
};
#define snd_emu1010_dac_pads_info snd_ctl_boolean_mono_info
@@ -639,7 +814,8 @@ static struct snd_kcontrol_new snd_emu1010_adc_pads[] = {
static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
+
ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
return 0;
}
@@ -647,163 +823,238 @@ static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
unsigned int val, cache;
+ int change;
+
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.dac_pads;
if (val == 1)
cache = cache | mask;
else
cache = cache & ~mask;
- if (cache != emu->emu1010.dac_pads) {
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+ change = (cache != emu->emu1010.dac_pads);
+ if (change) {
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache );
emu->emu1010.dac_pads = cache;
}
- return 0;
+ return change;
}
+static const struct snd_kcontrol_new emu1010_dac_pads_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_dac_pads_info,
+ .get = snd_emu1010_dac_pads_get,
+ .put = snd_emu1010_dac_pads_put
+};
-#define EMU1010_DAC_PADS(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_dac_pads_info, \
- .get = snd_emu1010_dac_pads_get, \
- .put = snd_emu1010_dac_pads_put, \
- .private_value = chid \
-}
+struct snd_emu1010_pads_info {
+ const char * const *adc_ctls, * const *dac_ctls;
+ unsigned n_adc_ctls, n_dac_ctls;
+};
-static 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),
- EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4),
- EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1),
+static const struct snd_emu1010_pads_info emu1010_pads_info[] = {
+ {
+ /* rev1 1010 */
+ .adc_ctls = snd_emu1010_adc_pads,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads),
+ .dac_ctls = snd_emu1010_dac_pads,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads),
+ },
+ {
+ /* rev2 1010 */
+ .adc_ctls = snd_emu1010_adc_pads,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 1,
+ .dac_ctls = snd_emu1010_dac_pads,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 1,
+ },
+ {
+ /* 1616(m) cardbus */
+ .adc_ctls = snd_emu1010_adc_pads + 1,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 2,
+ .dac_ctls = snd_emu1010_dac_pads + 1,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 2,
+ },
+ {
+ /* 0404 */
+ .adc_ctls = NULL,
+ .n_adc_ctls = 0,
+ .dac_ctls = NULL,
+ .n_dac_ctls = 0,
+ },
+};
+
+static const char * const emu1010_clock_texts[] = {
+ "44100", "48000", "SPDIF", "ADAT", "Dock", "BNC"
+};
+
+static const u8 emu1010_clock_vals[] = {
+ EMU_HANA_WCLOCK_INT_44_1K,
+ EMU_HANA_WCLOCK_INT_48K,
+ EMU_HANA_WCLOCK_HANA_SPDIF_IN,
+ EMU_HANA_WCLOCK_HANA_ADAT_IN,
+ EMU_HANA_WCLOCK_2ND_HANA,
+ EMU_HANA_WCLOCK_SYNC_BNC,
};
+static const char * const emu0404_clock_texts[] = {
+ "44100", "48000", "SPDIF", "BNC"
+};
+
+static const u8 emu0404_clock_vals[] = {
+ EMU_HANA_WCLOCK_INT_44_1K,
+ EMU_HANA_WCLOCK_INT_48K,
+ EMU_HANA_WCLOCK_HANA_SPDIF_IN,
+ EMU_HANA_WCLOCK_SYNC_BNC,
+};
+
+struct snd_emu1010_clock_info {
+ const char * const *texts;
+ const u8 *vals;
+ unsigned num;
+};
-static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
+static const struct snd_emu1010_clock_info emu1010_clock_info[] = {
+ {
+ // rev1 1010
+ .texts = emu1010_clock_texts,
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals),
+ },
+ {
+ // rev2 1010
+ .texts = emu1010_clock_texts,
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals) - 1,
+ },
+ {
+ // 1616(m) CardBus
+ .texts = emu1010_clock_texts,
+ // TODO: determine what is actually available.
+ // Pedantically, *every* source comes from the 2nd FPGA, as the
+ // card itself has no own (digital) audio ports. The user manual
+ // claims that ADAT and S/PDIF clock sources are separate, which
+ // can mean two things: either E-MU mapped the dock's sources to
+ // the primary ones, or they determine the meaning of the "Dock"
+ // source depending on how the ports are actually configured
+ // (which the 2nd FPGA must be doing anyway).
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals),
+ },
+ {
+ // 0404
+ .texts = emu0404_clock_texts,
+ .vals = emu0404_clock_vals,
+ .num = ARRAY_SIZE(emu0404_clock_vals),
+ },
+};
+
+static int snd_emu1010_clock_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char * const texts[4] = {
- "44100", "48000", "SPDIF", "ADAT"
- };
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ const struct snd_emu1010_clock_info *emu_ci =
+ &emu1010_clock_info[emu1010_idx(emu)];
- return snd_ctl_enum_info(uinfo, 1, 4, texts);
+ return snd_ctl_enum_info(uinfo, 1, emu_ci->num, emu_ci->texts);
}
-static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
+static int snd_emu1010_clock_source_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.internal_clock;
+ ucontrol->value.enumerated.item[0] = emu->emu1010.clock_source;
return 0;
}
-static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
+static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ const struct snd_emu1010_clock_info *emu_ci =
+ &emu1010_clock_info[emu1010_idx(emu)];
unsigned int val;
- int change = 0;
val = ucontrol->value.enumerated.item[0] ;
- /* Limit: uinfo->value.enumerated.items = 4; */
- if (val >= 4)
+ if (val >= emu_ci->num)
return -EINVAL;
- change = (emu->emu1010.internal_clock != val);
+ guard(snd_emu1010_fpga_lock)(emu);
+ scoped_guard(spinlock_irq, &emu->reg_lock) {
+ if (emu->emu1010.clock_source == val)
+ return 0;
+ emu->emu1010.clock_source = val;
+ emu->emu1010.wclock = emu_ci->vals[val];
+ snd_emu1010_update_clock(emu);
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
+ }
+
+ msleep(10); // Allow DLL to settle
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ return 1;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_clock_source =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Clock Source",
+ .count = 1,
+ .info = snd_emu1010_clock_source_info,
+ .get = snd_emu1010_clock_source_get,
+ .put = snd_emu1010_clock_source_put
+};
+
+static int snd_emu1010_clock_fallback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = {
+ "44100", "48000"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_clock_fallback_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.clock_fallback;
+ return 0;
+}
+
+static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ int change;
+
+ if (val >= 2)
+ return -EINVAL;
+ change = (emu->emu1010.clock_fallback != val);
if (change) {
- emu->emu1010.internal_clock = val;
- switch (val) {
- case 0:
- /* 44100 */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- 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,
- EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
- case 1:
- /* 48000 */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- 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 | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
-
- case 2: /* Take clock from S/PDIF IN */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
- /* Word Clock source, sync to S/PDIF input */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
-
- case 3:
- /* Take clock from ADAT IN */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
- /* Word Clock source, sync to ADAT input */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
-
-
- break;
- }
+ emu->emu1010.clock_fallback = val;
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val);
}
- return change;
+ return change;
}
-static const struct snd_kcontrol_new snd_emu1010_internal_clock =
+static const struct snd_kcontrol_new snd_emu1010_clock_fallback =
{
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Clock Internal Rate",
- .count = 1,
- .info = snd_emu1010_internal_clock_info,
- .get = snd_emu1010_internal_clock_get,
- .put = snd_emu1010_internal_clock_put
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Clock Fallback",
+ .count = 1,
+ .info = snd_emu1010_clock_fallback_info,
+ .get = snd_emu1010_clock_fallback_get,
+ .put = snd_emu1010_clock_fallback_put
};
static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol,
@@ -840,9 +1091,9 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
change = (emu->emu1010.optical_out != val);
if (change) {
emu->emu1010.optical_out = val;
- tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
- (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
- snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ 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_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
}
return change;
}
@@ -891,9 +1142,9 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
change = (emu->emu1010.optical_in != val);
if (change) {
emu->emu1010.optical_in = val;
- tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
- (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
- snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ 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_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
}
return change;
}
@@ -938,9 +1189,8 @@ 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;
/* If the capture source has changed,
* update the capture volume from the cached value
@@ -954,13 +1204,13 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
change = (emu->i2c_capture_source != source_id);
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);
- if (source_id==0)
- outl(gpio | 0x4, emu->port + A_IOCFG);
- else
- outl(gpio & ~0x4, emu->port + A_IOCFG);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ gpio = inw(emu->port + A_IOCFG);
+ if (source_id == 0)
+ outw(gpio | 0x4, emu->port + A_IOCFG);
+ else
+ outw(gpio & ~0x4, emu->port + A_IOCFG);
+ }
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
@@ -1019,7 +1269,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;
@@ -1028,46 +1278,43 @@ 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;
}
return change;
}
-#define I2C_VOLUME(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
- .info = snd_audigy_i2c_volume_info, \
- .get = snd_audigy_i2c_volume_get, \
- .put = snd_audigy_i2c_volume_put, \
- .tlv = { .p = snd_audigy_db_scale2 }, \
- .private_value = chid \
-}
-
+static const struct snd_kcontrol_new i2c_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_audigy_i2c_volume_info,
+ .get = snd_audigy_i2c_volume_get,
+ .put = snd_audigy_i2c_volume_put,
+ .tlv = { .p = snd_audigy_db_scale2 }
+};
-static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] = {
- I2C_VOLUME("Mic Capture Volume", 0),
- I2C_VOLUME("Line Capture Volume", 0)
+static const char * const snd_audigy_i2c_volume_ctls[] = {
+ "Mic Capture Volume",
+ "Line Capture Volume",
};
#if 0
@@ -1083,10 +1330,7 @@ static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&emu->reg_lock, flags);
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
switch (tmp & A_SPDIF_RATE_MASK) {
case A_SPDIF_44100:
@@ -1101,7 +1345,6 @@ static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
default:
ucontrol->value.enumerated.item[0] = 1;
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -1111,7 +1354,6 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int change;
unsigned int reg, val, tmp;
- unsigned long flags;
switch(ucontrol->value.enumerated.item[0]) {
case 0:
@@ -1129,17 +1371,17 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
}
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
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,
@@ -1158,7 +1400,6 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
int change;
unsigned int val;
- unsigned long flags;
/* Limit: emu->spdif_bits */
if (idx >= 3)
@@ -1167,13 +1408,11 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- spin_lock_irqsave(&emu->reg_lock, flags);
change = val != emu->spdif_bits[idx];
if (change) {
snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
emu->spdif_bits[idx] = val;
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1201,10 +1440,10 @@ static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
{
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
- snd_emu10k1_compose_audigy_fxrt1(route));
- snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
- snd_emu10k1_compose_audigy_fxrt2(route));
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(route),
+ A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(route),
+ REGLIST_END);
} else {
snd_emu10k1_ptr_write(emu, FXRT, voice,
snd_emu10k1_compose_send_routing(route));
@@ -1218,11 +1457,8 @@ static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsig
snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
if (emu->audigy) {
- unsigned int val = ((unsigned int)volume[4] << 24) |
- ((unsigned int)volume[5] << 16) |
- ((unsigned int)volume[6] << 8) |
- (unsigned int)volume[7];
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+ snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+ snd_emu10k1_compose_audigy_sendamounts(volume));
}
}
@@ -1241,7 +1477,6 @@ static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct s
static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1249,19 +1484,16 @@ static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (voice = 0; voice < 3; voice++)
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[(voice * num_efx) + idx] =
mix->send_routing[voice][idx] & mask;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1269,7 +1501,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (voice = 0; voice < 3; voice++)
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
@@ -1278,18 +1510,17 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
&mix->send_routing[1][0]);
- update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number + 1,
&mix->send_routing[2][0]);
- } else if (mix->epcm->voices[0]) {
+ } else {
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1317,31 +1548,27 @@ static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct sn
static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < 3*num_efx; idx++)
ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < 3*num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
@@ -1349,18 +1576,17 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
&mix->send_volume[1][0]);
- update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+ update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number + 1,
&mix->send_volume[2][0]);
- } else if (mix->epcm->voices[0]) {
+ } else {
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1380,7 +1606,7 @@ static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 3;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 0xffff;
+ uinfo->value.integer.max = 0x1fffd;
return 0;
}
@@ -1390,42 +1616,38 @@ static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
- unsigned long flags;
int idx;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < 3; idx++)
- ucontrol->value.integer.value[idx] = mix->attn[idx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ ucontrol->value.integer.value[idx] = mix->attn[idx] * 0xffffU / 0x8000U;
return 0;
}
static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < 3; idx++) {
- val = ucontrol->value.integer.value[idx] & 0xffff;
+ unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff;
+ val = uval * 0x8000U / 0xffffU;
if (mix->attn[idx] != val) {
mix->attn[idx] = val;
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
- snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
- } else if (mix->epcm->voices[0]) {
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number + 1, mix->attn[2]);
+ } else {
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1455,7 +1677,6 @@ static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, stru
static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1463,18 +1684,15 @@ static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] =
mix->send_routing[0][idx] & mask;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
@@ -1482,7 +1700,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & mask;
if (mix->send_routing[0][idx] != val) {
@@ -1497,7 +1715,6 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1525,31 +1742,27 @@ static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struc
static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[0][idx] != val) {
@@ -1563,7 +1776,6 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1584,7 +1796,7 @@ static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_c
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 0xffff;
+ uinfo->value.integer.max = 0x1fffd;
return 0;
}
@@ -1594,25 +1806,23 @@ static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
- ucontrol->value.integer.value[0] = mix->attn[0];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ ucontrol->value.integer.value[0] = mix->attn[0] * 0xffffU / 0x8000U;
return 0;
}
static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, val;
+ unsigned uval;
- spin_lock_irqsave(&emu->reg_lock, flags);
- val = ucontrol->value.integer.value[0] & 0xffff;
+ guard(spinlock_irq)(&emu->reg_lock);
+ uval = ucontrol->value.integer.value[0] & 0x1ffff;
+ val = uval * 0x8000U / 0xffffU;
if (mix->attn[0] != val) {
mix->attn[0] = val;
change = 1;
@@ -1622,7 +1832,6 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1645,7 +1854,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)
@@ -1658,7 +1867,6 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int reg, val, sw;
int change = 0;
@@ -1666,17 +1874,17 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
sw = ucontrol->value.integer.value[0];
if (emu->card_capabilities->invert_shared_spdif)
sw = !sw;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
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);
@@ -1687,7 +1895,6 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
reg |= val;
outl(reg | val, emu->port + HCFG);
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -1762,25 +1969,16 @@ 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);
+ strscpy(id.name, name);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
@@ -1789,11 +1987,11 @@ static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
int pcm_device, int multi_device)
{
- int err, pcm;
+ int err;
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",
@@ -1807,13 +2005,13 @@ int 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 */
@@ -1840,7 +2038,7 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"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", */
@@ -1850,7 +2048,7 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"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",
@@ -1859,7 +2057,7 @@ int 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",
@@ -1868,7 +2066,7 @@ int 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",
@@ -1886,7 +2084,7 @@ int 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",
@@ -1912,12 +2110,13 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
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 */
@@ -1925,7 +2124,8 @@ int 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;
dev_info(emu->card->dev,
@@ -1974,11 +2174,11 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
} else {
no_ac97:
if (emu->card_capabilities->ecard)
- strcpy(emu->card->mixername, "EMU APS");
+ strscpy(emu->card->mixername, "EMU APS");
else if (emu->audigy)
- strcpy(emu->card->mixername, "SB Audigy");
+ strscpy(emu->card->mixername, "SB Audigy");
else
- strcpy(emu->card->mixername, "Emu10k1");
+ strscpy(emu->card->mixername, "Emu10k1");
}
if (emu->audigy)
@@ -2005,222 +2205,164 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
}
- 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 */
- for (pcm = 0; pcm < 32; pcm++) {
- struct snd_emu10k1_pcm_mixer *mix;
- int v;
-
- mix = &emu->pcm_mixer[pcm];
- mix->epcm = NULL;
-
- for (v = 0; v < 4; v++)
- mix->send_routing[0][v] =
- mix->send_routing[1][v] =
- mix->send_routing[2][v] = v;
-
- memset(&mix->send_volume, 0, sizeof(mix->send_volume));
- mix->send_volume[0][0] = mix->send_volume[0][1] =
- mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
-
- mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
- }
-
- /* initialize the routing and volume table for the multichannel playback stream */
- for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
- struct snd_emu10k1_pcm_mixer *mix;
- int v;
-
- mix = &emu->efx_pcm_mixer[pcm];
- mix->epcm = NULL;
-
- mix->send_routing[0][0] = pcm;
- mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
- for (v = 0; v < 2; v++)
- mix->send_routing[0][2+v] = 13+v;
- if (emu->audigy)
- for (v = 0; v < 4; v++)
- mix->send_routing[0][4+v] = 60+v;
-
- memset(&mix->send_volume, 0, sizeof(mix->send_volume));
- mix->send_volume[0][0] = 255;
-
- mix->attn[0] = 0xffff;
- }
-
- if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
+ if (!emu->card_capabilities->ecard && !emu->card_capabilities->emu_model) {
/* 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;
}
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- /* 1616(m) cardbus */
- int i;
-
- for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
- if (err < 0)
- return err;
+ if (emu->card_capabilities->emu_model) {
+ unsigned i, emu_idx = emu1010_idx(emu);
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu_idx];
+ const struct snd_emu1010_pads_info *emu_pi = &emu1010_pads_info[emu_idx];
+
+ for (i = 0; i < emu_ri->n_ins; i++)
+ emu->emu1010.input_source[i] =
+ emu1010_map_source(emu_ri, emu_ri->in_dflts[i]);
+ for (i = 0; i < emu_ri->n_outs; i++)
+ emu->emu1010.output_source[i] =
+ emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
+ scoped_guard(snd_emu1010_fpga_lock, emu) {
+ snd_emu1010_apply_sources(emu);
}
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+
+ kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
+ err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_optical_out, emu));
+ snd_ctl_new1(&snd_emu1010_clock_fallback, emu));
if (err < 0)
return err;
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_optical_in, emu));
+
+ err = add_ctls(emu, &emu1010_adc_pads_ctl,
+ emu_pi->adc_ctls, emu_pi->n_adc_ctls);
+ if (err < 0)
+ return err;
+ err = add_ctls(emu, &emu1010_dac_pads_ctl,
+ emu_pi->dac_ctls, emu_pi->n_dac_ctls);
if (err < 0)
return err;
- } else if (emu->card_capabilities->emu_model) {
- /* all other e-mu cards for now */
- int i;
-
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
+ if (!emu->card_capabilities->no_adat) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
- emu));
+ snd_ctl_new1(&snd_emu1010_optical_out, emu));
if (err < 0)
return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ snd_ctl_new1(&snd_emu1010_optical_in, emu));
if (err < 0)
return err;
}
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
- if (err < 0)
- return err;
- }
- err = snd_ctl_add(card,
- 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));
+
+ err = add_emu1010_source_mixers(emu);
if (err < 0)
return err;
}
if ( emu->card_capabilities->i2c_adc) {
- int i;
-
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
if (err < 0)
return err;
- for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
- if (err < 0)
- return err;
- }
+ err = add_ctls(emu, &i2c_volume_ctl,
+ snd_audigy_i2c_volume_ctls,
+ ARRAY_SIZE(snd_audigy_i2c_volume_ctls));
+ if (err < 0)
+ return err;
}
if (emu->card_capabilities->ac97_chip && emu->audigy) {
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index b6650f5c1621..c102a3599225 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>
@@ -83,28 +68,28 @@ static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- mpu401_clear_rx(emu, midi);
- } else {
- byte = mpu401_read_data(emu, midi);
- if (midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- mpu401_write_data(emu, midi, byte);
- } else {
- snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
}
static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
@@ -119,29 +104,28 @@ static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int st
static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- mpu401_write_data(emu, midi, 0x00);
- /* mpu401_clear_rx(emu, midi); */
-
- mpu401_write_cmd(emu, midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (mpu401_input_avail(emu, midi)) {
- if (mpu401_read_data(emu, midi) == MPU401_ACK)
- ok = 1;
+ scoped_guard(spinlock_irq, &midi->input_lock) {
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
}
- }
- if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
dev_err(emu->card->dev,
"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
@@ -157,100 +141,78 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)
+ return 0;
}
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)
+ return 0;
}
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1_intr_disable(emu, midi->rx_enable);
- midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ snd_emu10k1_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1_intr_disable(emu, midi->tx_enable);
- midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -271,7 +233,6 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
@@ -282,22 +243,21 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
- spin_lock_irqsave(&midi->output_lock, flags);
- while (max > 0) {
- if (mpu401_output_ready(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ scoped_guard(spinlock_irq, &midi->output_lock) {
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
}
- mpu401_write_data(emu, midi, byte);
- max--;
- } else {
- break;
}
}
- spin_unlock_irqrestore(&midi->output_lock, flags);
snd_emu10k1_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1_intr_disable(emu, midi->tx_enable);
@@ -334,13 +294,14 @@ static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *m
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);
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -357,7 +318,8 @@ 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;
@@ -375,7 +337,8 @@ int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
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;
@@ -386,7 +349,8 @@ int 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 2683b9717215..071c75ba81fd 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1,29 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / PCM routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * 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
*
+ * Routines for control of EMU10K1 chips / PCM routines
*/
#include <linux/pci.h>
@@ -39,7 +22,8 @@ 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;
@@ -89,82 +73,64 @@ 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)
+static void snd_emu10k1_pcm_free_voices(struct snd_emu10k1_pcm *epcm)
{
- 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;
-
- if (epcm->voices[1] != NULL && voices < 2) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
- epcm->voices[1] = NULL;
- }
- for (i = 0; i < voices; i++) {
- if (epcm->voices[i] == NULL)
- break;
- }
- if (i == voices)
- return 0; /* already allocated */
-
- for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+ for (unsigned i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
if (epcm->voices[i]) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
}
}
+}
+
+static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm *epcm,
+ int type, int count, int channels)
+{
+ int err;
+
+ snd_emu10k1_pcm_free_voices(epcm);
+
err = snd_emu10k1_voice_alloc(epcm->emu,
- epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
- voices,
- &epcm->voices[0]);
-
+ type, count, channels,
+ epcm, &epcm->voices[0]);
if (err < 0)
return err;
- 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 = epcm;
- }
- }
+
if (epcm->extra == NULL) {
+ // The hardware supports only (half-)loop interrupts, so to support an
+ // arbitrary number of periods per buffer, we use an extra voice with a
+ // period-sized loop as the interrupt source. Additionally, the interrupt
+ // timing of the hardware is "suboptimal" and needs some compensation.
err = snd_emu10k1_voice_alloc(epcm->emu,
- epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
- 1,
- &epcm->extra);
+ type + 1, 1, 1,
+ epcm, &epcm->extra);
if (err < 0) {
/*
dev_dbg(emu->card->dev, "pcm_channel_alloc: "
"failed extra: voices=%d, frame=%d\n",
voices, frame);
*/
- for (i = 0; i < voices; i++) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
- epcm->voices[i] = NULL;
- }
+ snd_emu10k1_pcm_free_voices(epcm);
return err;
}
- epcm->extra->epcm = epcm;
epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
}
+
return 0;
}
-static const unsigned int capture_period_sizes[31] = {
+// Primes 2-7 and 2^n multiples thereof, up to 16.
+static const unsigned int efx_capture_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_efx_capture_channels = {
+ .count = ARRAY_SIZE(efx_capture_channels),
+ .list = efx_capture_channels,
+ .mask = 0
+};
+
+static const unsigned int capture_buffer_sizes[31] = {
384, 448, 512, 640,
384*2, 448*2, 512*2, 640*2,
384*4, 448*4, 512*4, 640*4,
@@ -175,19 +141,9 @@ static const unsigned int capture_period_sizes[31] = {
384*128,448*128,512*128
};
-static const struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_buffer_sizes = {
.count = 31,
- .list = capture_period_sizes,
- .mask = 0
-};
-
-static const unsigned int capture_rates[8] = {
- 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
-};
-
-static const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
- .count = 8,
- .list = capture_rates,
+ .list = capture_buffer_sizes,
.mask = 0
};
@@ -213,7 +169,7 @@ static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
switch (rate) {
case 8000: return A_ADCCR_SAMPLERATE_8;
case 11025: return A_ADCCR_SAMPLERATE_11;
- case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */
+ case 12000: return A_ADCCR_SAMPLERATE_12;
case 16000: return ADCCR_SAMPLERATE_16;
case 22050: return ADCCR_SAMPLERATE_22;
case 24000: return ADCCR_SAMPLERATE_24;
@@ -226,6 +182,33 @@ static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
}
}
+static void snd_emu10k1_constrain_capture_rates(struct snd_emu10k1 *emu,
+ struct snd_pcm_runtime *runtime)
+{
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100) {
+ runtime->hw.rates = SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100;
+ runtime->hw.rate_min = 11025;
+ runtime->hw.rate_max = 44100;
+ } else if (emu->audigy) {
+ runtime->hw.rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_12000 |
+ SNDRV_PCM_RATE_24000;
+ }
+}
+
+static void snd_emu1010_constrain_efx_rate(struct snd_emu10k1 *emu,
+ struct snd_pcm_runtime *runtime)
+{
+ int rate;
+
+ rate = emu->emu1010.word_clock;
+ runtime->hw.rate_min = runtime->hw.rate_max = rate;
+ runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+}
+
static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
{
unsigned int pitch_target;
@@ -262,147 +245,104 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
return CCCA_INTERPROM_2;
}
-/*
- * calculate cache invalidate size
- *
- * stereo: channel is stereo
- * w_16: using 16bit samples
- *
- * returns: cache invalidate size in samples
- */
-static inline int emu10k1_ccis(int stereo, int w_16)
+static u16 emu10k1_send_target_from_amount(u8 amount)
{
- if (w_16) {
- return stereo ? 24 : 26;
- } else {
- return stereo ? 24*2 : 26*2;
- }
+ static const u8 shifts[8] = { 4, 4, 5, 6, 7, 8, 9, 10 };
+ static const u16 offsets[8] = { 0, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 };
+ u8 exp;
+
+ if (amount == 0xff)
+ return 0xffff;
+ exp = amount >> 5;
+ return ((amount & 0x1f) << shifts[exp]) + offsets[exp];
}
static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
- int master, int extra,
struct snd_emu10k1_voice *evoice,
+ bool w_16, bool stereo,
unsigned int start_addr,
unsigned int end_addr,
- struct snd_emu10k1_pcm_mixer *mix)
+ const unsigned char *send_routing,
+ const unsigned char *send_amount)
{
- struct snd_pcm_substream *substream = evoice->epcm->substream;
- 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_routing[8];
- unsigned long flags;
- unsigned int pitch_target;
- unsigned int ccis;
+ unsigned int silent_page;
+ int voice;
voice = evoice->number;
- stereo = runtime->channels == 2;
- w_16 = snd_pcm_format_width(runtime->format) == 16;
- if (!extra && stereo) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
- if (w_16) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
-
- spin_lock_irqsave(&emu->reg_lock, flags);
-
- /* volume parameters */
- if (extra) {
- attn = 0;
- memset(send_routing, 0, sizeof(send_routing));
- send_routing[0] = 0;
- send_routing[1] = 1;
- send_routing[2] = 2;
- send_routing[3] = 3;
- memset(send_amount, 0, sizeof(send_amount));
+ silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) |
+ (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ // Not really necessary for the slave, but it doesn't hurt
+ CPF, stereo ? CPF_STEREO_MASK : 0,
+ // Assumption that PT is already 0 so no harm overwriting
+ PTRX, (send_amount[0] << 8) | send_amount[1],
+ // Stereo slaves don't need to have the addresses set, but it doesn't hurt
+ DSL, end_addr | (send_amount[3] << 24),
+ PSST, start_addr | (send_amount[2] << 24),
+ CCCA, emu10k1_select_interprom(evoice->epcm->pitch_target) |
+ (w_16 ? 0 : CCCA_8BITSELECT),
+ // Clear filter delay memory
+ Z1, 0,
+ Z2, 0,
+ // Invalidate maps
+ MAPA, silent_page,
+ MAPB, silent_page,
+ // Disable filter (in conjunction with CCCA_RESONANCE == 0)
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ REGLIST_END);
+ // Setup routing
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(send_routing),
+ A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(send_routing),
+ A_SENDAMOUNTS, snd_emu10k1_compose_audigy_sendamounts(send_amount),
+ REGLIST_END);
+ for (int i = 0; i < 4; i++) {
+ u32 aml = emu10k1_send_target_from_amount(send_amount[2 * i]);
+ u32 amh = emu10k1_send_target_from_amount(send_amount[2 * i + 1]);
+ snd_emu10k1_ptr_write(emu, A_CSBA + i, voice, (amh << 16) | aml);
+ }
} else {
- /* mono, left, right (master voice = left) */
- tmp = stereo ? (master ? 1 : 2) : 0;
- memcpy(send_routing, &mix->send_routing[tmp][0], 8);
- memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+ snd_emu10k1_ptr_write(emu, FXRT, voice,
+ snd_emu10k1_compose_send_routing(send_routing));
}
- ccis = emu10k1_ccis(stereo, w_16);
-
- if (master) {
- evoice->epcm->ccca_start_addr = start_addr + ccis;
- if (extra) {
- start_addr += ccis;
- end_addr += ccis + emu->delay_pcm_irq;
- }
- if (stereo && !extra) {
- snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
- snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
- } else {
- snd_emu10k1_ptr_write(emu, CPF, voice, 0);
- }
- }
+ emu->voices[voice].dirty = 1;
+}
- /* setup routing */
- if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
- snd_emu10k1_compose_audigy_fxrt1(send_routing));
- snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
- snd_emu10k1_compose_audigy_fxrt2(send_routing));
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
- ((unsigned int)send_amount[4] << 24) |
- ((unsigned int)send_amount[5] << 16) |
- ((unsigned int)send_amount[6] << 8) |
- (unsigned int)send_amount[7]);
- } 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));
- snd_emu10k1_ptr_write(emu, PSST, voice,
- (start_addr + (extra ? emu->delay_pcm_irq : 0)) |
- (send_amount[2] << 24));
- if (emu->card_capabilities->emu_model)
- pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
- else
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
- if (extra)
- snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
- emu10k1_select_interprom(pitch_target) |
- (w_16 ? 0 : CCCA_8BITSELECT));
- else
- snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
- emu10k1_select_interprom(pitch_target) |
- (w_16 ? 0 : CCCA_8BITSELECT));
- /* Clear filter delay memory */
- 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 << 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, ATKHLDM, voice, 0);
- snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
- snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
- snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
- snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
- snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
- snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
- snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
- /* volume envelope */
- snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
- snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
- /* filter envelope */
- snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
- /* pitch envelope */
- snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
-
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool w_16, bool stereo,
+ unsigned int start_addr,
+ unsigned int end_addr,
+ struct snd_emu10k1_pcm_mixer *mix)
+{
+ guard(spinlock_irq)(&emu->reg_lock);
+ snd_emu10k1_pcm_init_voice(emu, evoice, w_16, stereo,
+ start_addr, end_addr,
+ &mix->send_routing[stereo][0],
+ &mix->send_volume[stereo][0]);
+ if (stereo)
+ snd_emu10k1_pcm_init_voice(emu, evoice + 1, w_16, true,
+ start_addr, end_addr,
+ &mix->send_routing[2][0],
+ &mix->send_volume[2][0]);
+}
+
+static void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool w_16,
+ unsigned int start_addr,
+ unsigned int end_addr)
+{
+ static const unsigned char send_routing[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ static const unsigned char send_amount[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ snd_emu10k1_pcm_init_voice(emu, evoice, w_16, false,
+ start_addr, end_addr,
+ send_routing, send_amount);
}
static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
@@ -411,12 +351,31 @@ 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 type, channels, count;
int err;
- if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+ if (epcm->type == PLAYBACK_EMUVOICE) {
+ type = EMU10K1_PCM;
+ channels = 1;
+ count = params_channels(hw_params);
+ } else {
+ type = EMU10K1_EFX;
+ channels = params_channels(hw_params);
+ count = 1;
+ }
+ err = snd_emu10k1_pcm_channel_alloc(epcm, type, count, channels);
+ 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)
@@ -446,43 +405,7 @@ static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
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)
- return 0;
- epcm = runtime->private_data;
- if (epcm->extra) {
- snd_emu10k1_voice_free(epcm->emu, epcm->extra);
- epcm->extra = NULL;
- }
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- if (epcm->voices[i]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
- epcm->voices[i] = NULL;
- }
- }
+ snd_emu10k1_pcm_free_voices(epcm);
if (epcm->memblk) {
snd_emu10k1_free_pages(emu, epcm->memblk);
epcm->memblk = NULL;
@@ -497,26 +420,28 @@ static int snd_emu10k1_playback_prepare(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;
+ bool w_16 = snd_pcm_format_width(runtime->format) == 16;
+ bool stereo = runtime->channels == 2;
unsigned int start_addr, end_addr;
+ unsigned int rate;
+
+ rate = runtime->rate;
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100)
+ rate = rate * 480 / 441;
+ epcm->pitch_target = emu10k1_calc_pitch_target(rate);
+
+ start_addr = epcm->start_addr >> w_16;
+ end_addr = start_addr + runtime->period_size;
+ snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, w_16,
+ start_addr, end_addr);
+ start_addr >>= stereo;
+ epcm->ccca_start_addr = start_addr;
+ end_addr = start_addr + runtime->buffer_size;
+ snd_emu10k1_pcm_init_voices(emu, epcm->voices[0], w_16, stereo,
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
- start_addr = epcm->start_addr;
- end_addr = snd_pcm_lib_period_bytes(substream);
- if (runtime->channels == 2) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
- end_addr += start_addr;
- snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
- start_addr, end_addr, NULL);
- start_addr = epcm->start_addr;
- end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
- snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
- start_addr, end_addr,
- &emu->pcm_mixer[substream->number]);
- if (epcm->voices[1])
- snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
- start_addr, end_addr,
- &emu->pcm_mixer[substream->number]);
return 0;
}
@@ -525,31 +450,25 @@ static int snd_emu10k1_efx_playback_prepare(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 start_addr, end_addr;
- unsigned int channel_size;
- int i;
+ unsigned int start_addr;
+ unsigned int extra_size, channel_size;
+ unsigned int i;
- start_addr = epcm->start_addr;
- end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+ epcm->pitch_target = PITCH_48000;
- /*
- * 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,
- start_addr, start_addr + (channel_size / 2), NULL);
-
- /* only difference with the master voice is we use it for the pointer */
- snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
- start_addr, start_addr + channel_size,
- &emu->efx_pcm_mixer[0]);
-
- start_addr += channel_size;
- for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
- start_addr, start_addr + channel_size,
- &emu->efx_pcm_mixer[i]);
+ start_addr = epcm->start_addr >> 1; // 16-bit voices
+
+ extra_size = runtime->period_size;
+ channel_size = runtime->buffer_size;
+
+ snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, true,
+ start_addr, start_addr + extra_size);
+
+ epcm->ccca_start_addr = start_addr;
+ for (i = 0; i < runtime->channels; i++) {
+ snd_emu10k1_pcm_init_voices(emu, epcm->voices[i], true, false,
+ start_addr, start_addr + channel_size,
+ &emu->efx_pcm_mixer[i]);
start_addr += channel_size;
}
@@ -566,27 +485,15 @@ static const struct snd_pcm_hardware snd_emu10k1_efx_playback =
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
- .channels_min = NUM_EFX_PLAYBACK,
+ .channels_min = 1,
.channels_max = NUM_EFX_PLAYBACK,
- .buffer_bytes_max = (64*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (64*1024),
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_max = (128*1024),
.periods_min = 2,
- .periods_max = 2,
+ .periods_max = 1024,
.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);
@@ -601,9 +508,17 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
break;
case CAPTURE_EFX:
+ if (emu->card_capabilities->emu_model) {
+ // The upper 32 16-bit capture voices, two for each of the 16 32-bit channels.
+ // The lower voices are occupied by A_EXTOUT_*_CAP*.
+ epcm->capture_cr_val = 0;
+ epcm->capture_cr_val2 = 0xffffffff >> (32 - runtime->channels * 2);
+ }
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
- snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, 0,
+ A_FXWC2, 0,
+ REGLIST_END);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
break;
@@ -614,7 +529,7 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
epcm->capture_bs_val = 0;
for (idx = 0; idx < 31; idx++) {
- if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+ if (capture_buffer_sizes[idx] == epcm->capture_bufsize) {
epcm->capture_bs_val = idx + 1;
break;
}
@@ -624,132 +539,181 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
epcm->capture_bs_val++;
}
if (epcm->type == CAPTURE_AC97ADC) {
+ unsigned rate = runtime->rate;
+ if (!(runtime->hw.rates & SNDRV_PCM_RATE_48000))
+ rate = rate * 480 / 441;
+
epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
if (runtime->channels > 1)
epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
epcm->capture_cr_val |= emu->audigy ?
- snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
- snd_emu10k1_capture_rate_reg(runtime->rate);
+ snd_emu10k1_audigy_capture_rate_reg(rate) :
+ snd_emu10k1_capture_rate_reg(rate);
}
return 0;
}
-static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int extra, struct snd_emu10k1_voice *evoice)
+static void snd_emu10k1_playback_fill_cache(struct snd_emu10k1 *emu,
+ unsigned voice,
+ u32 sample, bool stereo)
{
- struct snd_pcm_runtime *runtime;
- unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
+ u32 ccr;
- if (evoice == NULL)
- return;
- runtime = evoice->epcm->substream->runtime;
- voice = evoice->number;
- stereo = (!extra && runtime->channels == 2);
- sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
- ccis = emu10k1_ccis(stereo, sample == 0);
- /* set cs to 2 * number of cache registers beside the invalidated */
- cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
- if (cs > 16) cs = 16;
- for (i = 0; i < cs; i++) {
+ // We assume that the cache is resting at this point (i.e.,
+ // CCR_CACHEINVALIDSIZE is very small).
+
+ // Clear leading frames. For simplicitly, this does too much,
+ // except for 16-bit stereo. And the interpolator will actually
+ // access them at all only when we're pitch-shifting.
+ for (int i = 0; i < 3; i++)
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
- if (stereo) {
- snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
- }
- }
- /* reset cache */
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
- snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+
+ // Fill cache
+ ccr = (64 - 3) << REG_SHIFT(CCR_CACHEINVALIDSIZE);
if (stereo) {
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
- snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+ // The engine goes haywire if CCR_READADDRESS is out of sync
+ snd_emu10k1_ptr_write(emu, CCR, voice + 1, ccr);
}
- /* fill cache */
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
- if (stereo) {
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+ snd_emu10k1_ptr_write(emu, CCR, voice, ccr);
+}
+
+static void snd_emu10k1_playback_prepare_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ bool w_16, bool stereo,
+ int channels)
+{
+ struct snd_pcm_substream *substream = epcm->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned eloop_start = epcm->start_addr >> w_16;
+ unsigned loop_start = eloop_start >> stereo;
+ unsigned eloop_size = runtime->period_size;
+ unsigned loop_size = runtime->buffer_size;
+ u32 sample = w_16 ? 0 : 0x80808080;
+
+ // To make the playback actually start at the 1st frame,
+ // we need to compensate for two circumstances:
+ // - The actual position is delayed by the cache size (64 frames)
+ // - The interpolator is centered around the 4th frame
+ loop_start += (epcm->resume_pos + 64 - 3) % loop_size;
+ for (int i = 0; i < channels; i++) {
+ unsigned voice = epcm->voices[i]->number;
+ snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, voice, loop_start);
+ loop_start += loop_size;
+ snd_emu10k1_playback_fill_cache(emu, voice, sample, stereo);
}
+
+ // The interrupt is triggered when CCCA_CURRADDR (CA) wraps around,
+ // which is ahead of the actual playback position, so the interrupt
+ // source needs to be delayed.
+ //
+ // In principle, this wouldn't need to be the cache's entire size - in
+ // practice, CCR_CACHEINVALIDSIZE (CIS) > `fetch threshold` has never
+ // been observed, and assuming 40 _bytes_ should be safe.
+ //
+ // The cache fills are somewhat random, which makes it impossible to
+ // align them with the interrupts. This makes a non-delayed interrupt
+ // source not practical, as the interrupt handler would have to wait
+ // for (CA - CIS) >= period_boundary for every channel in the stream.
+ //
+ // This is why all other (open) drivers for these chips use timer-based
+ // interrupts.
+ //
+ eloop_start += (epcm->resume_pos + eloop_size - 3) % eloop_size;
+ snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, epcm->extra->number, eloop_start);
+
+ // It takes a moment until the cache fills complete,
+ // but the unmuting takes long enough for that.
}
-static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice,
- int master, int extra,
- struct snd_emu10k1_pcm_mixer *mix)
+static void snd_emu10k1_playback_commit_volume(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ unsigned int vattn)
{
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int attn, vattn;
- unsigned int voice, tmp;
+ snd_emu10k1_ptr_write_multiple(emu, evoice->number,
+ VTFT, vattn | VTFT_FILTERTARGET_MASK,
+ CVCF, vattn | CVCF_CURRENTFILTER_MASK,
+ REGLIST_END);
+}
- if (evoice == NULL) /* skip second voice for mono */
- return;
- substream = evoice->epcm->substream;
- runtime = substream->runtime;
- voice = evoice->number;
+static void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo, bool master,
+ struct snd_emu10k1_pcm_mixer *mix)
+{
+ unsigned int vattn;
+ unsigned int tmp;
- attn = extra ? 0 : 0x00ff;
- 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, DCYSUSV, voice, 0x7f7f);
- snd_emu10k1_voice_clear_loop_stop(emu, voice);
+ tmp = stereo ? (master ? 1 : 2) : 0;
+ vattn = mix->attn[tmp] << 16;
+ snd_emu10k1_playback_commit_volume(emu, evoice, vattn);
}
-static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra)
+static void snd_emu10k1_playback_unmute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo,
+ struct snd_emu10k1_pcm_mixer *mix)
{
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int voice, pitch, pitch_target;
+ snd_emu10k1_playback_unmute_voice(emu, evoice, stereo, true, mix);
+ if (stereo)
+ snd_emu10k1_playback_unmute_voice(emu, evoice + 1, true, false, mix);
+}
- if (evoice == NULL) /* skip second voice for mono */
- return;
- substream = evoice->epcm->substream;
- runtime = substream->runtime;
- voice = evoice->number;
+static void snd_emu10k1_playback_mute_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
+{
+ snd_emu10k1_playback_commit_volume(emu, evoice, 0);
+}
- pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
- if (emu->card_capabilities->emu_model)
- pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
- else
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
- snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
- if (master || evoice->epcm->type == PLAYBACK_EFX)
- snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
- snd_emu10k1_ptr_write(emu, IP, voice, pitch);
- if (extra)
- snd_emu10k1_voice_intr_enable(emu, voice);
+static void snd_emu10k1_playback_mute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo)
+{
+ snd_emu10k1_playback_mute_voice(emu, evoice);
+ if (stereo)
+ snd_emu10k1_playback_mute_voice(emu, evoice + 1);
}
-static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice)
+static void snd_emu10k1_playback_commit_pitch(struct snd_emu10k1 *emu,
+ u32 voice, u32 pitch_target)
+{
+ u32 ptrx = snd_emu10k1_ptr_read(emu, PTRX, voice);
+ u32 cpf = snd_emu10k1_ptr_read(emu, CPF, voice);
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ PTRX, (ptrx & ~PTRX_PITCHTARGET_MASK) | pitch_target,
+ CPF, (cpf & ~(CPF_CURRENTPITCH_MASK | CPF_FRACADDRESS_MASK)) | pitch_target,
+ REGLIST_END);
+}
+
+static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
{
unsigned int voice;
- if (evoice == NULL)
- return;
voice = evoice->number;
- snd_emu10k1_voice_intr_disable(emu, voice);
- 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, IP, voice, 0);
+ snd_emu10k1_playback_commit_pitch(emu, voice, evoice->epcm->pitch_target << 16);
}
-static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
- struct snd_emu10k1_pcm *epcm,
- struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
{
- unsigned int ptr, period_pos;
+ unsigned int voice;
- /* try to sychronize the current position for the interrupt
- source voice */
- period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt;
- period_pos %= runtime->period_size;
- ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number);
- ptr &= ~0x00ffffff;
- ptr |= epcm->ccca_start_addr + period_pos;
- snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr);
+ voice = evoice->number;
+ snd_emu10k1_playback_commit_pitch(emu, voice, 0);
+}
+
+static void snd_emu10k1_playback_set_running(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm)
+{
+ epcm->running = 1;
+ snd_emu10k1_voice_intr_enable(emu, epcm->extra->number);
+}
+
+static void snd_emu10k1_playback_set_stopped(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm)
+{
+ snd_emu10k1_voice_intr_disable(emu, epcm->extra->number);
+ epcm->running = 0;
}
static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
@@ -759,46 +723,39 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
struct snd_emu10k1_pcm_mixer *mix;
- int result = 0;
+ bool w_16 = snd_pcm_format_width(runtime->format) == 16;
+ bool stereo = runtime->channels == 2;
/*
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);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
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 */
+ snd_emu10k1_playback_prepare_voices(emu, epcm, w_16, stereo, 1);
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
- snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime);
mix = &emu->pcm_mixer[substream->number];
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
- snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
- epcm->running = 1;
+ snd_emu10k1_playback_unmute_voices(emu, epcm->voices[0], stereo, mix);
+ snd_emu10k1_playback_set_running(emu, epcm);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0]);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- epcm->running = 0;
snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
- snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ snd_emu10k1_playback_set_stopped(emu, epcm);
+ snd_emu10k1_playback_mute_voices(emu, epcm->voices[0], stereo);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
@@ -807,9 +764,8 @@ static int snd_emu10k1_capture_trigger(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 result = 0;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -826,8 +782,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
break;
case CAPTURE_EFX:
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_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, epcm->capture_cr_val,
+ A_FXWC2, epcm->capture_cr_val2,
+ REGLIST_END);
dev_dbg(emu->card->dev,
"cr_val=0x%x, cr_val2=0x%x\n",
epcm->capture_cr_val,
@@ -854,8 +812,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
break;
case CAPTURE_EFX:
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
- snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, 0,
+ A_FXWC2, 0,
+ REGLIST_END);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
break;
@@ -864,10 +824,9 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
}
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream)
@@ -875,24 +834,27 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_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;
+ int ptr;
if (!epcm->running)
return 0;
+
ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
-#if 0 /* Perex's code */
- ptr += runtime->buffer_size;
ptr -= epcm->ccca_start_addr;
- ptr %= runtime->buffer_size;
-#else /* EMU10K1 Open Source code from Creative */
- if (ptr < epcm->ccca_start_addr)
- ptr += runtime->buffer_size - epcm->ccca_start_addr;
- else {
- ptr -= epcm->ccca_start_addr;
- if (ptr >= runtime->buffer_size)
- ptr -= runtime->buffer_size;
- }
-#endif
+
+ // This is the size of the whole cache minus the interpolator read-ahead,
+ // which leads us to the actual playback position.
+ //
+ // The cache is constantly kept mostly filled, so in principle we could
+ // return a more advanced position representing how far the hardware has
+ // already read the buffer, and set runtime->delay accordingly. However,
+ // this would be slightly different for every channel (and remarkably slow
+ // to obtain), so only a fixed worst-case value would be practical.
+ //
+ ptr -= 64 - 3;
+ if (ptr < 0)
+ ptr += runtime->buffer_size;
+
/*
dev_dbg(emu->card->dev,
"ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
@@ -902,6 +864,49 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
return ptr;
}
+static u64 snd_emu10k1_efx_playback_voice_mask(struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ u64 mask = 0;
+
+ for (int i = 0; i < channels; i++) {
+ int voice = epcm->voices[i]->number;
+ mask |= 1ULL << voice;
+ }
+ return mask;
+}
+
+static void snd_emu10k1_efx_playback_freeze_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++) {
+ int voice = epcm->voices[i]->number;
+ snd_emu10k1_ptr_write(emu, CPF_STOP, voice, 1);
+ snd_emu10k1_playback_commit_pitch(emu, voice, PITCH_48000 << 16);
+ }
+}
+
+static void snd_emu10k1_efx_playback_unmute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_unmute_voice(emu, epcm->voices[i], false, true,
+ &emu->efx_pcm_mixer[i]);
+}
+
+static void snd_emu10k1_efx_playback_stop_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+ snd_emu10k1_playback_set_stopped(emu, epcm);
+
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_mute_voice(emu, epcm->voices[i]);
+}
static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
@@ -909,47 +914,60 @@ static int snd_emu10k1_efx_playback_trigger(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 i;
+ u64 mask;
int result = 0;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- /* prepare voices */
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
- }
- snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
-
- /* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
- &emu->efx_pcm_mixer[0]);
- for (i = 1; i < NUM_EFX_PLAYBACK; i++)
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
- &emu->efx_pcm_mixer[i]);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
- for (i = 1; i < NUM_EFX_PLAYBACK; i++)
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
- epcm->running = 1;
+ mask = snd_emu10k1_efx_playback_voice_mask(
+ epcm, runtime->channels);
+ for (int i = 0; i < 10; i++) {
+ // Note that the freeze is not interruptible, so we make no
+ // effort to reset the bits outside the error handling here.
+ snd_emu10k1_voice_set_loop_stop_multiple(emu, mask);
+ snd_emu10k1_efx_playback_freeze_voices(
+ emu, epcm, runtime->channels);
+ snd_emu10k1_playback_prepare_voices(
+ emu, epcm, true, false, runtime->channels);
+
+ // It might seem to make more sense to unmute the voices only after
+ // they have been started, to potentially avoid torturing the speakers
+ // if something goes wrong. However, we cannot unmute atomically,
+ // which means that we'd get some mild artifacts in the regular case.
+ snd_emu10k1_efx_playback_unmute_voices(emu, epcm, runtime->channels);
+
+ snd_emu10k1_playback_set_running(emu, epcm);
+ result = snd_emu10k1_voice_clear_loop_stop_multiple_atomic(emu, mask);
+ if (result == 0) {
+ // The extra voice is allowed to lag a bit
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra);
+ return 0;
+ }
+
+ snd_emu10k1_efx_playback_stop_voices(
+ emu, epcm, runtime->channels);
+
+ if (result != -EAGAIN)
+ break;
+ // The sync start can legitimately fail due to NMIs, etc.
+ }
+ snd_emu10k1_voice_clear_loop_stop_multiple(emu, mask);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- epcm->running = 0;
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
- }
snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ snd_emu10k1_efx_playback_stop_voices(
+ emu, epcm, runtime->channels);
+
+ epcm->resume_pos = snd_emu10k1_playback_pointer(substream);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
return result;
}
@@ -988,9 +1006,8 @@ static const struct snd_pcm_hardware snd_emu10k1_playback =
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
.period_bytes_max = (128*1024),
- .periods_min = 1,
+ .periods_min = 2,
.periods_max = 1024,
.fifo_size = 0,
};
@@ -1006,7 +1023,7 @@ static const struct snd_pcm_hardware snd_emu10k1_capture =
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
@@ -1026,13 +1043,11 @@ static const struct snd_pcm_hardware snd_emu10k1_capture_efx =
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
- .rate_min = 44100,
- .rate_max = 192000,
- .channels_min = 8,
- .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 16,
.buffer_bytes_max = (64*1024),
.period_bytes_min = 384,
.period_bytes_max = (64*1024),
@@ -1093,13 +1108,29 @@ static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
return 0;
}
+static int snd_emu10k1_playback_set_constraints(struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ // The buffer size must be a multiple of the period size, to avoid a
+ // mismatch between the extra voice and the regular voices.
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+ // The hardware is typically the cache's size of 64 frames ahead.
+ // Leave enough time for actually filling up the buffer.
+ err = snd_pcm_hw_constraint_minmax(
+ runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 128, UINT_MAX);
+ return err;
+}
+
static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_pcm *epcm;
struct snd_emu10k1_pcm_mixer *mix;
struct snd_pcm_runtime *runtime = substream->runtime;
- int i;
+ int i, j, err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1108,18 +1139,24 @@ 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;
-
+ if (emu->card_capabilities->emu_model)
+ snd_emu1010_constrain_efx_rate(emu, runtime);
+ err = snd_emu10k1_playback_set_constraints(runtime);
+ if (err < 0) {
+ kfree(epcm);
+ return err;
+ }
+
for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
- mix->send_routing[0][0] = i;
+ for (j = 0; j < 8; j++)
+ mix->send_routing[0][j] = i + j;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = 255;
- mix->attn[0] = 0xffff;
+ mix->attn[0] = 0x8000;
mix->epcm = epcm;
snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
}
@@ -1143,16 +1180,13 @@ 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) {
- kfree(epcm);
- return err;
- }
- if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+ err = snd_emu10k1_playback_set_constraints(runtime);
+ if (err < 0) {
kfree(epcm);
return err;
}
- if (emu->card_capabilities->emu_model && emu->emu1010.internal_clock == 0)
- sample_rate = 44100;
+ if (emu->card_capabilities->emu_model)
+ sample_rate = emu->emu1010.word_clock;
else
sample_rate = 48000;
err = snd_pcm_hw_rule_noresample(runtime, sample_rate);
@@ -1161,12 +1195,12 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
return err;
}
mix = &emu->pcm_mixer[substream->number];
- for (i = 0; i < 4; i++)
+ for (i = 0; i < 8; i++)
mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = mix->send_volume[0][1] =
mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
- mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0x8000;
mix->epcm = epcm;
snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
return 0;
@@ -1202,10 +1236,11 @@ static int snd_emu10k1_capture_open(struct snd_pcm_substream *substream)
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_capture;
+ snd_emu10k1_constrain_capture_rates(emu, runtime);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
emu->pcm_capture_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
return 0;
}
@@ -1240,10 +1275,10 @@ static int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream)
runtime->hw = snd_emu10k1_capture;
runtime->hw.rates = SNDRV_PCM_RATE_8000;
runtime->hw.rate_min = runtime->hw.rate_max = 8000;
- runtime->hw.channels_min = 1;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
emu->pcm_capture_mic_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
return 0;
}
@@ -1251,7 +1286,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;
}
@@ -1262,7 +1297,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int nefx = emu->audigy ? 64 : 32;
- int idx;
+ int idx, err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1278,66 +1313,30 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
substream->runtime->private_data = epcm;
substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_capture_efx;
- runtime->hw.rates = SNDRV_PCM_RATE_48000;
- runtime->hw.rate_min = runtime->hw.rate_max = 48000;
- spin_lock_irq(&emu->reg_lock);
if (emu->card_capabilities->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
- * rate_min = 44100,
- * rate_max = 192000,
- * channels_min = 16,
- * channels_max = 16,
- * Need to add mixer control to fix sample rate
- *
+ snd_emu1010_constrain_efx_rate(emu, runtime);
+ /*
* 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) {
- case 0:
- /* For 44.1kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_44100;
- runtime->hw.rate_min = runtime->hw.rate_max = 44100;
- runtime->hw.channels_min =
- runtime->hw.channels_max = 16;
- break;
- case 1:
- /* For 48kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_48000;
- runtime->hw.rate_min = runtime->hw.rate_max = 48000;
- runtime->hw.channels_min =
- runtime->hw.channels_max = 16;
- break;
- }
-#endif
#if 0
/* For 96kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_96000;
- runtime->hw.rate_min = runtime->hw.rate_max = 96000;
runtime->hw.channels_min = runtime->hw.channels_max = 4;
#endif
#if 0
/* For 192kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_192000;
- runtime->hw.rate_min = runtime->hw.rate_max = 192000;
runtime->hw.channels_min = runtime->hw.channels_max = 2;
#endif
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
- /* efx_voices_mask[0] is expected to be zero
- * efx_voices_mask[1] is expected to have 32bits set
- */
} else {
+ guard(spinlock_irq)(&emu->reg_lock);
runtime->hw.channels_min = runtime->hw.channels_max = 0;
for (idx = 0; idx < nefx; idx++) {
if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
@@ -1345,13 +1344,19 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.channels_max++;
}
}
+ epcm->capture_cr_val = emu->efx_voices_mask[0];
+ epcm->capture_cr_val2 = emu->efx_voices_mask[1];
+ }
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_efx_capture_channels);
+ if (err < 0) {
+ kfree(epcm);
+ return err;
}
- epcm->capture_cr_val = emu->efx_voices_mask[0];
- epcm->capture_cr_val2 = emu->efx_voices_mask[1];
- spin_unlock_irq(&emu->reg_lock);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
emu->pcm_capture_efx_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
return 0;
}
@@ -1359,7 +1364,7 @@ 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;
}
@@ -1367,21 +1372,16 @@ static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
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 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,
@@ -1391,13 +1391,11 @@ static const struct snd_pcm_ops snd_emu10k1_capture_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 snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
@@ -1406,7 +1404,8 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
struct snd_pcm_substream *substream;
int err;
- 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;
@@ -1416,15 +1415,18 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
+ strscpy(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);
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 64*1024, 64*1024);
return 0;
}
@@ -1435,7 +1437,8 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
struct snd_pcm_substream *substream;
int err;
- 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;
@@ -1444,12 +1447,13 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "Multichannel Playback");
+ strscpy(pcm->name, "Multichannel Playback");
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;
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
@@ -1458,9 +1462,6 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
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,
@@ -1471,7 +1472,8 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1479,10 +1481,11 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "Mic Capture");
+ strscpy(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);
return 0;
}
@@ -1504,10 +1507,8 @@ static int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, st
int nefx = emu->audigy ? 64 : 32;
int idx;
- spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < nefx; idx++)
ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
@@ -1516,7 +1517,6 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int nval[2], bits;
int nefx = emu->audigy ? 64 : 32;
- int nefxb = emu->audigy ? 7 : 6;
int change, idx;
nval[0] = nval[1] = 0;
@@ -1525,20 +1525,15 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
nval[idx / 32] |= 1 << (idx % 32);
bits++;
}
-
- for (idx = 0; idx < nefxb; idx++)
- if (1 << idx == bits)
- break;
-
- if (idx >= nefxb)
+
+ if (bits == 9 || bits == 11 || bits == 13 || bits == 15 || bits > 16)
return -EINVAL;
- spin_lock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
change = (nval[0] != emu->efx_voices_mask[0]) ||
(nval[1] != emu->efx_voices_mask[1]);
emu->efx_voices_mask[0] = nval[0];
emu->efx_voices_mask[1] = nval[1];
- spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1553,9 +1548,6 @@ static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
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,
@@ -1636,12 +1628,6 @@ static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substr
fx8010_pb_trans_copy);
}
-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));
-}
-
static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
@@ -1650,7 +1636,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;
}
@@ -1672,12 +1657,14 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre
pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
pcm->tram_shift = 0;
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ emu->gpr_base + pcm->gpr_running, 0, /* reset */
+ emu->gpr_base + pcm->gpr_trigger, 0, /* reset */
+ emu->gpr_base + pcm->gpr_size, runtime->buffer_size,
+ emu->gpr_base + pcm->gpr_ptr, 0, /* reset ptr number */
+ emu->gpr_base + pcm->gpr_count, runtime->period_size,
+ emu->gpr_base + pcm->gpr_tmpcount, runtime->period_size,
+ REGLIST_END);
for (i = 0; i < pcm->channels; i++)
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
return 0;
@@ -1687,9 +1674,9 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- int result = 0;
+ int result;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* follow thru */
@@ -1709,25 +1696,22 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
#endif
result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
if (result < 0)
- goto __err;
+ return result;
snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
break;
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;
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- __err:
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream)
@@ -1746,7 +1730,8 @@ 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,
@@ -1770,13 +1755,10 @@ static int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream)
runtime->hw = snd_emu10k1_fx8010_playback;
runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
- spin_lock_irq(&emu->reg_lock);
- if (pcm->valid == 0) {
- spin_unlock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
+ if (pcm->valid == 0)
return -ENODEV;
- }
pcm->opened = 1;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
@@ -1785,17 +1767,14 @@ static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- spin_lock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
pcm->opened = 0;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
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,
@@ -1809,50 +1788,49 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
struct snd_kcontrol *kctl;
int err;
- 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)
+ strscpy(pcm->name, "Multichannel Capture");
+ else
+ strscpy(pcm->name, "Multichannel Capture/PT Playback");
emu->pcm_efx = 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)
- /* Pavel Hofman - 32 voices will be used for
- * capture (write mode) -
- * each bit = corresponding voice
- */
- emu->efx_voices_mask[1] = 0xffffffff;
- else
+ if (!emu->card_capabilities->emu_model) {
+ // On Sound Blasters, the DSP code copies the EXTINs to FXBUS2.
+ // The mask determines which of these and the EXTOUTs the multi-
+ // channel capture actually records (the channel order is fixed).
+ if (emu->audigy) {
+ emu->efx_voices_mask[0] = 0;
emu->efx_voices_mask[1] = 0xffff;
+ } else {
+ emu->efx_voices_mask[0] = 0xffff0000;
+ emu->efx_voices_mask[1] = 0;
+ }
+ kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.device = device;
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0)
+ return err;
} else {
- emu->efx_voices_mask[0] = 0xffff0000;
- emu->efx_voices_mask[1] = 0;
+ // On E-MU cards, the DSP code copies the P16VINs/EMU32INs to
+ // FXBUS2. These are already selected & routed by the FPGA,
+ // so there is no need to apply additional masking.
}
- /* For emu1010, the control has to set 32 upper bits (voices)
- * out of the 64 bits (voices) to true for the 16-channels capture
- * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
- * is already defined but the snd_emu10k1_pcm_efx_voices_mask
- * control can override this register's value.
- */
- kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
- if (!kctl)
- return -ENOMEM;
- kctl->id.device = device;
- snd_ctl_add(emu->card, kctl);
- 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 bde0d1954f56..f6186b5be049 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -1,35 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / proc interface routines
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * 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
*
+ * Routines for control of EMU10K1 chips / proc interface routines
*/
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include "p16v.h"
@@ -40,10 +22,10 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
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);
@@ -51,9 +33,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
snd_iprintf(buffer, "\n%s\n", title);
if (status != 0xffffffff) {
- snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
- snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
- snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+ snd_iprintf(buffer, "Professional Mode : %s\n", str_yes_no(status & SPCS_PROFESSIONAL));
+ snd_iprintf(buffer, "Not Audio Data : %s\n", str_yes_no(status & SPCS_NOTAUDIODATA));
+ snd_iprintf(buffer, "Copyright : %s\n", str_yes_no(status & SPCS_COPYRIGHT));
snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6);
snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
@@ -65,9 +47,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
if (rate_reg > 0) {
rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
- snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
- snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
- snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "S/PDIF Valid : %s\n", str_on_off(rate & SRCS_SPDIFVALID));
+ snd_iprintf(buffer, "S/PDIF Locked : %s\n", str_on_off(rate & SRCS_SPDIFLOCKED));
+ snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(rate & SRCS_RATELOCKED));
/* From ((Rate * 48000 ) / 262144); */
snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11);
}
@@ -80,158 +62,100 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
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] = {
- /* 00 */ "AC97 Left",
- /* 01 */ "AC97 Right",
- /* 02 */ "Optical IEC958 Left",
- /* 03 */ "Optical IEC958 Right",
- /* 04 */ "Center",
- /* 05 */ "LFE",
- /* 06 */ "Headphone Left",
- /* 07 */ "Headphone Right",
- /* 08 */ "Surround Left",
- /* 09 */ "Surround Right",
- /* 10 */ "PCM Capture Left",
- /* 11 */ "PCM Capture Right",
- /* 12 */ "MIC Capture",
- /* 13 */ "AC97 Surround Left",
- /* 14 */ "AC97 Surround Right",
- /* 15 */ "???",
- /* 16 */ "???",
- /* 17 */ "Analog Center",
- /* 18 */ "Analog LFE",
- /* 19 */ "???",
- /* 20 */ "???",
- /* 21 */ "???",
- /* 22 */ "???",
- /* 23 */ "???",
- /* 24 */ "???",
- /* 25 */ "???",
- /* 26 */ "???",
- /* 27 */ "???",
- /* 28 */ "???",
- /* 29 */ "???",
- /* 30 */ "???",
- /* 31 */ "???"
- };
-
- static char *audigy_outs[64] = {
- /* 00 */ "Digital Front Left",
- /* 01 */ "Digital Front Right",
- /* 02 */ "Digital Center",
- /* 03 */ "Digital LEF",
- /* 04 */ "Headphone Left",
- /* 05 */ "Headphone Right",
- /* 06 */ "Digital Rear Left",
- /* 07 */ "Digital Rear Right",
- /* 08 */ "Front Left",
- /* 09 */ "Front Right",
- /* 10 */ "Center",
- /* 11 */ "LFE",
- /* 12 */ "???",
- /* 13 */ "???",
- /* 14 */ "Rear Left",
- /* 15 */ "Rear Right",
- /* 16 */ "AC97 Front Left",
- /* 17 */ "AC97 Front Right",
- /* 18 */ "ADC Caputre Left",
- /* 19 */ "ADC Capture Right",
- /* 20 */ "???",
- /* 21 */ "???",
- /* 22 */ "???",
- /* 23 */ "???",
- /* 24 */ "???",
- /* 25 */ "???",
- /* 26 */ "???",
- /* 27 */ "???",
- /* 28 */ "???",
- /* 29 */ "???",
- /* 30 */ "???",
- /* 31 */ "???",
- /* 32 */ "FXBUS2_0",
- /* 33 */ "FXBUS2_1",
- /* 34 */ "FXBUS2_2",
- /* 35 */ "FXBUS2_3",
- /* 36 */ "FXBUS2_4",
- /* 37 */ "FXBUS2_5",
- /* 38 */ "FXBUS2_6",
- /* 39 */ "FXBUS2_7",
- /* 40 */ "FXBUS2_8",
- /* 41 */ "FXBUS2_9",
- /* 42 */ "FXBUS2_10",
- /* 43 */ "FXBUS2_11",
- /* 44 */ "FXBUS2_12",
- /* 45 */ "FXBUS2_13",
- /* 46 */ "FXBUS2_14",
- /* 47 */ "FXBUS2_15",
- /* 48 */ "FXBUS2_16",
- /* 49 */ "FXBUS2_17",
- /* 50 */ "FXBUS2_18",
- /* 51 */ "FXBUS2_19",
- /* 52 */ "FXBUS2_20",
- /* 53 */ "FXBUS2_21",
- /* 54 */ "FXBUS2_22",
- /* 55 */ "FXBUS2_23",
- /* 56 */ "FXBUS2_24",
- /* 57 */ "FXBUS2_25",
- /* 58 */ "FXBUS2_26",
- /* 59 */ "FXBUS2_27",
- /* 60 */ "FXBUS2_28",
- /* 61 */ "FXBUS2_29",
- /* 62 */ "FXBUS2_30",
- /* 63 */ "FXBUS2_31"
- };
-
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 *inputs = emu->audigy ?
+ snd_emu10k1_audigy_ins : snd_emu10k1_sblive_ins;
+ const char * const *outputs = emu->audigy ?
+ snd_emu10k1_audigy_outs : snd_emu10k1_sblive_outs;
+ unsigned short extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
+ unsigned short extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
+ unsigned int val, val1, ptrx, psst, dsl, snda;
+ int nefx = emu->audigy ? 32 : 16;
int idx;
snd_iprintf(buffer, "EMU10K1\n\n");
snd_iprintf(buffer, "Card : %s\n",
- emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
+ emu->card_capabilities->emu_model ? "E-MU D.A.S." :
+ emu->card_capabilities->ecard ? "E-MU A.P.S." :
+ emu->audigy ? "SB Audigy" : "SB Live!");
snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
- snd_iprintf(buffer, "\n");
- snd_iprintf(buffer, "Effect Send Routing :\n");
+
+ snd_iprintf(buffer, "\nEffect Send Routing & Amounts:\n");
for (idx = 0; idx < NUM_G; idx++) {
- val = emu->audigy ?
- snd_emu10k1_ptr_read(emu, A_FXRT1, idx) :
- snd_emu10k1_ptr_read(emu, FXRT, idx);
- val1 = emu->audigy ?
- snd_emu10k1_ptr_read(emu, A_FXRT2, idx) :
- 0;
+ ptrx = snd_emu10k1_ptr_read(emu, PTRX, idx);
+ psst = snd_emu10k1_ptr_read(emu, PSST, idx);
+ dsl = snd_emu10k1_ptr_read(emu, DSL, idx);
if (emu->audigy) {
- snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i, ",
+ val = snd_emu10k1_ptr_read(emu, A_FXRT1, idx);
+ val1 = snd_emu10k1_ptr_read(emu, A_FXRT2, idx);
+ snda = snd_emu10k1_ptr_read(emu, A_SENDAMOUNTS, idx);
+ snd_iprintf(buffer, "Ch%-2i: A=%2i:%02x, B=%2i:%02x, C=%2i:%02x, D=%2i:%02x, ",
idx,
- val & 0x3f,
- (val >> 8) & 0x3f,
- (val >> 16) & 0x3f,
- (val >> 24) & 0x3f);
- snd_iprintf(buffer, "E=%i, F=%i, G=%i, H=%i\n",
- val1 & 0x3f,
- (val1 >> 8) & 0x3f,
- (val1 >> 16) & 0x3f,
- (val1 >> 24) & 0x3f);
+ val & 0x3f, REG_VAL_GET(PTRX_FXSENDAMOUNT_A, ptrx),
+ (val >> 8) & 0x3f, REG_VAL_GET(PTRX_FXSENDAMOUNT_B, ptrx),
+ (val >> 16) & 0x3f, REG_VAL_GET(PSST_FXSENDAMOUNT_C, psst),
+ (val >> 24) & 0x3f, REG_VAL_GET(DSL_FXSENDAMOUNT_D, dsl));
+ snd_iprintf(buffer, "E=%2i:%02x, F=%2i:%02x, G=%2i:%02x, H=%2i:%02x\n",
+ val1 & 0x3f, (snda >> 24) & 0xff,
+ (val1 >> 8) & 0x3f, (snda >> 16) & 0xff,
+ (val1 >> 16) & 0x3f, (snda >> 8) & 0xff,
+ (val1 >> 24) & 0x3f, snda & 0xff);
} else {
- snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i\n",
+ val = snd_emu10k1_ptr_read(emu, FXRT, idx);
+ snd_iprintf(buffer, "Ch%-2i: A=%2i:%02x, B=%2i:%02x, C=%2i:%02x, D=%2i:%02x\n",
idx,
- (val >> 16) & 0x0f,
- (val >> 20) & 0x0f,
- (val >> 24) & 0x0f,
- (val >> 28) & 0x0f);
+ (val >> 16) & 0x0f, REG_VAL_GET(PTRX_FXSENDAMOUNT_A, ptrx),
+ (val >> 20) & 0x0f, REG_VAL_GET(PTRX_FXSENDAMOUNT_B, ptrx),
+ (val >> 24) & 0x0f, REG_VAL_GET(PSST_FXSENDAMOUNT_C, psst),
+ (val >> 28) & 0x0f, REG_VAL_GET(DSL_FXSENDAMOUNT_D, dsl));
}
}
- snd_iprintf(buffer, "\nCaptured FX Outputs :\n");
- for (idx = 0; idx < nefx; idx++) {
- if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
- snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
+ snd_iprintf(buffer, "\nEffect Send Targets:\n");
+ // Audigy actually has 64, but we don't use them all.
+ for (idx = 0; idx < 32; idx++) {
+ const char *c = snd_emu10k1_fxbus[idx];
+ if (c)
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, c);
+ }
+ if (!emu->card_capabilities->emu_model) {
+ snd_iprintf(buffer, "\nOutput Channels:\n");
+ for (idx = 0; idx < 32; idx++)
+ if (outputs[idx] && (extout_mask & (1 << idx)))
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, outputs[idx]);
+ snd_iprintf(buffer, "\nInput Channels:\n");
+ for (idx = 0; idx < 16; idx++)
+ if (inputs[idx] && (extin_mask & (1 << idx)))
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, inputs[idx]);
+ snd_iprintf(buffer, "\nMultichannel Capture Sources:\n");
+ for (idx = 0; idx < nefx; idx++)
+ if (emu->efx_voices_mask[0] & (1 << idx))
+ snd_iprintf(buffer, " Channel %02i [Output: %s]\n",
+ idx, outputs[idx] ? outputs[idx] : "???");
+ if (emu->audigy) {
+ for (idx = 0; idx < 32; idx++)
+ if (emu->efx_voices_mask[1] & (1 << idx))
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 32, inputs[idx] ? inputs[idx] : "???");
+ } else {
+ for (idx = 0; idx < 16; idx++) {
+ if (emu->efx_voices_mask[0] & ((1 << 16) << idx)) {
+ if (emu->card_capabilities->sblive51) {
+ s8 c = snd_emu10k1_sblive51_fxbus2_map[idx];
+ if (c == -1)
+ snd_iprintf(buffer, " Channel %02i [Output: %s]\n",
+ idx + 16, outputs[idx + 16]);
+ else
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 16, inputs[c]);
+ } else {
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 16, inputs[idx] ? inputs[idx] : "???");
+ }
+ }
+ }
+ }
}
- snd_iprintf(buffer, "\nAll FX Outputs :\n");
- for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
- snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
}
static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
@@ -240,27 +164,42 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
u32 value2;
- u32 rate;
if (emu->card_capabilities->emu_model) {
- snd_emu1010_fpga_read(emu, 0x38, &value);
- if ((value & 0x1) == 0) {
- snd_emu1010_fpga_read(emu, 0x2a, &value);
- snd_emu1010_fpga_read(emu, 0x2b, &value2);
- rate = 0x1770000 / (((value << 5) | value2)+1);
- snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
- } else {
- snd_iprintf(buffer, "ADAT Unlocked\n");
- }
- snd_emu1010_fpga_read(emu, 0x20, &value);
- if ((value & 0x4) == 0) {
- snd_emu1010_fpga_read(emu, 0x28, &value);
- snd_emu1010_fpga_read(emu, 0x29, &value2);
- rate = 0x1770000 / (((value << 5) | value2)+1);
- snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
- } else {
- snd_iprintf(buffer, "SPDIF Unlocked\n");
+ guard(snd_emu1010_fpga_lock)(emu);
+
+ // This represents the S/PDIF lock status on 0404b, which is
+ // kinda weird and unhelpful, because monitoring it via IRQ is
+ // impractical (one gets an IRQ flood as long as it is desynced).
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &value);
+ snd_iprintf(buffer, "Lock status 1: %#x\n", value & 0x10);
+
+ // Bit 0x1 in LO being 0 is supposedly for ADAT lock.
+ // The registers are always all zero on 0404b.
+ snd_emu1010_fpga_read(emu, EMU_HANA_LOCK_STS_LO, &value);
+ snd_emu1010_fpga_read(emu, EMU_HANA_LOCK_STS_HI, &value2);
+ snd_iprintf(buffer, "Lock status 2: %#x %#x\n", value, value2);
+
+ snd_iprintf(buffer, "S/PDIF rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_HANA_SPDIF_IN));
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404) {
+ snd_iprintf(buffer, "ADAT rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_HANA_ADAT_IN));
+ snd_iprintf(buffer, "Dock rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_2ND_HANA));
}
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU0404 ||
+ emu->card_capabilities->emu_model == EMU_MODEL_EMU1010)
+ snd_iprintf(buffer, "BNC rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_SYNC_BNC));
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
+ if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
+ snd_iprintf(buffer, "\nS/PDIF input invalid\n");
+ else
+ snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n",
+ value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer",
+ value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : "");
} else {
snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
@@ -268,7 +207,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
#if 0
val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
snd_iprintf(buffer, "\nZoomed Video\n");
- snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(val & SRCS_RATELOCKED));
snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
#endif
}
@@ -276,7 +215,7 @@ 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);
@@ -287,37 +226,148 @@ static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
}
}
-static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
+struct emu10k1_reg_entry {
+ unsigned short base, size;
+ const char *name;
+};
+
+static const struct emu10k1_reg_entry sblive_reg_entries[] = {
+ { 0, 0x10, "FXBUS" },
+ { 0x10, 0x10, "EXTIN" },
+ { 0x20, 0x10, "EXTOUT" },
+ { 0x30, 0x10, "FXBUS2" },
+ { 0x40, 0x20, NULL }, // Constants
+ { 0x100, 0x100, "GPR" },
+ { 0x200, 0x80, "ITRAM_DATA" },
+ { 0x280, 0x20, "ETRAM_DATA" },
+ { 0x300, 0x80, "ITRAM_ADDR" },
+ { 0x380, 0x20, "ETRAM_ADDR" },
+ { 0x400, 0, NULL }
+};
+
+static const struct emu10k1_reg_entry audigy_reg_entries[] = {
+ { 0, 0x40, "FXBUS" },
+ { 0x40, 0x10, "EXTIN" },
+ { 0x50, 0x10, "P16VIN" },
+ { 0x60, 0x20, "EXTOUT" },
+ { 0x80, 0x20, "FXBUS2" },
+ { 0xa0, 0x10, "EMU32OUTH" },
+ { 0xb0, 0x10, "EMU32OUTL" },
+ { 0xc0, 0x20, NULL }, // Constants
+ // This can't be quite right - overlap.
+ //{ 0x100, 0xc0, "ITRAM_CTL" },
+ //{ 0x1c0, 0x40, "ETRAM_CTL" },
+ { 0x160, 0x20, "A3_EMU32IN" },
+ { 0x1e0, 0x20, "A3_EMU32OUT" },
+ { 0x200, 0xc0, "ITRAM_DATA" },
+ { 0x2c0, 0x40, "ETRAM_DATA" },
+ { 0x300, 0xc0, "ITRAM_ADDR" },
+ { 0x3c0, 0x40, "ETRAM_ADDR" },
+ { 0x400, 0x200, "GPR" },
+ { 0x600, 0, NULL }
+};
+
+static const char * const emu10k1_const_entries[] = {
+ "C_00000000",
+ "C_00000001",
+ "C_00000002",
+ "C_00000003",
+ "C_00000004",
+ "C_00000008",
+ "C_00000010",
+ "C_00000020",
+ "C_00000100",
+ "C_00010000",
+ "C_00000800",
+ "C_10000000",
+ "C_20000000",
+ "C_40000000",
+ "C_80000000",
+ "C_7fffffff",
+ "C_ffffffff",
+ "C_fffffffe",
+ "C_c0000000",
+ "C_4f1bbcdc",
+ "C_5a7ef9db",
+ "C_00100000",
+ "GPR_ACCU",
+ "GPR_COND",
+ "GPR_NOISE0",
+ "GPR_NOISE1",
+ "GPR_IRQ",
+ "GPR_DBAC",
+ "GPR_DBACE",
+ "???",
+};
+
+static int disasm_emu10k1_reg(char *buffer,
+ const struct emu10k1_reg_entry *entries,
+ unsigned reg, const char *pfx)
+{
+ for (int i = 0; ; i++) {
+ unsigned base = entries[i].base;
+ unsigned size = entries[i].size;
+ if (!size)
+ return sprintf(buffer, "%s0x%03x", pfx, reg);
+ if (reg >= base && reg < base + size) {
+ const char *name = entries[i].name;
+ reg -= base;
+ if (name)
+ return sprintf(buffer, "%s%s(%u)", pfx, name, reg);
+ return sprintf(buffer, "%s%s", pfx, emu10k1_const_entries[reg]);
+ }
+ }
+}
+
+static int disasm_sblive_reg(char *buffer, unsigned reg, const char *pfx)
+{
+ return disasm_emu10k1_reg(buffer, sblive_reg_entries, reg, pfx);
+}
+
+static int disasm_audigy_reg(char *buffer, unsigned reg, const char *pfx)
+{
+ return disasm_emu10k1_reg(buffer, audigy_reg_entries, reg, pfx);
+}
+
+static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
u32 pc;
struct snd_emu10k1 *emu = entry->private_data;
+ static const char * const insns[16] = {
+ "MAC0", "MAC1", "MAC2", "MAC3", "MACINT0", "MACINT1", "ACC3", "MACMV",
+ "ANDXOR", "TSTNEG", "LIMITGE", "LIMITLT", "LOG", "EXP", "INTERP", "SKIP",
+ };
+ static const char spaces[] = " ";
+ const int nspaces = sizeof(spaces) - 1;
snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
snd_iprintf(buffer, " Code dump :\n");
for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
u32 low, high;
+ int len;
+ char buf[100];
+ char *bufp = buf;
low = snd_emu10k1_efx_read(emu, pc * 2);
high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
- if (emu->audigy)
- snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
- (high >> 24) & 0x0f,
- (high >> 12) & 0x7ff,
- (high >> 0) & 0x7ff,
- (low >> 12) & 0x7ff,
- (low >> 0) & 0x7ff,
- pc,
- high, low);
- else
- snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
- (high >> 20) & 0x0f,
- (high >> 10) & 0x3ff,
- (high >> 0) & 0x3ff,
- (low >> 10) & 0x3ff,
- (low >> 0) & 0x3ff,
- pc,
- high, low);
+ if (emu->audigy) {
+ bufp += sprintf(bufp, " %-7s ", insns[(high >> 24) & 0x0f]);
+ bufp += disasm_audigy_reg(bufp, (high >> 12) & 0x7ff, "");
+ bufp += disasm_audigy_reg(bufp, (high >> 0) & 0x7ff, ", ");
+ bufp += disasm_audigy_reg(bufp, (low >> 12) & 0x7ff, ", ");
+ bufp += disasm_audigy_reg(bufp, (low >> 0) & 0x7ff, ", ");
+ } else {
+ bufp += sprintf(bufp, " %-7s ", insns[(high >> 20) & 0x0f]);
+ bufp += disasm_sblive_reg(bufp, (high >> 10) & 0x3ff, "");
+ bufp += disasm_sblive_reg(bufp, (high >> 0) & 0x3ff, ", ");
+ bufp += disasm_sblive_reg(bufp, (low >> 10) & 0x3ff, ", ");
+ bufp += disasm_sblive_reg(bufp, (low >> 0) & 0x3ff, ", ");
+ }
+ len = (int)(ptrdiff_t)(bufp - buf);
+ snd_iprintf(buffer, "%s %s /* 0x%04x: 0x%08x%08x */\n",
+ buf, &spaces[nspaces - clamp(65 - len, 0, nspaces)],
+ pc, high, low);
}
}
@@ -379,32 +429,78 @@ static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
struct snd_emu10k1_voice *voice;
int idx;
-
- snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
+ static const char * const types[] = {
+ "Unused", "EFX", "EFX IRQ", "PCM", "PCM IRQ", "Synth"
+ };
+ static_assert(ARRAY_SIZE(types) == EMU10K1_NUM_TYPES);
+
+ snd_iprintf(buffer, "ch\tdirty\tlast\tuse\n");
for (idx = 0; idx < NUM_G; idx++) {
voice = &emu->voices[idx];
- snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
+ snd_iprintf(buffer, "%i\t%u\t%u\t%s\n",
idx,
- voice->use,
- voice->pcm,
- voice->efx,
- voice->synth,
- voice->midi);
+ voice->dirty,
+ voice->last,
+ types[voice->use]);
}
}
#ifdef CONFIG_SND_DEBUG
+
+static void snd_emu_proc_emu1010_link_read(struct snd_emu10k1 *emu,
+ struct snd_info_buffer *buffer,
+ u32 dst)
+{
+ u32 src = snd_emu1010_fpga_link_dst_src_read(emu, dst);
+ snd_iprintf(buffer, "%04x: %04x\n", dst, src);
+}
+
static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
int i;
+
+ guard(snd_emu1010_fpga_lock)(emu);
+
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
for(i = 0; i < 0x40; i+=1) {
snd_emu1010_fpga_read(emu, i, &value);
- snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
+ snd_iprintf(buffer, "%02x: %02x\n", i, value);
+ }
+
+ snd_iprintf(buffer, "\nEMU1010 Routes:\n\n");
+
+ for (i = 0; i < 16; i++) // To Alice2/Tina[2] via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, i);
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404)
+ for (i = 0; i < 32; i++) // To Dock via EDI
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x100 + i);
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU1616)
+ for (i = 0; i < 8; i++) // To Hamoa/local
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x200 + i);
+ for (i = 0; i < 8; i++) // To Hamoa/Mana/local
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x300 + i);
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+ for (i = 0; i < 16; i++) // To Tina2 via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x400 + i);
+ } else if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404) {
+ for (i = 0; i < 8; i++) // To Hana ADAT
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x400 + i);
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010B) {
+ for (i = 0; i < 16; i++) // To Tina via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x500 + i);
+ } else {
+ // To Alice2 via I2S
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x500);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x501);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x600);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x601);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x700);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x701);
+ }
}
}
@@ -413,13 +509,10 @@ static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
{
struct snd_emu10k1 *emu = entry->private_data;
unsigned long value;
- unsigned long flags;
int i;
snd_iprintf(buffer, "IO Registers:\n\n");
for(i = 0; i < 0x40; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "%02X: %08lX\n", i, value);
}
}
@@ -428,16 +521,13 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_emu10k1 *emu = entry->private_data;
- unsigned long flags;
char line[64];
u32 reg, val;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg < 0x40 && val <= 0xffffffff) {
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
}
}
@@ -447,16 +537,13 @@ static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
- val = inl(emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inl(emu->port + iobase + DATA);
}
static void snd_ptr_write(struct snd_emu10k1 *emu,
@@ -466,14 +553,12 @@ static void snd_ptr_write(struct snd_emu10k1 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
outl(data, emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -491,10 +576,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");
@@ -502,7 +584,8 @@ static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
}
static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer, int iobase)
+ struct snd_info_buffer *buffer,
+ int iobase, int length, int voices)
{
struct snd_emu10k1 *emu = entry->private_data;
char line[64];
@@ -510,7 +593,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
+ if (reg < length && channel_id < voices)
snd_ptr_write(emu, iobase, reg, channel_id, val);
}
}
@@ -518,13 +601,15 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
static void snd_emu_proc_ptr_reg_write00(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_emu_proc_ptr_reg_write(entry, buffer, 0);
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0, 0x80, 64);
}
static void snd_emu_proc_ptr_reg_write20(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
+ struct snd_emu10k1 *emu = entry->private_data;
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0x20,
+ emu->card_capabilities->ca0108_chip ? 0xa0 : 0x80, 4);
}
@@ -559,7 +644,7 @@ 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,
};
@@ -568,89 +653,74 @@ 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;
+ snd_card_ro_proc_new(emu->card, "emu1010_regs",
+ emu, snd_emu_proc_emu1010_reg_read);
}
- 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_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);
+ if (!emu->card_capabilities->emu_model &&
+ (emu->card_capabilities->ca0151_chip || emu->card_capabilities->ca0108_chip)) {
+ 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);
+ if (emu->card_capabilities->ca0108_chip)
+ 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;
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 706b4f0c6806..9c897c3e8c28 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -1,28 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * 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
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
@@ -32,33 +16,41 @@
#include <linux/export.h>
#include "p17v.h"
+static inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
+{
+ if (snd_BUG_ON(!emu))
+ return false;
+ if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
+ : (0xffff0000 & ~PTR_ADDRESS_MASK))))
+ return false;
+ if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
+ return false;
+ return true;
+}
+
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
{
- unsigned long flags;
unsigned int regptr, val;
unsigned int mask;
- mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
- regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+ regptr = (reg << 16) | chn;
+ if (!check_ptr_reg(emu, regptr))
+ return 0;
+
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ }
if (reg & 0xff000000) {
unsigned char size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
- mask = ((1 << size) - 1) << offset;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ mask = (1 << size) - 1;
- return (val & mask) >> offset;
+ return (val >> offset) & mask;
} else {
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
}
@@ -68,51 +60,74 @@ EXPORT_SYMBOL(snd_emu10k1_ptr_read);
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
unsigned int mask;
- if (snd_BUG_ON(!emu))
+ regptr = (reg << 16) | chn;
+ if (!check_ptr_reg(emu, regptr))
return;
- mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
- regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (reg & 0xff000000) {
unsigned char size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
- mask = ((1 << size) - 1) << offset;
- data = (data << offset) & mask;
+ mask = (1 << size) - 1;
+ if (snd_BUG_ON(data & ~mask))
+ return;
+ mask <<= offset;
+ data <<= offset;
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(regptr, emu->port + PTR);
data |= inl(emu->port + DATA) & ~mask;
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
} else {
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+ outl(data, emu->port + DATA);
}
EXPORT_SYMBOL(snd_emu10k1_ptr_write);
+void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
+{
+ va_list va;
+ u32 addr_mask;
+
+ if (snd_BUG_ON(!emu))
+ return;
+ if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
+ return;
+ addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);
+
+ va_start(va, chn);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ for (;;) {
+ u32 data;
+ u32 reg = va_arg(va, u32);
+ if (reg == REGLIST_END)
+ break;
+ data = va_arg(va, u32);
+ if (snd_BUG_ON(reg & addr_mask)) // Only raw registers supported here
+ continue;
+ outl((reg << 16) | chn, emu->port + PTR);
+ outl(data, emu->port + DATA);
+ }
+ va_end(va);
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);
+
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- val = inl(emu->port + 0x20 + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + PTR2);
+ return inl(emu->port + DATA2);
}
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
@@ -121,14 +136,12 @@ void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- outl(data, emu->port + 0x20 + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + PTR2);
+ outl(data, emu->port + DATA2);
}
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
@@ -137,22 +150,19 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
unsigned int reset, set;
unsigned int reg, tmp;
int n, result;
- int err = 0;
/* This function is not re-entrant, so protect against it. */
- spin_lock(&emu->spi_lock);
+ guard(spinlock)(&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. */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
if (data > 0xffff) {
/* Only 16bit values allowed */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
@@ -173,15 +183,11 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
}
if (result) {
/* Timed out */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
- err = 0;
-spi_write_exit:
- spin_unlock(&emu->spi_lock);
- return err;
+ return 0;
}
/* The ADC does not support i2c read, so only write is implemented */
@@ -193,7 +199,6 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
int timeout = 0;
int status;
int retry;
- int err = 0;
if ((reg > 0x7f) || (value > 0x1ff)) {
dev_err(emu->card->dev, "i2c_write: invalid values.\n");
@@ -201,7 +206,7 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
}
/* This function is not re-entrant, so protect against it. */
- spin_lock(&emu->i2c_lock);
+ guard(spinlock)(&emu->i2c_lock);
tmp = reg << 25 | value << 16;
@@ -240,94 +245,238 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
status, reg, value);
/* dump_stack(); */
- err = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&emu->i2c_lock);
- return err;
+ return 0;
}
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+static void snd_emu1010_fpga_write_locked(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;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(reg, emu->port + A_IOCFG);
+ if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
+ return;
+ 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. */
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
+ udelay(10);
+}
- return 0;
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
+{
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
+ snd_emu1010_fpga_write_locked(emu, reg, value);
}
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
- unsigned long flags;
- if (reg > 0x3f)
- return 1;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_fpga_write_locked(emu, reg, 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.
+ // But note that any other input pin change will also cause an IRQ,
+ // so using this function often causes an IRQ as a side effect.
+ u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
+
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
+ 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);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return 0;
+ *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
}
/* 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)
+{
+ 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);
+}
+
+u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
{
- 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) );
+ u32 hi, lo;
+
+ if (snd_BUG_ON(dst & ~0x71f))
+ return 0;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo);
+ return (hi << 8) | lo;
+}
- return 0;
+int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
+{
+ u32 reg_lo, reg_hi, value, value2;
+
+ switch (src) {
+ case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
+ if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
+ return 0;
+ reg_lo = EMU_HANA_WC_SPDIF_LO;
+ reg_hi = EMU_HANA_WC_SPDIF_HI;
+ break;
+ case EMU_HANA_WCLOCK_HANA_ADAT_IN:
+ reg_lo = EMU_HANA_WC_ADAT_LO;
+ reg_hi = EMU_HANA_WC_ADAT_HI;
+ break;
+ case EMU_HANA_WCLOCK_SYNC_BNC:
+ reg_lo = EMU_HANA_WC_BNC_LO;
+ reg_hi = EMU_HANA_WC_BNC_HI;
+ break;
+ case EMU_HANA_WCLOCK_2ND_HANA:
+ reg_lo = EMU_HANA2_WC_SPDIF_LO;
+ reg_hi = EMU_HANA2_WC_SPDIF_HI;
+ break;
+ default:
+ return 0;
+ }
+ snd_emu1010_fpga_read(emu, reg_hi, &value);
+ snd_emu1010_fpga_read(emu, reg_lo, &value2);
+ // FIXME: The /4 is valid for 0404b, but contradicts all other info.
+ return 0x1770000 / 4 / (((value << 5) | value2) + 1);
+}
+
+void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
+{
+ int clock;
+ u32 leds;
+
+ switch (emu->emu1010.wclock) {
+ case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
+ clock = 44100;
+ leds = EMU_HANA_DOCK_LEDS_2_44K;
+ break;
+ case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
+ clock = 48000;
+ leds = EMU_HANA_DOCK_LEDS_2_48K;
+ break;
+ default:
+ clock = snd_emu1010_get_raw_rate(
+ emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
+ // The raw rate reading is rather coarse (it cannot accurately
+ // represent 44.1 kHz) and fluctuates slightly. Luckily, the
+ // clock comes from digital inputs, which use standardized rates.
+ // So we round to the closest standard rate and ignore discrepancies.
+ if (clock < 46000) {
+ clock = 44100;
+ leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
+ } else {
+ clock = 48000;
+ leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
+ }
+ break;
+ }
+ emu->emu1010.word_clock = clock;
+
+ // FIXME: this should probably represent the AND of all currently
+ // used sources' lock status. But we don't know how to get that ...
+ leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
+}
+
+void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock,
+ const struct firmware *fw_entry)
+{
+ __always_unused u16 write_post;
+
+ // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E.
+ // On E-MU 0404b it is a Xilinx Spartan III XC3S50.
+ // The wiring is as follows:
+ // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output
+ // In normal operation, the active low reset line is held up by
+ // an FPGA output, while the GPO pin performs its duty as control
+ // register access strobe signal. Writing the respective bit to
+ // EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at
+ // which point the GPO pin can control the reset line through the
+ // resistor.
+ // GPO6 -> FPGA CCLK & FPGA input
+ // GPO5 -> FPGA DIN (dual function)
+
+ // If the FPGA is already programmed, return it to programming mode
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+ dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK :
+ EMU_HANA_FPGA_CONFIG_HANA);
+
+ // Assert reset line for 100uS
+ outw(0x00, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100);
+ outw(0x80, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100); // Allow FPGA memory to clean
+
+ // Upload the netlist. Keep reset line high!
+ for (int n = 0; n < fw_entry->size; n++) {
+ u8 value = fw_entry->data[n];
+ for (int i = 0; i < 8; i++) {
+ u16 reg = 0x80;
+ if (value & 1)
+ reg |= 0x20;
+ value >>= 1;
+ 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.
+ // This appears to be a config word that the rev1 Hana
+ // firmware reads; weird things happen without this.
+ outw(0x10, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
}
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE) | intrenb;
outl(enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE) & ~intrenb;
outl(enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -338,16 +487,13 @@ void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenu
val |= 1 << voicenum;
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -358,15 +504,11 @@ void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicen
val &= ~(1 << voicenum);
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
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 */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -375,16 +517,13 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
voicenum = 1 << voicenum;
}
outl(voicenum, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -395,16 +534,13 @@ void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned i
val |= 1 << voicenum;
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -415,15 +551,11 @@ void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned
val &= ~(1 << voicenum);
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -432,16 +564,14 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
voicenum = 1 << voicenum;
}
outl(voicenum, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+#if 0
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int sol;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -452,16 +582,13 @@ void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voice
sol |= 1 << voicenum;
}
outl(sol, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int sol;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -472,7 +599,83 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi
sol &= ~(1 << voicenum);
}
outl(sol, emu->port + DATA);
+}
+#endif
+
+void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
+}
+
+void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
+}
+
+int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
+{
+ unsigned long flags;
+ u32 soll, solh;
+ int ret = -EIO;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+
+ outl(SOLEL << 16, emu->port + PTR);
+ soll = inl(emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ solh = inl(emu->port + DATA);
+
+ soll &= (u32)~voices;
+ solh &= (u32)(~voices >> 32);
+
+ for (int tries = 0; tries < 1000; tries++) {
+ const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
+ // First we wait for the third quarter of the sample cycle ...
+ u32 wc = inl(emu->port + WC);
+ u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
+ if (cc >= quart * 2 && cc < quart * 3) {
+ // ... and release the low voices, while the high ones are serviced.
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(soll, emu->port + DATA);
+ // Then we wait for the first quarter of the next sample cycle ...
+ for (; tries < 1000; tries++) {
+ cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
+ if (cc < quart)
+ goto good;
+ // We will block for 10+ us with interrupts disabled. This is
+ // not nice at all, but necessary for reasonable reliability.
+ udelay(1);
+ }
+ break;
+ good:
+ // ... and release the high voices, while the low ones are serviced.
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(solh, emu->port + DATA);
+ // Finally we verify that nothing interfered in fact.
+ if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
+ ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+ break;
+ }
+ // Don't block for too long
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ udelay(1);
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ }
+
spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return ret;
}
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
@@ -497,84 +700,17 @@ void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_emu10k1 *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inw(emu->port + AC97DATA);
}
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
{
struct snd_emu10k1 *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
outw(data, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-}
-
-/*
- * convert rate to pitch
- */
-
-unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
-{
- static 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,
- 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
- 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
- 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
- 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
- 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
- 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
- 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
- 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
- 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
- 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
- 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
- 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
- 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
- };
- static 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,
- 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
- 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
- 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
- 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
- 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
- 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
- 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
- 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
- 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
- };
- int i;
-
- if (rate == 0)
- return 0; /* Bail out if no leading "1" */
- rate *= 11185; /* Scale 48000 to 0x20002380 */
- for (i = 31; i > 0; i--) {
- if (rate & 0x80000000) { /* Detect leading "1" */
- return (((unsigned int) (i - 15) << 20) +
- logMagTable[0x7f & (rate >> 24)] +
- (0x7f & (rate >> 17)) *
- logSlopeTable[0x7f & (rate >> 24)]);
- }
- rate <<= 1;
- }
-
- return 0; /* Should never reach this point */
}
-
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 3c5c5e3dc2d9..71aa90b9cc88 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -1,28 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
* Routines for IRQ control of EMU10K1 chips
- *
- * 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/time.h>
@@ -32,19 +12,22 @@
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;
- while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
- timeout++;
- orig_status = status;
+ while ((status = inl(emu->port + IPR)) != 0) {
handled = 1;
if ((status & 0xffffffff) == 0xffffffff) {
dev_info(emu->card->dev,
"Suspected sound card removal\n");
break;
}
+ if (++timeout == 1000) {
+ dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
+ break;
+ }
+ orig_status = status;
if (status & IPR_PCIERROR) {
dev_err(emu->card->dev, "interrupt: PCI error\n");
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
@@ -58,12 +41,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
}
if (status & IPR_CHANNELLOOP) {
+ struct snd_emu10k1_voice *pvoice;
int voice;
int voice_max = status & IPR_CHANNELNUMBERMASK;
u32 val;
- struct snd_emu10k1_voice *pvoice = emu->voices;
val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+ pvoice = emu->voices;
for (voice = 0; voice <= voice_max; voice++) {
if (voice == 0x20)
val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
@@ -79,6 +63,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
pvoice++;
}
val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
+ pvoice = emu->voices;
for (voice = 0; voice <= voice_max; voice++) {
if (voice == 0x20)
val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
@@ -93,9 +78,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
val >>= 1;
pvoice++;
}
- status &= ~IPR_CHANNELLOOP;
+ status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK);
}
- status &= ~IPR_CHANNELNUMBERMASK;
if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
if (emu->capture_interrupt)
emu->capture_interrupt(emu, status);
@@ -153,61 +137,26 @@ 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);
-
- /* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */
- orig_status2 = status2;
- if(status2 & mask) {
- if(pvoice->use) {
- snd_pcm_period_elapsed(pvoice->epcm->substream);
- } else {
- dev_err(emu->card->dev,
- "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n",
- status2, mask, pvoice,
- pvoice->use);
- }
- }
- if(status2 & 0x110000) {
- /* dev_info(emu->card->dev, "capture int found\n"); */
- if(cvoice->use) {
- /* dev_info(emu->card->dev, "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 & IPR_A_GPIO) {
+ if (emu->gpio_interrupt)
+ emu->gpio_interrupt(emu);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE);
+ status &= ~IPR_A_GPIO;
+ }
if (status) {
- unsigned int bits;
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 |
- INTE_VOLINCRENABLE |
- INTE_VOLDECRENABLE |
- INTE_MUTEENABLE |
- INTE_MICBUFENABLE |
- INTE_ADCBUFENABLE |
- INTE_EFXBUFENABLE |
- INTE_GPSPDIFENABLE |
- INTE_CDSPDIFENABLE |
- INTE_INTERVALTIMERENB |
- INTE_MIDITXENABLE |
- INTE_MIDIRXENABLE;
- if (emu->audigy)
- bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
- snd_emu10k1_intr_disable(emu, bits);
}
outl(orig_status, emu->port + IPR); /* ack all */
}
- if (timeout == 1000)
- 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 4f1f69be1865..be889a4ccf9a 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -1,24 +1,9 @@
+// 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>
@@ -34,7 +19,10 @@
* aligned pages in others
*/
#define __set_ptb_entry(emu,page,addr) \
- (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << (emu->address_mode)) | (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_PAGES0 (MAXPAGES0 / UNIT_PAGES)
@@ -44,8 +32,7 @@
/* 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 */
@@ -58,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;
}
}
@@ -65,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 */
@@ -102,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;
@@ -147,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 */
@@ -173,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
+ } else {
end_page = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0);
+ }
/* remove links */
list_del(&blk->mapped_link);
@@ -237,13 +237,13 @@ __found_pages:
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
{
if (addr & ~emu->dma_mask) {
- dev_err(emu->card->dev,
+ 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)) {
- dev_err(emu->card->dev, "page is not aligned\n");
+ dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
return 0;
}
return 1;
@@ -261,17 +261,16 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b
int size;
struct list_head *p, *nextp;
struct snd_emu10k1_memblk *deleted;
- unsigned long flags;
- spin_lock_irqsave(&emu->memblk_lock, flags);
+ guard(spinlock_irqsave)(&emu->memblk_lock);
if (blk->mapped_page >= 0) {
/* update order link */
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;
@@ -288,7 +287,6 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b
}
}
}
- spin_unlock_irqrestore(&emu->memblk_lock, flags);
return err;
}
@@ -314,16 +312,12 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
if (snd_BUG_ON(!hdr))
return NULL;
- idx = runtime->period_size >= runtime->buffer_size ?
- (emu->delay_pcm_irq * 2) : 0;
- mutex_lock(&hdr->block_mutex);
- blk = search_empty(emu, runtime->dma_bytes + idx);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
+ blk = search_empty(emu, runtime->dma_bytes);
+ if (blk == NULL)
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++) {
@@ -334,9 +328,8 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
else
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
- dev_err(emu->card->dev,
+ dev_err_ratelimited(emu->card->dev,
"emu: failure page = %d\n", idx);
- mutex_unlock(&hdr->block_mutex);
return NULL;
}
emu->page_addr_table[page] = addr;
@@ -348,10 +341,8 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
err = snd_emu10k1_memblk_map(emu, blk);
if (err < 0) {
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
- mutex_unlock(&hdr->block_mutex);
return NULL;
}
- mutex_unlock(&hdr->block_mutex);
return (struct snd_util_memblk *)blk;
}
@@ -366,6 +357,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)
@@ -381,19 +399,15 @@ snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size)
struct snd_emu10k1_memblk *blk;
struct snd_util_memhdr *hdr = hw->memhdr;
- mutex_lock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
blk = (struct snd_emu10k1_memblk *)__snd_util_mem_alloc(hdr, size);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ if (blk == NULL)
return NULL;
- }
if (synth_alloc_pages(hw, blk)) {
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
- mutex_unlock(&hdr->block_mutex);
return NULL;
}
snd_emu10k1_memblk_map(hw, blk);
- mutex_unlock(&hdr->block_mutex);
return (struct snd_util_memblk *)blk;
}
@@ -407,16 +421,14 @@ snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk)
{
struct snd_util_memhdr *hdr = emu->memhdr;
struct snd_emu10k1_memblk *blk = (struct snd_emu10k1_memblk *)memblk;
- unsigned long flags;
- mutex_lock(&hdr->block_mutex);
- spin_lock_irqsave(&emu->memblk_lock, flags);
- if (blk->mapped_page >= 0)
- unmap_memblk(emu, blk);
- spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ guard(mutex)(&hdr->block_mutex);
+ scoped_guard(spinlock_irqsave, &emu->memblk_lock) {
+ if (blk->mapped_page >= 0)
+ unmap_memblk(emu, blk);
+ }
synth_free_pages(emu, blk);
- __snd_util_mem_free(hdr, memblk);
- mutex_unlock(&hdr->block_mutex);
+ __snd_util_mem_free(hdr, memblk);
return 0;
}
@@ -431,13 +443,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 */
@@ -450,10 +464,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;
}
@@ -465,30 +496,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;
}
/*
@@ -520,15 +551,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
}
/*
- * bzero(blk + offset, size)
+ * memset(blk + offset, value, size)
*/
-int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, int size)
+int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
+ int offset, int size, u8 value)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -540,25 +574,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
if (ptr)
- memset(ptr, 0, temp);
+ memset(ptr, value, temp);
offset = nextofs;
page++;
} while (offset < end_offset);
return 0;
}
-EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+EXPORT_SYMBOL(snd_emu10k1_synth_memset);
+
+// Note that the value is assumed to be suitably repetitive.
+static void xor_range(void *ptr, int size, u32 value)
+{
+ if ((long)ptr & 1) {
+ *(u8 *)ptr ^= (u8)value;
+ ptr++;
+ size--;
+ }
+ if (size > 1 && ((long)ptr & 2)) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ while (size > 3) {
+ *(u32 *)ptr ^= value;
+ ptr += 4;
+ size -= 4;
+ }
+ if (size > 1) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ if (size > 0)
+ *(u8 *)ptr ^= (u8)value;
+}
/*
- * copy_from_user(blk + offset, data, size)
+ * copy_from_user(blk + offset, data, size) ^ xor
*/
int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, const char __user *data, int size)
+ int offset, const char __user *data, int size, u32 xor)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -569,8 +633,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me
if (temp1 < temp)
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
- if (ptr && copy_from_user(ptr, data, temp))
- return -EFAULT;
+ if (ptr) {
+ if (copy_from_user(ptr, data, temp))
+ return -EFAULT;
+ if (xor)
+ xor_range(ptr, temp, xor);
+ }
offset = nextofs;
data += temp;
page++;
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index a30da78a95b7..b74128e61254 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
@@ -71,21 +72,6 @@
*
* 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>
@@ -163,42 +149,19 @@ static const 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;
-
- 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);
- /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
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 */
dev_dbg(emu->card->dev,
"p16v: open channel_id=%d, channel=%p, use=0x%x\n",
@@ -207,56 +170,28 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea
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;
- runtime->sync.id32[1] = 'P';
- runtime->sync.id32[2] = 16;
- runtime->sync.id32[3] = 'V';
-
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);
- /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
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 */
- dev_dbg(emu->card->dev,
- "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
- channel_id, channel, channel->use);
- 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;
return 0;
@@ -266,22 +201,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;
}
@@ -296,43 +221,24 @@ 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)
+static int snd_p16v_pcm_ioctl_playback(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_params callback */
-static int snd_p16v_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_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* hw_free callback */
-static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
+ if (cmd == SNDRV_PCM_IOCTL1_SYNC_ID) {
+ static const unsigned char id[4] = { 'P', '1', '6', 'V' };
+ snd_pcm_set_sync_per_card(substream, arg, id, 4);
+ return 0;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
}
-
/* 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;
@@ -350,23 +256,28 @@ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
runtime->dma_addr, runtime->dma_area, table_base);
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);
+ 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. */
@@ -375,15 +286,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;
}
@@ -394,7 +305,6 @@ 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;
/*
dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
@@ -404,24 +314,23 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
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);
@@ -433,24 +342,56 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE2) | intrenb;
outl(enable, emu->port + INTE2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int disable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
disable = inl(emu->port + INTE2) & (~intrenb);
outl(disable, emu->port + INTE2);
- 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 */
@@ -459,7 +400,6 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
{
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;
@@ -481,10 +421,9 @@ 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;
/* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
- epcm->running = running;
+ runtime->private_data = (void *)(ptrdiff_t)running;
basic |= (0x1<<channel);
inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
snd_pcm_trigger_done(s, substream);
@@ -513,7 +452,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;
@@ -522,13 +460,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;
@@ -543,10 +481,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);
@@ -568,11 +506,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);
@@ -596,9 +533,7 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
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,
+ .ioctl = snd_p16v_pcm_ioctl_playback,
.prepare = snd_p16v_pcm_prepare_playback,
.trigger = snd_p16v_pcm_trigger_playback,
.pointer = snd_p16v_pcm_pointer_playback,
@@ -607,28 +542,11 @@ static const struct snd_pcm_ops snd_p16v_playback_front_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);
- /*
- dev_dbg(chip->card->dev, "period lables free: %p\n",
- &chip->p16v_buffer);
- */
- }
- return 0;
-}
-
int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
@@ -639,7 +557,8 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
/* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
emu->p16v_device_offset = device;
- 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;
@@ -650,17 +569,17 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "p16v");
+ strscpy(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);
/*
dev_dbg(emu->card->dev,
"preallocate playback substream: err=%d\n", err);
@@ -670,11 +589,9 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
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);
/*
dev_dbg(emu->card->dev,
"preallocate capture substream: err=%d\n", err);
@@ -829,7 +746,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[] = {
+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),
@@ -861,8 +778,8 @@ int snd_p16v_mixer(struct snd_emu10k1 *emu)
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;
@@ -874,7 +791,7 @@ int snd_p16v_mixer(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 4e0ee1a9747a..95ab8071751b 100644
--- a/sound/pci/emu10k1/p16v.h
+++ b/sound/pci/emu10k1/p16v.h
@@ -1,85 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
- * Version: 0.21
- *
- * FEATURES currently supported:
- * Output fixed at S32_LE, 2 channel to hw:0,0
- * Rates: 44.1, 48, 96, 192.
- *
- * Changelog:
- * 0.8
- * Use separate card based buffer for periods table.
- * 0.9
- * Use 2 channel output streams instead of 8 channel.
- * (8 channel output streams might be good for ASIO type output)
- * Corrected speaker output, so Front -> Front etc.
- * 0.10
- * Fixed missed interrupts.
- * 0.11
- * Add Sound card model number and names.
- * Add Analog volume controls.
- * 0.12
- * Corrected playback interrupts. Now interrupt per period, instead of half period.
- * 0.13
- * Use single trigger for multichannel.
- * 0.14
- * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
- * 0.15
- * Force buffer_size / period_size == INTEGER.
- * 0.16
- * Update p16v.c to work with changed alsa api.
- * 0.17
- * Update p16v.c to work with changed alsa api. Removed boot_devs.
- * 0.18
- * Merging with snd-emu10k1 driver.
- * 0.19
- * One stereo channel at 24bit now works.
- * 0.20
- * Added better register defines.
- * 0.21
- * Split from p16v.c
- *
- *
- * BUGS:
- * Some stability problems when unloading the snd-p16v kernel module.
- * --
- *
- * TODO:
- * SPDIF out.
- * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
- * Currently capture fixed at 48000Hz.
- *
- * --
- * GENERAL INFO:
- * Model: SB0240
- * P16V Chip: CA0151-DBS
- * Audigy 2 Chip: CA0102-IAT
- * AC97 Codec: STAC 9721
- * ADC: Philips 1361T (Stereo 24bit)
- * DAC: CS4382-K (8-channel, 24bit, 192Khz)
*
* 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.
diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h
index 4ef5f68a9cd0..ee4f4ab4b79c 100644
--- a/sound/pci/emu10k1/p17v.h
+++ b/sound/pci/emu10k1/p17v.h
@@ -1,27 +1,12 @@
+/* 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 b69a7f8a216c..1231ae2bf931 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -1,28 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Lee Revell <rlrevell@joe-job.com>
* Clemens Ladisch <clemens@ladisch.de>
- * Routines for control of EMU10K1 chips
- *
- * 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
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
@@ -32,46 +14,56 @@
static int snd_emu10k1_timer_start(struct snd_timer *timer)
{
struct snd_emu10k1 *emu;
- unsigned long flags;
unsigned int delay;
emu = snd_timer_chip(timer);
delay = timer->sticks - 1;
if (delay < 5 ) /* minimum time is 5 ticks */
delay = 5;
- spin_lock_irqsave(&emu->reg_lock, flags);
snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_timer_stop(struct snd_timer *timer)
{
struct snd_emu10k1 *emu;
- unsigned long flags;
emu = snd_timer_chip(timer);
- spin_lock_irqsave(&emu->reg_lock, flags);
snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
+static unsigned long snd_emu10k1_timer_c_resolution(struct snd_timer *timer)
+{
+ struct snd_emu10k1 *emu = snd_timer_chip(timer);
+
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100)
+ return 22676; // 1 sample @ 44.1 kHz = 22.675736...us
+ else
+ return 20833; // 1 sample @ 48 kHz = 20.833...us
+}
+
static int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
unsigned long *num, unsigned long *den)
{
+ struct snd_emu10k1 *emu = snd_timer_chip(timer);
+
*num = 1;
- *den = 48000;
+ if (emu->card_capabilities->emu_model)
+ *den = emu->emu1010.word_clock;
+ else
+ *den = 48000;
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,
.start = snd_emu10k1_timer_start,
.stop = snd_emu10k1_timer_stop,
+ .c_resolution = snd_emu10k1_timer_c_resolution,
.precise_resolution = snd_emu10k1_timer_precise_resolution,
};
@@ -86,8 +78,9 @@ int 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) {
- strcpy(timer->name, "EMU10K1 timer");
+ err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer);
+ if (err >= 0) {
+ strscpy(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..e3fcb290271c 100644
--- a/sound/pci/emu10k1/tina2.h
+++ b/sound/pci/emu10k1/tina2.h
@@ -1,22 +1,7 @@
+/* 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 f16fd5cfb7cd..7fe1d1727768 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -1,31 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Creative Labs, Inc.
* Lee Revell <rlrevell@joe-job.com>
- * Routines for control of EMU10K1 chips - voice manager
- *
- * Rewrote voice allocator for multichannel support - rlrevell 12/2004
- *
- * 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
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
+ * Creative Labs, Inc.
*
+ * Routines for control of EMU10K1 chips - voice manager
*/
#include <linux/time.h>
@@ -37,112 +17,101 @@
* allocator uses a round robin scheme. The next free voice is tracked in
* the card record and each allocation begins where the last left off. The
* hardware requires stereo interleaved voices be aligned to an even/odd
- * boundary. For multichannel voice allocation we ensure than the block of
- * voices does not cross the 32 voice boundary. This simplifies the
- * multichannel support and ensures we can use a single write to the
- * (set|clear)_loop_stop registers. Otherwise (for example) the voices would
- * get out of sync when pausing/resuming a stream.
+ * boundary.
* --rlrevell
*/
static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
- struct snd_emu10k1_voice **rvoice)
+ struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
struct snd_emu10k1_voice *voice;
- int i, j, k, first_voice, last_voice, skip;
+ int i, j, k, skip;
- *rvoice = NULL;
- first_voice = last_voice = 0;
- for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+ for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) {
/*
dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
i, j, emu->next_free_voice);
*/
- i %= NUM_G;
/* stereo voices must be even/odd */
- if ((number == 2) && (i % 2)) {
- i++;
+ if ((number > 1) && (i % 2)) {
+ skip = 1;
continue;
}
-
- skip = 0;
+
for (k = 0; k < number; k++) {
- voice = &emu->voices[(i+k) % NUM_G];
+ voice = &emu->voices[i + k];
if (voice->use) {
- skip = 1;
- break;
+ skip = k + 1;
+ goto next;
}
}
- if (!skip) {
- /* 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;
- break;
- }
- }
-
- if (first_voice == last_voice)
- return -ENOMEM;
-
- for (i = 0; i < number; i++) {
- voice = &emu->voices[(first_voice + i) % NUM_G];
- /*
- dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
- voice->number, idx-first_voice+1, number);
- */
- voice->use = 1;
- switch (type) {
- case EMU10K1_PCM:
- voice->pcm = 1;
- break;
- case EMU10K1_SYNTH:
- voice->synth = 1;
- break;
- case EMU10K1_MIDI:
- voice->midi = 1;
- break;
- case EMU10K1_EFX:
- voice->efx = 1;
- break;
+
+ for (k = 0; k < number; k++) {
+ voice = &emu->voices[i + k];
+ voice->use = type;
+ voice->epcm = epcm;
+ /* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */
}
+ voice->last = 1;
+
+ *rvoice = &emu->voices[i];
+ emu->next_free_voice = (i + number) % NUM_G;
+ return 0;
+
+ next: ;
}
- *rvoice = &emu->voices[first_voice];
- return 0;
+ return -ENOMEM; // -EBUSY would have been better
+}
+
+static void voice_free(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *pvoice)
+{
+ if (pvoice->dirty)
+ snd_emu10k1_voice_init(emu, pvoice->number);
+ pvoice->interrupt = NULL;
+ pvoice->use = pvoice->dirty = pvoice->last = 0;
+ pvoice->epcm = NULL;
}
-int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
- struct snd_emu10k1_voice **rvoice)
+int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,
+ struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
- unsigned long flags;
int result;
if (snd_BUG_ON(!rvoice))
return -EINVAL;
- if (snd_BUG_ON(!number))
+ if (snd_BUG_ON(!count))
+ return -EINVAL;
+ if (snd_BUG_ON(!channels))
return -EINVAL;
- spin_lock_irqsave(&emu->voice_lock, flags);
- for (;;) {
- result = voice_alloc(emu, type, number, rvoice);
- if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
- break;
-
- /* free a voice from synth */
- if (emu->get_synth_voice) {
+ guard(spinlock_irqsave)(&emu->voice_lock);
+ for (int got = 0; got < channels; ) {
+ result = voice_alloc(emu, type, count, epcm, &rvoice[got]);
+ if (result == 0) {
+ got++;
+ /*
+ dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
+ rvoice[got - 1]->number, got, want);
+ */
+ continue;
+ }
+ if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
+ /* free a voice from synth */
result = emu->get_synth_voice(emu);
if (result >= 0) {
- struct snd_emu10k1_voice *pvoice = &emu->voices[result];
- pvoice->interrupt = NULL;
- pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
- pvoice->epcm = NULL;
+ voice_free(emu, &emu->voices[result]);
+ continue;
}
}
- if (result < 0)
- break;
+ for (int i = 0; i < got; i++) {
+ for (int j = 0; j < count; j++)
+ voice_free(emu, rvoice[i] + j);
+ rvoice[i] = NULL;
+ }
+ break;
}
- spin_unlock_irqrestore(&emu->voice_lock, flags);
return result;
}
@@ -152,16 +121,15 @@ EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
struct snd_emu10k1_voice *pvoice)
{
- unsigned long flags;
+ int last;
if (snd_BUG_ON(!pvoice))
return -EINVAL;
- spin_lock_irqsave(&emu->voice_lock, flags);
- pvoice->interrupt = NULL;
- pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
- pvoice->epcm = NULL;
- snd_emu10k1_voice_init(emu, pvoice->number);
- spin_unlock_irqrestore(&emu->voice_lock, flags);
+ guard(spinlock_irqsave)(&emu->voice_lock);
+ do {
+ last = pvoice->last;
+ voice_free(emu, pvoice++);
+ } while (!last);
return 0;
}
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 39f79a6b5283..657056a59175 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1,28 +1,14 @@
+// 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
*/
@@ -66,17 +52,9 @@ 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 IS_REACHABLE(CONFIG_GAMEPORT)
@@ -436,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
@@ -618,7 +596,7 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97,
unsigned int t, x, flag;
flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0;
- mutex_lock(&ensoniq->src_mutex);
+ guard(mutex)(&ensoniq->src_mutex);
for (t = 0; t < POLL_COUNT; t++) {
if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
/* save the current state for latter */
@@ -644,11 +622,9 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97,
/* restore SRC reg */
snd_es1371_wait_src_ready(ensoniq);
outl(x, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
return;
}
}
- mutex_unlock(&ensoniq->src_mutex);
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)));
}
@@ -694,7 +670,8 @@ 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));
@@ -734,7 +711,7 @@ static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
{
unsigned int n, truncm, freq;
- mutex_lock(&ensoniq->src_mutex);
+ guard(mutex)(&ensoniq->src_mutex);
n = rate / 3000;
if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
n--;
@@ -758,15 +735,14 @@ static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
- mutex_unlock(&ensoniq->src_mutex);
}
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;
+ guard(mutex)(&ensoniq->src_mutex);
+ 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;
@@ -779,15 +755,14 @@ static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P2 | ES_1371_DIS_R1));
outl(r, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
}
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;
+ guard(mutex)(&ensoniq->src_mutex);
+ 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;
@@ -801,7 +776,6 @@ static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P1 | ES_1371_DIS_R1));
outl(r, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
}
#endif /* CHIP1371 */
@@ -825,13 +799,13 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
} else if (s == ensoniq->capture_substream)
return -EINVAL;
}
- spin_lock(&ensoniq->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
- ensoniq->sctrl |= what;
- else
- ensoniq->sctrl &= ~what;
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ ensoniq->sctrl |= what;
+ else
+ ensoniq->sctrl &= ~what;
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ }
break;
}
case SNDRV_PCM_TRIGGER_START:
@@ -851,13 +825,13 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&ensoniq->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_START)
- ensoniq->ctrl |= what;
- else
- ensoniq->ctrl &= ~what;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ ensoniq->ctrl |= what;
+ else
+ ensoniq->ctrl &= ~what;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
break;
}
default:
@@ -870,17 +844,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);
@@ -893,36 +856,36 @@ static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_DAC1_EN;
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_DAC1_EN;
#ifdef CHIP1371
- /* 48k doesn't need SRC (it breaks AC3-passthru) */
- if (runtime->rate == 48000)
- ensoniq->ctrl |= ES_1373_BYPASS_P1;
- else
- ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
+ /* 48k doesn't need SRC (it breaks AC3-passthru) */
+ if (runtime->rate == 48000)
+ ensoniq->ctrl |= ES_1373_BYPASS_P1;
+ else
+ ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
- outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
- ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
- ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, DAC1_COUNT));
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
+ outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
+ ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
+ ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, DAC1_COUNT));
#ifdef CHIP1370
- ensoniq->ctrl &= ~ES_1370_WTSRSELM;
- switch (runtime->rate) {
- case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
- case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
- case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
- case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
- default: snd_BUG();
- }
+ ensoniq->ctrl &= ~ES_1370_WTSRSELM;
+ switch (runtime->rate) {
+ case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
+ case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
+ case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
+ case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
+ default: snd_BUG();
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_dac1_rate(ensoniq, runtime->rate);
#endif
@@ -941,28 +904,28 @@ static int snd_ensoniq_playback2_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_DAC2_EN;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
- outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
- ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
- ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
- ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
- ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, DAC2_COUNT));
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_DAC2_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
+ outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
+ ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
+ ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
+ ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
+ ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, DAC2_COUNT));
#ifdef CHIP1370
- if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
- ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
- ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
- ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
- }
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_dac2_rate(ensoniq, runtime->rate);
#endif
@@ -981,26 +944,26 @@ static int snd_ensoniq_capture_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_ADC_EN;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
- outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
- ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
- ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, ADC_COUNT));
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_ADC_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
+ outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
+ ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
+ ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, ADC_COUNT));
#ifdef CHIP1370
- if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
- ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
- ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
- ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
- }
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_adc_rate(ensoniq, runtime->rate);
#endif
@@ -1012,16 +975,14 @@ static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(struct snd_pcm_substream
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream *substream)
@@ -1029,16 +990,14 @@ static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *substream)
@@ -1046,16 +1005,14 @@ static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *s
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
static const struct snd_pcm_hardware snd_ensoniq_playback1 =
@@ -1133,10 +1090,10 @@ static int snd_ensoniq_playback1_open(struct snd_pcm_substream *substream)
ensoniq->playback1_substream = substream;
runtime->hw = snd_ensoniq_playback1;
snd_pcm_set_sync(substream);
- spin_lock_irq(&ensoniq->reg_lock);
- if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
- ensoniq->spdif_stream = ensoniq->spdif_default;
- spin_unlock_irq(&ensoniq->reg_lock);
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ }
#ifdef CHIP1370
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_rates);
@@ -1156,10 +1113,10 @@ static int snd_ensoniq_playback2_open(struct snd_pcm_substream *substream)
ensoniq->playback2_substream = substream;
runtime->hw = snd_ensoniq_playback2;
snd_pcm_set_sync(substream);
- spin_lock_irq(&ensoniq->reg_lock);
- if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
- ensoniq->spdif_stream = ensoniq->spdif_default;
- spin_unlock_irq(&ensoniq->reg_lock);
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ }
#ifdef CHIP1370
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_clock);
@@ -1203,12 +1160,11 @@ static int snd_ensoniq_playback2_close(struct snd_pcm_substream *substream)
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
ensoniq->playback2_substream = NULL;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
#ifdef CHIP1370
ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
#endif
ensoniq->mode &= ~ES_MODE_PLAY2;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1217,21 +1173,17 @@ static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream)
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
ensoniq->capture_substream = NULL;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
#ifdef CHIP1370
ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
#endif
ensoniq->mode &= ~ES_MODE_CAPTURE;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
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,
@@ -1240,9 +1192,6 @@ static const struct snd_pcm_ops snd_ensoniq_playback1_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,
@@ -1251,9 +1200,6 @@ static const struct snd_pcm_ops snd_ensoniq_playback2_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,
@@ -1285,11 +1231,11 @@ static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device)
pcm->private_data = ensoniq;
pcm->info_flags = 0;
- strcpy(pcm->name, CHIP_NAME " DAC2/ADC");
+ strscpy(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);
#ifdef CHIP1370
err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -1317,11 +1263,11 @@ static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device)
#endif
pcm->private_data = ensoniq;
pcm->info_flags = 0;
- strcpy(pcm->name, CHIP_NAME " DAC1");
+ strscpy(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);
#ifdef CHIP1370
err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -1353,12 +1299,12 @@ static int snd_ens1373_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1373,13 +1319,12 @@ static int snd_ens1373_spdif_default_put(struct snd_kcontrol *kcontrol,
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = ensoniq->spdif_default != val;
ensoniq->spdif_default = val;
if (change && ensoniq->playback1_substream == NULL &&
ensoniq->playback2_substream == NULL)
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1397,12 +1342,12 @@ static int snd_ens1373_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1417,13 +1362,12 @@ static int snd_ens1373_spdif_stream_put(struct snd_kcontrol *kcontrol,
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = ensoniq->spdif_stream != val;
ensoniq->spdif_stream = val;
if (change && (ensoniq->playback1_substream != NULL ||
ensoniq->playback2_substream != NULL))
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1438,9 +1382,8 @@ static int snd_es1371_spdif_get(struct snd_kcontrol *kcontrol,
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1453,7 +1396,7 @@ static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0;
nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1;
ensoniq->ctrl &= ~ES_1373_SPDIF_THRU;
ensoniq->ctrl |= nval1;
@@ -1461,13 +1404,12 @@ static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
ensoniq->cssr |= nval2;
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
/* spdif controls */
-static struct snd_kcontrol_new snd_es1371_mixer_spdif[] = {
+static const struct snd_kcontrol_new snd_es1371_mixer_spdif[] = {
ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)),
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1501,12 +1443,11 @@ static int snd_es1373_rear_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int val = 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|
ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26)
val = 1;
ucontrol->value.integer.value[0] = val;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1519,13 +1460,12 @@ static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
nval1 = ucontrol->value.integer.value[0] ?
ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->cssr & (ES_1373_REAR_BIT27|
ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1;
ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24);
ensoniq->cssr |= nval1;
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1546,11 +1486,10 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int val = 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
val = 1;
ucontrol->value.integer.value[0] = val;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1561,7 +1500,7 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
int changed;
unsigned int ctrl;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ctrl = ensoniq->ctrl;
if (ucontrol->value.integer.value[0])
ensoniq->ctrl |= ES_1371_GPIO_OUT(4); /* switch line-in -> rear out */
@@ -1570,7 +1509,6 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
changed = (ctrl != ensoniq->ctrl);
if (changed)
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
return changed;
}
@@ -1596,7 +1534,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 &&
@@ -1608,7 +1546,7 @@ static int es1371_quirk_lookup(struct ensoniq *ensoniq,
return 0;
}
-static struct es1371_quirk es1371_spdif_present[] = {
+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 },
@@ -1617,7 +1555,7 @@ static struct es1371_quirk es1371_spdif_present[] = {
{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
};
-static struct snd_pci_quirk ens1373_line_quirk[] = {
+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 */
@@ -1630,13 +1568,14 @@ static int snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
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));
@@ -1644,7 +1583,8 @@ static int 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))) {
@@ -1704,9 +1644,8 @@ static int snd_ensoniq_control_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int mask = kcontrol->private_value;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1719,12 +1658,11 @@ static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
int change;
nval = ucontrol->value.integer.value[0] ? mask : 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->ctrl & mask) != nval;
ensoniq->ctrl &= ~mask;
ensoniq->ctrl |= nval;
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1732,7 +1670,7 @@ static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
* ENS1370 mixer
*/
-static struct snd_kcontrol_new snd_es1370_controls[2] = {
+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)
};
@@ -1764,7 +1702,8 @@ static int 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));
@@ -1888,12 +1827,12 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
snd_iprintf(buffer, "Joystick enable : %s\n",
- ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_JYSTK_EN));
#ifdef CHIP1370
snd_iprintf(buffer, "MIC +5V bias : %s\n",
- ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_1370_XCTL1));
snd_iprintf(buffer, "Line In to AOUT : %s\n",
- ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_1370_XCTL0));
#else
snd_iprintf(buffer, "Joystick port : 0x%x\n",
(ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
@@ -1902,21 +1841,19 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
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 */
@@ -1924,30 +1861,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, PCI_D3hot);
- __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[] = {
+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 */
@@ -1955,7 +1872,7 @@ static struct snd_pci_quirk es1371_amplifier_hack[] = {
{ } /* 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 },
@@ -1977,7 +1894,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));
@@ -2026,10 +1943,8 @@ 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_SLEEP
static int snd_ensoniq_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -2037,9 +1952,6 @@ static int snd_ensoniq_suspend(struct device *dev)
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
@@ -2071,55 +1983,38 @@ static int snd_ensoniq_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume);
static int snd_ensoniq_create(struct snd_card *card,
- struct pci_dev *pci,
- struct ensoniq **rensoniq)
+ 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 = pcim_request_all_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,
- KBUILD_MODNAME, 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);
- snd_ensoniq_free(ensoniq);
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) {
- dev_err(card->dev, "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;
@@ -2142,16 +2037,10 @@ static int 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);
-
- *rensoniq = ensoniq;
return 0;
}
@@ -2167,19 +2056,19 @@ static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq)
if (rmidi == NULL)
return;
/* do Rx at first */
- spin_lock(&ensoniq->reg_lock);
- mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
- while (mask) {
- status = inb(ES_REG(ensoniq, UART_STATUS));
- if ((status & mask) == 0)
- break;
- byte = inb(ES_REG(ensoniq, UART_DATA));
- snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
+ while (mask) {
+ status = inb(ES_REG(ensoniq, UART_STATUS));
+ if ((status & mask) == 0)
+ break;
+ byte = inb(ES_REG(ensoniq, UART_DATA));
+ snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+ }
}
- spin_unlock(&ensoniq->reg_lock);
/* do Tx at second */
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
while (mask) {
status = inb(ES_REG(ensoniq, UART_STATUS));
@@ -2193,14 +2082,13 @@ static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq)
outb(byte, ES_REG(ensoniq, UART_DATA));
}
}
- spin_unlock(&ensoniq->reg_lock);
}
static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ensoniq->uartm |= ES_MODE_INPUT;
ensoniq->midi_input = substream;
if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
@@ -2208,7 +2096,6 @@ static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream)
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
}
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2216,7 +2103,7 @@ static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
@@ -2225,7 +2112,6 @@ static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream)
}
ensoniq->midi_input = NULL;
ensoniq->uartm &= ~ES_MODE_INPUT;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2233,7 +2119,7 @@ static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ensoniq->uartm |= ES_MODE_OUTPUT;
ensoniq->midi_output = substream;
if (!(ensoniq->uartm & ES_MODE_INPUT)) {
@@ -2241,7 +2127,6 @@ static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream)
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
}
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2249,7 +2134,7 @@ static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if (!(ensoniq->uartm & ES_MODE_INPUT)) {
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
@@ -2258,17 +2143,15 @@ static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream
}
ensoniq->midi_output = NULL;
ensoniq->uartm &= ~ES_MODE_OUTPUT;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct ensoniq *ensoniq = substream->rmidi->private_data;
int idx;
- spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ guard(spinlock_irqsave)(&ensoniq->reg_lock);
if (up) {
if ((ensoniq->uartc & ES_RXINTEN) == 0) {
/* empty input FIFO */
@@ -2283,16 +2166,14 @@ static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substre
outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
}
}
- spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}
static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct ensoniq *ensoniq = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ guard(spinlock_irqsave)(&ensoniq->reg_lock);
if (up) {
if (ES_TXINTENI(ensoniq->uartc) == 0) {
ensoniq->uartc |= ES_TXINTENO(1);
@@ -2313,7 +2194,6 @@ static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substr
outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
}
}
- spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}
static const struct snd_rawmidi_ops snd_ensoniq_midi_output =
@@ -2335,9 +2215,10 @@ static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device)
struct snd_rawmidi *rmidi;
int err;
- 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;
- strcpy(rmidi->name, CHIP_NAME);
+ strscpy(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 |
@@ -2363,17 +2244,17 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id)
if (!(status & ES_INTR))
return IRQ_NONE;
- spin_lock(&ensoniq->reg_lock);
- sctrl = ensoniq->sctrl;
- if (status & ES_DAC1)
- sctrl &= ~ES_P1_INT_EN;
- if (status & ES_DAC2)
- sctrl &= ~ES_P2_INT_EN;
- if (status & ES_ADC)
- sctrl &= ~ES_R1_INT_EN;
- outl(sctrl, ES_REG(ensoniq, SERIAL));
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ sctrl = ensoniq->sctrl;
+ if (status & ES_DAC1)
+ sctrl &= ~ES_P1_INT_EN;
+ if (status & ES_DAC2)
+ sctrl &= ~ES_P2_INT_EN;
+ if (status & ES_ADC)
+ sctrl &= ~ES_R1_INT_EN;
+ outl(sctrl, ES_REG(ensoniq, SERIAL));
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ }
if (status & ES_UART)
snd_ensoniq_midi_interrupt(ensoniq);
@@ -2386,13 +2267,13 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int 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;
@@ -2401,76 +2282,68 @@ static int snd_audiopci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm(ensoniq, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_pcm2(ensoniq, 1)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm2(ensoniq, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_midi(ensoniq, 0)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_midi(ensoniq, 0);
+ if (err < 0)
return err;
- }
snd_ensoniq_create_gameport(ensoniq, dev);
- strcpy(card->driver, DRIVER_NAME);
+ strscpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "Ensoniq AudioPCI");
+ strscpy(card->shortname, "Ensoniq AudioPCI");
sprintf(card->longname, "%s %s at 0x%lx, irq %i",
card->shortname,
card->driver,
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_audiopci_probe(pci, pci_id));
}
static struct pci_driver ens137x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_audiopci_ids,
.probe = snd_audiopci_probe,
- .remove = snd_audiopci_remove,
.driver = {
- .pm = SND_ENSONIQ_PM_OPS,
+ .pm = &snd_ensoniq_pm,
},
};
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 9d248eb2e26c..280125eff362 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
- *
*/
/*
@@ -67,10 +52,6 @@
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 IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -235,9 +216,7 @@ struct es1938 {
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
-#ifdef CONFIG_PM_SLEEP
unsigned char saved_regs[SAVED_REG_SIZE];
-#endif
};
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
@@ -258,11 +237,9 @@ MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
* -----------------------------------------------------------------*/
static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
outb(val, SLSB_REG(chip, MIXERDATA));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
dev_dbg(chip->card->dev, "Mixer reg %02x set to %02x\n", reg, val);
}
@@ -272,11 +249,10 @@ static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsig
static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
{
int data;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
data = inb(SLSB_REG(chip, MIXERDATA));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
dev_dbg(chip->card->dev, "Mixer reg %02x now is %02x\n", reg, data);
return data;
}
@@ -287,9 +263,9 @@ static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
unsigned char mask, unsigned char val)
{
- unsigned long flags;
unsigned char old, new, oval;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
old = inb(SLSB_REG(chip, MIXERDATA));
oval = old & mask;
@@ -300,7 +276,6 @@ static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
"Mixer reg %02x was %02x, set to %02x\n",
reg, old, new);
}
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
return oval;
}
@@ -312,7 +287,8 @@ 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;
}
@@ -328,9 +304,11 @@ 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));
+ }
dev_err(chip->card->dev, "get_byte timeout: status 0x02%x\n", v);
return -ENODEV;
}
@@ -340,11 +318,9 @@ static int snd_es1938_get_byte(struct es1938 *chip)
* -----------------------------------------------------------------*/
static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, reg);
snd_es1938_write_cmd(chip, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
dev_dbg(chip->card->dev, "Reg %02x set to %02x\n", reg, val);
}
@@ -354,12 +330,11 @@ static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned ch
static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
{
unsigned char val;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
val = snd_es1938_get_byte(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
dev_dbg(chip->card->dev, "Reg %02x now is %02x\n", reg, val);
return val;
}
@@ -370,9 +345,9 @@ static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char mask,
unsigned char val)
{
- unsigned long flags;
unsigned char old, new, oval;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
old = snd_es1938_get_byte(chip);
@@ -384,7 +359,6 @@ static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char
dev_dbg(chip->card->dev, "Reg %02x was %02x, set to %02x\n",
reg, old, new);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return oval;
}
@@ -840,7 +814,7 @@ 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, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1938 *chip = snd_pcm_substream_chip(substream);
@@ -848,55 +822,17 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
if (snd_BUG_ON(pos + count > chip->dma1_size))
return -EINVAL;
if (pos + count < chip->dma1_size) {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count, dst) != count)
return -EFAULT;
} else {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count - 1, dst) != count - 1)
return -EFAULT;
- if (put_user(runtime->dma_area[0],
- ((unsigned char __user *)dst) + count - 1))
+ if (copy_to_iter(runtime->dma_area, 1, dst) != 1)
return -EFAULT;
}
return 0;
}
-static int snd_es1938_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 es1938 *chip = snd_pcm_substream_chip(substream);
-
- 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;
-}
-
-/*
- * buffer management
- */
-static int snd_es1938_pcm_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 0;
-}
-
-static int snd_es1938_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* ----------------------------------------------------------------------
* Audio1 Capture (ADC)
* ----------------------------------------------------------------------*/
@@ -1011,9 +947,6 @@ static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
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,
@@ -1022,14 +955,10 @@ static const struct snd_pcm_ops snd_es1938_playback_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_user = snd_es1938_capture_copy,
- .copy_kernel = snd_es1938_capture_copy_kernel,
+ .copy = snd_es1938_capture_copy,
};
static int snd_es1938_new_pcm(struct es1938 *chip, int device)
@@ -1037,17 +966,18 @@ 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);
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "ESS Solo-1");
+ strscpy(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;
@@ -1347,7 +1277,7 @@ static const DECLARE_TLV_DB_RANGE(db_scale_line,
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),
@@ -1455,12 +1385,11 @@ static void snd_es1938_chip_init(struct es1938 *chip)
outb(0, SLDM_REG(chip, DMACLEAR));
}
-#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,
@@ -1472,10 +1401,10 @@ static int es1938_suspend(struct device *dev)
{
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++)
@@ -1485,6 +1414,7 @@ static int es1938_suspend(struct device *dev)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
return 0;
}
@@ -1494,7 +1424,8 @@ static int es1938_resume(struct device *dev)
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;
+ const unsigned char *s;
+ unsigned char *d;
if (request_irq(pci->irq, snd_es1938_interrupt,
IRQF_SHARED, KBUILD_MODNAME, chip)) {
@@ -1504,6 +1435,7 @@ static int es1938_resume(struct device *dev)
return -EIO;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
snd_es1938_chip_init(chip);
/* restore mixer-related registers */
@@ -1518,11 +1450,7 @@ static int es1938_resume(struct device *dev)
return 0;
}
-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 */
+static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);
#ifdef SUPPORT_JOYSTICK
static int snd_es1938_create_gameport(struct es1938 *chip)
@@ -1558,8 +1486,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)
@@ -1569,69 +1499,47 @@ 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 snd_es1938_create(struct snd_card *card,
- struct pci_dev *pci,
- struct es1938 **rchip)
+ 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
+ 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");
- pci_disable_device(pci);
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 = pcim_request_all_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,
KBUILD_MODNAME, chip)) {
dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
- snd_es1938_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
+ 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);
@@ -1639,13 +1547,6 @@ static int snd_es1938_create(struct snd_card *card,
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;
- }
-
- *rchip = chip;
return 0;
}
@@ -1655,7 +1556,8 @@ static int 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));
@@ -1748,7 +1650,7 @@ static int snd_es1938_mixer(struct es1938 *chip)
card = chip->card;
- strcpy(card->mixername, "ESS Solo-1");
+ strscpy(card->mixername, "ESS Solo-1");
for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
struct snd_kcontrol *kctl;
@@ -1771,15 +1673,16 @@ static int 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 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;
@@ -1794,38 +1697,34 @@ static int snd_es1938_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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)");
+ strscpy(card->driver, "ES1938");
+ strscpy(card->shortname, "ESS ES1938 (Solo-1)");
sprintf(card->longname, "%s rev %i, irq %i",
card->shortname,
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),
@@ -1833,14 +1732,12 @@ static int snd_es1938_probe(struct pci_dev *pci,
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,
@@ -1855,28 +1752,27 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_es1938_probe(pci, pci_id));
}
static struct pci_driver es1938_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_es1938_ids,
.probe = snd_es1938_probe,
- .remove = snd_es1938_remove,
.driver = {
- .pm = ES1938_PM_OPS,
+ .pm = &es1938_pm,
},
};
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 0b1845ca6005..51aee2c4d461 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
@@ -121,10 +107,6 @@
MODULE_DESCRIPTION("ESS Maestro");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e},"
- "{ESS,Maestro 2},"
- "{ESS,Maestro 1},"
- "{TerraTec,DMX}}");
#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -491,9 +473,7 @@ struct esschan {
/* linked list */
struct list_head list;
-#ifdef CONFIG_PM_SLEEP
u16 wc_map[4];
-#endif
};
struct es1968 {
@@ -544,9 +524,7 @@ struct es1968 {
struct list_head substream_list;
spinlock_t substream_lock;
-#ifdef CONFIG_PM_SLEEP
u16 apu_map[NR_APUS][NR_APU_REGS];
-#endif
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
@@ -596,10 +574,8 @@ static void __maestro_write(struct es1968 *chip, u16 reg, u16 data)
static inline void maestro_write(struct es1968 *chip, u16 reg, u16 data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__maestro_write(chip, reg, data);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/* no spinlock */
@@ -614,12 +590,8 @@ static u16 __maestro_read(struct es1968 *chip, u16 reg)
static inline u16 maestro_read(struct es1968 *chip, u16 reg)
{
- unsigned long flags;
- u16 result;
- spin_lock_irqsave(&chip->reg_lock, flags);
- result = __maestro_read(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __maestro_read(chip, reg);
}
/* Wait for the codec bus to be free */
@@ -707,9 +679,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_SLEEP
chip->apu_map[channel][reg] = data;
-#endif
reg |= (channel << 4);
apu_index_set(chip, reg);
apu_data_set(chip, data);
@@ -717,10 +687,8 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat
static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__apu_set_register(chip, channel, reg, data);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
@@ -734,62 +702,40 @@ static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
static u16 apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
{
- unsigned long flags;
- u16 v;
- spin_lock_irqsave(&chip->reg_lock, flags);
- v = __apu_get_register(chip, channel, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return v;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __apu_get_register(chip, channel, reg);
}
#if 0 /* ASSP is not supported */
static void assp_set_register(struct es1968 *chip, u32 reg, u32 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave),(&chip->reg_lock);
outl(reg, chip->io_port + ASSP_INDEX);
outl(value, chip->io_port + ASSP_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u32 assp_get_register(struct es1968 *chip, u32 reg)
{
- unsigned long flags;
- u32 value;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outl(reg, chip->io_port + ASSP_INDEX);
- value = inl(chip->io_port + ASSP_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- return value;
+ return inl(chip->io_port + ASSP_DATA);
}
#endif
static void wave_set_register(struct es1968 *chip, u16 reg, u16 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outw(reg, chip->io_port + WC_INDEX);
outw(value, chip->io_port + WC_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u16 wave_get_register(struct es1968 *chip, u16 reg)
{
- unsigned long flags;
- u16 value;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outw(reg, chip->io_port + WC_INDEX);
- value = inw(chip->io_port + WC_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- return value;
+ return inw(chip->io_port + WC_DATA);
}
/* *******************
@@ -948,7 +894,7 @@ static inline void snd_es1968_trigger_apu(struct es1968 *esm, int apu, int mode)
static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
__apu_set_register(chip, es->apu[0], 5, es->base[0]);
snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]);
if (es->mode == ESM_MODE_CAPTURE) {
@@ -963,19 +909,17 @@ static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es)
snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]);
}
}
- spin_unlock(&chip->reg_lock);
}
static void snd_es1968_pcm_stop(struct es1968 *chip, struct esschan *es)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
snd_es1968_trigger_apu(chip, es->apu[0], 0);
snd_es1968_trigger_apu(chip, es->apu[1], 0);
if (es->mode == ESM_MODE_CAPTURE) {
snd_es1968_trigger_apu(chip, es->apu[2], 0);
snd_es1968_trigger_apu(chip, es->apu[3], 0);
}
- spin_unlock(&chip->reg_lock);
}
/* set the wavecache control reg */
@@ -994,9 +938,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_SLEEP
es->wc_map[channel] = tmpval;
-#endif
}
@@ -1007,7 +949,6 @@ static void snd_es1968_playback_setup(struct es1968 *chip, struct esschan *es,
int high_apu = 0;
int channel, apu;
int i, size;
- unsigned long flags;
u32 freq;
size = es->dma_size >> es->wav_shift;
@@ -1077,12 +1018,12 @@ static void snd_es1968_playback_setup(struct es1968 *chip, struct esschan *es,
apu_set_register(chip, apu, 10, 0x8F08);
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- /* clear WP interrupts */
- outw(1, chip->io_port + 0x04);
- /* enable WP ints */
- outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ /* clear WP interrupts */
+ outw(1, chip->io_port + 0x04);
+ /* enable WP ints */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+ }
freq = runtime->rate;
/* set frequency */
@@ -1153,7 +1094,6 @@ static void snd_es1968_capture_setup(struct es1968 *chip, struct esschan *es,
{
int size;
u32 freq;
- unsigned long flags;
size = es->dma_size >> es->wav_shift;
@@ -1205,12 +1145,11 @@ static void snd_es1968_capture_setup(struct es1968 *chip, struct esschan *es,
snd_es1968_apu_set_freq(chip, es->apu[2], freq);
snd_es1968_apu_set_freq(chip, es->apu[3], freq);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* clear WP interrupts */
outw(1, chip->io_port + 0x04);
/* enable WP ints */
outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/*******************
@@ -1254,7 +1193,7 @@ static int snd_es1968_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct es1968 *chip = snd_pcm_substream_chip(substream);
struct esschan *es = substream->runtime->private_data;
- spin_lock(&chip->substream_lock);
+ guard(spinlock)(&chip->substream_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -1275,7 +1214,6 @@ static int snd_es1968_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_es1968_bob_dec(chip);
break;
}
- spin_unlock(&chip->substream_lock);
return 0;
}
@@ -1344,12 +1282,11 @@ static int calc_available_memory_size(struct es1968 *chip)
int max_size = 0;
struct esm_memory *buf;
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
list_for_each_entry(buf, &chip->buf_list, list) {
if (buf->empty && buf->buf.bytes > max_size)
max_size = buf->buf.bytes;
}
- mutex_unlock(&chip->memory_mutex);
if (max_size >= 128*1024)
max_size = 127*1024;
return max_size;
@@ -1361,21 +1298,18 @@ static struct esm_memory *snd_es1968_new_memory(struct es1968 *chip, int size)
struct esm_memory *buf;
size = ALIGN(size, ESM_MEM_ALIGN);
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
list_for_each_entry(buf, &chip->buf_list, list) {
if (buf->empty && buf->buf.bytes >= size)
goto __found;
}
- mutex_unlock(&chip->memory_mutex);
return NULL;
__found:
if (buf->buf.bytes > size) {
struct esm_memory *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
- if (chunk == NULL) {
- mutex_unlock(&chip->memory_mutex);
+ if (chunk == NULL)
return NULL;
- }
chunk->buf = buf->buf;
chunk->buf.bytes -= size;
chunk->buf.area += size;
@@ -1385,7 +1319,6 @@ __found:
list_add(&chunk->list, &buf->list);
}
buf->empty = 0;
- mutex_unlock(&chip->memory_mutex);
return buf;
}
@@ -1394,7 +1327,7 @@ static void snd_es1968_free_memory(struct es1968 *chip, struct esm_memory *buf)
{
struct esm_memory *chunk;
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
buf->empty = 1;
if (buf->list.prev != &chip->buf_list) {
chunk = list_entry(buf->list.prev, struct esm_memory, list);
@@ -1413,7 +1346,6 @@ static void snd_es1968_free_memory(struct es1968 *chip, struct esm_memory *buf)
kfree(chunk);
}
}
- mutex_unlock(&chip->memory_mutex);
}
static void snd_es1968_free_dmabuf(struct es1968 *chip)
@@ -1436,10 +1368,8 @@ 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);
err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
+ &chip->pci->dev,
chip->total_bufsize, &chip->dma);
if (err < 0 || ! chip->dma.area) {
dev_err(chip->card->dev,
@@ -1577,9 +1507,8 @@ static int snd_es1968_playback_open(struct snd_pcm_substream *substream)
runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
calc_available_memory_size(chip);
- spin_lock_irq(&chip->substream_lock);
+ guard(spinlock_irq)(&chip->substream_lock);
list_add(&es->list, &chip->substream_list);
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
@@ -1589,7 +1518,7 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1968 *chip = snd_pcm_substream_chip(substream);
struct esschan *es;
- int apu1, apu2;
+ int err, apu1, apu2;
apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE);
if (apu1 < 0)
@@ -1620,7 +1549,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);
@@ -1632,11 +1562,12 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
runtime->hw = snd_es1968_capture;
runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */
- snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ if (err < 0)
+ return err;
- spin_lock_irq(&chip->substream_lock);
+ guard(spinlock_irq)(&chip->substream_lock);
list_add(&es->list, &chip->substream_list);
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
@@ -1649,9 +1580,9 @@ static int snd_es1968_playback_close(struct snd_pcm_substream *substream)
if (substream->runtime->private_data == NULL)
return 0;
es = substream->runtime->private_data;
- spin_lock_irq(&chip->substream_lock);
- list_del(&es->list);
- spin_unlock_irq(&chip->substream_lock);
+ scoped_guard(spinlock_irq, &chip->substream_lock) {
+ list_del(&es->list);
+ }
snd_es1968_free_apu_pair(chip, es->apu[0]);
kfree(es);
@@ -1666,9 +1597,9 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
if (substream->runtime->private_data == NULL)
return 0;
es = substream->runtime->private_data;
- spin_lock_irq(&chip->substream_lock);
- list_del(&es->list);
- spin_unlock_irq(&chip->substream_lock);
+ scoped_guard(spinlock_irq, &chip->substream_lock) {
+ list_del(&es->list);
+ }
snd_es1968_free_memory(chip, es->mixbuf);
snd_es1968_free_apu_pair(chip, es->apu[0]);
snd_es1968_free_apu_pair(chip, es->apu[2]);
@@ -1680,7 +1611,6 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
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,
@@ -1691,7 +1621,6 @@ static const struct snd_pcm_ops snd_es1968_playback_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,
@@ -1717,11 +1646,13 @@ static void es1968_measure_clock(struct es1968 *chip)
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) {
+ 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) {
+ 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);
@@ -1749,29 +1680,29 @@ static void es1968_measure_clock(struct es1968 *chip)
apu_set_register(chip, apu, 9, 0xD000);
apu_set_register(chip, apu, 10, 0x8F08);
apu_set_register(chip, apu, 11, 0x0000);
- spin_lock_irq(&chip->reg_lock);
- outw(1, chip->io_port + 0x04); /* clear WP interrupts */
- outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
+ }
snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */
chip->in_measurement = 1;
chip->measure_apu = apu;
- spin_lock_irq(&chip->reg_lock);
- 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);
- start_time = ktime_get();
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ 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);
+ start_time = ktime_get();
+ }
msleep(50);
- spin_lock_irq(&chip->reg_lock);
- offset = __apu_get_register(chip, apu, 5);
- stop_time = ktime_get();
- snd_es1968_trigger_apu(chip, apu, 0); /* stop */
- snd_es1968_bob_dec(chip);
- chip->in_measurement = 0;
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ offset = __apu_get_register(chip, apu, 5);
+ stop_time = ktime_get();
+ snd_es1968_trigger_apu(chip, apu, 0); /* stop */
+ snd_es1968_bob_dec(chip);
+ chip->in_measurement = 0;
+ }
/* check the current position */
offset -= (pa & 0xffff);
@@ -1813,7 +1744,8 @@ snd_es1968_pcm(struct es1968 *chip, int device)
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 */
@@ -1822,9 +1754,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;
@@ -1835,7 +1768,7 @@ snd_es1968_pcm(struct es1968 *chip, int device)
pcm->info_flags = 0;
- strcpy(pcm->name, "ESS Maestro");
+ strscpy(pcm->name, "ESS Maestro");
chip->pcm = pcm;
@@ -1975,7 +1908,8 @@ 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);
@@ -1992,15 +1926,15 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
if (event & ESM_SOUND_IRQ) {
struct esschan *es;
- spin_lock(&chip->substream_lock);
- list_for_each_entry(es, &chip->substream_list, list) {
- if (es->running) {
- snd_es1968_update_pcm(chip, es);
- if (es->fmt & ESS_FMT_STEREO)
- snd_es1968_suppress_jitter(chip, es);
+ scoped_guard(spinlock, &chip->substream_lock) {
+ list_for_each_entry(es, &chip->substream_list, list) {
+ if (es->running) {
+ snd_es1968_update_pcm(chip, es);
+ if (es->fmt & ESS_FMT_STEREO)
+ snd_es1968_suppress_jitter(chip, es);
+ }
}
}
- spin_unlock(&chip->substream_lock);
if (chip->in_measurement) {
unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5);
if (curp < chip->measure_lastpos)
@@ -2021,34 +1955,29 @@ snd_es1968_mixer(struct es1968 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
-#ifndef CONFIG_SND_ES1968_INPUT
- 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
/* attach master switch / volumes for h/w volume control */
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+ chip->master_switch = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Switch");
+ chip->master_volume = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Volume");
#endif
return 0;
@@ -2377,7 +2306,6 @@ static void snd_es1968_start_irq(struct es1968 *chip)
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
}
-#ifdef CONFIG_PM_SLEEP
/*
* PM support
*/
@@ -2392,7 +2320,6 @@ static int es1968_suspend(struct device *dev)
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);
return 0;
@@ -2440,11 +2367,7 @@ static int es1968_resume(struct device *dev)
return 0;
}
-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 */
+static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume);
#ifdef SUPPORT_JOYSTICK
#define JOYSTICK_ADDR 0x200
@@ -2457,7 +2380,8 @@ static int 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;
@@ -2465,7 +2389,6 @@ static int snd_es1968_create_gameport(struct es1968 *chip, int dev)
if (!gp) {
dev_err(chip->card->dev,
"cannot allocate memory for gameport\n");
- release_and_free_resource(r);
return -ENOMEM;
}
@@ -2476,7 +2399,6 @@ static int 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);
@@ -2486,12 +2408,8 @@ static int 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
@@ -2505,7 +2423,7 @@ 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;
@@ -2525,10 +2443,8 @@ static int 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;
@@ -2548,7 +2464,7 @@ struct snd_es1968_tea575x_gpio {
char *name;
};
-static struct snd_es1968_tea575x_gpio snd_es1968_tea575x_gpios[] = {
+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" },
};
@@ -2612,17 +2528,13 @@ static const struct snd_tea575x_ops snd_es1968_tea_ops = {
};
#endif
-static int snd_es1968_free(struct es1968 *chip)
+static void snd_es1968_free(struct snd_card *card)
{
+ struct es1968 *chip = card->private_data;
+
cancel_work_sync(&chip->hwvol_work);
-#ifdef CONFIG_SND_ES1968_INPUT
- if (chip->input_dev)
- input_unregister_device(chip->input_dev);
-#endif
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 */
}
@@ -2632,19 +2544,7 @@ static int snd_es1968_free(struct es1968 *chip)
v4l2_device_unregister(&chip->v4l2_dev);
#endif
- 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;
-}
-
-static int snd_es1968_dev_free(struct snd_device *device)
-{
- struct es1968 *chip = device->device_data;
- return snd_es1968_free(chip);
}
struct ess_device_list {
@@ -2652,7 +2552,7 @@ struct ess_device_list {
unsigned short vendor; /* subsystem vendor id */
};
-static struct ess_device_list pm_whitelist[] = {
+static const struct ess_device_list pm_allowlist[] = {
{ TYPE_MAESTRO2E, 0x0e11 }, /* Compaq Armada */
{ TYPE_MAESTRO2E, 0x1028 },
{ TYPE_MAESTRO2E, 0x103c },
@@ -2663,7 +2563,7 @@ static struct ess_device_list pm_whitelist[] = {
{ TYPE_MAESTRO2, 0x125d }, /* a PCI card, e.g. SF64-PCE2 */
};
-static struct ess_device_list mpu_blacklist[] = {
+static const struct ess_device_list mpu_denylist[] = {
{ TYPE_MAESTRO2, 0x125d },
};
@@ -2674,35 +2574,22 @@ static int snd_es1968_create(struct snd_card *card,
int capt_streams,
int chip_type,
int do_pm,
- int radio_nr,
- struct es1968 **chip_ret)
+ int radio_nr)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_es1968_dev_free,
- };
- struct es1968 *chip;
+ 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ 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");
- pci_disable_device(pci);
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);
@@ -2718,19 +2605,18 @@ static int 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 = pcim_request_all_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,
- KBUILD_MODNAME, 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);
- snd_es1968_free(chip);
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++)
@@ -2744,12 +2630,12 @@ static int 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;
}
@@ -2764,20 +2650,13 @@ static int 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);
- return err;
- }
-
#ifdef CONFIG_SND_ES1968_RADIO
/* don't play with GPIOs on laptops */
if (chip->pci->subsystem_vendor != 0x125d)
- goto no_radio;
+ return 0;
err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
- if (err < 0) {
- snd_es1968_free(chip);
+ if (err < 0)
return err;
- }
chip->tea.v4l2_dev = &chip->v4l2_dev;
chip->tea.private_data = chip;
chip->tea.radio_nr = radio_nr;
@@ -2788,24 +2667,20 @@ static int snd_es1968_create(struct snd_card *card,
if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
dev_info(card->dev, "detected TEA575x radio type %s\n",
get_tea575x_gpio(chip)->name);
- strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
sizeof(chip->tea.card));
break;
}
}
-no_radio:
#endif
-
- *chip_ret = chip;
-
return 0;
}
/*
*/
-static int 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;
@@ -2820,73 +2695,69 @@ static int snd_es1968_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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],
- radio_nr[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:
- strcpy(card->driver, "ES1978");
- strcpy(card->shortname, "ESS ES1978 (Maestro 2E)");
+ strscpy(card->driver, "ES1978");
+ strscpy(card->shortname, "ESS ES1978 (Maestro 2E)");
break;
case TYPE_MAESTRO2:
- strcpy(card->driver, "ES1968");
- strcpy(card->shortname, "ESS ES1968 (Maestro 2)");
+ strscpy(card->driver, "ES1968");
+ strscpy(card->shortname, "ESS ES1968 (Maestro 2)");
break;
case TYPE_MAESTRO:
- strcpy(card->driver, "ESM1");
- strcpy(card->shortname, "ESS Maestro 1");
+ strscpy(card->driver, "ESM1");
+ strscpy(card->shortname, "ESS Maestro 1");
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 |
- MPU401_INFO_IRQ_HOOK,
- -1, &chip->rmidi)) < 0) {
+ 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);
@@ -2907,27 +2778,26 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_es1968_probe(pci, pci_id));
}
static struct pci_driver es1968_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_es1968_ids,
.probe = snd_es1968_probe,
- .remove = snd_es1968_remove,
.driver = {
- .pm = ES1968_PM_OPS,
+ .pm = &es1968_pm,
},
};
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 73a67bc3586b..4ca992449ea3 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the ForteMedia FM801 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.
- *
*/
#include <linux/delay.h>
@@ -36,8 +26,6 @@
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 */
@@ -154,6 +142,8 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
/**
* struct fm801 - describes FM801 chip
+ * @dev: device for this chio
+ * @irq: irq number
* @port: I/O port number
* @multichannel: multichannel support
* @secondary: secondary codec
@@ -161,6 +151,31 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
* @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;
@@ -207,9 +222,7 @@ struct fm801 {
struct snd_tea575x tea;
#endif
-#ifdef CONFIG_PM_SLEEP
u16 saved_regs[0x20];
-#endif
};
/*
@@ -266,16 +279,14 @@ static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short mask, unsigned short value)
{
int change;
- unsigned long flags;
unsigned short old, new;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
old = fm801_ioread16(chip, reg);
new = (old & ~mask) | value;
change = old != new;
if (change)
fm801_iowrite16(chip, reg, new);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -380,7 +391,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
chip->ply_ctrl &= ~(FM801_BUF1_LAST |
@@ -401,12 +412,10 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
chip->ply_ctrl &= ~FM801_PAUSE;
break;
default:
- spin_unlock(&chip->reg_lock);
snd_BUG();
return -EINVAL;
}
fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
- spin_unlock(&chip->reg_lock);
return 0;
}
@@ -415,7 +424,7 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
chip->cap_ctrl &= ~(FM801_BUF1_LAST |
@@ -436,26 +445,13 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
chip->cap_ctrl &= ~FM801_PAUSE;
break;
default:
- spin_unlock(&chip->reg_lock);
snd_BUG();
return -EINVAL;
}
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);
@@ -463,7 +459,7 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
chip->ply_count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
FM801_STEREO | FM801_RATE_MASK |
FM801_CHANNELS_MASK);
@@ -485,7 +481,6 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
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;
}
@@ -496,7 +491,7 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
chip->cap_count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
FM801_STEREO | FM801_RATE_MASK);
if (snd_pcm_format_width(runtime->format) == 16)
@@ -512,7 +507,6 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
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;
}
@@ -523,13 +517,12 @@ 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);
+ guard(spinlock)(&chip->reg_lock);
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;
}
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, ptr);
}
@@ -540,13 +533,12 @@ 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);
+ guard(spinlock)(&chip->reg_lock);
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;
}
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, ptr);
}
@@ -563,31 +555,31 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
/* ack first */
fm801_writew(chip, IRQ_STATUS, status);
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
- spin_lock(&chip->reg_lock);
- chip->ply_buf++;
- chip->ply_pos += chip->ply_count;
- chip->ply_pos %= chip->ply_size;
- tmp = chip->ply_pos + chip->ply_count;
- tmp %= chip->ply_size;
- 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);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ chip->ply_buf++;
+ chip->ply_pos += chip->ply_count;
+ chip->ply_pos %= chip->ply_size;
+ tmp = chip->ply_pos + chip->ply_count;
+ tmp %= chip->ply_size;
+ if (chip->ply_buf & 1)
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
+ else
+ fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
+ }
snd_pcm_period_elapsed(chip->playback_substream);
}
if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
- spin_lock(&chip->reg_lock);
- chip->cap_buf++;
- chip->cap_pos += chip->cap_count;
- chip->cap_pos %= chip->cap_size;
- tmp = chip->cap_pos + chip->cap_count;
- tmp %= chip->cap_size;
- 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);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ chip->cap_buf++;
+ chip->cap_pos += chip->cap_count;
+ chip->cap_pos %= chip->cap_size;
+ tmp = chip->cap_pos + chip->cap_count;
+ tmp %= chip->cap_size;
+ if (chip->cap_buf & 1)
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
+ else
+ fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
+ }
snd_pcm_period_elapsed(chip->capture_substream);
}
if (chip->rmidi && (status & FM801_IRQ_MPU))
@@ -655,7 +647,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;
}
@@ -670,7 +663,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;
}
@@ -694,9 +688,6 @@ static int snd_fm801_capture_close(struct snd_pcm_substream *substream)
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,
@@ -705,9 +696,6 @@ static const struct snd_pcm_ops snd_fm801_playback_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,
@@ -719,7 +707,8 @@ static int snd_fm801_pcm(struct fm801 *chip, int device)
struct snd_pcm *pcm;
int err;
- 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);
@@ -727,12 +716,11 @@ static int snd_fm801_pcm(struct fm801 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "FM801");
+ strscpy(pcm->name, "FM801");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(pdev),
- 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);
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_alt_chmaps,
@@ -752,7 +740,7 @@ struct snd_fm801_tea575x_gpio {
char *name;
};
-static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+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" },
@@ -926,10 +914,9 @@ static int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
int invert = (kcontrol->private_value >> 24) & 0xff;
long *value = ucontrol->value.integer.value;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
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) {
value[0] = mask - value[0];
value[1] = mask - value[1];
@@ -988,7 +975,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);
}
@@ -997,7 +985,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[] = {
+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),
@@ -1018,7 +1006,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[] = {
+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),
@@ -1027,52 +1015,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 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;
}
@@ -1165,60 +1146,42 @@ static void snd_fm801_chip_init(struct fm801 *chip)
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 = fm801_readw(chip, IRQ_MASK);
cmdw |= 0x00c3;
fm801_writew(chip, IRQ_MASK, cmdw);
- devm_free_irq(chip->dev, chip->irq, chip);
-
- __end_hw:
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
snd_tea575x_exit(&chip->tea);
v4l2_device_unregister(&chip->v4l2_dev);
}
#endif
- 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 snd_fm801_create(struct snd_card *card,
struct pci_dev *pci,
int tea575x_tuner,
- int radio_nr,
- struct fm801 **rchip)
+ 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 = pcim_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->dev = &pci->dev;
chip->irq = -1;
chip->tea575x_tuner = tea575x_tuner;
- if ((err = pci_request_regions(pci, "FM801")) < 0)
+ err = pcim_request_all_regions(pci, "FM801");
+ if (err < 0)
return err;
chip->port = pci_resource_start(pci, 0);
@@ -1239,26 +1202,20 @@ static int snd_fm801_create(struct snd_card *card,
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);
- snd_fm801_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
pci_set_master(pci);
}
+ card->private_free = snd_fm801_free;
snd_fm801_chip_init(chip);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_fm801_free(chip);
- return err;
- }
-
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
- if (err < 0) {
- snd_fm801_free(chip);
+ if (err < 0)
return err;
- }
chip->tea.v4l2_dev = &chip->v4l2_dev;
chip->tea.radio_nr = radio_nr;
chip->tea.private_data = chip;
@@ -1268,7 +1225,6 @@ static int snd_fm801_create(struct snd_card *card,
(chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) {
if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
dev_err(card->dev, "TEA575x radio not found\n");
- snd_fm801_free(chip);
return -ENODEV;
}
} else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) {
@@ -1292,17 +1248,15 @@ static int snd_fm801_create(struct snd_card *card,
chip->tea575x_tuner |= tuner_only;
}
if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
- strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
sizeof(chip->tea.card));
}
#endif
-
- *rchip = chip;
return 0;
}
-static int 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;
@@ -1317,18 +1271,17 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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], radio_nr[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-");
+ strscpy(card->driver, "FM801");
+ strscpy(card->shortname, "ForteMedia FM801-");
strcat(card->shortname, chip->multichannel ? "AU" : "AS");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
@@ -1336,50 +1289,44 @@ static int 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)) < 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,
- chip->port + FM801_MPU401_DATA,
- MPU401_INFO_INTEGRATED |
- MPU401_INFO_IRQ_HOOK,
- -1, &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, chip->port + FM801_OPL3_BANK0,
- chip->port + FM801_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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id));
}
-#ifdef CONFIG_PM_SLEEP
-static unsigned char saved_regs[] = {
+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,
@@ -1400,7 +1347,6 @@ static int snd_fm801_suspend(struct device *dev)
if (chip->tea575x_tuner & TUNER_ONLY) {
/* FIXME: tea575x suspend */
} else {
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
snd_ac97_suspend(chip->ac97_sec);
}
@@ -1436,19 +1382,14 @@ static int snd_fm801_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
static struct pci_driver fm801_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
- .remove = snd_card_fm801_remove,
.driver = {
- .pm = SND_FM801_PM_OPS,
+ .pm = &snd_fm801_pm,
},
};
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
deleted file mode 100644
index f7a492c382d9..000000000000
--- a/sound/pci/hda/Kconfig
+++ /dev/null
@@ -1,227 +0,0 @@
-menu "HD-Audio"
-
-config SND_HDA
- tristate
- select SND_PCM
- select SND_VMASTER
- select SND_JACK
- select SND_HDA_CORE
-
-config SND_HDA_INTEL
- tristate "HD Audio PCI"
- depends on SND_PCI
- select SND_HDA
- help
- Say Y here to include support for Intel "High Definition
- Audio" (Azalia) and its compatible devices.
-
- This option enables the HD-audio controller. Don't forget
- to choose the appropriate codec options below.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-hda-intel.
-
-config SND_HDA_TEGRA
- tristate "NVIDIA Tegra HD Audio"
- depends on ARCH_TEGRA
- select SND_HDA
- 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"
- select SND_HWDEP
- help
- Say Y here to build a hwdep interface for HD-audio driver.
- This interface can be used for out-of-band communication
- with codecs for debugging purposes.
-
-config SND_HDA_RECONFIG
- bool "Allow dynamic codec reconfiguration"
- help
- Say Y here to enable the HD-audio codec re-configuration feature.
- 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
- 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)"
- depends on SND_HDA_INPUT_BEEP=y
- default "1"
- 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.
-
-config SND_HDA_PATCH_LOADER
- bool "Support initialization patch loading for HD-audio"
- select FW_LOADER
- select SND_HDA_RECONFIG
- help
- Say Y here to allow the HD-audio driver to load a pseudo
- firmware file ("patch") for overriding the BIOS setup at
- start up. The "patch" file can be specified via patch module
- option, such as patch=hda-init.
-
-config SND_HDA_CODEC_REALTEK
- tristate "Build Realtek HD-audio codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include Realtek HD-audio codec support in
- snd-hda-intel driver, such as ALC880.
-
-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
- tristate "Build Analog Device HD-audio codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include Analog Device HD-audio codec support in
- snd-hda-intel driver, such as AD1986A.
-
-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
- tristate "Build IDT/Sigmatel HD-audio codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
- snd-hda-intel driver, such as STAC9200.
-
-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
- tristate "Build VIA HD-audio codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include VIA HD-audio codec support in
- snd-hda-intel driver, such as VT1708.
-
-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
- tristate "Build HDMI/DisplayPort HD-audio codec support"
- help
- 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.
-
-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
- tristate "Build Cirrus Logic codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include Cirrus Logic codec support in
- snd-hda-intel driver, such as CS4206.
-
-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_CONEXANT
- tristate "Build Conexant HD-audio codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include Conexant HD-audio codec support in
- snd-hda-intel driver, such as CX20549.
-
-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
- tristate "Build Creative CA0110-IBG codec support"
- select SND_HDA_GENERIC
- help
- Say Y or M here to include Creative CA0110-IBG codec support in
- snd-hda-intel driver, found on some Creative X-Fi cards.
-
-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_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
- select SND_HDA_DSP_LOADER
- select FW_LOADER
- help
- 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.
-
-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
- tristate "Build Silicon Labs 3054 HD-modem codec support"
- help
- Say Y or M here to include Silicon Labs 3054 HD-modem codec
- (and compatibles) 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_SI3054=m
-
-config SND_HDA_GENERIC
- tristate "Enable generic HD-audio codec parser"
- help
- Say Y or M here to enable the generic HD-audio codec parser
- in snd-hda-intel driver.
-
-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 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.
-
-endif
-
-endmenu
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
deleted file mode 100644
index b57432f00056..000000000000
--- a/sound/pci/hda/Makefile
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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-$(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-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
-
-# common driver
-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_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
-
-# 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/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c
deleted file mode 100644
index 1b48a8c19d28..000000000000
--- a/sound/pci/hda/dell_wmi_helper.c
+++ /dev/null
@@ -1,152 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Helper functions for Dell Mic Mute LED control;
- * to be included from codec driver
- */
-
-#if IS_ENABLED(CONFIG_DELL_LAPTOP)
-#include <linux/dell-led.h>
-
-enum {
- MICMUTE_LED_ON,
- MICMUTE_LED_OFF,
- MICMUTE_LED_FOLLOW_CAPTURE,
- MICMUTE_LED_FOLLOW_MUTE,
-};
-
-static int dell_led_mode = MICMUTE_LED_FOLLOW_MUTE;
-static int dell_capture;
-static int dell_led_value;
-static int (*dell_micmute_led_set_func)(int);
-static void (*dell_old_cap_hook)(struct hda_codec *,
- struct snd_kcontrol *,
- struct snd_ctl_elem_value *);
-
-static void call_micmute_led_update(void)
-{
- int val;
-
- switch (dell_led_mode) {
- case MICMUTE_LED_ON:
- val = 1;
- break;
- case MICMUTE_LED_OFF:
- val = 0;
- break;
- case MICMUTE_LED_FOLLOW_CAPTURE:
- val = dell_capture;
- break;
- case MICMUTE_LED_FOLLOW_MUTE:
- default:
- val = !dell_capture;
- break;
- }
-
- if (val == dell_led_value)
- return;
- dell_led_value = val;
- dell_micmute_led_set_func(dell_led_value);
-}
-
-static void update_dell_wmi_micmute_led(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (dell_old_cap_hook)
- dell_old_cap_hook(codec, kcontrol, ucontrol);
-
- if (!ucontrol || !dell_micmute_led_set_func)
- return;
- if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
- /* TODO: How do I verify if it's a mono or stereo here? */
- dell_capture = (ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1]);
- call_micmute_led_update();
- }
-}
-
-static int dell_mic_mute_led_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = {
- "On", "Off", "Follow Capture", "Follow Mute",
- };
-
- return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
-}
-
-static int dell_mic_mute_led_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = dell_led_mode;
- return 0;
-}
-
-static int dell_mic_mute_led_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned int mode;
-
- mode = ucontrol->value.enumerated.item[0];
- if (mode > MICMUTE_LED_FOLLOW_MUTE)
- mode = MICMUTE_LED_FOLLOW_MUTE;
- if (mode == dell_led_mode)
- return 0;
- dell_led_mode = mode;
- call_micmute_led_update();
- return 1;
-}
-
-static const struct snd_kcontrol_new dell_mic_mute_mode_ctls[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Mute-LED Mode",
- .info = dell_mic_mute_led_mode_info,
- .get = dell_mic_mute_led_mode_get,
- .put = dell_mic_mute_led_mode_put,
- },
- {}
-};
-
-static void alc_fixup_dell_wmi(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- bool removefunc = false;
-
- if (action == HDA_FIXUP_ACT_PROBE) {
- if (!dell_micmute_led_set_func)
- dell_micmute_led_set_func = symbol_request(dell_micmute_led_set);
- if (!dell_micmute_led_set_func) {
- codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n");
- return;
- }
-
- removefunc = true;
- if (dell_micmute_led_set_func(false) >= 0) {
- dell_led_value = 0;
- if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
- codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
- else {
- dell_old_cap_hook = spec->gen.cap_sync_hook;
- spec->gen.cap_sync_hook = update_dell_wmi_micmute_led;
- removefunc = false;
- add_mixer(spec, dell_mic_mute_mode_ctls);
- }
- }
-
- }
-
- if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
- symbol_put(dell_micmute_led_set);
- dell_micmute_led_set_func = NULL;
- dell_old_cap_hook = NULL;
- }
-}
-
-#else /* CONFIG_DELL_LAPTOP */
-static void alc_fixup_dell_wmi(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
-}
-
-#endif /* CONFIG_DELL_LAPTOP */
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
deleted file mode 100644
index 681c360f29f9..000000000000
--- a/sound/pci/hda/hda_codec.h
+++ /dev/null
@@ -1,533 +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 <linux/kref.h>
-#include <linux/mod_devicetable.h>
-#include <sound/info.h>
-#include <sound/control.h>
-#include <sound/pcm.h>
-#include <sound/hwdep.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_verbs.h>
-#include <sound/hda_regmap.h>
-
-/*
- * Structures
- */
-
-struct hda_bus;
-struct hda_beep;
-struct hda_codec;
-struct hda_pcm;
-struct hda_pcm_stream;
-
-/*
- * 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 hdac_bus core;
-
- struct snd_card *card;
-
- struct pci_dev *pci;
- const char *modelname;
-
- struct mutex prepare_mutex;
-
- /* 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 */
- /* status for codec/controller */
- unsigned int shutdown :1; /* being unloaded */
- unsigned int response_reset:1; /* controller was reset */
- unsigned int in_reset:1; /* during reset operation */
- unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
-
- int primary_dig_out_type; /* primary digital out PCM type */
- unsigned int mixer_assigned; /* codec addr for mixer name */
-};
-
-/* from hdac_bus to hda_bus */
-#define to_hda_bus(bus) container_of(bus, struct hda_bus, core)
-
-/*
- * codec preset
- *
- * Known codecs have the patch to build and set up the controls/PCMs
- * better than the generic parser.
- */
-typedef int (*hda_codec_patch_t)(struct hda_codec *);
-
-#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
-#define HDA_CODEC_ID_GENERIC 0x00000201
-
-#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
- { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
- .api_version = HDA_DEV_LEGACY, \
- .driver_data = (unsigned long)(_patch) }
-#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
- HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
-
-struct hda_codec_driver {
- struct hdac_driver core;
- const struct hda_device_id *id;
-};
-
-int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
- struct module *owner);
-#define hda_codec_driver_register(drv) \
- __hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE)
-void hda_codec_driver_unregister(struct hda_codec_driver *drv);
-#define module_hda_codec_driver(drv) \
- module_driver(drv, hda_codec_driver_register, \
- hda_codec_driver_unregister)
-
-/* 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);
- void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
-#ifdef CONFIG_PM
- int (*suspend)(struct hda_codec *codec);
- int (*resume)(struct hda_codec *codec);
- int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
-#endif
- void (*reboot_notify)(struct hda_codec *codec);
- void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
-};
-
-/* 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);
- unsigned int (*get_delay)(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 */
- const struct snd_pcm_chmap_elem *chmap; /* chmap to override */
- 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
-};
-
-#define SNDRV_PCM_INVALID_DEVICE (-1)
-/* 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 */
- bool own_chmap; /* codec driver provides own channel maps */
- /* private: */
- struct hda_codec *codec;
- struct kref kref;
- struct list_head list;
-};
-
-/* codec information */
-struct hda_codec {
- struct hdac_device core;
- struct hda_bus *bus;
- struct snd_card *card;
- unsigned int addr; /* codec addr*/
- u32 probe_id; /* overridden id for probing */
-
- /* detected preset */
- const struct hda_device_id *preset;
- 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 */
- struct list_head pcm_list_head;
-
- /* codec specific info */
- void *spec;
-
- /* beep device */
- struct hda_beep *beep;
- unsigned int beep_mode;
-
- /* widget capabilities cache */
- u32 *wcaps;
-
- struct snd_array mixers; /* list of assigned mixer elements */
- struct snd_array nids; /* list of mapped mixer elements */
-
- struct list_head conn_list; /* linked-list of connection-list */
-
- struct mutex spdif_mutex;
- struct mutex control_mutex;
- struct snd_array spdif_out;
- unsigned int spdif_in_enable; /* SPDIF input enable? */
- const 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 */
-
- struct mutex user_mutex;
-#ifdef CONFIG_SND_HDA_RECONFIG
- 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
-
-#ifdef CONFIG_SND_HDA_HWDEP
- struct snd_hwdep *hwdep; /* assigned hwdep device */
-#endif
-
- /* misc flags */
- unsigned int in_freeing:1; /* being released */
- unsigned int registered:1; /* codec was registered */
- 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 single_adc_amp:1; /* adc in-amp takes no index
- * (e.g. CX20549 codec)
- */
- 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 */
- unsigned int no_jack_detect:1; /* Machine has no jack-detection */
- unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
- unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */
- unsigned int pcm_format_first:1; /* PCM format must be set first */
- unsigned int cached_write:1; /* write only to caches */
- unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
- unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
- unsigned int power_save_node:1; /* advanced PM for each widget */
- unsigned int auto_runtime_pm:1; /* enable automatic codec runtime pm */
- unsigned int force_pin_prefix:1; /* Add location prefix */
-#ifdef CONFIG_PM
- unsigned long power_on_acct;
- unsigned long power_off_acct;
- unsigned long power_jiffies;
-#endif
-
- /* filter the requested power state per nid */
- unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state);
-
- /* codec-specific additional proc output */
- void (*proc_widget_hook)(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid);
-
- /* jack detection */
- struct snd_array jacktbl;
- unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
- struct delayed_work jackpoll_work;
-
- /* jack detection */
- struct snd_array jacks;
-
- int depop_delay; /* depop delay in ms, -1 for default delay time */
-
- /* fix-up list */
- int fixup_id;
- const struct hda_fixup *fixup_list;
- const char *fixup_name;
-
- /* additional init verbs */
- struct snd_array verbs;
-};
-
-#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev)
-#define hda_codec_dev(_dev) (&(_dev)->core.dev)
-
-#define list_for_each_codec(c, bus) \
- list_for_each_entry(c, &(bus)->core.codec_list, core.list)
-#define list_for_each_codec_safe(c, n, bus) \
- list_for_each_entry_safe(c, n, &(bus)->core.codec_list, core.list)
-
-/* snd_hda_codec_read/write optional flags */
-#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
-
-/*
- * constructors
- */
-int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec **codecp);
-int snd_hda_codec_configure(struct hda_codec *codec);
-int snd_hda_codec_update_widgets(struct hda_codec *codec);
-
-/*
- * low level functions
- */
-static inline unsigned int
-snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int flags,
- unsigned int verb, unsigned int parm)
-{
- return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
-}
-
-static inline int
-snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm)
-{
- return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
-}
-
-#define snd_hda_param_read(codec, nid, param) \
- snd_hdac_read_parm(&(codec)->core, nid, param)
-#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
- snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid)
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns);
-static inline int
-snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_get_connections(codec, nid, NULL, 0);
-}
-
-#define snd_hda_get_raw_connections(codec, nid, list, max_conns) \
- snd_hdac_get_connections(&(codec)->core, nid, list, max_conns)
-#define snd_hda_get_num_raw_conns(codec, nid) \
- snd_hdac_get_connections(&(codec)->core, nid, NULL, 0);
-
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
- const hda_nid_t **listp);
-int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
- const hda_nid_t *list);
-int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid, int recursive);
-unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
- u8 *dev_list, int max_devices);
-int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id);
-
-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 */
-static inline void
-snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
-{
- snd_hdac_bus_queue_event(&bus->core, res, res_ex);
-}
-
-/* cached write */
-static inline int
-snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int flags, unsigned int verb, unsigned int parm)
-{
- return snd_hdac_regmap_write(&codec->core, nid, verb, parm);
-}
-
-#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \
- snd_hda_codec_write_cache(codec, nid, flags, verb, parm)
-
-/* the struct for codec->pin_configs */
-struct hda_pincfg {
- hda_nid_t nid;
- unsigned char ctrl; /* original pin control value */
- unsigned char target; /* target pin control value */
- 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);
-
-/* SPDIF controls */
-struct hda_spdif_out {
- hda_nid_t nid; /* Converter nid values relate to */
- unsigned int status; /* IEC958 status bits */
- unsigned short ctls; /* SPDIF control bits */
-};
-struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
- hda_nid_t nid);
-void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
-void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
-
-/*
- * Mixer
- */
-int snd_hda_codec_build_controls(struct hda_codec *codec);
-
-/*
- * PCM
- */
-int snd_hda_codec_parse_pcms(struct hda_codec *codec);
-int snd_hda_codec_build_pcms(struct hda_codec *codec);
-
-__printf(2, 3)
-struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
- const char *fmt, ...);
-
-static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
-{
- kref_get(&pcm->kref);
-}
-void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
-
-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)
-
-#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \
- snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp)
-#define snd_hda_is_supported_format(codec, nid, fmt) \
- snd_hdac_is_supported_format(&(codec)->core, nid, fmt)
-
-extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
-
-int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
- struct hda_pcm *cpcm);
-
-/*
- * Misc
- */
-void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
-void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
-
-int snd_hda_lock_devices(struct hda_bus *bus);
-void snd_hda_unlock_devices(struct hda_bus *bus);
-void snd_hda_bus_reset(struct hda_bus *bus);
-void snd_hda_bus_reset_codecs(struct hda_bus *bus);
-
-int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
-
-/*
- * power management
- */
-extern const struct dev_pm_ops hda_codec_driver_pm;
-
-static inline
-int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
-#ifdef CONFIG_PM
- if (codec->patch_ops.check_power_status)
- return codec->patch_ops.check_power_status(codec, nid);
-#endif
- return 0;
-}
-
-/*
- * power saving
- */
-#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core)
-#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core)
-#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core)
-#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core)
-#ifdef CONFIG_PM
-void snd_hda_set_power_save(struct hda_bus *bus, int delay);
-void snd_hda_update_power_acct(struct hda_codec *codec);
-#else
-static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
-#endif
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-/*
- * patch firmware
- */
-int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
-#endif
-
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
- unsigned int size,
- struct snd_dma_buffer *bufp);
-void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start);
-void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
- struct snd_dma_buffer *dmab);
-#else
-static inline int
-snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
- unsigned int size,
- struct snd_dma_buffer *bufp)
-{
- return -ENOSYS;
-}
-static inline void
-snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
-static inline void
-snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
- struct snd_dma_buffer *dmab) {}
-#endif
-
-#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
deleted file mode 100644
index ba7fe9b6655c..000000000000
--- a/sound/pci/hda/hda_eld.c
+++ /dev/null
@@ -1,780 +0,0 @@
-/*
- * 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 <sound/hda_chmap.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-enum eld_versions {
- ELD_VER_CEA_861D = 2,
- ELD_VER_PARTIAL = 31,
-};
-
-enum cea_edid_versions {
- CEA_EDID_VER_NONE = 0,
- CEA_EDID_VER_CEA861 = 1,
- CEA_EDID_VER_CEA861A = 2,
- CEA_EDID_VER_CEA861BCD = 3,
- CEA_EDID_VER_RESERVED = 4,
-};
-
-static const char * const eld_connection_type_names[4] = {
- "HDMI",
- "DisplayPort",
- "2-reserved",
- "3-reserved"
-};
-
-enum cea_audio_coding_types {
- AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
- AUDIO_CODING_TYPE_LPCM = 1,
- AUDIO_CODING_TYPE_AC3 = 2,
- AUDIO_CODING_TYPE_MPEG1 = 3,
- AUDIO_CODING_TYPE_MP3 = 4,
- AUDIO_CODING_TYPE_MPEG2 = 5,
- AUDIO_CODING_TYPE_AACLC = 6,
- AUDIO_CODING_TYPE_DTS = 7,
- AUDIO_CODING_TYPE_ATRAC = 8,
- AUDIO_CODING_TYPE_SACD = 9,
- AUDIO_CODING_TYPE_EAC3 = 10,
- AUDIO_CODING_TYPE_DTS_HD = 11,
- AUDIO_CODING_TYPE_MLP = 12,
- AUDIO_CODING_TYPE_DST = 13,
- AUDIO_CODING_TYPE_WMAPRO = 14,
- AUDIO_CODING_TYPE_REF_CXT = 15,
- /* also include valid xtypes below */
- AUDIO_CODING_TYPE_HE_AAC = 15,
- AUDIO_CODING_TYPE_HE_AAC2 = 16,
- AUDIO_CODING_TYPE_MPEG_SURROUND = 17,
-};
-
-enum cea_audio_coding_xtypes {
- AUDIO_CODING_XTYPE_HE_REF_CT = 0,
- AUDIO_CODING_XTYPE_HE_AAC = 1,
- AUDIO_CODING_XTYPE_HE_AAC2 = 2,
- AUDIO_CODING_XTYPE_MPEG_SURROUND = 3,
- AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
-};
-
-static const char * const cea_audio_coding_type_names[] = {
- /* 0 */ "undefined",
- /* 1 */ "LPCM",
- /* 2 */ "AC-3",
- /* 3 */ "MPEG1",
- /* 4 */ "MP3",
- /* 5 */ "MPEG2",
- /* 6 */ "AAC-LC",
- /* 7 */ "DTS",
- /* 8 */ "ATRAC",
- /* 9 */ "DSD (One Bit Audio)",
- /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
- /* 11 */ "DTS-HD",
- /* 12 */ "MLP (Dolby TrueHD)",
- /* 13 */ "DST",
- /* 14 */ "WMAPro",
- /* 15 */ "HE-AAC",
- /* 16 */ "HE-AACv2",
- /* 17 */ "MPEG Surround",
-};
-
-/*
- * The following two lists are shared between
- * - HDMI audio InfoFrame (source to sink)
- * - CEA E-EDID Extension (sink to source)
- */
-
-/*
- * SS1:SS0 index => sample size
- */
-static 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 */
- AC_SUPPCM_BITS_24, /* 3: 24 bits */
-};
-
-/*
- * SF2:SF1:SF0 index => sampling frequency
- */
-static int cea_sampling_frequencies[8] = {
- 0, /* 0: Refer to Stream Header */
- SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
- SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
- SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
- SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
- SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
- SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
- SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
-};
-
-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
- codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
-#endif
- return val;
-}
-
-#define GRAB_BITS(buf, byte, lowbit, bits) \
-({ \
- BUILD_BUG_ON(lowbit > 7); \
- BUILD_BUG_ON(bits > 8); \
- BUILD_BUG_ON(bits <= 0); \
- \
- (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
-})
-
-static void hdmi_update_short_audio_desc(struct hda_codec *codec,
- struct cea_sad *a,
- const unsigned char *buf)
-{
- int i;
- int val;
-
- val = GRAB_BITS(buf, 1, 0, 7);
- a->rates = 0;
- for (i = 0; i < 7; i++)
- if (val & (1 << i))
- a->rates |= cea_sampling_frequencies[i + 1];
-
- a->channels = GRAB_BITS(buf, 0, 0, 3);
- a->channels++;
-
- a->sample_bits = 0;
- a->max_bitrate = 0;
-
- a->format = GRAB_BITS(buf, 0, 3, 4);
- switch (a->format) {
- case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
- codec_info(codec, "HDMI: audio coding type 0 not expected\n");
- break;
-
- case AUDIO_CODING_TYPE_LPCM:
- val = GRAB_BITS(buf, 2, 0, 3);
- for (i = 0; i < 3; i++)
- if (val & (1 << i))
- a->sample_bits |= cea_sample_sizes[i + 1];
- break;
-
- case AUDIO_CODING_TYPE_AC3:
- case AUDIO_CODING_TYPE_MPEG1:
- case AUDIO_CODING_TYPE_MP3:
- case AUDIO_CODING_TYPE_MPEG2:
- case AUDIO_CODING_TYPE_AACLC:
- case AUDIO_CODING_TYPE_DTS:
- case AUDIO_CODING_TYPE_ATRAC:
- a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
- a->max_bitrate *= 8000;
- break;
-
- case AUDIO_CODING_TYPE_SACD:
- break;
-
- case AUDIO_CODING_TYPE_EAC3:
- break;
-
- case AUDIO_CODING_TYPE_DTS_HD:
- break;
-
- case AUDIO_CODING_TYPE_MLP:
- break;
-
- case AUDIO_CODING_TYPE_DST:
- break;
-
- case AUDIO_CODING_TYPE_WMAPRO:
- a->profile = GRAB_BITS(buf, 2, 0, 3);
- break;
-
- case AUDIO_CODING_TYPE_REF_CXT:
- a->format = GRAB_BITS(buf, 2, 3, 5);
- if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
- a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
- codec_info(codec,
- "HDMI: audio coding xtype %d not expected\n",
- a->format);
- a->format = 0;
- } else
- a->format += AUDIO_CODING_TYPE_HE_AAC -
- AUDIO_CODING_XTYPE_HE_AAC;
- break;
- }
-}
-
-/*
- * Be careful, ELD buf could be totally rubbish!
- */
-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) {
- codec_info(codec, "HDMI: Unknown ELD version %d\n", e->eld_ver);
- goto out_fail;
- }
-
- 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);
-
- e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
- e->support_ai = GRAB_BITS(buf, 5, 1, 1);
- e->conn_type = GRAB_BITS(buf, 5, 2, 2);
- e->sad_count = GRAB_BITS(buf, 5, 4, 4);
-
- e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
- e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
-
- e->port_id = get_unaligned_le64(buf + 8);
-
- /* not specified, but the spec's tendency is little endian */
- e->manufacture_id = get_unaligned_le16(buf + 16);
- e->product_id = get_unaligned_le16(buf + 18);
-
- if (mnl > ELD_MAX_MNL) {
- codec_info(codec, "HDMI: MNL is reserved value %d\n", mnl);
- goto out_fail;
- } else if (ELD_FIXED_BYTES + mnl > size) {
- codec_info(codec, "HDMI: out of range MNL %d\n", mnl);
- goto out_fail;
- } else
- strlcpy(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) {
- codec_info(codec, "HDMI: out of range SAD %d\n", i);
- goto out_fail;
- }
- 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:
- return -EINVAL;
-}
-
-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 hda_codec *codec, hda_nid_t nid,
- unsigned char *buf, int *eld_size)
-{
- int i;
- int ret = 0;
- int size;
-
- /*
- * 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 */
- codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
- size = 128;
- }
- if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
- codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
- return -ERANGE;
- }
-
- /* 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;
- }
-
- *eld_size = size;
-error:
- return ret;
-}
-
-/*
- * 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 unsigned int alsa_rates[] = {
- 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
- 88200, 96000, 176400, 192000, 384000
- };
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
- if (pcm & (1 << i))
- j += snprintf(buf + j, buflen - j, " %d",
- alsa_rates[i]);
-
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-
-#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 =";
-
- if (!a->format)
- return;
-
- 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);
- else if (a->max_bitrate)
- snprintf(buf2, sizeof(buf2),
- ", max bitrate = %d", a->max_bitrate);
- else
- buf2[0] = '\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 hda_codec *codec, struct parsed_hdmi_eld *e)
-{
- int i;
-
- 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_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(codec, e->sad + i);
-}
-
-#ifdef CONFIG_SND_PROC_FS
-
-static void hdmi_print_sad_info(int i, struct cea_sad *a,
- struct snd_info_buffer *buffer)
-{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
-
- snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
- i, a->format, cea_audio_coding_type_names[a->format]);
- snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
-
- 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) {
- snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
- snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
- i, a->sample_bits, buf);
- }
-
- if (a->max_bitrate)
- snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
- i, a->max_bitrate);
-
- if (a->profile)
- snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
-}
-
-void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
- struct snd_info_buffer *buffer)
-{
- struct parsed_hdmi_eld *e = &eld->info;
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
- int i;
- static const char * const eld_version_names[32] = {
- "reserved",
- "reserved",
- "CEA-861D or below",
- [3 ... 30] = "reserved",
- [31] = "partial"
- };
- static const char * const cea_edid_version_names[8] = {
- "no CEA EDID Timing Extension block present",
- "CEA-861",
- "CEA-861-A",
- "CEA-861-B, C or D",
- [4 ... 7] = "reserved"
- };
-
- snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
- snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
- 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_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);
- snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
- snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
- snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
- 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_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);
-
- for (i = 0; i < e->sad_count; i++)
- hdmi_print_sad_info(i, e->sad + i, buffer);
-}
-
-void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
- struct snd_info_buffer *buffer)
-{
- struct parsed_hdmi_eld *e = &eld->info;
- char line[64];
- char name[64];
- char *sname;
- long long val;
- unsigned int n;
-
- while (!snd_info_get_line(buffer, line, sizeof(line))) {
- if (sscanf(line, "%s %llx", name, &val) != 2)
- continue;
- /*
- * We don't allow modification to these fields:
- * monitor_name manufacture_id product_id
- * eld_version edid_version
- */
- if (!strcmp(name, "monitor_present"))
- eld->monitor_present = val;
- else if (!strcmp(name, "eld_valid"))
- eld->eld_valid = val;
- else if (!strcmp(name, "connection_type"))
- e->conn_type = val;
- else if (!strcmp(name, "port_id"))
- e->port_id = val;
- else if (!strcmp(name, "support_hdcp"))
- e->support_hdcp = val;
- else if (!strcmp(name, "support_ai"))
- e->support_ai = val;
- else if (!strcmp(name, "audio_sync_delay"))
- e->aud_synch_delay = val;
- else if (!strcmp(name, "speakers"))
- e->spk_alloc = val;
- else if (!strcmp(name, "sad_count"))
- e->sad_count = val;
- else if (!strncmp(name, "sad", 3)) {
- sname = name + 4;
- n = name[3] - '0';
- if (name[4] >= '0' && name[4] <= '9') {
- sname++;
- n = 10 * n + name[4] - '0';
- }
- if (n >= ELD_MAX_SAD)
- continue;
- if (!strcmp(sname, "_coding_type"))
- e->sad[n].format = val;
- else if (!strcmp(sname, "_channels"))
- e->sad[n].channels = val;
- else if (!strcmp(sname, "_rates"))
- e->sad[n].rates = val;
- else if (!strcmp(sname, "_bits"))
- e->sad[n].sample_bits = val;
- else if (!strcmp(sname, "_max_bitrate"))
- e->sad[n].max_bitrate = val;
- else if (!strcmp(sname, "_profile"))
- e->sad[n].profile = val;
- if (n >= e->sad_count)
- e->sad_count = n + 1;
- }
- }
-}
-#endif /* CONFIG_SND_PROC_FS */
-
-/* update PCM info based on ELD */
-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) */
- 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) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (maxbps < 20)
- maxbps = 20;
- }
- if (a->sample_bits & AC_SUPPCM_BITS_24) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (maxbps < 24)
- maxbps = 24;
- }
- }
- }
-
- /* restrict the parameters by the values the codec provides */
- 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_intel.h b/sound/pci/hda/hda_intel.h
deleted file mode 100644
index ff0c4d617bc1..000000000000
--- a/sound/pci/hda/hda_intel.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public 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_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 work_struct 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 */
-
- /* secondary power domain for hdmi audio under vga device */
- struct dev_pm_domain hdmi_pm_domain;
-
- bool need_i915_power:1; /* the hda controller needs i915 power */
-};
-
-#endif
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
deleted file mode 100644
index e9814c0168ea..000000000000
--- a/sound/pci/hda/hda_jack.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Jack-detection handling for HD-audio
- *
- * Copyright (c) 2011 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.
- */
-
-#ifndef __SOUND_HDA_JACK_H
-#define __SOUND_HDA_JACK_H
-
-#include <linux/err.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;
- hda_jack_callback_fn func;
- unsigned int private_data; /* arbitrary data */
- struct hda_jack_callback *next;
-};
-
-struct hda_jack_tbl {
- hda_nid_t nid;
- 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 */
- int type;
- struct snd_jack *jack;
-};
-
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
-
-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);
-struct hda_jack_callback *
-snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
- hda_jack_callback_fn cb);
-
-int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
- hda_nid_t gating_nid);
-
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-
-/* 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(struct hda_codec *codec, hda_nid_t nid);
-
-/**
- * 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_state(codec, nid) != HDA_JACK_NOT_PRESENT;
-}
-
-bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
-
-int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, bool phantom_jack);
-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/local.h b/sound/pci/hda/local.h
deleted file mode 100644
index 3b8b7d78f9e0..000000000000
--- a/sound/pci/hda/local.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- */
-
-#ifndef __HDAC_LOCAL_H
-#define __HDAC_LOCAL_H
-
-int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm);
-
-#define get_wcaps(codec, nid) \
- 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;
-}
-
-#define get_pin_caps(codec, nid) \
- hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)
-
-static inline
-unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid)
-{
- unsigned int val;
-
- if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
- return -1;
- return val;
-}
-
-#define get_amp_caps(codec, nid, dir) \
- hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP)
-
-#define get_power_caps(codec, nid) \
- hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)
-
-#endif /* __HDAC_LOCAL_H */
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
deleted file mode 100644
index 768ea8651993..000000000000
--- a/sound/pci/hda/patch_ca0132.c
+++ /dev/null
@@ -1,4804 +0,0 @@
-/*
- * 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>
- *
- * 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/mutex.h>
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/kernel.h>
-#include <sound/core.h>
-#include "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*/
-
-#define FLOAT_ZERO 0x00000000
-#define FLOAT_ONE 0x3f800000
-#define FLOAT_TWO 0x40000000
-#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"
-
-#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
-MODULE_FIRMWARE(EFX_FILE);
-#endif
-
-static char *dirstr[2] = { "Playback", "Capture" };
-
-enum {
- SPEAKER_OUT,
- HEADPHONE_OUT
-};
-
-enum {
- DIGITAL_MIC,
- LINE_MIC_IN
-};
-
-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
-#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
-};
-
-/* Effects values size*/
-#define EFFECT_VALS_MAX_COUNT 12
-
-/* 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 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 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 struct ct_voicefx ca0132_voicefx = {
- .name = "VoiceFX Capture Switch",
- .nid = VOICEFX,
- .mid = 0x95,
- .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
-};
-
-static 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 }
- }
-};
-
-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_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_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,
-
- /* 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 {
- 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;
- 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_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;
-
- /* 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;
-
- struct hda_codec *codec;
- struct delayed_work unsol_hp_work;
- int quirk;
-
-#ifdef ENABLE_TUNING_CONTROLS
- long cur_ctl_vals[TUNING_CTLS_COUNT];
-#endif
-};
-
-/*
- * CA0132 quirks table
- */
-enum {
- QUIRK_NONE,
- QUIRK_ALIENWARE,
-};
-
-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 */
- {}
-};
-
-static const struct snd_pci_quirk ca0132_quirks[] = {
- 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),
- {}
-};
-
-/*
- * 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 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 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);
-}
-
-/*
- * Enable clocks.
- */
-static void chipio_enable_clocks(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
-
- mutex_lock(&spec->chipio_mutex);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 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 int dummy = 0;
- int status = -1;
-
- /* clear all from the response queue */
- do {
- status = dspio_read(codec, &dummy);
- } while (status == 0);
-}
-
-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 = -1;
- 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;
-}
-
-/**
- * Prepare and send the SCP message to DSP
- * @codec: the HDA codec
- * @mod_id: ID of the DSP module to send the command
- * @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 req, int dir, 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, 0x20, (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 req, void *data, unsigned int len)
-{
- return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
-}
-
-static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
- int req, unsigned int data)
-{
- return dspio_set_param(codec, mod_id, 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, 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, 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;
-}
-
-/**
- * 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)
-{
- int status;
- 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;
-
- status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
-
- return status;
-}
-
-/*
- * 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[0];
-};
-
-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 sizeof(*p) + p->count*sizeof(u32);
-}
-
-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;
-}
-
-/**
- * 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;
-}
-
-/**
- * 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)
-{
- codec_dbg(codec, "---- dspload_post_setup ------\n");
-
- /*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;
-}
-
-/*
- * 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) }
-
-#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_CODEC_MUTE(xname, nid, dir) \
- CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
-
-/* The following are for tuning of products */
-#ifdef ENABLE_TUNING_CONTROLS
-
-static 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 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 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,
- unsigned int *lookup, int idx)
-{
- int i = 0;
-
- for (i = 0; i < TUNING_CTLS_COUNT; i++)
- if (nid == ca0132_tuning_ctls[i].nid)
- break;
-
- snd_hda_power_up(codec);
- dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
- 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 DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
-static const DECLARE_TLV_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 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;
-
- 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 int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, 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;
-}
-
-/*
- * 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;
-}
-
-/*
- * 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;
- 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;
- }
-
- /* 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;
- }
-
- 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]);
-
- 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);
- 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_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)
- 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) {
- 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 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;
- }
-
- 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;
-
- /* Mic boost does not apply to Digital Mic */
- if (spec->cur_mic_type != DIGITAL_MIC)
- changed = ca0132_mic_boost_set(codec, *valp);
- goto exit;
- }
-
-exit:
- snd_hda_power_down(codec);
- return changed;
-}
-
-/*
- * Volume related
- */
-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;
-}
-
-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;
-}
-
-static int add_fx_switch(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 =
- CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
- 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));
-}
-
-/*
- * When changing Node IDs for Mixer Controls below, make sure to update
- * Node IDs in ca0132_config() as well.
- */
-static 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 */
-};
-
-static int ca0132_build_controls(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- int i, num_fx;
- 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;
- }
-
- /* 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++) {
- err = add_fx_switch(codec, ca0132_effects[i].nid,
- ca0132_effects[i].name,
- ca0132_effects[i].direct);
- if (err < 0)
- return err;
- }
-
- 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;
-
- add_voicefx(codec);
-
-#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;
- }
- 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;
- 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];
-
- 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 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
- */
- 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);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_DATA_WRITE, 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);
- }
-}
-
-/*
- * 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);
-}
-
-/*
- * Initialization of flags in chip
- */
-static void ca0132_init_flags(struct hda_codec *codec)
-{
- 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)
-{
- 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;
- const struct dsp_image_seg *dsp_os_image;
- const struct firmware *fw_entry;
-
- 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);
- spec->dsp_state = DSP_DOWNLOADING;
- if (!ca0132_download_dsp_images(codec))
- spec->dsp_state = DSP_DOWNLOAD_FAILED;
- else
- spec->dsp_state = DSP_DOWNLOADED;
-
- if (spec->dsp_state == DSP_DOWNLOADED)
- 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");
- if (spec->wait_scp) {
- if (dspio_get_response_data(codec) >= 0)
- spec->wait_scp = 0;
- }
-
- dspio_clear_response_queue(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.
- */
- cancel_delayed_work_sync(&spec->unsol_hp_work);
- schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
- tbl = snd_hda_jack_tbl_get(codec, cb->nid);
- if (tbl)
- tbl->block_report = 1;
-}
-
-static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
-{
- ca0132_select_mic(codec);
-}
-
-static void ca0132_init_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);
-}
-
-/*
- * Verbs tables.
- */
-
-/* Sends before DSP download. */
-static struct hda_verb ca0132_base_init_verbs[] = {
- /*enable ct extension*/
- {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
- {}
-};
-
-/* Send at exit. */
-static 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 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},
- {0x15, 0x717, 0x0D},
- {0x15, 0x718, 0x20},
- {}
-};
-
-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);
-
- spec->cur_out_type = SPEAKER_OUT;
- spec->cur_mic_type = DIGITAL_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;
- }
-
- spec->voicefx_val = 0;
- spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
- spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
-
-#ifdef ENABLE_TUNING_CONTROLS
- ca0132_init_tuning_defaults(codec);
-#endif
-}
-
-static void ca0132_exit_chip(struct hda_codec *codec)
-{
- /* put any chip cleanup stuffs here. */
-
- if (dspload_is_loaded(codec))
- dsp_reset(codec);
-}
-
-static int ca0132_init(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- if (spec->dsp_state != DSP_DOWNLOAD_FAILED)
- spec->dsp_state = DSP_DOWNLOAD_INIT;
- spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
-
- snd_hda_power_up_pm(codec);
-
- ca0132_init_unsol(codec);
-
- ca0132_init_params(codec);
- ca0132_init_flags(codec);
- snd_hda_sequence_write(codec, spec->base_init_verbs);
- ca0132_download_dsp(codec);
- ca0132_refresh_widget_caps(codec);
- ca0132_setup_defaults(codec);
- ca0132_init_analog_mic2(codec);
- ca0132_init_dmic(codec);
-
- 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);
-
- snd_hda_sequence_write(codec, spec->chip_init_verbs);
- snd_hda_sequence_write(codec, spec->spec_init_verbs);
-
- ca0132_select_out(codec);
- ca0132_select_mic(codec);
-
- snd_hda_jack_report_sync(codec);
-
- snd_hda_power_down_pm(codec);
-
- 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);
- snd_hda_sequence_write(codec, spec->base_exit_verbs);
- ca0132_exit_chip(codec);
- snd_hda_power_down(codec);
- kfree(spec->spec_init_verbs);
- kfree(codec->spec);
-}
-
-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,
-};
-
-static void ca0132_config(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- spec->dacs[0] = 0x2;
- spec->dacs[1] = 0x3;
- spec->dacs[2] = 0x4;
-
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = 3;
- spec->multiout.max_channels = 2;
-
- if (spec->quirk == QUIRK_ALIENWARE) {
- codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
- snd_hda_apply_pincfgs(codec, alienware_pincfgs);
-
- 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;
- } else {
- 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;
- cfg->dig_out_pins[0] = 0x0c;
- cfg->dig_outs = 1;
- cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
- spec->dig_in = 0x09;
- cfg->dig_in_pin = 0x0e;
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
- }
-}
-
-static int ca0132_prepare_verbs(struct hda_codec *codec)
-{
-/* Verbs + terminator (an empty element) */
-#define NUM_SPEC_VERBS 4
- struct ca0132_spec *spec = codec->spec;
-
- spec->chip_init_verbs = ca0132_init_verbs0;
- spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL);
- if (!spec->spec_init_verbs)
- return -ENOMEM;
-
- /* HP jack autodetection */
- spec->spec_init_verbs[0].nid = spec->unsol_tag_hp;
- spec->spec_init_verbs[0].param = AC_VERB_SET_UNSOLICITED_ENABLE;
- spec->spec_init_verbs[0].verb = AC_USRSP_EN | spec->unsol_tag_hp;
-
- /* MIC1 jack autodetection */
- spec->spec_init_verbs[1].nid = spec->unsol_tag_amic1;
- spec->spec_init_verbs[1].param = AC_VERB_SET_UNSOLICITED_ENABLE;
- spec->spec_init_verbs[1].verb = AC_USRSP_EN | spec->unsol_tag_amic1;
-
- /* config EAPD */
- spec->spec_init_verbs[2].nid = 0x0b;
- spec->spec_init_verbs[2].param = 0x78D;
- spec->spec_init_verbs[2].verb = 0x00;
-
- /* Previously commented configuration */
- /*
- spec->spec_init_verbs[3].nid = 0x0b;
- spec->spec_init_verbs[3].param = AC_VERB_SET_EAPD_BTLENABLE;
- spec->spec_init_verbs[3].verb = 0x02;
-
- spec->spec_init_verbs[4].nid = 0x10;
- spec->spec_init_verbs[4].param = 0x78D;
- spec->spec_init_verbs[4].verb = 0x02;
-
- spec->spec_init_verbs[5].nid = 0x10;
- spec->spec_init_verbs[5].param = AC_VERB_SET_EAPD_BTLENABLE;
- spec->spec_init_verbs[5].verb = 0x02;
- */
-
- /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */
- return 0;
-}
-
-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;
-
- codec->patch_ops = ca0132_patch_ops;
- codec->pcm_format_first = 1;
- codec->no_sticky_stream = 1;
-
- /* Detect codec quirk */
- quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks);
- if (quirk)
- spec->quirk = quirk->value;
- else
- spec->quirk = QUIRK_NONE;
-
- spec->dsp_state = DSP_DOWNLOAD_INIT;
- spec->num_mixers = 1;
- spec->mixers[0] = ca0132_mixer;
-
- 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;
-
- return 0;
-
- error:
- ca0132_free(codec);
- return err;
-}
-
-/*
- * patch entries
- */
-static 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_cmedia.c b/sound/pci/hda/patch_cmedia.c
deleted file mode 100644
index 1b2195dd2b26..000000000000
--- a/sound/pci/hda/patch_cmedia.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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/slab.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
-
-struct cmi_spec {
- struct hda_gen_spec gen;
-};
-
-/*
- * stuff for auto-parser
- */
-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 int patch_cmi9880(struct hda_codec *codec)
-{
- struct cmi_spec *spec;
- struct auto_pin_cfg *cfg;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- codec->patch_ops = cmi_auto_patch_ops;
- cfg = &spec->gen.autocfg;
- snd_hda_gen_spec_init(&spec->gen);
-
- 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;
-
- return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
-}
-
-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)
- return -ENOMEM;
-
- codec->spec = spec;
- codec->patch_ops = cmi_auto_patch_ops;
- cfg = &spec->gen.autocfg;
- snd_hda_gen_spec_init(&spec->gen);
-
- /* mask NID 0x10 from the playback volume selection;
- * it's a headphone boost volume handled manually below
- */
- spec->gen.out_vol_mask = (1ULL << 0x10);
-
- 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;
- }
- }
-
- return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
-}
-
-/*
- * patch entries
- */
-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_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("C-Media HD-audio codec");
-
-static struct hda_codec_driver cmedia_driver = {
- .id = snd_hda_id_cmedia,
-};
-
-module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
deleted file mode 100644
index b4f1b6e88305..000000000000
--- a/sound/pci/hda/patch_hdmi.c
+++ /dev/null
@@ -1,3868 +0,0 @@
-/*
- *
- * patch_hdmi.c - routines for HDMI/DisplayPort codecs
- *
- * Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
- * 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/slab.h>
-#include <linux/module.h>
-#include <sound/core.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 "hda_codec.h"
-#include "hda_local.h"
-#include "hda_jack.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");
-
-#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807)
-#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808)
-#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809)
-#define is_broxton(codec) ((codec)->core.vendor_id == 0x8086280a)
-#define is_kabylake(codec) ((codec)->core.vendor_id == 0x8086280b)
-#define is_geminilake(codec) (((codec)->core.vendor_id == 0x8086280d) || \
- ((codec)->core.vendor_id == 0x80862800))
-#define is_cannonlake(codec) ((codec)->core.vendor_id == 0x8086280c)
-#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
- || is_skylake(codec) || is_broxton(codec) \
- || is_kabylake(codec)) || is_geminilake(codec) \
- || is_cannonlake(codec)
-#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882)
-#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883)
-#define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec))
-
-struct hdmi_spec_per_cvt {
- hda_nid_t cvt_nid;
- int assigned;
- unsigned int channels_min;
- unsigned int channels_max;
- u32 rates;
- u64 formats;
- unsigned int maxbps;
-};
-
-/* 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 repoll_count;
- bool setup; /* the stream has been set up by prepare callback */
- 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,
- unsigned char *buf, int *eld_size);
-
- void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
- 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, bool hbr);
-
- int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
- hda_nid_t pin_nid, 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;
-};
-
-struct hdmi_spec {
- int num_cvts;
- struct snd_array cvts; /* struct hdmi_spec_per_cvt */
- hda_nid_t cvt_nids[4]; /* only for haswell fix */
-
- /*
- * 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
- */
- int num_pins;
- /*
- * num_nids is the number of real pins
- * In the above example, num_nids is 3
- */
- int num_nids;
- /*
- * 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[16];
- struct mutex pcm_lock;
- /* 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.
- */
- unsigned long pcm_in_use;
-
- struct hdmi_eld temp_eld;
- struct hdmi_ops ops;
-
- bool dyn_pin_out;
- bool dyn_pcm_assign;
- /*
- * Non-generic VIA/NVIDIA specific
- */
- struct hda_multi_out multiout;
- struct hda_pcm_stream pcm_playback;
-
- /* i915/powerwell (Haswell+/Valleyview+) specific */
- bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
- struct i915_audio_component_audio_ops i915_audio_ops;
-
- struct hdac_chmap chmap;
- hda_nid_t vendor_nid;
-};
-
-#ifdef CONFIG_SND_HDA_I915
-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 */
- u8 ver; /* 0x01 */
- u8 len; /* 0x0a */
-
- u8 checksum;
-
- u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
- u8 SS01_SF24;
- u8 CXT04;
- u8 CA;
- u8 LFEPBL01_LSV36_DM_INH7;
-};
-
-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;
-};
-
-union audio_infoframe {
- struct hdmi_audio_infoframe hdmi;
- struct dp_audio_infoframe dp;
- u8 bytes[0];
-};
-
-/*
- * HDMI routines
- */
-
-#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;
-
- /*
- * (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;
- }
-
- codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid);
- return -EINVAL;
-}
-
-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;
-
- codec_warn(codec, "HDMI: hinfo %p not registered\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 not registered\n", hinfo);
- return -EINVAL;
-}
-
-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; 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;
-
- codec_warn(codec, "HDMI: cvt nid %d not registered\n", cvt_nid);
- return -EINVAL;
-}
-
-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;
- mutex_unlock(&spec->pcm_lock);
- return 0;
- }
- eld = &per_pin->sink_eld;
- uinfo->count = eld->eld_valid ? eld->eld_size : 0;
- 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;
-
- 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));
- mutex_unlock(&spec->pcm_lock);
- return 0;
- }
- eld = &per_pin->sink_eld;
-
- if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
- eld->eld_size > ELD_MAX_SIZE) {
- mutex_unlock(&spec->pcm_lock);
- snd_BUG();
- return -EINVAL;
- }
-
- 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);
- mutex_unlock(&spec->pcm_lock);
-
- return 0;
-}
-
-static const struct snd_kcontrol_new eld_bytes_ctl = {
- .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_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
- int device)
-{
- 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
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
- int *packet_index, int *byte_index)
-{
- int val;
-
- val = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_INDEX, 0);
-
- *packet_index = val >> 5;
- *byte_index = val & 0x1f;
-}
-#endif
-
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
- int packet_index, int byte_index)
-{
- int val;
-
- val = (packet_index << 5) | (byte_index & 0x1f);
-
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
-}
-
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
- unsigned char val)
-{
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
-}
-
-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);
-
- 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;
-
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
-}
-
-/*
- * ELD proc files
- */
-
-#ifdef CONFIG_SND_PROC_FS
-static void print_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- 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);
- mutex_unlock(&per_pin->lock);
-}
-
-static void write_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct hdmi_spec_per_pin *per_pin = entry->private_data;
-
- mutex_lock(&per_pin->lock);
- snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
- mutex_unlock(&per_pin->lock);
-}
-
-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;
-
- 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_info_set_text_ops(entry, per_pin, print_eld_info);
- entry->c.text.write = write_eld_info;
- entry->mode |= S_IWUSR;
- per_pin->proc_entry = entry;
-
- return 0;
-}
-
-static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
-{
- if (!per_pin->codec->bus->shutdown) {
- snd_info_free_entry(per_pin->proc_entry);
- per_pin->proc_entry = NULL;
- }
-}
-#else
-static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
- int index)
-{
- return 0;
-}
-static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
-{
-}
-#endif
-
-/*
- * Audio InfoFrame routines
- */
-
-/*
- * Enable Audio InfoFrame Transmission
- */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
- AC_DIPXMIT_BEST);
-}
-
-/*
- * Disable Audio InfoFrame Transmission
- */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
- AC_DIPXMIT_DISABLE);
-}
-
-static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int i;
- int size;
-
- size = snd_hdmi_get_eld_size(codec, pin_nid);
- 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);
- codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
- }
-#endif
-}
-
-static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef BE_PARANOID
- int i, j;
- int size;
- int pi, bi;
- for (i = 0; i < 8; i++) {
- size = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_SIZE, i);
- if (size == 0)
- continue;
-
- hdmi_set_dip_index(codec, pin_nid, i, 0x0);
- for (j = 1; j < 1000; j++) {
- hdmi_write_dip_byte(codec, pin_nid, 0x0);
- hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
- if (pi != i)
- codec_dbg(codec, "dip index %d: %d != %d\n",
- bi, pi, i);
- if (bi == 0) /* byte index wrapped around */
- break;
- }
- codec_dbg(codec,
- "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
- i, size, j);
- }
-#endif
-}
-
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
-{
- u8 *bytes = (u8 *)hdmi_ai;
- u8 sum = 0;
- int i;
-
- hdmi_ai->checksum = 0;
-
- for (i = 0; i < sizeof(*hdmi_ai); i++)
- sum += bytes[i];
-
- hdmi_ai->checksum = -sum;
-}
-
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
- hda_nid_t pin_nid,
- u8 *dip, int size)
-{
- int i;
-
- hdmi_debug_dip_size(codec, pin_nid);
- hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
-
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < size; i++)
- hdmi_write_dip_byte(codec, pin_nid, dip[i]);
-}
-
-static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
- u8 *dip, int size)
-{
- u8 val;
- int i;
-
- if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
- != AC_DIPXMIT_BEST)
- return false;
-
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < size; i++) {
- val = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_DATA, 0);
- if (val != dip[i])
- return false;
- }
-
- return true;
-}
-
-static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
- hda_nid_t pin_nid,
- int ca, int active_channels,
- int conn_type)
-{
- union audio_infoframe ai;
-
- memset(&ai, 0, sizeof(ai));
- if (conn_type == 0) { /* HDMI */
- struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
-
- hdmi_ai->type = 0x84;
- hdmi_ai->ver = 0x01;
- hdmi_ai->len = 0x0a;
- 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 %d\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.bytes,
- sizeof(ai))) {
- codec_dbg(codec,
- "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n",
- 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 channels = per_pin->channels;
- int active_channels;
- struct hdmi_eld *eld;
- int ca;
-
- if (!channels)
- return;
-
- /* 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, ca, active_channels,
- eld->info.conn_type);
-
- per_pin->non_pcm = non_pcm;
-}
-
-/*
- * Unsolicited events
- */
-
-static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
-
-static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
- int dev_id)
-{
- struct hdmi_spec *spec = codec->spec;
- int pin_idx = pin_id_to_pin_index(codec, nid, dev_id);
-
- if (pin_idx < 0)
- return;
- if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
- snd_hda_jack_report_sync(codec);
-}
-
-static void jack_callback(struct hda_codec *codec,
- struct hda_jack_callback *jack)
-{
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
-}
-
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- struct hda_jack_tbl *jack;
- int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
-
- /*
- * assume DP MST uses dyn_pcm_assign and acomp and
- * never comes here
- * if DP MST supports unsol event, below code need
- * consider dev_entry
- */
- jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
- if (!jack)
- return;
- jack->jack_dirty = 1;
-
- codec_dbg(codec,
- "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
- codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
- !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
-
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
-}
-
-static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
- int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
-
- 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_ready)
- ;
-}
-
-
-static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-
- if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
- codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
- return;
- }
-
- if (subtag == 0)
- hdmi_intrinsic_event(codec, res);
- 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 pin 0x%x is now D%d\n", nid, pwr);
- }
-}
-
-/*
- * Callbacks
- */
-
-/* HBR should be Non-PCM, 8 channels */
-#define is_hbr_format(format) \
- ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
-
-static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
- bool hbr)
-{
- int pinctl, new_pinctl;
-
- if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
- 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 (hbr)
- new_pinctl |= AC_PINCTL_EPT_HBR;
- else
- new_pinctl |= AC_PINCTL_EPT_NATIVE;
-
- 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, 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, 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, is_hbr_format(format));
-
- if (err) {
- codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
- return err;
- }
-
- if (is_haswell_plus(codec)) {
-
- /*
- * 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)
-{
- 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);
-
- /* 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)
- 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 %d\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
- * in dyn_pcm_assign mode.
- */
-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);
- if (err)
- return err;
-
- per_cvt = get_cvt(spec, cvt_idx);
- per_cvt->assigned = 1;
- 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 */
-
- /* 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;
-}
-
-/*
- * HDA PCM callbacks
- */
-static int hdmi_pcm_open(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 pin_idx, cvt_idx, pcm_idx;
- struct hdmi_spec_per_pin *per_pin;
- struct hdmi_eld *eld;
- struct hdmi_spec_per_cvt *per_cvt = NULL;
- int err;
-
- /* Validate hinfo */
- pcm_idx = hinfo_to_pcm_index(codec, hinfo);
- if (pcm_idx < 0)
- return -EINVAL;
-
- mutex_lock(&spec->pcm_lock);
- pin_idx = hinfo_to_pin_index(codec, hinfo);
- if (!spec->dyn_pcm_assign) {
- if (snd_BUG_ON(pin_idx < 0)) {
- mutex_unlock(&spec->pcm_lock);
- return -EINVAL;
- }
- } else {
- /* 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);
- mutex_unlock(&spec->pcm_lock);
- return err;
- }
- }
-
- err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx);
- if (err < 0) {
- mutex_unlock(&spec->pcm_lock);
- return err;
- }
-
- per_cvt = get_cvt(spec, cvt_idx);
- /* Claim converter */
- per_cvt->assigned = 1;
-
- 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;
-
- 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) {
- per_cvt->assigned = 0;
- hinfo->nid = 0;
- snd_hda_spdif_ctls_unassign(codec, pcm_idx);
- mutex_unlock(&spec->pcm_lock);
- return -ENODEV;
- }
- }
-
- mutex_unlock(&spec->pcm_lock);
- /* 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;
-}
-
-/*
- * HDA/HDMI auto parsing
- */
-static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- hda_nid_t pin_nid = per_pin->pin_nid;
-
- if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
- codec_warn(codec,
- "HDMI: pin %d wcaps %#x does not support connection list\n",
- pin_nid, get_wcaps(codec, pin_nid));
- return -EINVAL;
- }
-
- /* all the device entries on the same pin have the same conn list */
- per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
- per_pin->mux_nids,
- HDA_MAX_CONNECTIONS);
-
- return 0;
-}
-
-static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
- struct hdmi_spec_per_pin *per_pin)
-{
- int i;
-
- /* try the prefer PCM */
- if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
- return per_pin->pin_nid_idx;
-
- /* have a second try; check the "reserved area" over num_pins */
- for (i = spec->num_nids; i < spec->pcm_used; i++) {
- if (!test_bit(i, &spec->pcm_bitmap))
- return i;
- }
-
- /* the last try; check the empty slots in pins */
- for (i = 0; i < spec->num_nids; 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;
- idx = hdmi_find_pcm_slot(spec, per_pin);
- if (idx == -EBUSY)
- return;
- 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 idx;
-
- /* pcm already be detached from the pin */
- if (!per_pin->pcm)
- return;
- idx = per_pin->pcm_idx;
- per_pin->pcm_idx = -1;
- 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;
-
- 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 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)
- pcm = get_pcm_rec(spec, per_pin->pcm_idx);
- else
- 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;
-}
-
-/* update per_pin ELD from the given new ELD;
- * setup info frame and notification accordingly
- */
-static void update_eld(struct hda_codec *codec,
- struct hdmi_spec_per_pin *per_pin,
- struct hdmi_eld *eld)
-{
- struct hdmi_eld *pin_eld = &per_pin->sink_eld;
- struct hdmi_spec *spec = codec->spec;
- bool old_eld_valid = pin_eld->eld_valid;
- bool eld_changed;
- int pcm_idx = -1;
-
- /* for monitor disconnection, save pcm_idx firstly */
- pcm_idx = per_pin->pcm_idx;
- if (spec->dyn_pcm_assign) {
- 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 (eld->eld_valid)
- snd_hdmi_show_eld(codec, &eld->info);
-
- eld_changed = (pin_eld->eld_valid != eld->eld_valid);
- if (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;
-
- 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);
-}
-
-/* update ELD and jack state via HD-audio verbs */
-static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
- int repoll)
-{
- struct hda_jack_tbl *jack;
- struct hda_codec *codec = per_pin->codec;
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld = &spec->temp_eld;
- hda_nid_t pin_nid = per_pin->pin_nid;
- /*
- * 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;
- bool ret;
- bool do_repoll = false;
-
- present = snd_hda_pin_sense(codec, pin_nid);
-
- 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 Pin=%d 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, eld->eld_buffer,
- &eld->eld_size) < 0)
- eld->eld_valid = false;
- else {
- if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
- eld->eld_size) < 0)
- eld->eld_valid = false;
- }
- if (!eld->eld_valid && repoll)
- do_repoll = true;
- }
-
- if (do_repoll)
- schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
- else
- update_eld(codec, per_pin, eld);
-
- ret = !repoll || !eld->monitor_present || eld->eld_valid;
-
- jack = snd_hda_jack_tbl_get(codec, pin_nid);
- if (jack)
- jack->block_report = !ret;
-
- mutex_unlock(&per_pin->lock);
- return ret;
-}
-
-static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
- struct hdmi_spec_per_pin *per_pin)
-{
- struct hdmi_spec *spec = codec->spec;
- struct snd_jack *jack = NULL;
- struct hda_jack_tbl *jack_tbl;
-
- /* if !dyn_pcm_assign, get jack from hda_jack_tbl
- * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
- * NULL even after snd_hda_jack_tbl_clear() is called to
- * free snd_jack. This may cause access invalid memory
- * when calling snd_jack_report
- */
- if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
- jack = spec->pcm_rec[per_pin->pcm_idx].jack;
- else if (!spec->dyn_pcm_assign) {
- /*
- * jack tbl doesn't support DP MST
- * DP MST will use dyn_pcm_assign,
- * so DP MST will never come here
- */
- jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
- if (jack_tbl)
- jack = jack_tbl->jack;
- }
- return jack;
-}
-
-/* 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;
- struct snd_jack *jack = NULL;
- int size;
-
- mutex_lock(&per_pin->lock);
- eld->monitor_present = false;
- 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);
- if (size > 0) {
- size = min(size, ELD_MAX_SIZE);
- if (snd_hdmi_parse_eld(codec, &eld->info,
- eld->eld_buffer, size) < 0)
- size = -EINVAL;
- }
-
- if (size > 0) {
- eld->eld_valid = true;
- eld->eld_size = size;
- } else {
- eld->eld_valid = false;
- eld->eld_size = 0;
- }
-
- /* pcm_idx >=0 before update_eld() means it is in monitor
- * disconnected event. Jack must be fetched before update_eld()
- */
- jack = pin_idx_to_jack(codec, per_pin);
- update_eld(codec, per_pin, eld);
- if (jack == NULL)
- jack = pin_idx_to_jack(codec, per_pin);
- if (jack == NULL)
- goto unlock;
- snd_jack_report(jack,
- eld->monitor_present ? SND_JACK_AVOUT : 0);
- unlock:
- mutex_unlock(&per_pin->lock);
-}
-
-static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
-{
- struct hda_codec *codec = per_pin->codec;
- struct hdmi_spec *spec = codec->spec;
- int ret;
-
- /* no temporary power up/down needed for component notifier */
- if (!codec_has_acomp(codec))
- snd_hda_power_up_pm(codec);
-
- mutex_lock(&spec->pcm_lock);
- if (codec_has_acomp(codec)) {
- sync_eld_via_acomp(codec, per_pin);
- ret = false; /* don't call snd_hda_jack_report_sync() */
- } else {
- ret = hdmi_present_sense_via_verbs(per_pin, repoll);
- }
- mutex_unlock(&spec->pcm_lock);
-
- if (!codec_has_acomp(codec))
- snd_hda_power_down_pm(codec);
-
- return ret;
-}
-
-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);
-
- if (per_pin->repoll_count++ > 6)
- per_pin->repoll_count = 0;
-
- if (hdmi_present_sense(per_pin, per_pin->repoll_count))
- snd_hda_jack_report_sync(per_pin->codec);
-}
-
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
- hda_nid_t nid);
-
-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;
-
- caps = snd_hda_query_pin_caps(codec, pin_nid);
- if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
- return 0;
-
- /*
- * 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)
- return 0;
-
- /*
- * To simplify the implementation, malloc all
- * the virtual pins in the initialization statically
- */
- if (is_haswell_plus(codec)) {
- /*
- * On Intel platforms, device entries number is
- * changed dynamically. If there is a DP MST
- * hub connected, the device entries number is 3.
- * Otherwise, it is 1.
- * Here we manually set dev_num to 3, so that
- * we can initialize all the device entries when
- * bootup statically.
- */
- dev_num = 3;
- spec->dev_num = 3;
- } else if (spec->dyn_pcm_assign && 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;
-
- if (spec->dyn_pcm_assign) {
- per_pin->pcm = NULL;
- per_pin->pcm_idx = -1;
- } else {
- per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
- per_pin->pcm_idx = pin_idx;
- }
- 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);
- if (is_haswell_plus(codec))
- intel_haswell_fixup_connect_list(codec, pin_nid);
- err = hdmi_read_pin_conn(codec, pin_idx);
- if (err < 0)
- return err;
- spec->num_pins++;
- }
- spec->num_nids++;
-
- return 0;
-}
-
-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);
-
- 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;
- }
-
- 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 int hdmi_parse_codec(struct hda_codec *codec)
-{
- hda_nid_t nid;
- int i, nodes;
-
- nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid);
- if (!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;
-
- caps = get_wcaps(codec, nid);
- type = get_wcaps_type(caps);
-
- if (!(caps & AC_WCAP_DIGITAL))
- continue;
-
- switch (type) {
- case AC_WID_AUD_OUT:
- hdmi_add_cvt(codec, nid);
- break;
- case AC_WID_PIN:
- hdmi_add_pin(codec, nid);
- break;
- }
- }
-
- return 0;
-}
-
-/*
- */
-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))
- return true;
- non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
- mutex_unlock(&codec->spdif_mutex);
- return non_pcm;
-}
-
-/*
- * HDMI callbacks
- */
-
-static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hda_nid_t cvt_nid = hinfo->nid;
- struct hdmi_spec *spec = codec->spec;
- int pin_idx;
- struct hdmi_spec_per_pin *per_pin;
- hda_nid_t pin_nid;
- struct snd_pcm_runtime *runtime = substream->runtime;
- bool non_pcm;
- int pinctl;
- int err;
-
- mutex_lock(&spec->pcm_lock);
- pin_idx = hinfo_to_pin_index(codec, hinfo);
- if (spec->dyn_pcm_assign && pin_idx < 0) {
- /* when dyn_pcm_assign and 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);
- mutex_unlock(&spec->pcm_lock);
- return 0;
- }
-
- if (snd_BUG_ON(pin_idx < 0)) {
- mutex_unlock(&spec->pcm_lock);
- return -EINVAL;
- }
- per_pin = get_pin(spec, pin_idx);
- pin_nid = per_pin->pin_nid;
-
- /* 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, 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;
-
- hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
- mutex_unlock(&per_pin->lock);
- if (spec->dyn_pin_out) {
- pinctl = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl | PIN_OUT);
- }
-
- /* snd_hda_set_dev_select() has been called before */
- err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
- stream_tag, format);
- mutex_unlock(&spec->pcm_lock);
- return err;
-}
-
-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;
-
- if (hinfo->nid) {
- pcm_idx = hinfo_to_pcm_index(codec, hinfo);
- if (snd_BUG_ON(pcm_idx < 0))
- return -EINVAL;
- cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
- if (snd_BUG_ON(cvt_idx < 0))
- return -EINVAL;
- per_cvt = get_cvt(spec, cvt_idx);
-
- snd_BUG_ON(!per_cvt->assigned);
- per_cvt->assigned = 0;
- hinfo->nid = 0;
-
- mutex_lock(&spec->pcm_lock);
- snd_hda_spdif_ctls_unassign(codec, pcm_idx);
- clear_bit(pcm_idx, &spec->pcm_in_use);
- pin_idx = hinfo_to_pin_index(codec, hinfo);
- if (spec->dyn_pcm_assign && pin_idx < 0) {
- mutex_unlock(&spec->pcm_lock);
- return 0;
- }
-
- if (snd_BUG_ON(pin_idx < 0)) {
- mutex_unlock(&spec->pcm_lock);
- return -EINVAL;
- }
- per_pin = get_pin(spec, pin_idx);
-
- if (spec->dyn_pin_out) {
- 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);
- mutex_unlock(&spec->pcm_lock);
- }
-
- return 0;
-}
-
-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 = container_of(hdac, struct hda_codec, core);
- 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 = container_of(hdac, struct hda_codec, core);
- 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 = container_of(hdac, struct hda_codec, core);
- 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 = container_of(hdac, struct hda_codec, core);
- 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;
- int idx;
-
- /*
- * for non-mst mode, pcm number is the same as before
- * for DP MST mode, pcm number is (nid number + dev_num - 1)
- * dev_num is the device entry number in a pin
- *
- */
- for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
- struct hda_pcm *info;
- struct hda_pcm_stream *pstr;
-
- info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
- if (!info)
- return -ENOMEM;
-
- 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];
- pstr->substreams = 1;
- pstr->ops = generic_ops;
- /* pcm number is less than 16 */
- if (spec->pcm_used >= 16)
- break;
- /* other pstr fields are set in open */
- }
-
- return 0;
-}
-
-static void free_hdmi_jack_priv(struct snd_jack *jack)
-{
- struct hdmi_pcm *pcm = jack->private_data;
-
- pcm->jack = NULL;
-}
-
-static int add_hdmi_jack_kctl(struct hda_codec *codec,
- struct hdmi_spec *spec,
- int pcm_idx,
- const char *name)
-{
- struct snd_jack *jack;
- int err;
-
- err = snd_jack_new(codec->card, name, 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_jack(struct hda_codec *codec, int pcm_idx)
-{
- char hdmi_str[32] = "HDMI/DP";
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin;
- struct hda_jack_tbl *jack;
- int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
- bool phantom_jack;
- int ret;
-
- if (pcmdev > 0)
- sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
-
- if (spec->dyn_pcm_assign)
- return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
-
- /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
- /* if !dyn_pcm_assign, it must be non-MST mode.
- * This means pcms and pins are statically mapped.
- * And pcm_idx is pin_idx.
- */
- per_pin = get_pin(spec, pcm_idx);
- phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
- if (phantom_jack)
- strncat(hdmi_str, " Phantom",
- sizeof(hdmi_str) - strlen(hdmi_str) - 1);
- ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
- phantom_jack);
- if (ret < 0)
- return ret;
- jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
- if (jack == NULL)
- return 0;
- /* assign jack->jack to pcm_rec[].jack to
- * align with dyn_pcm_assign mode
- */
- spec->pcm_rec[pcm_idx].jack = jack->jack;
- 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++) {
- 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
- */
- if (spec->dyn_pcm_assign)
- err = snd_hda_create_dig_out_ctls(codec,
- 0, spec->cvt_nids[0],
- HDA_PCM_TYPE_HDMI);
- else {
- struct hdmi_spec_per_pin *per_pin =
- get_pin(spec, pcm_idx);
- err = snd_hda_create_dig_out_ctls(codec,
- per_pin->pin_nid,
- per_pin->mux_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);
-
- 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;
- }
-
- 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 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);
- 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))
- snd_hda_jack_detect_enable_callback(codec, pin_nid,
- codec->jackpoll_interval > 0 ?
- jack_callback : NULL);
- }
- 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 pin_idx, pcm_idx;
-
- if (codec_has_acomp(codec))
- snd_hdac_i915_register_notifier(NULL);
-
- 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;
- if (spec->dyn_pcm_assign)
- snd_device_free(codec->card,
- spec->pcm_rec[pcm_idx].jack);
- else
- spec->pcm_rec[pcm_idx].jack = NULL;
- }
-
- generic_spec_free(codec);
-}
-
-#ifdef CONFIG_PM
-static int generic_hdmi_resume(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int pin_idx;
-
- codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
-
- 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
- .resume = generic_hdmi_resume,
-#endif
-};
-
-static const struct hdmi_ops generic_standard_hdmi_ops = {
- .pin_get_eld = snd_hdmi_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;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
-
- spec->ops = generic_standard_hdmi_ops;
- spec->dev_num = 1; /* initialize to 1 */
- mutex_init(&spec->pcm_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;
- 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;
-}
-
-/*
- * Intel codec parsers and helpers
- */
-
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t conns[4];
- int nconns;
-
- nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
- if (nconns == spec->num_cvts &&
- !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
- return;
-
- /* override pins connection list */
- codec_dbg(codec, "hdmi: haswell: override pin connection 0x%x\n", nid);
- snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
-}
-
-#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 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);
-}
-
-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;
-
- /* we assume only from port-B to port-D */
- if (port < 1 || port > 3)
- return;
-
- switch (codec->core.vendor_id) {
- case 0x80860054: /* ILK */
- case 0x80862804: /* ILK */
- case 0x80862882: /* VLV */
- pin_nid = port + 0x03;
- break;
- default:
- pin_nid = port + 0x04;
- break;
- }
-
- /* skip notification during system suspend (but not in runtime PM);
- * the state will be updated at resume
- */
- if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
- return;
- /* ditto during suspend/resume process itself */
- if (atomic_read(&(codec)->core.in_pm))
- return;
-
- snd_hdac_i915_set_bclk(&codec->bus->core);
- check_presence_and_report(codec, pin_nid, dev_id);
-}
-
-/* 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->i915_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->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
- snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
-}
-
-/* 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, u32 stream_tag, int format)
-{
- haswell_verify_D0(codec, cvt_nid, pin_nid);
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
-}
-
-/* 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) {
- 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);
- }
-}
-
-/* precondition and allocation for Intel codecs */
-static int alloc_intel_hdmi(struct hda_codec *codec)
-{
- /* requires i915 binding */
- if (!codec->bus->core.audio_component) {
- codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
- return -ENODEV;
- }
-
- return alloc_generic_hdmi(codec);
-}
-
-/* parse and post-process for Intel codecs */
-static int parse_intel_hdmi(struct hda_codec *codec)
-{
- int err;
-
- err = hdmi_parse_codec(codec);
- 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)
-{
- struct hdmi_spec *spec;
- int err;
-
- err = alloc_intel_hdmi(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
- codec->dp_mst = true;
- spec->dyn_pcm_assign = true;
- spec->vendor_nid = vendor_nid;
-
- intel_haswell_enable_all_pins(codec, true);
- intel_haswell_fixup_enable_dp12(codec);
-
- /* For Haswell/Broadwell, the controller is also in the power well and
- * can cover the codec power request, and so need not set this flag.
- */
- if (!is_haswell(codec) && !is_broadwell(codec))
- codec->core.link_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;
-
- return parse_intel_hdmi(codec);
-}
-
-static int patch_i915_hsw_hdmi(struct hda_codec *codec)
-{
- return intel_hsw_common_init(codec, INTEL_VENDOR_NID);
-}
-
-static int patch_i915_glk_hdmi(struct hda_codec *codec)
-{
- return intel_hsw_common_init(codec, INTEL_GLK_VENDOR_NID);
-}
-
-/* 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->core.link_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);
- 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
- */
-
-#define Nv_VERB_SET_Channel_Allocation 0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
-#define Nv_VERB_SET_Audio_Protection_On 0xF98
-#define Nv_VERB_SET_Audio_Protection_Off 0xF99
-
-#define nvhdmi_master_con_nid_7x 0x04
-#define nvhdmi_master_pin_nid_7x 0x05
-
-static const hda_nid_t nvhdmi_con_nids_7x[4] = {
- /*front, rear, clfe, rear_surr */
- 0x6, 0x8, 0xa, 0xc,
-};
-
-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 */
- { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- {} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS 16
-#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_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 SUPPORTED_MAXBPS 24
-#define SUPPORTED_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-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_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);
-}
-
-static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int simple_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 hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- 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;
-
- 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)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
- 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- for (i = 0; i < 4; i++) {
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- 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);
-}
-
-static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- int chs;
- 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;
-
- dataDCC2 = 0x2;
-
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- 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,
- spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
-
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
-
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- 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,
- spdif->ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
-
- for (i = 0; i < 4; i++) {
- if (chs == 2)
- channel_id = 0;
- else
- channel_id = i * 2;
-
- /* turn off SPDIF once;
- *otherwise the IEC958 bits won't be updated
- */
- if (codec->spdif_status_reset &&
- (spdif->ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
- /* set the stream id */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- (stream_tag << 4) | channel_id);
- /* set the stream format */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset &&
- (spdif->ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- spdif->ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
- }
-
- nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
-
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-
-static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = nvhdmi_8ch_7x_pcm_close,
- .prepare = nvhdmi_8ch_7x_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;
-
- 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;
- return 0;
-}
-
-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 nvhdmi_7x_8ch_build_controls(struct hda_codec *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;
-
- /* 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_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;
-
- /* Initialize the audio infoframe channel mask and checksum to something
- * valid */
- nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
-
- return 0;
-}
-
-/*
- * 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;
-}
-
-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(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;
-
- return 0;
-}
-
-/*
- * 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.
- *
- * | 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).
- */
-#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, unsigned int format)
-{
- unsigned int value;
-
- /* bits [31:30] contain the trigger and valid bits */
- value = snd_hda_codec_read(codec, NVIDIA_AFG_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, NVIDIA_AFG_NID, 0,
- NVIDIA_SET_SCRATCH0_BYTE0,
- (format >> 0) & 0xff);
- snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
- NVIDIA_SET_SCRATCH0_BYTE1,
- (format >> 8) & 0xff);
-
- /* bits [16:24] are unused */
- snd_hda_codec_write(codec, NVIDIA_AFG_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;
-
- /*
- * Whenever the 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, NVIDIA_AFG_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;
-
- /* notify the HDMI codec of the format change */
- tegra_hdmi_set_format(codec, 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, 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 patch_tegra_hdmi(struct hda_codec *codec)
-{
- int err;
-
- err = patch_generic_hdmi(codec);
- if (err)
- return err;
-
- codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
-
- return 0;
-}
-
-/*
- * 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,
- unsigned char *buf, int *eld_size)
-{
- /* 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 ca,
- int active_channels, int conn_type)
-{
- 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 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 = container_of(hdac, struct hda_codec, core);
- 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 = container_of(hdac, struct hda_codec, core);
- 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,
- bool hbr)
-{
- int hbr_ctl, hbr_ctl_new;
-
- 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, 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, stream_tag, format);
-}
-
-
-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);
- }
-
- return 0;
-}
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- struct hdmi_spec_per_cvt *per_cvt;
- int err, cvt_idx;
-
- err = patch_generic_hdmi(codec);
-
- if (err)
- return err;
-
- codec->patch_ops.init = atihdmi_init;
-
- spec = codec->spec;
-
- 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);
- }
-
- spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
-
- 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);
-}
-
-/*
- * patch entries
- */
-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),
-HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
-/* 17 is known to be absent */
-HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
-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(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(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
-HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch),
-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(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(0x80862800, "Geminilake HDMI", patch_i915_glk_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_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("HDMI HD-audio codec");
-MODULE_ALIAS("snd-hda-codec-intelhdmi");
-MODULE_ALIAS("snd-hda-codec-nvhdmi");
-MODULE_ALIAS("snd-hda-codec-atihdmi");
-
-static struct hda_codec_driver hdmi_driver = {
- .id = snd_hda_id_hdmi,
-};
-
-module_hda_codec_driver(hdmi_driver);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
deleted file mode 100644
index 23475888192b..000000000000
--- a/sound/pci/hda/patch_realtek.c
+++ /dev/null
@@ -1,8161 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * 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@just42.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/dmi.h>
-#include <linux/module.h>
-#include <linux/input.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
-
-/* keep halting ALC5505 DSP, for power saving */
-#define HALT_REALTEK_ALC5505
-
-/* extra amp-initialization sequence types */
-enum {
- ALC_INIT_NONE,
- ALC_INIT_DEFAULT,
- ALC_INIT_GPIO1,
- ALC_INIT_GPIO2,
- ALC_INIT_GPIO3,
-};
-
-enum {
- ALC_HEADSET_MODE_UNKNOWN,
- ALC_HEADSET_MODE_UNPLUGGED,
- ALC_HEADSET_MODE_HEADSET,
- ALC_HEADSET_MODE_MIC,
- ALC_HEADSET_MODE_HEADPHONE,
-};
-
-enum {
- ALC_HEADSET_TYPE_UNKNOWN,
- ALC_HEADSET_TYPE_CTIA,
- ALC_HEADSET_TYPE_OMTP,
-};
-
-enum {
- ALC_KEY_MICMUTE_INDEX,
-};
-
-struct alc_customize_define {
- unsigned int sku_cfg;
- unsigned char port_connectivity;
- unsigned char check_sum;
- unsigned char customization;
- unsigned char external_amp;
- unsigned int enable_pcbeep:1;
- unsigned int platform_type:1;
- unsigned int swap:1;
- unsigned int override:1;
- unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
-};
-
-struct alc_spec {
- struct hda_gen_spec gen; /* must be at head */
-
- /* codec parameterization */
- const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
- unsigned int num_mixers;
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
-
- struct alc_customize_define cdefine;
- unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
-
- /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
- int mute_led_polarity;
- hda_nid_t mute_led_nid;
- hda_nid_t cap_mute_led_nid;
-
- unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */
- unsigned int gpio_mute_led_mask;
- unsigned int gpio_mic_led_mask;
-
- 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);
-#ifdef CONFIG_PM
- void (*power_hook)(struct hda_codec *codec);
-#endif
- void (*shutup)(struct hda_codec *codec);
- void (*reboot_notify)(struct hda_codec *codec);
-
- int init_amp;
- int codec_variant; /* flag for other variants */
- unsigned int has_alc5505_dsp:1;
- unsigned int no_depop_delay: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];
-};
-
-/*
- * COEF access helper functions
- */
-
-static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
- unsigned int coef_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;
-}
-
-#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)
-{
- 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);
-}
-
-#define alc_write_coef_idx(codec, coef_idx, coef_val) \
- alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
-
-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 val = alc_read_coefex_idx(codec, nid, coef_idx);
-
- if (val != -1)
- alc_write_coefex_idx(codec, nid, coef_idx,
- (val & ~mask) | bits_set);
-}
-
-#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)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec->coef0)
- spec->coef0 = alc_read_coef_idx(codec, 0);
- return spec->coef0;
-}
-
-/* coef writes/updates batch */
-struct coef_fw {
- unsigned char nid;
- unsigned char idx;
- unsigned short mask;
- unsigned short val;
-};
-
-#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 void alc_process_coef_fw(struct hda_codec *codec,
- const struct coef_fw *fw)
-{
- 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);
- }
-}
-
-/*
- * Append the given mixer and verb elements for the later use
- * The mixer array is referred in build_controls(), and init_verbs are
- * called in init().
- */
-static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
-{
- if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
- return;
- spec->mixers[spec->num_mixers++] = mix;
-}
-
-/*
- * GPIO setup tables, used in initialization
- */
-/* Enable GPIO mask and set output */
-static const 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 const 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 const 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},
- { }
-};
-
-/*
- * Fix hardware PLL issue
- * On some codecs, the analog PLL gating control must be off while
- * the default value is 1.
- */
-static void alc_fix_pll(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- 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,
- unsigned int coef_idx, unsigned int coef_bit)
-{
- struct alc_spec *spec = codec->spec;
- spec->pll_nid = nid;
- spec->pll_coef_idx = coef_idx;
- spec->pll_coef_bit = coef_bit;
- alc_fix_pll(codec);
-}
-
-/* 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)
-{
- unsigned int val;
- struct snd_kcontrol *kctl;
- struct snd_ctl_elem_value *uctl;
-
- 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 alc880_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- /* For some reason, the res given from ALC880 is broken.
- Here we adjust it properly. */
- snd_hda_jack_unsol_event(codec, res >> 2);
-}
-
-/* Change EAPD to verb control */
-static void alc_fill_eapd_coef(struct hda_codec *codec)
-{
- int coef;
-
- coef = alc_get_coef0(codec);
-
- 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 0x10ec0233:
- case 0x10ec0236:
- case 0x10ec0255:
- case 0x10ec0256:
- case 0x10ec0257:
- case 0x10ec0282:
- case 0x10ec0283:
- case 0x10ec0286:
- case 0x10ec0288:
- case 0x10ec0285:
- case 0x10ec0298:
- case 0x10ec0289:
- alc_update_coef_idx(codec, 0x10, 1<<9, 0);
- break;
- case 0x10ec0275:
- alc_update_coef_idx(codec, 0xe, 0, 1<<0);
- 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:
- 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 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:
- alc_update_coef_idx(codec, 0x7, 1<<5, 0);
- break;
- case 0x10ec0899:
- case 0x10ec0900:
- case 0x10ec1168:
- case 0x10ec1220:
- alc_update_coef_idx(codec, 0x7, 1<<1, 0);
- break;
- }
-}
-
-/* additional initialization for ALC888 variants */
-static void alc888_coef_init(struct hda_codec *codec)
-{
- 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;
- }
-}
-
-/* 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);
-}
-
-/* turn on/off EAPD controls of the codec */
-static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
-{
- /* We currently only handle front, HP */
- static hda_nid_t pins[] = {
- 0x0f, 0x10, 0x14, 0x15, 0x17, 0
- };
- hda_nid_t *p;
- for (p = pins; *p; p++)
- set_eapd(codec, *p, on);
-}
-
-/* generic shutup callback;
- * just turning off EAPD and a little pause for avoiding pop-noise
- */
-static void alc_eapd_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- alc_auto_setup_eapd(codec, false);
- if (!spec->no_depop_delay)
- msleep(200);
- snd_hda_shutup_pins(codec);
-}
-
-/* generic EAPD initialization */
-static void alc_auto_init_amp(struct hda_codec *codec, int type)
-{
- alc_fill_eapd_coef(codec);
- alc_auto_setup_eapd(codec, true);
- 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->core.vendor_id) {
- case 0x10ec0260:
- alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
- break;
- case 0x10ec0880:
- case 0x10ec0882:
- case 0x10ec0883:
- case 0x10ec0885:
- alc_update_coef_idx(codec, 7, 0, 0x2030);
- break;
- case 0x10ec0888:
- alc888_coef_init(codec);
- break;
- }
- break;
- }
-}
-
-
-/*
- * Realtek SSID verification
- */
-
-/* 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 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;
- }
-}
-
-static void alc_fixup_no_depop_delay(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PROBE) {
- spec->no_depop_delay = 1;
- codec->depop_delay = 0;
- }
-}
-
-static int alc_auto_parse_customize_define(struct hda_codec *codec)
-{
- unsigned int ass, tmp, i;
- unsigned nid = 0;
- struct alc_spec *spec = codec->spec;
-
- spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
-
- if (spec->cdefine.fixup) {
- ass = spec->cdefine.sku_cfg;
- if (ass == ALC_FIXUP_SKU_IGNORE)
- return -1;
- goto do_sku;
- }
-
- 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->core.vendor_id == 0x10ec0260)
- nid = 0x17;
- ass = snd_hda_codec_get_pincfg(codec, nid);
-
- if (!(ass & 1)) {
- codec_info(codec, "%s: SKU not ready 0x%08x\n",
- codec->core.chip_name, ass);
- return -1;
- }
-
- /* check sum */
- tmp = 0;
- for (i = 1; i < 16; i++) {
- if ((ass >> i) & 1)
- tmp++;
- }
- if (((ass >> 16) & 0xf) != tmp)
- return -1;
-
- spec->cdefine.port_connectivity = ass >> 30;
- spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
- spec->cdefine.check_sum = (ass >> 16) & 0xf;
- spec->cdefine.customization = ass >> 8;
-do_sku:
- spec->cdefine.sku_cfg = ass;
- spec->cdefine.external_amp = (ass & 0x38) >> 3;
- spec->cdefine.platform_type = (ass & 0x4) >> 2;
- spec->cdefine.swap = (ass & 0x2) >> 1;
- spec->cdefine.override = ass & 0x1;
-
- codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n",
- nid, spec->cdefine.sku_cfg);
- codec_dbg(codec, "SKU: port_connectivity=0x%x\n",
- spec->cdefine.port_connectivity);
- 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
- */
-/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
- * 31 ~ 16 : Manufacture ID
- * 15 ~ 8 : SKU ID
- * 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, const hda_nid_t *ports)
-{
- unsigned int ass, tmp, i;
- unsigned nid;
- struct alc_spec *spec = codec->spec;
-
- if (spec->cdefine.fixup) {
- ass = spec->cdefine.sku_cfg;
- if (ass == ALC_FIXUP_SKU_IGNORE)
- return 0;
- goto do_sku;
- }
-
- 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 */
- /*
- * 31~30 : port connectivity
- * 29~21 : reserve
- * 20 : PCBEEP input
- * 19~16 : Check sum (15:1)
- * 15~1 : Custom
- * 0 : override
- */
- nid = 0x1d;
- if (codec->core.vendor_id == 0x10ec0260)
- nid = 0x17;
- ass = snd_hda_codec_get_pincfg(codec, nid);
- codec_dbg(codec,
- "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n",
- ass, nid);
- if (!(ass & 1))
- return 0;
- if ((ass >> 30) != 1) /* no physical connection */
- return 0;
-
- /* check sum */
- tmp = 0;
- for (i = 1; i < 16; i++) {
- if ((ass >> i) & 1)
- tmp++;
- }
- if (((ass >> 16) & 0xf) != tmp)
- return 0;
-do_sku:
- 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
- * 2 : 0 --> Desktop, 1 --> Laptop
- * 3~5 : External Amplifier control
- * 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;
- }
-
- /* is laptop or Desktop and enable the function "Mute internal speaker
- * when the external headphone out jack is plugged"
- */
- if (!(ass & 0x8000))
- return 1;
- /*
- * 10~8 : Jack location
- * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
- * 14~13: Resvered
- * 15 : 1 --> enable the function "Mute internal speaker
- * when the external headphone out jack is plugged"
- */
- if (!spec->gen.autocfg.hp_pins[0] &&
- !(spec->gen.autocfg.line_out_pins[0] &&
- spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
- hda_nid_t nid;
- tmp = (ass >> 11) & 0x3; /* HP to chassis */
- nid = ports[tmp];
- if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
- spec->gen.autocfg.line_outs))
- return 1;
- spec->gen.autocfg.hp_pins[0] = nid;
- }
- return 1;
-}
-
-/* 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, ports)) {
- struct alc_spec *spec = codec->spec;
- codec_dbg(codec,
- "realtek: Enable default setup for auto mode as fallback\n");
- spec->init_amp = ALC_INIT_DEFAULT;
- }
-}
-
-/*
- */
-
-static void alc_fixup_inv_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->gen.inv_dmic_split = 1;
-}
-
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-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)
-{
- struct alc_spec *spec = codec->spec;
- int i, err;
-
- err = snd_hda_gen_build_controls(codec);
- 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;
- }
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- const 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
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
- return 0;
-}
-
-
-/*
- * Common callbacks
- */
-
-static int alc_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->init_hook)
- spec->init_hook(codec);
-
- alc_fix_pll(codec);
- alc_auto_init_amp(codec, spec->init_amp);
-
- snd_hda_gen_init(codec);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
-
- return 0;
-}
-
-static inline void alc_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec && spec->shutup)
- spec->shutup(codec);
- else
- snd_hda_shutup_pins(codec);
-}
-
-static void alc_reboot_notify(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec && spec->reboot_notify)
- spec->reboot_notify(codec);
- else
- alc_shutup(codec);
-}
-
-/* power down codec to D3 at reboot/shutdown; set as reboot_notify ops */
-static void alc_d3_at_reboot(struct hda_codec *codec)
-{
- snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
- snd_hda_codec_write(codec, codec->core.afg, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- msleep(10);
-}
-
-#define alc_free snd_hda_gen_free
-
-#ifdef CONFIG_PM
-static void alc_power_eapd(struct hda_codec *codec)
-{
- alc_auto_setup_eapd(codec, false);
-}
-
-static int alc_suspend(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
-}
-#endif
-
-#ifdef CONFIG_PM
-static int alc_resume(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec->no_depop_delay)
- msleep(150); /* to avoid pop noise */
- codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-#endif
-
-/*
- */
-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
- .reboot_notify = alc_reboot_notify,
-};
-
-
-#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
-
-/*
- * Rename codecs appropriately from COEF value or subvendor id
- */
-struct alc_codec_rename_table {
- unsigned int vendor_id;
- unsigned short coef_mask;
- unsigned short coef_bits;
- const char *name;
-};
-
-struct alc_codec_rename_pci_table {
- unsigned int codec_vendor_id;
- unsigned short pci_subvendor;
- unsigned short pci_subdevice;
- const char *name;
-};
-
-static 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 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);
- }
-
- 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);
- }
-
- return 0;
-}
-
-
-/*
- * Digital-beep handlers
- */
-#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 beep_white_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),
- {}
-};
-
-static inline int has_cdefine_beep(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;
-}
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define has_cdefine_beep(codec) 0
-#endif
-
-/* 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;
-
- err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
- spec->parse_flags);
- if (err < 0)
- return err;
-
- if (ssid_nids)
- alc_ssid_check(codec, ssid_nids);
-
- err = snd_hda_gen_parse_auto_config(codec, cfg);
- if (err < 0)
- return err;
-
- return 1;
-}
-
-/* common preparation job for alc_spec */
-static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
-{
- struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- int err;
-
- 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->patch_ops = alc_patch_ops;
-
- err = alc_codec_rename_from_preset(codec);
- if (err < 0) {
- kfree(spec);
- return err;
- }
- return 0;
-}
-
-static int alc880_parse_auto_config(struct hda_codec *codec)
-{
- 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);
-}
-
-/*
- * ALC880 fix-ups
- */
-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_VERBS,
- .v.verbs = alc_gpio1_init_verbs,
- },
- [ALC880_FIXUP_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = alc_gpio2_init_verbs,
- },
- [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 },
- { }
- }
- },
- [ALC880_FIXUP_LG_LW25] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x1a, 0x0181344f }, /* line-in */
- { 0x1b, 0x0321403f }, /* headphone */
- { }
- }
- },
- [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 },
- {}
- },
- },
- [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 */
- { }
- },
- },
- [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 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"},
- {}
-};
-
-
-/*
- * OK, here we have finally the patch for ALC880
- */
-static int patch_alc880(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.need_dac_fix = 1;
- spec->gen.beep_nid = 0x01;
-
- codec->patch_ops.unsol_event = alc880_unsol_event;
-
- snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
- alc880_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /* automatic parse from the BIOS config */
- err = alc880_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog)
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-
-/*
- * ALC260 support
- */
-static int alc260_parse_auto_config(struct hda_codec *codec)
-{
- 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);
-}
-
-/*
- * Pin config fixes
- */
-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,
-};
-
-static void alc260_gpio1_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gen.hp_jack_present);
-}
-
-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);
- snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
- }
-}
-
-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 },
- { }
- };
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_apply_pincfgs(codec, pincfgs);
- break;
- case HDA_FIXUP_ACT_PROBE:
- spec->init_amp = ALC_INIT_NONE;
- break;
- }
-}
-
-static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PROBE)
- spec->init_amp = ALC_INIT_NONE;
-}
-
-static void alc260_fixup_fsc_s7020_jwse(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->gen.add_jack_modes = 1;
- spec->gen.hp_mic = 1;
- }
-}
-
-static const struct hda_fixup alc260_fixups[] = {
- [ALC260_FIXUP_HP_DC5750] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x11, 0x90130110 }, /* speaker */
- { }
- }
- },
- [ALC260_FIXUP_HP_PIN_0F] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x0f, 0x01214000 }, /* HP */
- { }
- }
- },
- [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 },
- { }
- },
- },
- [ALC260_FIXUP_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = alc_gpio1_init_verbs,
- },
- [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 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),
- {}
-};
-
-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 patch_alc260(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x07);
- if (err < 0)
- return err;
-
- 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;
-
- spec->shutup = alc_eapd_shutup;
-
- snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
- alc260_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /* automatic parse from the BIOS config */
- err = alc260_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog)
- set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-
-/*
- * 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).
- */
-
-/*
- * Pin config fixes
- */
-enum {
- 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,
- 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_CLEVO_P950,
-};
-
-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);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->core.afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
-
- if (!muted)
- gpiostate |= (1 << pin);
- else
- gpiostate &= ~(1 << pin);
-
- gpiomask = snd_hda_codec_read(codec, codec->core.afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= (1 << pin);
-
- gpiodir = snd_hda_codec_read(codec, codec->core.afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= (1 << pin);
-
-
- snd_hda_codec_write(codec, codec->core.afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_write(codec, codec->core.afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir);
-
- msleep(1);
-
- snd_hda_codec_write(codec, codec->core.afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate);
-}
-
-/* set up GPIO at initialization */
-static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action != HDA_FIXUP_ACT_INIT)
- return;
- alc882_gpio_mute(codec, 0, 0);
- alc882_gpio_mute(codec, 1, 0);
-}
-
-/* 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)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- /* fake the connections during parsing the tree */
- hda_nid_t conn1[2] = { 0x0c, 0x0d };
- hda_nid_t conn2[2] = { 0x0e, 0x0f };
- snd_hda_override_conn_list(codec, 0x14, 2, conn1);
- snd_hda_override_conn_list(codec, 0x15, 2, conn1);
- snd_hda_override_conn_list(codec, 0x18, 2, conn2);
- snd_hda_override_conn_list(codec, 0x1a, 2, conn2);
- } else if (action == HDA_FIXUP_ACT_PROBE) {
- /* restore the connections */
- hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
- snd_hda_override_conn_list(codec, 0x14, 5, conn);
- snd_hda_override_conn_list(codec, 0x15, 5, conn);
- snd_hda_override_conn_list(codec, 0x18, 5, conn);
- snd_hda_override_conn_list(codec, 0x1a, 5, conn);
- }
-}
-
-/* Set VREF on HP pin */
-static void alc889_fixup_mbp_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- static hda_nid_t nids[3] = { 0x14, 0x15, 0x19 };
- int 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;
- 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;
- }
-}
-
-static void alc889_fixup_mac_pins(struct hda_codec *codec,
- const hda_nid_t *nids, int num_nids)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- 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);
- }
- spec->gen.keep_vref_in_automute = 1;
-}
-
-/* Set VREF on speaker pins on imac91 */
-static void alc889_fixup_imac91_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- static hda_nid_t nids[2] = { 0x18, 0x1a };
-
- if (action == HDA_FIXUP_ACT_INIT)
- alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
-}
-
-/* Set VREF on speaker pins on mba11 */
-static void alc889_fixup_mba11_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- static hda_nid_t nids[1] = { 0x18 };
-
- if (action == HDA_FIXUP_ACT_INIT)
- alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
-}
-
-/* Set VREF on speaker pins on mba21 */
-static void alc889_fixup_mba21_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- static hda_nid_t nids[2] = { 0x18, 0x19 };
-
- if (action == HDA_FIXUP_ACT_INIT)
- alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
-}
-
-/* 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)
-{
- 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 void alc_fixup_bass_chmap(struct hda_codec *codec,
- const struct hda_fixup *fix, int action);
-
-/* 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;
-
- 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 rename_ctl(struct hda_codec *codec, const char *oldname,
- const char *newname)
-{
- struct snd_kcontrol *kctl;
-
- kctl = snd_hda_find_mixer_ctl(codec, oldname);
- if (kctl)
- strcpy(kctl->id.name, newname);
-}
-
-static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- 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 alc1220_fixup_clevo_p950(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- hda_nid_t conn1[1] = { 0x0c };
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- 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, 1, conn1);
- snd_hda_override_conn_list(codec, 0x1b, 1, conn1);
-}
-
-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 */
- { }
- }
- },
- [ALC882_FIXUP_LENOVO_Y530] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x15, 0x99130112 }, /* rear int speakers */
- { 0x16, 0x99130111 }, /* subwoofer */
- { }
- }
- },
- [ALC882_FIXUP_PB_M5210] = {
- .type = HDA_FIXUP_PINCTLS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x19, PIN_VREF50 },
- {}
- }
- },
- [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 */
- { }
- }
- },
- [ALC889_FIXUP_CD] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x1c, 0x993301f0 }, /* CD */
- { }
- }
- },
- [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 },
- { }
- }
- },
- [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_VERBS,
- .v.verbs = alc_gpio1_init_verbs,
- },
- [ALC882_FIXUP_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = alc_gpio2_init_verbs,
- },
- [ALC882_FIXUP_GPIO3] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = alc_gpio3_init_verbs,
- },
- [ALC882_FIXUP_ASUS_W2JC] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = alc_gpio1_init_verbs,
- .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_CLEVO_P950] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc1220_fixup_clevo_p950,
- },
-};
-
-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, 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, 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, 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, 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(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),
- 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),
-
- /* 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(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(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
- 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, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950),
- 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 const struct hda_model_fixup alc882_fixup_models[] = {
- {.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 = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
- {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
- {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"},
- {}
-};
-
-/*
- * BIOS auto configuration
- */
-/* almost identical with ALC880 parser... */
-static int alc882_parse_auto_config(struct hda_codec *codec)
-{
- 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);
-}
-
-/*
- */
-static int patch_alc882(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
-
- switch (codec->core.vendor_id) {
- case 0x10ec0882:
- case 0x10ec0885:
- case 0x10ec0900:
- case 0x10ec1220:
- break;
- default:
- /* ALC883 and variants */
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- break;
- }
-
- snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
- alc882_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- alc_auto_parse_customize_define(codec);
-
- if (has_cdefine_beep(codec))
- spec->gen.beep_nid = 0x01;
-
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog && spec->gen.beep_nid)
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-
-/*
- * 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);
-}
-
-/*
- * Pin config fixes
- */
-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 */
- { }
- }
- },
- [ALC262_FIXUP_FSC_S7110] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x15, 0x90170110 }, /* speaker */
- { }
- },
- .chained = true,
- .chain_id = ALC262_FIXUP_BENQ,
- },
- [ALC262_FIXUP_HP_Z200] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x16, 0x99130120 }, /* internal speaker */
- { }
- }
- },
- [ALC262_FIXUP_TYAN] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x14, 0x1993e1f0 }, /* int AUX */
- { }
- }
- },
- [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 },
- {}
- }
- },
- [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 },
- {}
- }
- },
- [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,
- },
-};
-
-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, 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 const struct hda_model_fixup alc262_fixup_models[] = {
- {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
- {}
-};
-
-/*
- */
-static int patch_alc262(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.shared_mic_vref_pin = 0x18;
-
- spec->shutup = alc_eapd_shutup;
-
-#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);
-
- snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
- alc262_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- alc_auto_parse_customize_define(codec);
-
- if (has_cdefine_beep(codec))
- spec->gen.beep_nid = 0x01;
-
- /* automatic parse from the BIOS config */
- err = alc262_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog && spec->gen.beep_nid)
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * 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)
-{
- 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 & ~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;
-}
-
-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)
- },
- { }
-};
-
-/* 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)},
- { }
-};
-
-enum {
- ALC268_FIXUP_INV_DMIC,
- ALC268_FIXUP_HP_EAPD,
- ALC268_FIXUP_SPDIF,
-};
-
-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 */
- {}
- }
- },
-};
-
-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"},
- {}
-};
-
-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
- */
- SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD),
- {}
-};
-
-/*
- * 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);
-}
-
-/*
- */
-static int patch_alc268(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- /* ALC268 has no aa-loopback mixer */
- err = alc_alloc_spec(codec, 0);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.beep_nid = 0x01;
-
- spec->shutup = alc_eapd_shutup;
-
- snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /* 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) {
- add_mixer(spec, alc268_beep_mixer);
- 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);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * ALC269
- */
-
-static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
-};
-
-static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
-};
-
-/* 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_ALC294,
- ALC269_TYPE_ALC700,
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc269_parse_auto_config(struct hda_codec *codec)
-{
- 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;
-
- 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_ALC294:
- case ALC269_TYPE_ALC700:
- ssids = alc269_ssids;
- break;
- default:
- ssids = alc269_ssids;
- break;
- }
-
- return alc_parse_auto_config(codec, alc269_ignore, ssids);
-}
-
-static int find_ext_mic_pin(struct hda_codec *codec);
-
-static void alc286_shutup(struct hda_codec *codec)
-{
- int i;
- int mic_pin = find_ext_mic_pin(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;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- /* 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);
- }
- codec->pins_shutup = 1;
-}
-
-static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
-{
- alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
-}
-
-static void alc269_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- 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);
- }
- snd_hda_shutup_pins(codec);
-}
-
-static 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 */
- {}
-};
-
-static void alc282_restore_default_value(struct hda_codec *codec)
-{
- alc_process_coef_fw(codec, alc282_coefs);
-}
-
-static void alc282_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
- int coef78;
-
- alc282_restore_default_value(codec);
-
- if (!hp_pin)
- return;
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
- coef78 = alc_read_coef_idx(codec, 0x78);
-
- /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
- /* Headphone capless set to high power mode */
- 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);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
- if (hp_pin_sense)
- msleep(100);
-
- /* Headphone capless set to normal mode */
- alc_write_coef_idx(codec, 0x78, coef78);
-}
-
-static void alc282_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
- int coef78;
-
- if (!hp_pin) {
- alc269_shutup(codec);
- return;
- }
-
- 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);
-
- 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);
- snd_hda_shutup_pins(codec);
- alc_write_coef_idx(codec, 0x78, coef78);
-}
-
-static 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 void alc283_restore_default_value(struct hda_codec *codec)
-{
- 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 = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!spec->gen.autocfg.hp_outs) {
- if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
- hp_pin = spec->gen.autocfg.line_out_pins[0];
- }
-
- alc283_restore_default_value(codec);
-
- if (!hp_pin)
- return;
-
- msleep(30);
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
- /* Headphone capless set to high power mode */
- alc_write_coef_idx(codec, 0x43, 0x9004);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- if (hp_pin_sense)
- msleep(85);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
- 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 void alc283_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!spec->gen.autocfg.hp_outs) {
- if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
- hp_pin = spec->gen.autocfg.line_out_pins[0];
- }
-
- if (!hp_pin) {
- alc269_shutup(codec);
- return;
- }
-
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- alc_write_coef_idx(codec, 0x43, 0x9004);
-
- /*depop hp during suspend*/
- alc_write_coef_idx(codec, 0x06, 0x2100);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- if (hp_pin_sense)
- msleep(100);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-
- alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
-
- if (hp_pin_sense)
- msleep(100);
- alc_auto_setup_eapd(codec, false);
- snd_hda_shutup_pins(codec);
- alc_write_coef_idx(codec, 0x43, 0x9614);
-}
-
-static void alc256_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!hp_pin)
- return;
-
- msleep(30);
-
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- if (hp_pin_sense)
- msleep(2);
-
- alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- if (hp_pin_sense)
- msleep(85);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
- if (hp_pin_sense)
- msleep(100);
-
- alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
- alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
-}
-
-static void alc256_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!hp_pin) {
- alc269_shutup(codec);
- return;
- }
-
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- 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);
-
- /* 3k pull low control for Headset jack. */
- /* NOTE: call this before clearing the pin, otherwise codec stalls */
- alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
-
- 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);
- snd_hda_shutup_pins(codec);
-}
-
-static void alc225_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp1_pin_sense, hp2_pin_sense;
-
- if (!hp_pin)
- return;
-
- msleep(30);
-
- 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);
-
- alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
-
- if (hp1_pin_sense)
- 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);
-
- if (hp1_pin_sense || hp2_pin_sense)
- msleep(85);
-
- if (hp1_pin_sense)
- 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);
-
- if (hp1_pin_sense || hp2_pin_sense)
- msleep(100);
-
- alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
- alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
-}
-
-static void alc225_shutup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp1_pin_sense, hp2_pin_sense;
-
- if (!hp_pin) {
- alc269_shutup(codec);
- return;
- }
-
- /* 3k pull low control for Headset jack. */
- alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
-
- 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);
-
- if (hp1_pin_sense)
- 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);
-
- if (hp1_pin_sense || hp2_pin_sense)
- msleep(85);
-
- if (hp1_pin_sense)
- 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);
-
- if (hp1_pin_sense || hp2_pin_sense)
- msleep(100);
-
- alc_auto_setup_eapd(codec, false);
- snd_hda_shutup_pins(codec);
-}
-
-static void alc_default_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!hp_pin)
- return;
-
- msleep(30);
-
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- 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);
-
- snd_hda_codec_write(codec, hp_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
- 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 = spec->gen.autocfg.hp_pins[0];
- bool hp_pin_sense;
-
- if (!hp_pin) {
- alc269_shutup(codec);
- return;
- }
-
- hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-
- 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);
-
- 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);
- snd_hda_shutup_pins(codec);
-}
-
-static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
- unsigned int val)
-{
- 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 int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
-{
- unsigned int val;
-
- 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 alc5505_dsp_halt(struct hda_codec *codec)
-{
- unsigned int val;
-
- 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 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);
-}
-
-static void alc5505_dsp_init(struct hda_codec *codec)
-{
- unsigned int val;
-
- 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
-}
-
-#ifdef HALT_REALTEK_ALC5505
-#define alc5505_dsp_suspend(codec) /* NOP */
-#define alc5505_dsp_resume(codec) /* NOP */
-#else
-#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
-#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
-#endif
-
-#ifdef CONFIG_PM
-static int alc269_suspend(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->has_alc5505_dsp)
- alc5505_dsp_suspend(codec);
- return alc_suspend(codec);
-}
-
-static int alc269_resume(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- 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);
- }
-
- codec->patch_ops.init(codec);
-
- 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);
- }
-
- regcache_sync(codec->core.regmap);
- hda_call_check_power_status(codec, 0x01);
-
- /* 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_led)
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_led);
-
- if (spec->has_alc5505_dsp)
- alc5505_dsp_resume(codec);
-
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static void alc269_fixup_pincfg_no_hp_to_lineout(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->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
-}
-
-static void alc269_fixup_hweq(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_INIT)
- alc_update_coef_idx(codec, 0x1e, 0, 0x80);
-}
-
-static void alc269_fixup_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
-}
-
-static void alc271_fixup_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- static const struct hda_verb verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
- {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
- {}
- };
- unsigned int cfg;
-
- 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);
-}
-
-static void alc269_fixup_pcm_44k(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PROBE)
- return;
-
- /* 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 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);
-}
-
-static void alc269_quanta_automute(struct hda_codec *codec)
-{
- snd_hda_gen_update_outputs(codec);
-
- alc_write_coef_idx(codec, 0x0c, 0x680);
- alc_write_coef_idx(codec, 0x0c, 0x480);
-}
-
-static void alc269_fixup_quanta_mute(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- if (action != HDA_FIXUP_ACT_PROBE)
- return;
- spec->gen.automute_hook = alc269_quanta_automute;
-}
-
-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);
-
- 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);
-}
-
-static void alc269_fixup_x101_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
- spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
- }
-}
-
-
-/* update mute-LED according to the speaker mute state via mic VREF pin */
-static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
-{
- struct hda_codec *codec = private_data;
- struct alc_spec *spec = codec->spec;
- unsigned int pinval;
-
- if (spec->mute_led_polarity)
- enabled = !enabled;
- pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid);
- pinval &= ~AC_PINCTL_VREFEN;
- pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80;
- if (spec->mute_led_nid)
- snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
-}
-
-/* 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)
-{
- 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 alc269_fixup_hp_mute_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
- 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;
- }
-}
-
-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;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->mute_led_polarity = 0;
- spec->mute_led_nid = 0x18;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
- codec->power_filter = led_power_filter;
- }
-}
-
-static void alc269_fixup_hp_mute_led_mic2(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->mute_led_polarity = 0;
- spec->mute_led_nid = 0x19;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
- codec->power_filter = led_power_filter;
- }
-}
-
-/* update LED status via GPIO */
-static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
- bool enabled)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int oldval = spec->gpio_led;
-
- if (spec->mute_led_polarity)
- enabled = !enabled;
-
- if (enabled)
- spec->gpio_led &= ~mask;
- else
- spec->gpio_led |= mask;
- if (spec->gpio_led != oldval)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_led);
-}
-
-/* turn on/off mute LED via GPIO per vmaster hook */
-static void alc_fixup_gpio_mute_hook(void *private_data, int enabled)
-{
- struct hda_codec *codec = private_data;
- struct alc_spec *spec = codec->spec;
-
- alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
-}
-
-/* turn on/off mic-mute LED via GPIO per capture hook */
-static void alc_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct alc_spec *spec = codec->spec;
-
- if (ucontrol)
- alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
- ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1]);
-}
-
-static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x08;
- spec->gpio_mic_led_mask = 0x10;
- snd_hda_add_verbs(codec, gpio_init);
- }
-}
-
-static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x22 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x22 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x02;
- spec->gpio_mic_led_mask = 0x20;
- snd_hda_add_verbs(codec, gpio_init);
- }
-}
-
-/* turn on/off mic-mute LED per capture hook */
-static void alc269_fixup_hp_cap_mic_mute_hook(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int pinval, enable, disable;
-
- pinval = snd_hda_codec_get_pin_target(codec, spec->cap_mute_led_nid);
- pinval &= ~AC_PINCTL_VREFEN;
- enable = pinval | AC_PINCTL_VREF_80;
- disable = pinval | AC_PINCTL_VREF_HIZ;
-
- if (!ucontrol)
- return;
-
- if (ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1])
- pinval = disable;
- else
- pinval = enable;
-
- if (spec->cap_mute_led_nid)
- snd_hda_set_pin_ctl_cache(codec, spec->cap_mute_led_nid, pinval);
-}
-
-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;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x08 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x08 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x08;
- spec->cap_mute_led_nid = 0x18;
- snd_hda_add_verbs(codec, gpio_init);
- codec->power_filter = led_power_filter;
- }
-}
-
-static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- /* Like hp_gpio_mic1_led, but also needs GPIO4 low to enable headphone amp */
- struct alc_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x08;
- spec->cap_mute_led_nid = 0x18;
- snd_hda_add_verbs(codec, gpio_init);
- codec->power_filter = led_power_filter;
- }
-}
-
-#if IS_REACHABLE(INPUT)
-static void gpio2_mic_hotkey_event(struct hda_codec *codec,
- struct hda_jack_callback *event)
-{
- struct alc_spec *spec = codec->spec;
-
- /* 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);
-}
-
-static int alc_register_micmute_input_device(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- spec->kb_dev = input_allocate_device();
- if (!spec->kb_dev) {
- codec_err(codec, "Out of memory (input_allocate_device)\n");
- return -ENOMEM;
- }
-
- spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
-
- 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);
-
- 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;
- }
-
- return 0;
-}
-
-static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- /* GPIO1 = set according to SKU external amp
- GPIO2 = mic mute hotkey
- GPIO3 = mute LED
- GPIO4 = mic mute LED */
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a },
- { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 },
- {}
- };
-
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- if (alc_register_micmute_input_device(codec) != 0)
- return;
-
- snd_hda_add_verbs(codec, gpio_init);
- 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);
-
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x08;
- spec->gpio_mic_led_mask = 0x10;
- return;
- }
-
- if (!spec->kb_dev)
- return;
-
- switch (action) {
- case HDA_FIXUP_ACT_PROBE:
- spec->init_amp = ALC_INIT_DEFAULT;
- break;
- case HDA_FIXUP_ACT_FREE:
- input_unregister_device(spec->kb_dev);
- spec->kb_dev = NULL;
- }
-}
-
-static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- /* Line2 = mic mute hotkey
- GPIO2 = mic mute LED */
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
- {}
- };
-
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- if (alc_register_micmute_input_device(codec) != 0)
- return;
-
- snd_hda_add_verbs(codec, gpio_init);
- snd_hda_jack_detect_enable_callback(codec, 0x1b,
- gpio2_mic_hotkey_event);
-
- spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 0;
- spec->gpio_mic_led_mask = 0x04;
- return;
- }
-
- if (!spec->kb_dev)
- return;
-
- switch (action) {
- case HDA_FIXUP_ACT_PROBE:
- spec->init_amp = ALC_INIT_DEFAULT;
- break;
- 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 void alc269_fixup_hp_line1_mic1_led(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->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
- spec->mute_led_polarity = 0;
- spec->mute_led_nid = 0x1a;
- spec->cap_mute_led_nid = 0x18;
- spec->gen.vmaster_mute_enum = 1;
- codec->power_filter = led_power_filter;
- }
-}
-
-static 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),
- {}
-};
-
-static void alc_headset_mode_unplugged(struct hda_codec *codec)
-{
- static struct coef_fw coef0255[] = {
- 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 struct coef_fw coef0255_1[] = {
- WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
- {}
- };
- static struct coef_fw coef0256[] = {
- WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
- {}
- };
- static 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 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 struct coef_fw coef0298[] = {
- UPDATE_COEF(0x19, 0x1300, 0x0300),
- {}
- };
- static struct coef_fw coef0292[] = {
- WRITE_COEF(0x76, 0x000e),
- WRITE_COEF(0x6c, 0x2400),
- WRITE_COEF(0x18, 0x7308),
- WRITE_COEF(0x6b, 0xc429),
- {}
- };
- static 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 struct coef_fw coef0668[] = {
- WRITE_COEF(0x15, 0x0d40),
- WRITE_COEF(0xb7, 0x802b),
- {}
- };
- static struct coef_fw coef0225[] = {
- UPDATE_COEF(0x63, 3<<14, 0),
- {}
- };
- static 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),
- {}
- };
-
- switch (codec->core.vendor_id) {
- case 0x10ec0255:
- alc_process_coef_fw(codec, coef0255_1);
- alc_process_coef_fw(codec, coef0255);
- break;
- case 0x10ec0236:
- case 0x10ec0256:
- alc_process_coef_fw(codec, coef0256);
- alc_process_coef_fw(codec, coef0255);
- 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, 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");
-}
-
-
-static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
- hda_nid_t mic_pin)
-{
- static struct coef_fw coef0255[] = {
- WRITE_COEFEX(0x57, 0x03, 0x8aa6),
- WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
- {}
- };
- static struct coef_fw coef0233[] = {
- UPDATE_COEF(0x35, 0, 1<<14),
- WRITE_COEF(0x06, 0x2100),
- WRITE_COEF(0x1a, 0x0021),
- WRITE_COEF(0x26, 0x008c),
- {}
- };
- static 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 struct coef_fw coef0292[] = {
- WRITE_COEF(0x19, 0xa208),
- WRITE_COEF(0x2e, 0xacf0),
- {}
- };
- static 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 struct coef_fw coef0688[] = {
- WRITE_COEF(0xb7, 0x802b),
- WRITE_COEF(0xb5, 0x1040),
- UPDATE_COEF(0xc3, 0, 1<<12),
- {}
- };
- static 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 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 0x10ec0236:
- case 0x10ec0255:
- case 0x10ec0256:
- 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 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);
- /* fallthru */
- 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 void alc_headset_mode_default(struct hda_codec *codec)
-{
- static 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 struct coef_fw coef0255[] = {
- WRITE_COEF(0x45, 0xc089),
- WRITE_COEF(0x45, 0xc489),
- WRITE_COEFEX(0x57, 0x03, 0x8ea6),
- WRITE_COEF(0x49, 0x0049),
- {}
- };
- static struct coef_fw coef0233[] = {
- WRITE_COEF(0x06, 0x2100),
- WRITE_COEF(0x32, 0x4ea3),
- {}
- };
- static 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 struct coef_fw coef0292[] = {
- WRITE_COEF(0x76, 0x000e),
- WRITE_COEF(0x6c, 0x2400),
- WRITE_COEF(0x6b, 0xc429),
- WRITE_COEF(0x18, 0x7308),
- {}
- };
- static 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 struct coef_fw coef0688[] = {
- WRITE_COEF(0x11, 0x0041),
- WRITE_COEF(0x15, 0x0d40),
- WRITE_COEF(0xb7, 0x802b),
- {}
- };
- static struct coef_fw coef0274[] = {
- WRITE_COEF(0x45, 0x4289),
- UPDATE_COEF(0x4a, 0x0010, 0x0010),
- UPDATE_COEF(0x6b, 0x0f00, 0),
- UPDATE_COEF(0x49, 0x0300, 0x0300),
- {}
- };
-
- 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 0x10ec0236:
- case 0x10ec0255:
- case 0x10ec0256:
- alc_process_coef_fw(codec, coef0255);
- 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");
-}
-
-/* Iphone type */
-static void alc_headset_mode_ctia(struct hda_codec *codec)
-{
- int val;
-
- static struct coef_fw coef0255[] = {
- WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
- WRITE_COEF(0x1b, 0x0c2b),
- WRITE_COEFEX(0x57, 0x03, 0x8ea6),
- {}
- };
- static struct coef_fw coef0256[] = {
- WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
- WRITE_COEF(0x1b, 0x0c6b),
- WRITE_COEFEX(0x57, 0x03, 0x8ea6),
- {}
- };
- static struct coef_fw coef0233[] = {
- WRITE_COEF(0x45, 0xd429),
- WRITE_COEF(0x1b, 0x0c2b),
- WRITE_COEF(0x32, 0x4ea3),
- {}
- };
- static 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 struct coef_fw coef0292[] = {
- WRITE_COEF(0x6b, 0xd429),
- WRITE_COEF(0x76, 0x0008),
- WRITE_COEF(0x18, 0x7388),
- {}
- };
- static 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 struct coef_fw coef0688[] = {
- WRITE_COEF(0x11, 0x0001),
- WRITE_COEF(0x15, 0x0d60),
- WRITE_COEF(0xc3, 0x0000),
- {}
- };
- static struct coef_fw coef0225_1[] = {
- UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
- UPDATE_COEF(0x63, 3<<14, 2<<14),
- {}
- };
- static struct coef_fw coef0225_2[] = {
- UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
- UPDATE_COEF(0x63, 3<<14, 1<<14),
- {}
- };
-
- switch (codec->core.vendor_id) {
- case 0x10ec0255:
- alc_process_coef_fw(codec, coef0255);
- break;
- case 0x10ec0236:
- case 0x10ec0256:
- 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");
-}
-
-/* Nokia type */
-static void alc_headset_mode_omtp(struct hda_codec *codec)
-{
- static struct coef_fw coef0255[] = {
- WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
- WRITE_COEF(0x1b, 0x0c2b),
- WRITE_COEFEX(0x57, 0x03, 0x8ea6),
- {}
- };
- static struct coef_fw coef0256[] = {
- WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
- WRITE_COEF(0x1b, 0x0c6b),
- WRITE_COEFEX(0x57, 0x03, 0x8ea6),
- {}
- };
- static struct coef_fw coef0233[] = {
- WRITE_COEF(0x45, 0xe429),
- WRITE_COEF(0x1b, 0x0c2b),
- WRITE_COEF(0x32, 0x4ea3),
- {}
- };
- static 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 struct coef_fw coef0292[] = {
- WRITE_COEF(0x6b, 0xe429),
- WRITE_COEF(0x76, 0x0008),
- WRITE_COEF(0x18, 0x7388),
- {}
- };
- static 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 struct coef_fw coef0688[] = {
- WRITE_COEF(0x11, 0x0001),
- WRITE_COEF(0x15, 0x0d50),
- WRITE_COEF(0xc3, 0x0000),
- {}
- };
- static struct coef_fw coef0225[] = {
- UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
- UPDATE_COEF(0x63, 3<<14, 2<<14),
- {}
- };
-
- switch (codec->core.vendor_id) {
- case 0x10ec0255:
- alc_process_coef_fw(codec, coef0255);
- break;
- case 0x10ec0236:
- case 0x10ec0256:
- 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 alc_determine_headset_type(struct hda_codec *codec)
-{
- int val;
- bool is_ctia = false;
- struct alc_spec *spec = codec->spec;
- static 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 struct coef_fw coef0288[] = {
- UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */
- {}
- };
- static 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 struct coef_fw coef0293[] = {
- UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */
- WRITE_COEF(0x45, 0xD429), /* Set to ctia type */
- {}
- };
- static struct coef_fw coef0688[] = {
- WRITE_COEF(0x11, 0x0001),
- WRITE_COEF(0xb7, 0x802b),
- WRITE_COEF(0x15, 0x0d60),
- WRITE_COEF(0xc3, 0x0c00),
- {}
- };
- static struct coef_fw coef0274[] = {
- UPDATE_COEF(0x4a, 0x0010, 0),
- UPDATE_COEF(0x4a, 0x8000, 0),
- WRITE_COEF(0x45, 0xd289),
- UPDATE_COEF(0x49, 0x0300, 0x0300),
- {}
- };
-
- switch (codec->core.vendor_id) {
- case 0x10ec0236:
- case 0x10ec0255:
- case 0x10ec0256:
- alc_process_coef_fw(codec, coef0255);
- msleep(300);
- val = alc_read_coef_idx(codec, 0x46);
- is_ctia = (val & 0x0070) == 0x0070;
- break;
- case 0x10ec0234:
- case 0x10ec0274:
- case 0x10ec0294:
- alc_process_coef_fw(codec, coef0274);
- msleep(80);
- 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);
-
- 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;
- }
-
- 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;
-}
-
-static void alc_update_headset_mode(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
- hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
-
- int new_headset_mode;
-
- 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;
-
- if (new_headset_mode == spec->current_headset_mode) {
- snd_hda_gen_update_outputs(codec);
- return;
- }
-
- switch (new_headset_mode) {
- case ALC_HEADSET_MODE_UNPLUGGED:
- alc_headset_mode_unplugged(codec);
- 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;
-
- snd_hda_gen_update_outputs(codec);
-}
-
-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 void alc_update_headset_jack_cb(struct hda_codec *codec,
- struct hda_jack_callback *jack)
-{
- struct alc_spec *spec = codec->spec;
- spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
- snd_hda_gen_hp_automute(codec, jack);
-}
-
-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;
-
- /* 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;
- }
-
- 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;
-}
-
-static void alc_fixup_headset_mode(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- 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:
- spec->current_headset_mode = 0;
- alc_update_headset_mode(codec);
- break;
- }
-}
-
-static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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 alc255_set_default_jack_type(struct hda_codec *codec)
-{
- /* Set to iphone type */
- static 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 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 0x10ec0236:
- case 0x10ec0256:
- alc_process_coef_fw(codec, alc256fw);
- break;
- }
- msleep(30);
-}
-
-static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- alc255_set_default_jack_type(codec);
- }
- alc_fixup_headset_mode(codec, fix, action);
-}
-
-static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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 void alc288_update_headset_jack_cb(struct hda_codec *codec,
- struct hda_jack_callback *jack)
-{
- struct alc_spec *spec = codec->spec;
- int present;
-
- alc_update_headset_jack_cb(codec, jack);
- /* Headset Mic enable or disable, only for Dell Dino */
- present = spec->gen.hp_jack_present ? 0x40 : 0;
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- present);
-}
-
-static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- alc_fixup_headset_mode(codec, fix, action);
- if (action == HDA_FIXUP_ACT_PROBE) {
- struct alc_spec *spec = codec->spec;
- spec->gen.hp_automute_hook = alc288_update_headset_jack_cb;
- }
-}
-
-static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- struct alc_spec *spec = codec->spec;
- spec->gen.auto_mute_via_amp = 1;
- }
-}
-
-static void alc_no_shutup(struct hda_codec *codec)
-{
-}
-
-static void alc_fixup_no_shutup(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PROBE) {
- struct alc_spec *spec = codec->spec;
- spec->shutup = alc_no_shutup;
- }
-}
-
-static void alc_fixup_disable_aamix(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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;
- }
-}
-
-/* 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)
-{
- static const struct hda_pintbl pincfgs[] = {
- { 0x16, 0x21211010 }, /* dock headphone */
- { 0x19, 0x21a11010 }, /* dock mic */
- { }
- };
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->shutup = alc_no_shutup; /* reduce click noise */
- spec->reboot_notify = alc_d3_at_reboot; /* reduce noise */
- spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
- codec->power_save_node = 0; /* avoid click noises */
- snd_hda_apply_pincfgs(codec, pincfgs);
- }
-}
-
-static void alc_shutup_dell_xps13(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int hp_pin = spec->gen.autocfg.hp_pins[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);
-}
-
-static void alc_fixup_dell_xps13(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;
-
- 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);
- break;
- case HDA_FIXUP_ACT_PROBE:
- spec->shutup = alc_shutup_dell_xps13;
-
- /* 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;
- }
- }
- break;
- }
-}
-
-static void alc_fixup_headset_mode_alc662(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
- spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */
-
- /* 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 void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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);
-}
-
-/* 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;
-
- 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;
- }
-
- return 0;
-}
-
-static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PROBE) {
- int mic_pin = find_ext_mic_pin(codec);
- int hp_pin = spec->gen.autocfg.hp_pins[0];
-
- if (snd_BUG_ON(!mic_pin || !hp_pin))
- return;
- snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
- }
-}
-
-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;
-
- /* The mic boosts on level 2 and 3 are too noisy
- on the internal mic input.
- Therefore limit the boost to 0 or 1. */
-
- if (action != HDA_FIXUP_ACT_PROBE)
- return;
-
- 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;
-
- 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 void alc283_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);
-
- vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
-
- msleep(600);
- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- vref);
-}
-
-static void alc283_fixup_chromebook(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- 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;
- }
-}
-
-static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- 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;
- }
-}
-
-/* mute tablet speaker pin (0x14) via dock plugging in addition */
-static void asus_tx300_automute(struct hda_codec *codec)
-{
- 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 alc282_fixup_asus_tx300(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- /* TX300 needs to set up GPIO2 for the speaker amp */
- static const struct hda_verb gpio2_verbs[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
- { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
- {}
- };
- static const struct hda_pintbl dock_pins[] = {
- { 0x1b, 0x21114000 }, /* dock speaker pin */
- {}
- };
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_add_verbs(codec, gpio2_verbs);
- 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_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;
- }
-}
-
-static void alc290_fixup_mono_speakers(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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. */
- hda_nid_t conn1[2] = { 0x0c };
- snd_hda_override_conn_list(codec, 0x14, 1, conn1);
- snd_hda_override_conn_list(codec, 0x15, 1, conn1);
- }
-}
-
-static void alc298_fixup_speaker_volume(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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. */
-
- hda_nid_t conn1[1] = { 0x0c };
- snd_hda_override_conn_list(codec, 0x17, 1, conn1);
- }
-}
-
-/* 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;
-
- 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->gen.hp_jack_present);
-}
-
-/* Manage GPIOs for HP EliteBook Folio 9480m.
- *
- * GPIO4 is the headphone amplifier power control
- * GPIO3 is the audio output mute indicator LED
- */
-
-static void alc280_fixup_hp_9480m(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- struct alc_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- /* Set the hooks to turn the headphone amp on/off
- * as needed
- */
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook;
-
- /* The GPIOs are currently off */
- spec->gpio_led = 0;
-
- /* GPIO3 is connected to the output mute LED,
- * high is on, low is off
- */
- spec->mute_led_polarity = 0;
- spec->gpio_mute_led_mask = 0x08;
-
- /* Initialize GPIO configuration */
- snd_hda_add_verbs(codec, gpio_init);
- }
-}
-
-static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- 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;
- }
-}
-
-/* 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)
-{
- struct alc_spec *spec = codec->spec;
- static hda_nid_t preferred_pairs[] = {
- 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02,
- 0
- };
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- spec->gen.preferred_dacs = preferred_pairs;
-}
-
-/* for hda_fixup_thinkpad_acpi() */
-#include "thinkpad_helper.c"
-
-/* for dell wmi mic mute led */
-#include "dell_wmi_helper.c"
-
-enum {
- ALC269_FIXUP_SONY_VAIO,
- ALC275_FIXUP_SONY_VAIO_GPIO2,
- ALC269_FIXUP_DELL_M101Z,
- ALC269_FIXUP_SKU_IGNORE,
- ALC269_FIXUP_ASUS_G73JW,
- 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,
- 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_GPIO_LED,
- ALC269_FIXUP_HP_GPIO_MIC1_LED,
- ALC269_FIXUP_HP_LINE1_MIC1_LED,
- ALC269_FIXUP_INV_DMIC,
- ALC269_FIXUP_LENOVO_DOCK,
- 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_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,
- 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_DELL_WMI_MIC_MUTE_LED,
- ALC282_FIXUP_ASPIRE_V5_PINS,
- 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,
- ALC288_FIXUP_DELL_HEADSET_MODE,
- ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC288_FIXUP_DELL_XPS_13_GPIO6,
- ALC288_FIXUP_DELL_XPS_13,
- ALC288_FIXUP_DISABLE_AAMIX,
- ALC292_FIXUP_DELL_E7X,
- ALC292_FIXUP_DISABLE_AAMIX,
- ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
- ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
- ALC275_FIXUP_DELL_XPS,
- ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
- ALC293_FIXUP_LENOVO_SPK_NOISE,
- ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
- ALC255_FIXUP_DELL_SPK_NOISE,
- ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC280_FIXUP_HP_HEADSET_MIC,
- ALC221_FIXUP_HP_FRONT_MIC,
- ALC292_FIXUP_TPT460,
- ALC298_FIXUP_SPK_VOLUME,
- ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
- ALC269_FIXUP_ATIV_BOOK_8,
- 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,
- ALC294_FIXUP_LENOVO_MIC_LOCATION,
- ALC700_FIXUP_INTEL_REFERENCE,
- ALC274_FIXUP_DELL_BIND_DACS,
- ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
-};
-
-static const struct hda_fixup alc269_fixups[] = {
- [ALC269_FIXUP_SONY_VAIO] = {
- .type = HDA_FIXUP_PINCTLS,
- .v.pins = (const struct hda_pintbl[]) {
- {0x19, PIN_VREFGRD},
- {}
- }
- },
- [ALC275_FIXUP_SONY_VAIO_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x04},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
- { }
- },
- .chained = true,
- .chain_id = ALC269_FIXUP_SONY_VAIO
- },
- [ALC269_FIXUP_DELL_M101Z] = {
- .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_SKU_IGNORE] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc_fixup_sku_ignore,
- },
- [ALC269_FIXUP_ASUS_G73JW] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x17, 0x99130111 }, /* subwoofer */
- { }
- }
- },
- [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,
- },
- [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_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_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_DELL_WMI_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
- },
- [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,
- },
- [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 = hda_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_DELL_WMI_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_DELL_WMI_MIC_MUTE_LED] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc_fixup_dell_wmi,
- },
- [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 },
- { },
- },
- },
- [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,
- },
- [ALC288_FIXUP_DELL_HEADSET_MODE] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc_fixup_headset_mode_dell_alc288,
- .chained = true,
- .chain_id = ALC255_FIXUP_DELL_WMI_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_DELL_XPS_13_GPIO6] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x40},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x40},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
- { }
- },
- .chained = true,
- .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE
- },
- [ALC288_FIXUP_DISABLE_AAMIX] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc_fixup_disable_aamix,
- .chained = true,
- .chain_id = ALC288_FIXUP_DELL_XPS_13_GPIO6
- },
- [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] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc_fixup_dell_xps13,
- .chained = true,
- .chain_id = ALC292_FIXUP_DISABLE_AAMIX
- },
- [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},
- {}
- }
- },
- [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = {
- .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, 0x1737},
- {}
- },
- .chained = true,
- .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
- },
- [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,
- },
- [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_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 = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
- },
- [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,
- },
- [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_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_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- /* Set up GPIO2 for the speaker amp */
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
- { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
- {}
- },
- },
- [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,
- },
- [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 },
- { }
- },
- },
- [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
- },
-};
-
-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, 0x080d, "Acer Aspire V5-122P", 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, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
- SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
- 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, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
- 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, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
- 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, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
- 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, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY),
- /* ALC282 */
- 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, 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, 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", ALC280_FIXUP_HP_DOCK_PINS),
- SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS),
- 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, 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),
- /* ALC290 */
- SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- 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, 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, 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, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- 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, 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, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
- 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, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
- SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
- SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
- 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, 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, 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, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC),
- SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC),
- 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, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
- 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, 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, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC),
- 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, 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(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(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, 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(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
- SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
- 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(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
- 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, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", 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, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
- SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
- 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, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
- SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
- 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, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
- 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, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
- SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
- SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
-
-#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
- {}
-};
-
-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),
- {}
-};
-
-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_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 = 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 = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
- {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"},
- {}
-};
-#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(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(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60140},
- {0x14, 0x90170110},
- {0x21, 0x02211020}),
- SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60140},
- {0x14, 0x90170150},
- {0x21, 0x02211020}),
- 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, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60130},
- {0x14, 0x90170110},
- {0x1b, 0x01011020},
- {0x21, 0x0221101f}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60160},
- {0x14, 0x90170120},
- {0x21, 0x02211030}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60170},
- {0x14, 0x90170120},
- {0x21, 0x02211030}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell Inspiron 5468", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x90a60180},
- {0x14, 0x90170120},
- {0x21, 0x02211030}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0xb7a60130},
- {0x14, 0x90170110},
- {0x21, 0x02211020}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC256_STANDARD_PINS),
- 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(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
- {0x12, 0xb7a60130},
- {0x13, 0xb8a61140},
- {0x16, 0x90170110},
- {0x21, 0x04211020}),
- 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(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(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6,
- {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(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(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
- ALC225_STANDARD_PINS,
- {0x12, 0xb7a60130},
- {0x17, 0x90170110}),
- {}
-};
-
-static void alc269_fill_coef(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int val;
-
- 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_get_coef0(codec) & 0x00ff) == 0x016) {
- alc_write_coef_idx(codec, 0xf, 0x960b);
- alc_write_coef_idx(codec, 0xe, 0x8814);
- }
-
- if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
- /* Power up output pin */
- alc_update_coef_idx(codec, 0x04, 0, 1<<11);
- }
-
- if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
- val = alc_read_coef_idx(codec, 0xd);
- if (val != -1 && (val & 0x0c00) >> 10 != 0x1) {
- /* Capless ramp up clock control */
- alc_write_coef_idx(codec, 0xd, val | (1<<10));
- }
- val = alc_read_coef_idx(codec, 0x17);
- if (val != -1 && (val & 0x01c0) >> 6 != 0x4) {
- /* Class D power on reset */
- alc_write_coef_idx(codec, 0x17, val | (1<<7));
- }
- }
-
- /* HP */
- alc_update_coef_idx(codec, 0x4, 0, 1<<11);
-}
-
-/*
- */
-static int patch_alc269(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.shared_mic_vref_pin = 0x18;
- codec->power_save_node = 1;
-
-#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;
-
- snd_hda_pick_fixup(codec, alc269_fixup_models,
- alc269_fixup_tbl, alc269_fixups);
- snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
- snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
- alc269_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- alc_auto_parse_customize_define(codec);
-
- if (has_cdefine_beep(codec))
- spec->gen.beep_nid = 0x01;
-
- 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);
- }
- if (err < 0)
- goto error;
- spec->shutup = alc269_shutup;
- spec->init_hook = alc269_fill_coef;
- alc269_fill_coef(codec);
- break;
-
- 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;
- spec->shutup = alc286_shutup;
- break;
- case 0x10ec0298:
- spec->codec_variant = ALC269_TYPE_ALC298;
- break;
- case 0x10ec0255:
- spec->codec_variant = ALC269_TYPE_ALC255;
- break;
- case 0x10ec0236:
- case 0x10ec0256:
- 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 */
- alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
- break;
- case 0x10ec0257:
- spec->codec_variant = ALC269_TYPE_ALC257;
- spec->gen.mixer_nid = 0;
- break;
- case 0x10ec0215:
- case 0x10ec0285:
- case 0x10ec0289:
- 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 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 */
- break;
- case 0x10ec0700:
- case 0x10ec0701:
- case 0x10ec0703:
- 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 */
- break;
-
- }
-
- if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
- spec->has_alc5505_dsp = 1;
- spec->init_hook = alc5505_dsp_init;
- }
-
- /* automatic parse from the BIOS config */
- err = alc269_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid)
- set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * ALC861
- */
-
-static int alc861_parse_auto_config(struct hda_codec *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);
-}
-
-/* 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,
-};
-
-/* 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;
- unsigned int val;
-
- if (action != HDA_FIXUP_ACT_INIT)
- return;
- 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;
-}
-
-/* suppress the jack-detection */
-static void alc_fixup_no_jack_detect(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- codec->no_jack_detect = 1;
-}
-
-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 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(0x1584, 0x2b01, "Haier W18", ALC861_FIXUP_AMP_VREF_0F),
- SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", 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 err;
-
- err = alc_alloc_spec(codec, 0x15);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.beep_nid = 0x23;
-
-#ifdef CONFIG_PM
- spec->power_hook = alc_power_eapd;
-#endif
-
- snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /* automatic parse from the BIOS config */
- err = alc861_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog)
- set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * ALC861-VD support
- *
- * Based on ALC882
- *
- * In addition, an independent DAC
- */
-static int alc861vd_parse_auto_config(struct hda_codec *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);
-}
-
-enum {
- ALC660VD_FIX_ASUS_GPIO1,
- ALC861VD_FIX_DALLAS,
-};
-
-/* exclude VREF80 */
-static void alc861vd_fixup_dallas(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
- snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
- }
-}
-
-static const struct hda_fixup alc861vd_fixups[] = {
- [ALC660VD_FIX_ASUS_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- /* reset GPIO1 */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
- }
- },
- [ALC861VD_FIX_DALLAS] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc861vd_fixup_dallas,
- },
-};
-
-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;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
- spec->gen.beep_nid = 0x23;
-
- spec->shutup = alc_eapd_shutup;
-
- snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- /* automatic parse from the BIOS config */
- err = alc861vd_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog)
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * ALC662 support
- *
- * ALC662 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).
- */
-
-/*
- * BIOS auto configuration
- */
-
-static int alc662_parse_auto_config(struct hda_codec *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;
-
- 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
- ssids = alc662_ssids;
- return alc_parse_auto_config(codec, alc662_ignore, ssids);
-}
-
-static void alc272_fixup_mario(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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 */
- { }
-};
-
-/* override the 2.1 chmap */
-static void alc_fixup_bass_chmap(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- 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;
- }
-}
-
-/* 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;
- if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_led)
- return AC_PWRST_D0;
- return power_state;
-}
-
-static void alc662_fixup_led_gpio1(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
- static const struct hda_verb gpio_init[] = {
- { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
- { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
- {}
- };
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
- spec->gpio_led = 0;
- spec->mute_led_polarity = 1;
- spec->gpio_mute_led_mask = 0x01;
- snd_hda_add_verbs(codec, gpio_init);
- codec->power_filter = gpio_led_power_filter;
- }
-}
-
-static void alc662_usi_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);
-
- 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);
-}
-
-static void alc662_fixup_usi_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
- spec->gen.hp_automute_hook = alc662_usi_automute_hook;
- }
-}
-
-static 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),
- {}
-};
-
-static void alc668_restore_default_value(struct hda_codec *codec)
-{
- alc_process_coef_fw(codec, alc668_coefs);
-}
-
-enum {
- ALC662_FIXUP_ASPIRE,
- ALC662_FIXUP_LED_GPIO1,
- ALC662_FIXUP_IDEAPAD,
- ALC272_FIXUP_MARIO,
- 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,
- 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,
-};
-
-static const struct hda_fixup alc662_fixups[] = {
- [ALC662_FIXUP_ASPIRE] = {
- .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] = {
- .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_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,
- },
- [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,
- },
-};
-
-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(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(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
- SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
- SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
- SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
- 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, 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, 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, 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 = ALC272_FIXUP_MARIO, .name = "mario"},
- {.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_INV_DMIC, .name = "inv-dmic"},
- {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
- {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
- {}
-};
-
-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(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}),
- {}
-};
-
-/*
- */
-static int patch_alc662(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err;
-
- err = alc_alloc_spec(codec, 0x0b);
- if (err < 0)
- return err;
-
- spec = codec->spec;
-
- spec->shutup = alc_eapd_shutup;
-
- /* handle multiple HPs as is */
- spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
-
- alc_fix_pll_init(codec, 0x20, 0x04, 15);
-
- switch (codec->core.vendor_id) {
- case 0x10ec0668:
- spec->init_hook = alc668_restore_default_value;
- break;
- }
-
- 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);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- alc_auto_parse_customize_define(codec);
-
- if (has_cdefine_beep(codec))
- spec->gen.beep_nid = 0x01;
-
- 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;
- }
-
- /* automatic parse from the BIOS config */
- err = alc662_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- if (!spec->gen.no_analog && spec->gen.beep_nid) {
- switch (codec->core.vendor_id) {
- case 0x10ec0662:
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- break;
- case 0x10ec0272:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0668:
- set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
- break;
- case 0x10ec0273:
- set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
- break;
- }
- }
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- alc_free(codec);
- return err;
-}
-
-/*
- * ALC680 support
- */
-
-static int alc680_parse_auto_config(struct hda_codec *codec)
-{
- return alc_parse_auto_config(codec, NULL, NULL);
-}
-
-/*
- */
-static int patch_alc680(struct hda_codec *codec)
-{
- int err;
-
- /* ALC680 has no aa-loopback mixer */
- err = alc_alloc_spec(codec, 0);
- if (err < 0)
- return err;
-
- /* automatic parse from the BIOS config */
- err = alc680_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- return 0;
-}
-
-/*
- * patch entries
- */
-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(0x10ec0225, "ALC225", 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(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(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_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(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(0x10ec0899, "ALC898", patch_alc882),
- HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
- HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882),
- HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882),
- {} /* terminator */
-};
-MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Realtek HD-audio codec");
-
-static struct hda_codec_driver realtek_driver = {
- .id = snd_hda_id_realtek,
-};
-
-module_hda_codec_driver(realtek_driver);
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
deleted file mode 100644
index 65bb3ac6af4c..000000000000
--- a/sound/pci/hda/thinkpad_helper.c
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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/thinkpad_acpi.h>
-
-static int (*led_set_func)(int, bool);
-static void (*old_vmaster_hook)(void *, int);
-
-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 update_tpacpi_mute_led(void *private_data, int enabled)
-{
- if (old_vmaster_hook)
- old_vmaster_hook(private_data, enabled);
-
- if (led_set_func)
- led_set_func(TPACPI_LED_MUTE, !enabled);
-}
-
-static void update_tpacpi_micmute_led(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (!ucontrol || !led_set_func)
- return;
- if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
- /* TODO: How do I verify if it's a mono or stereo here? */
- bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
- led_set_func(TPACPI_LED_MICMUTE, !val);
- }
-}
-
-static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct hda_gen_spec *spec = codec->spec;
- bool removefunc = false;
-
- if (action == HDA_FIXUP_ACT_PROBE) {
- if (!is_thinkpad(codec))
- return;
- if (!led_set_func)
- led_set_func = symbol_request(tpacpi_led_set);
- if (!led_set_func) {
- codec_warn(codec,
- "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
- return;
- }
-
- removefunc = true;
- if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
- old_vmaster_hook = spec->vmaster_mute.hook;
- spec->vmaster_mute.hook = update_tpacpi_mute_led;
- removefunc = false;
- }
- if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
- if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch)
- codec_dbg(codec,
- "Skipping micmute LED control due to several ADCs");
- else {
- spec->cap_sync_hook = update_tpacpi_micmute_led;
- removefunc = false;
- }
- }
- }
-
- if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
- symbol_put(tpacpi_led_set);
- led_set_func = NULL;
- old_vmaster_hook = 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 1196f22a9b45..f406a048374c 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-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 psc724.o wm8766.o wm8776.o
+snd-ice17xx-ak4xxx-y := ak4xxx.o
+snd-ice1712-y := ice1712.o delta.o hoontech.o ews.o
+snd-ice1724-y := 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 179ef7a5f0d1..cad33a2f26bc 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -1,24 +1,10 @@
+// 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 <linux/io.h>
@@ -179,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 2f9b93467867..a7b496de6ce2 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -1,24 +1,10 @@
+// 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 <linux/delay.h>
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 c9411dfff5a4..b4c9e7d11609 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.
@@ -372,14 +358,13 @@ static int aureon_ac97_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short vol;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
vol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
ucontrol->value.integer.value[0] = 0x1F - (vol & 0x1F);
if (kcontrol->private_value & AUREON_AC97_STEREO)
ucontrol->value.integer.value[1] = 0x1F - ((vol >> 8) & 0x1F);
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -415,12 +400,11 @@ static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = aureon_ac97_read(ice,
kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -453,11 +437,10 @@ static int aureon_ac97_micboost_get(struct snd_kcontrol *kcontrol, struct snd_ct
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = aureon_ac97_read(ice, AC97_MIC) & 0x0020 ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -656,11 +639,10 @@ static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX1) >> 1) & 0x01;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -718,9 +700,8 @@ static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -958,11 +939,10 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
val = val > PCM_MIN ? (val - PCM_MIN) : 0;
ucontrol->value.integer.value[0] = val;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -998,12 +978,11 @@ static int wm_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_GAIN + i);
ucontrol->value.integer.value[i] = ~val>>5 & 0x1;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1045,13 +1024,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
int i, idx;
unsigned short vol;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
idx = WM_ADC_GAIN + i;
vol = wm_get(ice, idx) & 0x1f;
ucontrol->value.integer.value[i] = vol;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1111,11 +1089,10 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_ADC_MUX);
ucontrol->value.enumerated.item[0] = val & 7;
ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1403,7 +1380,7 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
* mixers
*/
-static struct snd_kcontrol_new aureon_dac_controls[] = {
+static const struct snd_kcontrol_new aureon_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -1518,7 +1495,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] = {
}
};
-static struct snd_kcontrol_new wm_controls[] = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -1584,7 +1561,7 @@ static struct snd_kcontrol_new wm_controls[] = {
}
};
-static struct snd_kcontrol_new ac97_controls[] = {
+static const struct snd_kcontrol_new ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1689,7 +1666,7 @@ static struct snd_kcontrol_new ac97_controls[] = {
}
};
-static struct snd_kcontrol_new universe_ac97_controls[] = {
+static const struct snd_kcontrol_new universe_ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1821,7 +1798,7 @@ static struct snd_kcontrol_new universe_ac97_controls[] = {
};
-static struct snd_kcontrol_new cs8415_controls[] = {
+static const struct snd_kcontrol_new cs8415_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
@@ -1906,24 +1883,21 @@ static int 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)
dev_info(ice->card->dev,
"No CS8415 chip. Skipping CS8415 controls.\n");
- else if ((id & 0x0F) != 0x01)
- dev_info(ice->card->dev,
- "Detected unsupported CS8415 rev. (%c)\n",
- (char)((id & 0x0F) + 'A' - 1));
else {
for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
struct snd_kcontrol *kctl;
- err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
- if (err < 0)
- return err;
+ kctl = snd_ctl_new1(&cs8415_controls[i], ice);
if (i > 1)
kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
+ if (err < 0)
+ return err;
}
}
- snd_ice1712_restore_gpio_status(ice);
}
return 0;
@@ -2147,7 +2121,7 @@ static int aureon_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char aureon51_eeprom[] = {
+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 */
@@ -2163,7 +2137,7 @@ static unsigned char aureon51_eeprom[] = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char aureon71_eeprom[] = {
+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 */
@@ -2180,7 +2154,7 @@ static unsigned char aureon71_eeprom[] = {
};
#define prodigy71_eeprom aureon71_eeprom
-static unsigned char aureon71_universe_eeprom[] = {
+static const unsigned char aureon71_universe_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC,
* 4DACs
*/
@@ -2198,7 +2172,7 @@ static unsigned char aureon71_universe_eeprom[] = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char prodigy71lt_eeprom[] = {
+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 */
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 6808bed0105e..e5a9585cba4c 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,21 +6,6 @@
* 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 <linux/delay.h>
@@ -140,13 +126,12 @@ static int ap_cs8427_sendbytes(struct snd_i2c_device *device, unsigned char *byt
int res = count;
unsigned char tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
while (count-- > 0)
ap_cs8427_write_byte(ice, *bytes++, tmp);
ap_cs8427_codec_deassert(ice, tmp);
- mutex_unlock(&ice->gpio_mutex);
return res;
}
@@ -157,13 +142,12 @@ static int ap_cs8427_readbytes(struct snd_i2c_device *device, unsigned char *byt
int res = count;
unsigned char tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
while (count-- > 0)
*bytes++ = ap_cs8427_read_byte(ice, tmp);
ap_cs8427_codec_deassert(ice, tmp);
- mutex_unlock(&ice->gpio_mutex);
return res;
}
@@ -190,7 +174,7 @@ static void snd_ice1712_delta_cs8403_spdif_write(struct snd_ice1712 *ice, unsign
/* send byte to transmitter */
mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
for (idx = 7; idx >= 0; idx--) {
tmp &= ~(mask1 | mask2);
@@ -204,7 +188,6 @@ static void snd_ice1712_delta_cs8403_spdif_write(struct snd_ice1712 *ice, unsign
}
tmp &= ~mask1;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
- mutex_unlock(&ice->gpio_mutex);
}
@@ -219,15 +202,13 @@ static int delta_spdif_default_put(struct snd_ice1712 *ice, struct snd_ctl_elem_
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_bits != val;
- ice->spdif.cs8403_bits = val;
- if (change && ice->playback_pro_substream == NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_delta_cs8403_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
return change;
}
@@ -242,15 +223,13 @@ static int delta_spdif_stream_put(struct snd_ice1712 *ice, struct snd_ctl_elem_v
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_stream_bits != val;
- ice->spdif.cs8403_stream_bits = val;
- if (change && ice->playback_pro_substream != NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_delta_cs8403_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
return change;
}
@@ -320,14 +299,13 @@ static void delta_1010_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
if (tmp != tmp2)
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp2);
- mutex_unlock(&ice->gpio_mutex);
}
/*
@@ -342,9 +320,9 @@ static void delta_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
return;
/* check before reset ak4524 to avoid unnecessary clicks */
- mutex_lock(&ice->gpio_mutex);
- tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
- mutex_unlock(&ice->gpio_mutex);
+ scoped_guard(mutex, &ice->gpio_mutex) {
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ }
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
@@ -353,12 +331,12 @@ static void delta_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
/* do it again */
snd_akm4xxx_reset(ak, 1);
- mutex_lock(&ice->gpio_mutex);
- tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
- if (rate > 48000)
- tmp |= ICE1712_DELTA_DFS;
- snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
- mutex_unlock(&ice->gpio_mutex);
+ scoped_guard(mutex, &ice->gpio_mutex) {
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
+ if (rate > 48000)
+ tmp |= ICE1712_DELTA_DFS;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ }
snd_akm4xxx_reset(ak, 0);
}
@@ -393,23 +371,22 @@ static void delta_open_spdif(struct snd_ice1712 *ice, struct snd_pcm_substream *
/* set up */
static void delta_setup_spdif(struct snd_ice1712 *ice, int rate)
{
- unsigned long flags;
unsigned int tmp;
int change;
- spin_lock_irqsave(&ice->reg_lock, flags);
- tmp = ice->spdif.cs8403_stream_bits;
- if (tmp & 0x01) /* consumer */
- tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
- switch (rate) {
- case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
- case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
- case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
- default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x01) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+ default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
}
- change = ice->spdif.cs8403_stream_bits != tmp;
- ice->spdif.cs8403_stream_bits = tmp;
- spin_unlock_irqrestore(&ice->reg_lock, flags);
if (change)
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
@@ -690,13 +667,15 @@ static int 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) {
+ 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:
@@ -705,7 +684,7 @@ static int 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;
@@ -767,15 +746,15 @@ static int snd_ice1712_delta_init(struct snd_ice1712 *ice)
* additional controls for M-Audio cards
*/
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select =
+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 =
+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 =
+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 =
+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 =
+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);
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index 11a9c3a76507..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 \
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index 4ca33a800bc8..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>
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index b8af747ecb43..1dffcb011deb 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,21 +6,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
- *
*/
#include <linux/delay.h>
@@ -274,15 +260,13 @@ static int ews88_spdif_default_put(struct snd_ice1712 *ice, struct snd_ctl_elem_
int change;
val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_bits != val;
- ice->spdif.cs8403_bits = val;
- if (change && ice->playback_pro_substream == NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_ews_cs8404_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
return change;
}
@@ -297,15 +281,13 @@ static int ews88_spdif_stream_put(struct snd_ice1712 *ice, struct snd_ctl_elem_v
int change;
val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_stream_bits != val;
- ice->spdif.cs8403_stream_bits = val;
- if (change && ice->playback_pro_substream != NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_ews_cs8404_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
return change;
}
@@ -319,23 +301,22 @@ static void ews88_open_spdif(struct snd_ice1712 *ice, struct snd_pcm_substream *
/* set up SPDIF for EWS88MT / EWS88D */
static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate)
{
- unsigned long flags;
unsigned char tmp;
int change;
- spin_lock_irqsave(&ice->reg_lock, flags);
- tmp = ice->spdif.cs8403_stream_bits;
- if (tmp & 0x10) /* consumer */
- tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
- switch (rate) {
- case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
- case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
- case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
- default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x10) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+ default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
}
- change = ice->spdif.cs8403_stream_bits != tmp;
- ice->spdif.cs8403_stream_bits = tmp;
- spin_unlock_irqrestore(&ice->reg_lock, flags);
if (change)
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
@@ -456,7 +437,8 @@ static int 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) {
+ 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;
}
@@ -497,7 +479,8 @@ static int 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:
@@ -512,12 +495,14 @@ static int 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;
@@ -611,7 +596,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[] = {
+static const struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
@@ -806,7 +791,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[] = {
+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),
@@ -826,7 +811,12 @@ 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);
@@ -862,7 +852,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)
@@ -878,7 +869,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])
@@ -905,7 +897,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;
@@ -916,7 +909,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);
@@ -937,7 +931,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[] = {
+static const struct snd_kcontrol_new snd_ice1712_6fire_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Select",
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 fa301d31745b..071f94dc7390 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -1,24 +1,10 @@
+// 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 <linux/delay.h>
@@ -55,35 +41,35 @@ static void snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned cha
static void snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
- mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
@@ -125,15 +111,13 @@ static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, in
ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
-
- mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
@@ -153,17 +137,15 @@ static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int m
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
-
- mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
static int hoontech_init(struct snd_ice1712 *ice, bool staudio)
diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
index 7f94943392ce..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 \
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index f1fe497c2f9d..1e39b985bef2 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
- *
*/
/*
@@ -74,12 +60,6 @@
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 */
@@ -269,13 +249,12 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val, nval;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
nval = val & ~ICE1712_ROUTE_AC97;
if (ucontrol->value.integer.value[0])
nval |= ICE1712_ROUTE_AC97;
outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
- spin_unlock_irq(&ice->reg_lock);
return val != nval;
}
@@ -494,21 +473,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
*/
@@ -519,7 +483,7 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
int result = 0;
u32 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -533,7 +497,6 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -544,7 +507,7 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
int result = 0;
u32 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -558,7 +521,6 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -569,7 +531,7 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
int result = 0;
u8 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -579,7 +541,6 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -599,7 +560,7 @@ static int snd_ice1712_playback_prepare(struct snd_pcm_substream *substream)
rate = (runtime->rate * 8192) / 375;
if (rate > 0x000fffff)
rate = 0x000fffff;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outb(0, ice->ddma_port + 15);
outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
outl(runtime->dma_addr, ice->ddma_port + 0);
@@ -612,7 +573,6 @@ static int snd_ice1712_playback_prepare(struct snd_pcm_substream *substream)
snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -634,7 +594,7 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
ice->playback_con_active_buf[substream->number] = 0;
ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
chn = substream->number * 2;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
@@ -646,7 +606,6 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -664,13 +623,13 @@ static int snd_ice1712_capture_prepare(struct snd_pcm_substream *substream)
tmp &= ~0x04;
if (runtime->channels == 2)
tmp &= ~0x02;
- spin_lock_irq(&ice->reg_lock);
- outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
- outw(buf_size, ICEREG(ice, CONCAP_COUNT));
- snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
- snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
- snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+ outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ }
snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
return 0;
}
@@ -798,10 +757,9 @@ static int snd_ice1712_playback_ds_open(struct snd_pcm_substream *substream)
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_ice1712_playback_ds;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -831,10 +789,9 @@ static int snd_ice1712_playback_ds_close(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
u32 tmp;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
- spin_unlock_irq(&ice->reg_lock);
ice->playback_con_substream_ds[substream->number] = NULL;
return 0;
}
@@ -850,9 +807,6 @@ static int snd_ice1712_capture_close(struct snd_pcm_substream *substream)
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,
@@ -861,9 +815,6 @@ static const struct snd_pcm_ops snd_ice1712_playback_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,
@@ -872,9 +823,6 @@ static const struct snd_pcm_ops snd_ice1712_playback_ds_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,
@@ -894,11 +842,11 @@ static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 consumer");
+ strscpy(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 64*1024);
dev_warn(ice->card->dev,
"Consumer PCM code does not work well at the moment --jk\n");
@@ -919,11 +867,11 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 consumer (DS)");
+ strscpy(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -955,14 +903,13 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
what = ICE1712_PLAYBACK_PAUSE;
snd_pcm_trigger_done(substream, substream);
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
old = inl(ICEMT(ice, PLAYBACK_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
old |= what;
else
old &= ~what;
outl(old, ICEMT(ice, PLAYBACK_CONTROL));
- spin_unlock(&ice->reg_lock);
break;
}
case SNDRV_PCM_TRIGGER_START:
@@ -981,14 +928,13 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
old = inl(ICEMT(ice, PLAYBACK_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_START)
old |= what;
else
old &= ~what;
outl(old, ICEMT(ice, PLAYBACK_CONTROL));
- spin_unlock(&ice->reg_lock);
break;
}
default:
@@ -1001,7 +947,6 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
*/
static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force)
{
- unsigned long flags;
unsigned char val, old;
unsigned int i;
@@ -1026,24 +971,21 @@ static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
break;
}
- spin_lock_irqsave(&ice->reg_lock, flags);
- if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
- ICE1712_PLAYBACK_PAUSE|
- ICE1712_PLAYBACK_START)) {
-__out:
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return;
- }
- if (!force && is_pro_rate_locked(ice))
- goto __out;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+ ICE1712_PLAYBACK_PAUSE|
+ ICE1712_PLAYBACK_START))
+ return;
+ if (!force && is_pro_rate_locked(ice))
+ return;
- old = inb(ICEMT(ice, RATE));
- if (!force && old == val)
- goto __out;
+ old = inb(ICEMT(ice, RATE));
+ if (!force && old == val)
+ return;
- ice->cur_rate = rate;
- outb(val, ICEMT(ice, RATE));
- spin_unlock_irqrestore(&ice->reg_lock, flags);
+ ice->cur_rate = rate;
+ outb(val, ICEMT(ice, RATE));
+ }
if (ice->gpio.set_pro_rate)
ice->gpio.set_pro_rate(ice, rate);
@@ -1060,11 +1002,10 @@ static int snd_ice1712_playback_pro_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1075,7 +1016,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)
@@ -1083,11 +1024,10 @@ static int snd_ice1712_capture_pro_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1097,7 +1037,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)
@@ -1231,9 +1171,7 @@ static int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream)
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,
@@ -1242,9 +1180,7 @@ static const struct snd_pcm_ops snd_ice1712_playback_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,
@@ -1264,10 +1200,10 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 multi");
+ strscpy(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;
@@ -1306,12 +1242,11 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
ucontrol->value.integer.value[0] =
!((ice->pro_volumes[priv_idx] >> 15) & 1);
ucontrol->value.integer.value[1] =
!((ice->pro_volumes[priv_idx] >> 31) & 1);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1324,12 +1259,11 @@ static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struc
nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
(ucontrol->value.integer.value[1] ? 0 : 0x80000000);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
nval |= ice->pro_volumes[priv_idx] & ~0x80008000;
change = nval != ice->pro_volumes[priv_idx];
ice->pro_volumes[priv_idx] = nval;
snd_ice1712_update_volume(ice, priv_idx);
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -1348,12 +1282,11 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
ucontrol->value.integer.value[0] =
(ice->pro_volumes[priv_idx] >> 0) & 127;
ucontrol->value.integer.value[1] =
(ice->pro_volumes[priv_idx] >> 16) & 127;
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1366,18 +1299,17 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
nval = (ucontrol->value.integer.value[0] & 127) |
((ucontrol->value.integer.value[1] & 127) << 16);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
nval |= ice->pro_volumes[priv_idx] & ~0x007f007f;
change = nval != ice->pro_volumes[priv_idx];
ice->pro_volumes[priv_idx] = nval;
snd_ice1712_update_volume(ice, priv_idx);
- spin_unlock_irq(&ice->reg_lock);
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
-static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = {
+static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Playback Switch",
@@ -1506,11 +1438,11 @@ 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,
};
@@ -1603,10 +1535,7 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry,
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);
}
/*
@@ -1832,7 +1761,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
};
unsigned char val;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
if (is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = 13;
} else {
@@ -1843,7 +1772,6 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
}
ucontrol->value.enumerated.item[0] = val;
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1967,10 +1895,9 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_LOCKED != nval;
PRO_RATE_LOCKED = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -1998,10 +1925,9 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_RESET != nval;
PRO_RATE_RESET = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2037,10 +1963,10 @@ static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol,
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int val, cval;
- spin_lock_irq(&ice->reg_lock);
- val = inw(ICEMT(ice, ROUTE_PSDOUT03));
- cval = inl(ICEMT(ice, ROUTE_CAPTURE));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ cval = inl(ICEMT(ice, ROUTE_CAPTURE));
+ }
val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
val &= 3;
@@ -2074,35 +2000,35 @@ static int snd_ice1712_pro_route_analog_put(struct snd_kcontrol *kcontrol,
else
nval = 0; /* pcm */
shift = ((idx % 2) * 8) + ((idx / 2) * 2);
- spin_lock_irq(&ice->reg_lock);
- val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
- val &= ~(0x03 << shift);
- val |= nval << shift;
- change = val != old_val;
- if (change)
- outw(val, ICEMT(ice, ROUTE_PSDOUT03));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ val &= ~(0x03 << shift);
+ val |= nval << shift;
+ change = val != old_val;
+ if (change)
+ outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+ }
if (nval < 2) /* dig mixer of pcm */
return change;
/* update CAPTURE */
- spin_lock_irq(&ice->reg_lock);
- val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
- shift = ((idx / 2) * 8) + ((idx % 2) * 4);
- if (nval == 2) { /* analog in */
- nval = ucontrol->value.enumerated.item[0] - 1;
- val &= ~(0x07 << shift);
- val |= nval << shift;
- } else { /* spdif in */
- nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
- val &= ~(0x08 << shift);
- val |= nval << shift;
- }
- if (val != old_val) {
- change = 1;
- outl(val, ICEMT(ice, ROUTE_CAPTURE));
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
+ shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+ if (nval == 2) { /* analog in */
+ nval = ucontrol->value.enumerated.item[0] - 1;
+ val &= ~(0x07 << shift);
+ val |= nval << shift;
+ } else { /* spdif in */
+ nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+ val &= ~(0x08 << shift);
+ val |= nval << shift;
+ }
+ if (val != old_val) {
+ change = 1;
+ outl(val, ICEMT(ice, ROUTE_CAPTURE));
+ }
}
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2135,7 +2061,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
unsigned int val, old_val, nval;
/* update SPDOUT */
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
if (ucontrol->value.enumerated.item[0] >= 11)
nval = 1;
@@ -2161,7 +2087,6 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
change = val != old_val;
if (change)
outw(val, ICEMT(ice, ROUTE_SPDOUT));
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2208,10 +2133,9 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2239,12 +2163,11 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -2263,7 +2186,7 @@ static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
/*
* list of available boards
*/
-static struct snd_ice1712_card_info *card_tables[] = {
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_ice1712_hoontech_cards,
snd_ice1712_delta_cards,
snd_ice1712_ews_cards,
@@ -2287,7 +2210,7 @@ static int snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
{
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;
@@ -2377,7 +2300,8 @@ static int 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,
@@ -2421,22 +2345,26 @@ int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
if (snd_BUG_ON(!ice->pcm_pro))
return -EIO;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice);
+ kctl->id.device = ice->pcm_pro->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm_pro->device;
ice->spdif.stream_ctl = kctl;
return 0;
}
@@ -2482,31 +2410,18 @@ static int snd_ice1712_build_controls(struct snd_ice1712 *ice)
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(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)
-{
- struct snd_ice1712 *ice = device->device_data;
- return snd_ice1712_free(ice);
}
static int snd_ice1712_create(struct snd_card *card,
@@ -2514,35 +2429,22 @@ static int snd_ice1712_create(struct snd_card *card,
const char *modelname,
int omni,
int cs8427_timeout,
- int dxr_enable,
- struct snd_ice1712 **r_ice1712)
+ int dxr_enable)
{
- 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ 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");
- pci_disable_device(pci);
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;
@@ -2573,46 +2475,30 @@ static int snd_ice1712_create(struct snd_card *card,
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);
-
- card->private_data = ice;
- err = pci_request_regions(pci, "ICE1712");
- if (err < 0) {
- kfree(ice);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ICE1712");
+ 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,
- KBUILD_MODNAME, 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);
- snd_ice1712_free(ice);
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;
- }
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
- if (err < 0) {
- snd_ice1712_free(ice);
- return err;
- }
-
- *r_ice1712 = ice;
return 0;
}
@@ -2632,7 +2518,7 @@ static int snd_ice1712_probe(struct pci_dev *pci,
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;
@@ -2642,34 +2528,31 @@ static int snd_ice1712_probe(struct pci_dev *pci,
}
err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
+ sizeof(*ice), &card);
if (err < 0)
return err;
+ ice = card->private_data;
- strcpy(card->driver, "ICE1712");
- strcpy(card->shortname, "ICEnsemble ICE1712");
+ strscpy(card->driver, "ICE1712");
+ strscpy(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++) {
if (c->subvendor == ice->eeprom.subvendor) {
- ice->card_info = c;
- strcpy(card->shortname, c->name);
+ strscpy(card->shortname, c->name);
if (c->driver) /* specific driver? */
- strcpy(card->driver, c->driver);
+ strscpy(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;
}
}
@@ -2678,45 +2561,33 @@ static int snd_ice1712_probe(struct pci_dev *pci,
__found:
err = snd_ice1712_pcm_profi(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (ice_has_con_ac97(ice)) {
err = snd_ice1712_pcm(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ 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++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
if (!c->no_mpu401) {
@@ -2725,10 +2596,8 @@ static int snd_ice1712_probe(struct pci_dev *pci,
c->mpu401_1_info_flags |
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
-1, &ice->rmidi[0]);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (c->mpu401_1_name)
/* Preferred name available in card_info */
snprintf(ice->rmidi[0]->name,
@@ -2743,10 +2612,8 @@ static int snd_ice1712_probe(struct pci_dev *pci,
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)
/* Preferred name available in card_info */
snprintf(ice->rmidi[1]->name,
@@ -2762,25 +2629,13 @@ static int 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 snd_ice1712_remove(struct pci_dev *pci)
-{
- struct snd_card *card = pci_get_drvdata(pci);
- struct snd_ice1712 *ice = card->private_data;
-
- if (ice->card_info && ice->card_info->chip_exit)
- ice->card_info->chip_exit(ice);
- snd_card_free(card);
-}
-
#ifdef CONFIG_PM_SLEEP
static int snd_ice1712_suspend(struct device *dev)
{
@@ -2792,16 +2647,13 @@ static int snd_ice1712_suspend(struct device *dev)
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);
- 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);
+ scoped_guard(spinlock_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));
+ }
if (ice->pm_suspend)
ice->pm_suspend(ice);
@@ -2834,10 +2686,10 @@ static int snd_ice1712_resume(struct device *dev)
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);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ outb(inb(ICEMT(ice, RATE)) | ICE1712_SPDIF_MASTER,
+ ICEMT(ice, RATE));
+ }
snd_ice1712_set_input_clock_source(ice, 1);
} else {
/* internal on-card clock */
@@ -2864,7 +2716,6 @@ static struct pci_driver ice1712_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ice1712_ids,
.probe = snd_ice1712_probe,
- .remove = snd_ice1712_remove,
.driver = {
.pm = SND_VT1712_PM_OPS,
},
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 8ae8742662a5..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,21 +6,6 @@
* 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>
@@ -330,7 +316,7 @@ struct snd_ice1712 {
struct snd_info_entry *proc_entry;
struct snd_ice1712_eeprom eeprom;
- struct snd_ice1712_card_info *card_info;
+ const struct snd_ice1712_card_info *card_info;
unsigned int pro_volumes[20];
unsigned int omni:1; /* Delta Omni I/O */
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 057c2f394ea7..e2dbbbfbca9f 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,21 +6,6 @@
* 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/delay.h>
@@ -58,25 +44,6 @@
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 */
@@ -321,9 +288,8 @@ static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
{
struct snd_ice1712 *ice = substream->rmidi->private_data;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
enable_midi_irq(ice, flag, enable);
- spin_unlock_irq(&ice->reg_lock);
}
static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
@@ -339,9 +305,8 @@ static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
if (up) {
ice->midi_output = 1;
vt1724_midi_write(ice);
@@ -349,7 +314,6 @@ static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
ice->midi_output = 0;
enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
}
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
@@ -390,16 +354,14 @@ static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
if (up) {
ice->midi_input = 1;
vt1724_midi_read(ice);
} else {
ice->midi_input = 0;
}
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static const struct snd_rawmidi_ops vt1724_midi_input_ops = {
@@ -427,40 +389,39 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
status &= status_mask;
if (status == 0)
break;
- spin_lock(&ice->reg_lock);
- if (++timeout > 10) {
- status = inb(ICEREG1724(ice, IRQSTAT));
- dev_err(ice->card->dev,
- "Too long irq loop, status = 0x%x\n", status);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ if (++timeout > 10) {
+ status = inb(ICEREG1724(ice, IRQSTAT));
+ dev_err(ice->card->dev,
+ "Too long irq loop, status = 0x%x\n", status);
+ if (status & VT1724_IRQ_MPU_TX) {
+ dev_err(ice->card->dev, "Disabling MPU_TX\n");
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ }
+ goto out;
+ }
+ handled = 1;
if (status & VT1724_IRQ_MPU_TX) {
- dev_err(ice->card->dev, "Disabling MPU_TX\n");
- enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ if (ice->midi_output)
+ vt1724_midi_write(ice);
+ else
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ /* Due to mysterical reasons, MPU_TX is always
+ * generated (and can't be cleared) when a PCM
+ * playback is going. So let's ignore at the
+ * next loop.
+ */
+ status_mask &= ~VT1724_IRQ_MPU_TX;
}
- spin_unlock(&ice->reg_lock);
- break;
- }
- handled = 1;
- if (status & VT1724_IRQ_MPU_TX) {
- if (ice->midi_output)
- vt1724_midi_write(ice);
- else
- enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
- /* Due to mysterical reasons, MPU_TX is always
- * generated (and can't be cleared) when a PCM
- * playback is going. So let's ignore at the
- * next loop.
- */
- status_mask &= ~VT1724_IRQ_MPU_TX;
- }
- if (status & VT1724_IRQ_MPU_RX) {
- if (ice->midi_input)
- vt1724_midi_read(ice);
- else
- vt1724_midi_clear_rx(ice);
+ if (status & VT1724_IRQ_MPU_RX) {
+ if (ice->midi_input)
+ vt1724_midi_read(ice);
+ else
+ vt1724_midi_clear_rx(ice);
+ }
+ /* ack MPU irq */
+ outb(status, ICEREG1724(ice, IRQSTAT));
}
- /* ack MPU irq */
- outb(status, ICEREG1724(ice, IRQSTAT));
- spin_unlock(&ice->reg_lock);
if (status & VT1724_IRQ_MTPCM) {
/*
* Multi-track PCM
@@ -514,6 +475,7 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
}
}
+ out:
return IRQ_RETVAL(handled);
}
@@ -572,27 +534,27 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock(&ice->reg_lock);
- old = inb(ICEMT1724(ice, DMA_PAUSE));
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
- old |= what;
- else
- old &= ~what;
- outb(old, ICEMT1724(ice, DMA_PAUSE));
- spin_unlock(&ice->reg_lock);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ old = inb(ICEMT1724(ice, DMA_PAUSE));
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_PAUSE));
+ }
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- spin_lock(&ice->reg_lock);
- old = inb(ICEMT1724(ice, DMA_CONTROL));
- if (cmd == SNDRV_PCM_TRIGGER_START)
- old |= what;
- else
- old &= ~what;
- outb(old, ICEMT1724(ice, DMA_CONTROL));
- spin_unlock(&ice->reg_lock);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ old = inb(ICEMT1724(ice, DMA_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_CONTROL));
+ }
break;
case SNDRV_PCM_TRIGGER_RESUME:
@@ -658,46 +620,45 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
int force)
{
- 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;
- spin_lock_irqsave(&ice->reg_lock, flags);
- if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
- (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
- /* running? we cannot change the rate now... */
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
- }
- if (!force && is_pro_rate_locked(ice)) {
- /* comparing required and current rate - makes sense for
- * internal clock only */
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return (rate == ice->cur_rate) ? 0 : -EBUSY;
- }
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
+ (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
+ /* running? we cannot change the rate now... */
+ return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
+ }
+ if (!force && is_pro_rate_locked(ice)) {
+ /* comparing required and current rate - makes sense for
+ * internal clock only */
+ return (rate == ice->cur_rate) ? 0 : -EBUSY;
+ }
- if (force || !ice->is_spdif_master(ice)) {
- /* force means the rate was switched by ucontrol, otherwise
- * setting clock rate for internal clock mode */
- old_rate = ice->get_rate(ice);
- if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
- else if (rate == ice->cur_rate) {
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return 0;
+ if (force || !ice->is_spdif_master(ice)) {
+ /* force means the rate was switched by ucontrol, otherwise
+ * setting clock rate for internal clock mode */
+ old_rate = ice->get_rate(ice);
+ if (force || (old_rate != rate))
+ call_set_rate = true;
+ else if (rate == ice->cur_rate) {
+ return 0;
+ }
}
+
+ ice->cur_rate = rate;
}
- ice->cur_rate = rate;
+ 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)
@@ -714,24 +675,21 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
return 0;
}
-static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+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);
/* mark surround channels */
if (substream == ice->playback_pro_substream) {
/* PDMA0 can be multi-channel up to 8 */
chs = chs / 2 - 1;
for (i = 0; i < chs; i++) {
if (ice->pcm_reserved[i] &&
- ice->pcm_reserved[i] != substream) {
- mutex_unlock(&ice->open_mutex);
+ ice->pcm_reserved[i] != substream)
return -EBUSY;
- }
ice->pcm_reserved[i] = substream;
}
for (; i < 3; i++) {
@@ -743,22 +701,30 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
/* check individual playback stream */
if (ice->playback_con_substream_ds[i] == substream) {
if (ice->pcm_reserved[i] &&
- ice->pcm_reserved[i] != substream) {
- mutex_unlock(&ice->open_mutex);
+ ice->pcm_reserved[i] != substream)
return -EBUSY;
- }
ice->pcm_reserved[i] = substream;
break;
}
}
}
- mutex_unlock(&ice->open_mutex);
- err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
- if (err < 0)
- return err;
+ return 0;
+}
+
+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 err;
+
+ scoped_guard(mutex, &ice->open_mutex) {
+ err = __snd_vt1724_pcm_hw_params(substream, hw_params);
+ 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)
@@ -766,13 +732,12 @@ static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int i;
- mutex_lock(&ice->open_mutex);
+ guard(mutex)(&ice->open_mutex);
/* unmark surround channels */
for (i = 0; i < 3; i++)
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)
@@ -781,7 +746,7 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
unsigned char val;
unsigned int size;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = (8 - substream->runtime->channels) >> 1;
outb(val, ICEMT1724(ice, BURST));
@@ -796,8 +761,6 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
- spin_unlock_irq(&ice->reg_lock);
-
/*
dev_dbg(ice->card->dev, "pro prepare: ch = %d, addr = 0x%x, "
"buffer = 0x%x, period = 0x%x\n",
@@ -851,13 +814,12 @@ static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ice->profi_port + reg->addr);
outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1,
ice->profi_port + reg->size);
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1,
ice->profi_port + reg->count);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1047,18 +1009,18 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
set_rate_constraints(ice, substream);
- mutex_lock(&ice->open_mutex);
- /* calculate the currently available channels */
- num_indeps = ice->num_total_dacs / 2 - 1;
- for (chs = 0; chs < num_indeps; chs++) {
- if (ice->pcm_reserved[chs])
- break;
+ scoped_guard(mutex, &ice->open_mutex) {
+ /* calculate the currently available channels */
+ num_indeps = ice->num_total_dacs / 2 - 1;
+ for (chs = 0; chs < num_indeps; chs++) {
+ if (ice->pcm_reserved[chs])
+ break;
+ }
+ chs = (chs + 1) * 2;
+ runtime->hw.channels_max = chs;
+ if (chs > 2) /* channels must be even */
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
- chs = (chs + 1) * 2;
- runtime->hw.channels_max = chs;
- if (chs > 2) /* channels must be even */
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
- mutex_unlock(&ice->open_mutex);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
@@ -1114,7 +1076,6 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
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,
@@ -1125,7 +1086,6 @@ static const struct snd_pcm_ops snd_vt1724_playback_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,
@@ -1154,11 +1114,10 @@ static int snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1724");
+ strscpy(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;
@@ -1189,9 +1148,8 @@ static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned int val, nval;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
nval = val = inw(ICEMT1724(ice, SPDIF_CTRL));
nval &= ~(7 << 12);
switch (rate) {
@@ -1205,7 +1163,6 @@ static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
}
if (val != nval)
update_spdif_bits(ice, nval);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)
@@ -1293,7 +1250,6 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
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,
@@ -1304,7 +1260,6 @@ static const struct snd_pcm_ops snd_vt1724_playback_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,
@@ -1352,11 +1307,10 @@ static int snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, name);
+ strscpy(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;
@@ -1394,11 +1348,11 @@ static int snd_vt1724_playback_indep_prepare(struct snd_pcm_substream *substream
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
unsigned char val;
- spin_lock_irq(&ice->reg_lock);
- val = 3 - substream->number;
- if (inb(ICEMT1724(ice, BURST)) < val)
- outb(val, ICEMT1724(ice, BURST));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = 3 - substream->number;
+ if (inb(ICEMT1724(ice, BURST)) < val)
+ outb(val, ICEMT1724(ice, BURST));
+ }
return snd_vt1724_pcm_prepare(substream);
}
@@ -1407,13 +1361,11 @@ static int snd_vt1724_playback_indep_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&ice->open_mutex);
- /* already used by PDMA0? */
- if (ice->pcm_reserved[substream->number]) {
- mutex_unlock(&ice->open_mutex);
- return -EBUSY; /* FIXME: should handle blocking mode properly */
+ scoped_guard(mutex, &ice->open_mutex) {
+ /* already used by PDMA0? */
+ if (ice->pcm_reserved[substream->number])
+ return -EBUSY; /* FIXME: should handle blocking mode properly */
}
- mutex_unlock(&ice->open_mutex);
runtime->private_data = (void *)&vt1724_playback_dma_regs[substream->number];
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_vt1724_2ch_stereo;
@@ -1438,7 +1390,6 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
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,
@@ -1466,11 +1417,10 @@ static int snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1724 Surround PCM");
+ strscpy(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;
@@ -1489,7 +1439,7 @@ static int snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
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,
};
@@ -1571,10 +1521,7 @@ static void snd_vt1724_proc_read(struct snd_info_entry *entry,
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);
}
/*
@@ -1703,11 +1650,10 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val, old;
val = encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
old = inw(ICEMT1724(ice, SPDIF_CTRL));
if (val != old)
update_spdif_bits(ice, val);
- spin_unlock_irq(&ice->reg_lock);
return val != old;
}
@@ -1778,14 +1724,13 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char old, val;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
old = val = inb(ICEREG1724(ice, SPDIF_CFG));
val &= ~VT1724_CFG_SPDIF_OUT_EN;
if (ucontrol->value.integer.value[0])
val |= VT1724_CFG_SPDIF_OUT_EN;
if (old != val)
outb(val, ICEREG1724(ice, SPDIF_CFG));
- spin_unlock_irq(&ice->reg_lock);
return old != val;
}
@@ -1865,7 +1810,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
if (uinfo->value.enumerated.item >= hw_rates_count)
/* ext_clock items */
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
ice->ext_clock_names[
uinfo->value.enumerated.item - hw_rates_count]);
else
@@ -1881,7 +1826,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int i, rate;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
if (ice->is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = ice->hw_rates->count +
ice->get_spdif_master_type(ice);
@@ -1895,7 +1840,6 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
}
}
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1926,29 +1870,31 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
unsigned int old_rate, new_rate;
unsigned int item = ucontrol->value.enumerated.item[0];
unsigned int first_ext_clock = ice->hw_rates->count;
+ bool set_pro_rate = false;
if (item > first_ext_clock + ice->ext_clock_count - 1)
return -EINVAL;
/* if rate = 0 => external clock */
- spin_lock_irq(&ice->reg_lock);
- if (ice->is_spdif_master(ice))
- old_rate = 0;
- else
- old_rate = ice->get_rate(ice);
- if (item >= first_ext_clock) {
- /* switching to external clock */
- ice->set_spdif_clock(ice, item - first_ext_clock);
- new_rate = 0;
- } else {
- /* internal on-card clock */
- new_rate = ice->hw_rates->list[item];
- ice->pro_rate_default = new_rate;
- spin_unlock_irq(&ice->reg_lock);
- snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
- spin_lock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ if (ice->is_spdif_master(ice))
+ old_rate = 0;
+ else
+ old_rate = ice->get_rate(ice);
+ if (item >= first_ext_clock) {
+ /* switching to external clock */
+ ice->set_spdif_clock(ice, item - first_ext_clock);
+ new_rate = 0;
+ } else {
+ /* internal on-card clock */
+ new_rate = ice->hw_rates->list[item];
+ ice->pro_rate_default = new_rate;
+ set_pro_rate = true;
+ }
}
- spin_unlock_irq(&ice->reg_lock);
+
+ if (set_pro_rate)
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
/* the first switch to the ext. clock mode? */
if (old_rate != new_rate && !new_rate) {
@@ -1988,10 +1934,9 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_LOCKED != nval;
PRO_RATE_LOCKED = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2019,10 +1964,9 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_RESET != nval;
PRO_RATE_RESET = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2177,13 +2121,12 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
ucontrol->value.integer.value[idx] =
inb(ICEMT1724(ice, MONITOR_PEAKDATA));
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -2196,16 +2139,9 @@ static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
};
/*
- *
- */
-
-static struct snd_ice1712_card_info no_matched;
-
-
-/*
ooAoo cards with no controls
*/
-static unsigned char ooaoo_sq210_eeprom[] = {
+static const unsigned char ooaoo_sq210_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4c, /* 49MHz crystal, no mpu401, no ADC,
1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -2225,7 +2161,7 @@ static unsigned char ooaoo_sq210_eeprom[] = {
};
-static struct snd_ice1712_card_info snd_vt1724_ooaoo_cards[] = {
+static const struct snd_ice1712_card_info snd_vt1724_ooaoo_cards[] = {
{
.name = "ooAoo SQ210a",
.model = "sq210a",
@@ -2235,7 +2171,7 @@ static struct snd_ice1712_card_info snd_vt1724_ooaoo_cards[] = {
{ } /* terminator */
};
-static struct snd_ice1712_card_info *card_tables[] = {
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_vt1724_revo_cards,
snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
@@ -2272,13 +2208,12 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
{
unsigned char val;
- mutex_lock(&ice->i2c_mutex);
+ guard(mutex)(&ice->i2c_mutex);
wait_i2c_busy(ice);
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
wait_i2c_busy(ice);
val = inb(ICEREG1724(ice, I2C_DATA));
- mutex_unlock(&ice->i2c_mutex);
/*
dev_dbg(ice->card->dev, "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
*/
@@ -2288,7 +2223,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
unsigned char dev, unsigned char addr, unsigned char data)
{
- mutex_lock(&ice->i2c_mutex);
+ guard(mutex)(&ice->i2c_mutex);
wait_i2c_busy(ice);
/*
dev_dbg(ice->card->dev, "i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
@@ -2297,7 +2232,6 @@ void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
outb(data, ICEREG1724(ice, I2C_DATA));
outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
wait_i2c_busy(ice);
- mutex_unlock(&ice->i2c_mutex);
}
static int snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
@@ -2305,7 +2239,7 @@ static int snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
{
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;
@@ -2444,23 +2378,27 @@ static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice));
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice);
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice);
kctl->id.device = ice->pcm->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice);
kctl->id.device = ice->pcm->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm->device;
#if 0 /* use default only */
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice));
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice);
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm->device;
ice->spdif.stream_ctl = kctl;
#endif
return 0;
@@ -2499,54 +2437,29 @@ static int snd_vt1724_build_controls(struct snd_ice1712 *ice)
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 snd_vt1724_create(struct snd_card *card,
struct pci_dev *pci,
- const char *modelname,
- struct snd_ice1712 **r_ice1712)
+ 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);
@@ -2563,45 +2476,29 @@ static int 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);
+ err = pcim_request_all_regions(pci, "ICE1724");
+ 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, KBUILD_MODNAME, 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);
- snd_vt1724_free(ice);
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;
- }
-
- *r_ice1712 = ice;
return 0;
}
@@ -2612,14 +2509,14 @@ static int snd_vt1724_create(struct snd_card *card,
*
*/
-static int 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;
@@ -2628,44 +2525,34 @@ static int snd_vt1724_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "ICE1724");
+ strscpy(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->name; c++) {
- if ((model[dev] && c->model &&
- !strcmp(model[dev], c->model)) ||
- (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) {
+ strscpy(card->shortname, c->name);
+ if (c->driver) /* specific driver? */
+ strscpy(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).
@@ -2696,63 +2583,47 @@ __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");
+ strscpy(rmidi->name, "ICE1724 MIDI");
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -2774,23 +2645,17 @@ __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 snd_vt1724_remove(struct pci_dev *pci)
+static int snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- struct snd_card *card = pci_get_drvdata(pci);
- struct snd_ice1712 *ice = card->private_data;
-
- if (ice->card_info && ice->card_info->chip_exit)
- ice->card_info->chip_exit(ice);
- snd_card_free(card);
+ return snd_card_free_on_error(&pci->dev, __snd_vt1724_probe(pci, pci_id));
}
#ifdef CONFIG_PM_SLEEP
@@ -2804,17 +2669,14 @@ static int snd_vt1724_suspend(struct device *dev)
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);
- ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
- ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
- ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
- ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
+ ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
+ ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
+ ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+ }
if (ice->pm_suspend)
ice->pm_suspend(ice);
@@ -2873,7 +2735,6 @@ static struct pci_driver vt1724_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
- .remove = snd_vt1724_remove,
.driver = {
.pm = SND_VT1724_PM_OPS,
},
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 0dbaccf61f33..d679842ae1bd 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,28 +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 <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>
@@ -357,7 +343,7 @@ static int juli_mute_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new juli_mute_controls[] = {
+static const struct snd_kcontrol_new juli_mute_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -411,7 +397,7 @@ static struct snd_kcontrol_new juli_mute_controls[] = {
},
};
-static char *slave_vols[] = {
+static const char * const follower_vols[] = {
PCM_VOLUME,
MONITOR_AN_IN_VOLUME,
MONITOR_DIG_IN_VOLUME,
@@ -422,31 +408,6 @@ static char *slave_vols[] = {
static
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
-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);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void add_slaves(struct snd_card *card,
- struct snd_kcontrol *master,
- char * const *list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- /* dev_dbg(card->dev, "add_slaves - %s\n", *list); */
- if (slave) {
- /* dev_dbg(card->dev, "slave %s found\n", *list); */
- snd_ctl_add_slave(master, slave);
- }
- }
-}
-
static int juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
@@ -469,10 +430,12 @@ static int juli_add_controls(struct snd_ice1712 *ice)
juli_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
/* only capture SPDIF over AK4114 */
return snd_ak4114_build(spec->ak4114, NULL,
@@ -666,7 +629,7 @@ static int juli_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char juli_eeprom[] = {
+static const unsigned char juli_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c
index 0e30419f6bbd..551f478c59c4 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,21 +6,6 @@
*
* 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>
@@ -130,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 },
@@ -172,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;
@@ -189,10 +175,9 @@ static int maya_vol_get(struct snd_kcontrol *kcontrol,
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
ucontrol->value.integer.value[0] = wm->volumes[idx][0];
ucontrol->value.integer.value[1] = wm->volumes[idx][1];
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -203,11 +188,11 @@ 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;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
for (ch = 0; ch < 2; ch++) {
val = ucontrol->value.integer.value[ch];
if (val > vol->maxval)
@@ -227,7 +212,6 @@ static int maya_vol_put(struct snd_kcontrol *kcontrol,
val ? 0 : vol->mux_bits[ch]);
wm->volumes[idx][ch] = val;
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -264,7 +248,7 @@ static int maya_sw_put(struct snd_kcontrol *kcontrol,
unsigned int mask, val;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
mask = 1 << idx;
wm->switch_bits &= ~mask;
val = ucontrol->value.integer.value[0];
@@ -274,7 +258,6 @@ static int maya_sw_put(struct snd_kcontrol *kcontrol,
changed = wm8776_write_bits(chip->ice, wm,
GET_SW_VAL_REG(kcontrol->private_value),
mask, val ? mask : 0);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -329,14 +312,13 @@ static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
unsigned int val, mask;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
mask = 1 << shift;
val = ucontrol->value.integer.value[0];
if (GET_GPIO_VAL_INV(kcontrol->private_value))
val = !val;
val = val ? mask : 0;
changed = maya_set_gpio_bits(chip->ice, mask, val);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -383,11 +365,10 @@ static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
int sel = ucontrol->value.enumerated.item[0];
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = maya_set_gpio_bits(chip->ice, 1 << GPIO_MIC_RELAY,
sel ? (1 << GPIO_MIC_RELAY) : 0);
wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -438,7 +419,7 @@ static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
* controls to be added
*/
-static struct snd_kcontrol_new maya_controls[] = {
+static const struct snd_kcontrol_new maya_controls[] = {
{
.name = "Crossmix Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -649,12 +630,11 @@ static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
val |= 8;
val |= ratio << 4;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
for (i = 0; i < 2; i++)
wm8776_write_bits(ice, &chip->wm[i],
WM8776_REG_MASTER_MODE_CONTROL,
0x180, val);
- mutex_unlock(&chip->mutex);
}
/*
@@ -676,7 +656,7 @@ static const struct snd_pcm_hw_constraint_list dac_rates = {
/*
* chip addresses on I2C bus
*/
-static unsigned char wm8776_addr[2] = {
+static const unsigned char wm8776_addr[2] = {
0x34, 0x36, /* codec 0 & 1 */
};
@@ -726,7 +706,7 @@ static int maya44_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char maya44_eeprom[] = {
+static const unsigned char maya44_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x45,
/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
[ICE_EEP2_ACLINK] = 0x80,
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 67fbb28bf033..151b740ce66d 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:
@@ -171,7 +157,7 @@ static int phase22_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char phase22_eeprom[] = {
+static const unsigned char phase22_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 512, mpu 401,
spdif-in/1xADC, 1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -188,7 +174,7 @@ static unsigned char phase22_eeprom[] = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char phase28_eeprom[] = {
+static const unsigned char phase28_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401,
spdif-in/1xADC, 4xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -301,10 +287,9 @@ static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -652,11 +637,10 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
val = val > PCM_MIN ? (val - PCM_MIN) : 0;
ucontrol->value.integer.value[0] = val;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -759,7 +743,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[] = {
+static const struct snd_kcontrol_new phase28_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -874,7 +858,7 @@ static struct snd_kcontrol_new phase28_dac_controls[] = {
}
};
-static struct snd_kcontrol_new wm_controls[] = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
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 5101f40f6fbd..557473f0d59e 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -1,24 +1,10 @@
+// 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 <linux/delay.h>
@@ -126,13 +112,12 @@ static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff;
val = val > DAC_MIN ? (val - DAC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -142,7 +127,7 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short oval, nval;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nval = ucontrol->value.integer.value[i];
nval = (nval ? (nval + DAC_MIN) : 0) & 0xff;
@@ -154,7 +139,6 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -181,13 +165,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
val = val > ADC_MIN ? (val - ADC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -197,7 +180,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short ovol, nvol;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nvol = ucontrol->value.integer.value[i];
nvol = nvol ? (nvol + ADC_MIN) : 0;
@@ -208,7 +191,6 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -222,9 +204,8 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -235,7 +216,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short oval, nval;
int change;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
nval = oval = wm_get(ice, WM_ADC_MUX);
if (ucontrol->value.integer.value[0])
nval |= (1 << bit);
@@ -245,7 +226,6 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
if (change) {
wm_put(ice, WM_ADC_MUX, nval);
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -258,9 +238,8 @@ static int wm_bypass_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -270,7 +249,7 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = oval = wm_get(ice, WM_OUT_MUX);
if (ucontrol->value.integer.value[0])
val |= 0x04;
@@ -280,7 +259,6 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
wm_put(ice, WM_OUT_MUX, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -293,9 +271,8 @@ static int wm_chswap_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -305,7 +282,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
oval = wm_get(ice, WM_DAC_CTRL1);
val = oval & 0x0f;
if (ucontrol->value.integer.value[0])
@@ -317,7 +294,6 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
wm_put_nocache(ice, WM_DAC_CTRL1, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -424,9 +400,8 @@ static int cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.enumerated.item[0] = ice->gpio.saved[0];
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -436,14 +411,13 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned char val;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
if (ucontrol->value.enumerated.item[0] != ice->gpio.saved[0]) {
ice->gpio.saved[0] = ucontrol->value.enumerated.item[0] & 3;
val = 0x80 | (ice->gpio.saved[0] << 3);
spi_write(ice, CS_DEV, 0x04, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -463,10 +437,10 @@ static int pontis_gpio_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int pontis_gpio_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = (~ice->gpio.write_mask & 0xffff) | 0x00f0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -475,22 +449,22 @@ static int pontis_gpio_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
val = (~ucontrol->value.integer.value[0] & 0xffff) | 0x00f0;
changed = val != ice->gpio.write_mask;
ice->gpio.write_mask = val;
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_dir_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = ice->gpio.direction & 0xff0f;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -499,23 +473,23 @@ static int pontis_gpio_dir_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
val = ucontrol->value.integer.value[0] & 0xff0f;
changed = (val != ice->gpio.direction);
ice->gpio.direction = val;
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
ucontrol->value.integer.value[0] = snd_ice1712_gpio_read(ice) & 0xffff;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -524,7 +498,8 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val, nval;
int changed = 0;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
val = snd_ice1712_gpio_read(ice) & 0xffff;
@@ -533,7 +508,6 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
snd_ice1712_gpio_write(ice, nval);
changed = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
@@ -543,7 +517,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
* mixers
*/
-static struct snd_kcontrol_new pontis_controls[] = {
+static const struct snd_kcontrol_new pontis_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -634,14 +608,14 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf
struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg <= 0x17 && val <= 0xffff)
wm_put(ice, reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
@@ -649,22 +623,17 @@ static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x17; reg++) {
val = wm_get(ice, reg);
snd_iprintf(buffer, "%02x = %04x\n", reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ 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)
@@ -672,21 +641,18 @@ static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x26; reg++) {
val = spi_read(ice, CS_DEV, reg);
snd_iprintf(buffer, "%02x = %02x\n", reg, val);
}
val = spi_read(ice, CS_DEV, 0x7f);
snd_iprintf(buffer, "%02x = %02x\n", 0x7f, val);
- mutex_unlock(&ice->gpio_mutex);
}
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);
}
@@ -798,7 +764,7 @@ static int pontis_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char pontis_eeprom[] = {
+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 */
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 3919aed39ca0..cd7db2b65b51 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,27 +32,12 @@
* 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 <linux/delay.h>
@@ -124,21 +110,19 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct prodigy192_spec *spec = ice->spec;
- int idx, change;
+ int idx;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
/* due to possible conflicts with stac9460_set_rate_val, mutexing */
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
/*
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]);
- mutex_unlock(&spec->mute_mutex);
- return change;
+ return stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
}
/*
@@ -184,14 +168,9 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
- if (change) {
- ovol = (0x7f - nvol) | (tmp & 0x80);
- /*
- dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n",
- idx, ovol);
- */
+ if (change)
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
- }
+
return change;
}
@@ -335,7 +314,7 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
return;
/* change detected, setting master clock, muting first */
/* due to possible conflicts with mute controls - mutexing */
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
/* we have to remember current mute status for each DAC */
for (idx = 0; idx < 7 ; ++idx)
changed[idx] = stac9460_dac_mute(ice,
@@ -349,7 +328,6 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
if (changed[idx])
stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
}
- mutex_unlock(&spec->mute_mutex);
}
@@ -360,7 +338,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
* mixers
*/
-static struct snd_kcontrol_new stac_controls[] = {
+static const struct snd_kcontrol_new stac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -592,7 +570,7 @@ static int ak4114_input_sw_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new ak4114_controls[] = {
+static const struct snd_kcontrol_new ak4114_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIODIO IEC958 Capture Input",
@@ -651,9 +629,8 @@ 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);
}
@@ -768,7 +745,7 @@ static int prodigy192_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char prodigy71_eeprom[] = {
+static const unsigned char prodigy71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x6a, /* 49MHz crystal, mpu401,
* spdif-in+ 1 stereo ADC,
* 3 stereo DACs
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 8dabd4d0211d..eac233093865 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,21 +8,6 @@
* 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
- *
*/
@@ -282,7 +268,7 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int i;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[i]) {
spec->vol[i] = ucontrol->value.integer.value[i];
@@ -291,14 +277,13 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
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[] = {
+static const struct snd_kcontrol_new prodigy_hd2_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -314,26 +299,7 @@ static struct snd_kcontrol_new prodigy_hd2_controls[] = {
/* --------------- */
-/*
- * 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
@@ -409,7 +375,7 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct prodigy_hifi_spec *spec = ice->spec;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
idx = WM_DAC_ATTEN_L + i;
@@ -419,7 +385,6 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -461,7 +426,7 @@ static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < voices; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
idx = WM8766_LDA1 + ofs + i;
@@ -472,7 +437,6 @@ static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -507,7 +471,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
struct prodigy_hifi_spec *spec = ice->spec;
int ch, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (ch = 0; ch < 2; ch++) {
if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
spec->master[ch] = ucontrol->value.integer.value[ch];
@@ -527,7 +491,6 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -568,9 +531,8 @@ 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;
- mutex_unlock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
+ ucontrol->value.enumerated.item[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
return 0;
}
@@ -581,14 +543,13 @@ static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
unsigned short oval, nval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&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;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -619,13 +580,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
val = val > ADC_MIN ? (val - ADC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -636,7 +596,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
unsigned short ovol, nvol;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nvol = ucontrol->value.integer.value[i];
nvol = nvol ? (nvol + ADC_MIN) : 0;
@@ -647,7 +607,6 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -662,10 +621,9 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -677,7 +635,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
unsigned short oval, nval;
int change;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
nval = oval = wm_get(ice, WM_ADC_MUX);
if (ucontrol->value.integer.value[0])
nval |= (1 << bit);
@@ -687,7 +645,6 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
if (change) {
wm_put(ice, WM_ADC_MUX, nval);
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -701,10 +658,9 @@ static int wm_bypass_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -715,7 +671,7 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol,
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = oval = wm_get(ice, WM_OUT_MUX);
if (ucontrol->value.integer.value[0])
val |= 0x04;
@@ -725,7 +681,6 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol,
wm_put(ice, WM_OUT_MUX, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -739,10 +694,9 @@ static int wm_chswap_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -753,7 +707,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
oval = wm_get(ice, WM_DAC_CTRL1);
val = oval & 0x0f;
if (ucontrol->value.integer.value[0])
@@ -765,7 +719,6 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
wm_put_nocache(ice, WM_DAC_CTRL1, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -774,7 +727,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
* mixers
*/
-static struct snd_kcontrol_new prodigy_hifi_controls[] = {
+static const struct snd_kcontrol_new prodigy_hifi_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -897,14 +850,14 @@ static void wm_proc_regs_write(struct snd_info_entry *entry,
struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg <= 0x17 && val <= 0xffff)
wm_put(ice, reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_regs_read(struct snd_info_entry *entry,
@@ -913,22 +866,17 @@ static void wm_proc_regs_read(struct snd_info_entry *entry,
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x17; reg++) {
val = wm_get(ice, reg);
snd_iprintf(buffer, "%02x = %04x\n", reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read,
+ wm_proc_regs_write);
}
static int prodigy_hifi_add_controls(struct snd_ice1712 *ice)
@@ -967,7 +915,7 @@ static int prodigy_hd2_add_controls(struct snd_ice1712 *ice)
static void wm8766_init(struct snd_ice1712 *ice)
{
- static unsigned short wm8766_inits[] = {
+ static const unsigned short wm8766_inits[] = {
WM8766_RESET, 0x0000,
WM8766_DAC_CTRL, 0x0120,
WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
@@ -990,7 +938,7 @@ static void wm8766_init(struct snd_ice1712 *ice)
static void wm8776_init(struct snd_ice1712 *ice)
{
- static unsigned short wm8776_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 */
@@ -1010,7 +958,7 @@ static void wm8776_init(struct snd_ice1712 *ice)
#ifdef CONFIG_PM_SLEEP
static int prodigy_hifi_resume(struct snd_ice1712 *ice)
{
- static unsigned short wm8776_reinit_registers[] = {
+ static const unsigned short wm8776_reinit_registers[] = {
WM_MASTER_CTRL,
WM_DAC_INT,
WM_ADC_INT,
@@ -1031,7 +979,7 @@ static int prodigy_hifi_resume(struct snd_ice1712 *ice)
struct prodigy_hifi_spec *spec = ice->spec;
int i, ch;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* reinitialize WM8776 and re-apply old register values */
wm8776_init(ice);
@@ -1060,7 +1008,6 @@ static int prodigy_hifi_resume(struct snd_ice1712 *ice)
wm_put(ice, WM_DAC_MUTE, 0x00);
wm_put(ice, WM_DAC_CTRL1, 0x90);
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
#endif
@@ -1070,7 +1017,7 @@ static int prodigy_hifi_resume(struct snd_ice1712 *ice)
*/
static int prodigy_hifi_init(struct snd_ice1712 *ice)
{
- static unsigned short wm8776_defaults[] = {
+ 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 */
@@ -1145,7 +1092,7 @@ static int 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,
@@ -1171,11 +1118,11 @@ static int prodigy_hd2_resume(struct snd_ice1712 *ice)
/* initialize ak4396 codec and restore previous mixer volumes */
struct prodigy_hifi_spec *spec = ice->spec;
int i;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ak4396_init(ice);
for (i = 0; i < 2; i++)
ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
#endif
@@ -1217,7 +1164,7 @@ static int prodigy_hd2_init(struct snd_ice1712 *ice)
}
-static unsigned char prodigy71hifi_eeprom[] = {
+static const unsigned char prodigy71hifi_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1233,7 +1180,7 @@ static unsigned char prodigy71hifi_eeprom[] = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char prodigyhd2_eeprom[] = {
+static const unsigned char prodigyhd2_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1249,7 +1196,7 @@ static unsigned char prodigyhd2_eeprom[] = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char fortissimo4_eeprom[] = {
+static const unsigned char fortissimo4_eeprom[] = {
0x43, /* SYSCONF: clock 512, ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
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
index 4019cf27d117..0818e42c94ca 100644
--- a/sound/pci/ice1712/psc724.c
+++ b/sound/pci/ice1712/psc724.c
@@ -1,24 +1,10 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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>
@@ -191,7 +177,6 @@ static bool psc724_get_master_switch(struct snd_ice1712 *ice)
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;
@@ -201,17 +186,15 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
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;
- strlcpy(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);
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ "Master Speakers Playback Switch");
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
/* and headphone mute change */
- strlcpy(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);
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ spec->wm8776.ctl[WM8776_CTL_HP_SW].name);
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}
static void psc724_update_hp_jack_state(struct work_struct *work)
@@ -434,7 +417,7 @@ static void psc724_exit(struct snd_ice1712 *ice)
}
/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */
-static unsigned char psc724_eeprom[] = {
+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 */
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
index d145b5eb7ff8..099601edf1d0 100644
--- a/sound/pci/ice1712/quartet.c
+++ b/sound/pci/ice1712/quartet.c
@@ -1,31 +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 <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>
@@ -410,7 +396,7 @@ static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
{
unsigned int tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* set direction of used GPIOs*/
/* all outputs */
tmp = 0x00ffff;
@@ -443,7 +429,6 @@ static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
ice->gpio.set_mask(ice, 0xffffff);
/* outputs only 8-15 */
ice->gpio.set_dir(ice, 0x00ff00);
- mutex_unlock(&ice->gpio_mutex);
}
static unsigned int get_scr(struct snd_ice1712 *ice)
@@ -501,9 +486,7 @@ 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);
}
static int qtet_mute_get(struct snd_kcontrol *kcontrol,
@@ -582,7 +565,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)
@@ -673,7 +656,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"),
@@ -736,7 +719,7 @@ static int qtet_sw_put(struct snd_kcontrol *kcontrol,
.put = qtet_sw_put,\
.private_value = xpriv }
-static struct snd_kcontrol_new qtet_controls[] = {
+static const struct snd_kcontrol_new qtet_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -773,7 +756,7 @@ static struct snd_kcontrol_new qtet_controls[] = {
QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12),
};
-static char *slave_vols[] = {
+static const char * const follower_vols[] = {
PCM_12_PLAYBACK_VOLUME,
PCM_34_PLAYBACK_VOLUME,
NULL
@@ -782,27 +765,6 @@ static char *slave_vols[] = {
static
DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
-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);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char * const *list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
- }
-}
-
static int qtet_add_controls(struct snd_ice1712 *ice)
{
struct qtet_spec *spec = ice->spec;
@@ -823,10 +785,12 @@ static int qtet_add_controls(struct snd_ice1712 *ice)
qtet_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
/* only capture SPDIF over AK4113 */
return snd_ak4113_build(spec->ak4113,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
@@ -1070,7 +1034,7 @@ static int qtet_init(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char qtet_eeprom[] = {
+static const unsigned char qtet_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC,
1xDACs, SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index 6669c389f336..bcf114152dfd 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -1,24 +1,10 @@
+// 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 <linux/delay.h>
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 1c5d5b22c7a0..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,21 +6,6 @@
*
* 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 <linux/delay.h>
@@ -259,7 +245,7 @@ static const char * const se200pci_sel[] = {
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
};
@@ -302,7 +288,7 @@ static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
static void se200pci_WM8776_init(struct snd_ice1712 *ice)
{
int i;
- static unsigned short default_values[] = {
+ static const unsigned short default_values[] = {
0x100, 0x100, 0x100,
0x100, 0x100, 0x100,
0x000, 0x090, 0x000, 0x000,
@@ -715,7 +701,7 @@ static int se_add_controls(struct snd_ice1712 *ice)
/* entry point */
/****************************************************************************/
-static unsigned char se200pci_eeprom[] = {
+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 */
@@ -734,7 +720,7 @@ static unsigned char se200pci_eeprom[] = {
[ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */
};
-static unsigned char se90pci_eeprom[] = {
+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 */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 5dbb867e642c..47470b07197c 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -1,24 +1,10 @@
+// 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 <linux/delay.h>
@@ -53,7 +39,7 @@ static int k8x800_add_controls(struct snd_ice1712 *ice)
/* EEPROM image */
-static unsigned char k8x800_eeprom[] = {
+static const unsigned char k8x800_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
@@ -69,7 +55,7 @@ static unsigned char k8x800_eeprom[] = {
[ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
-static unsigned char sn25p_eeprom[] = {
+static const unsigned char sn25p_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
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
index 27c03e40c9b1..fe3e243b3854 100644
--- a/sound/pci/ice1712/wm8766.c
+++ b/sound/pci/ice1712/wm8766.c
@@ -1,24 +1,10 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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>
@@ -40,7 +26,7 @@ static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
-static struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = {
+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,
diff --git a/sound/pci/ice1712/wm8766.h b/sound/pci/ice1712/wm8766.h
index 18c8d9d47b38..44ab7c72a3d1 100644
--- a/sound/pci/ice1712/wm8766.h
+++ b/sound/pci/ice1712/wm8766.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_WM8766_H
#define __SOUND_WM8766_H
@@ -7,21 +8,6 @@
* Lowlevel functions for WM8766 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.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
- *
*/
#define WM8766_REG_DACL1 0x00
diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c
index 553669b103c2..493425697bb4 100644
--- a/sound/pci/ice1712/wm8776.c
+++ b/sound/pci/ice1712/wm8776.c
@@ -1,24 +1,10 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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>
@@ -48,13 +34,9 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
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));
- strlcpy(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);
+ kctl = snd_ctl_find_id_mixer(card, ctl_name);
if (!kctl)
return;
index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
@@ -143,7 +125,7 @@ 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 struct snd_wm8776_ctl snd_wm8776_default_ctl[WM8776_CTL_COUNT] = {
+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,
diff --git a/sound/pci/ice1712/wm8776.h b/sound/pci/ice1712/wm8776.h
index 42acef05540c..0d49d0c7488a 100644
--- a/sound/pci/ice1712/wm8776.h
+++ b/sound/pci/ice1712/wm8776.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_WM8776_H
#define __SOUND_WM8776_H
@@ -7,21 +8,6 @@
* Lowlevel functions for WM8776 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.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
- *
*/
#define WM8776_REG_HPLVOL 0x00
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index 9906119e0954..57a79536e7ba 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,21 +7,6 @@
* 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
- *
*/
@@ -132,7 +118,7 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
unsigned char val;
int idx, id;
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
@@ -147,7 +133,6 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
- mutex_unlock(&spec->mute_mutex);
return 0;
}
@@ -469,7 +454,7 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
return;
/* change detected, setting master clock, muting first */
/* due to possible conflicts with mute controls - mutexing */
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
/* we have to remember current mute status for each DAC */
changed = 0xFFFF;
stac9460_dac_mute_all(ice, 0, &changed);
@@ -480,7 +465,6 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
/* unmuting - only originally unmuted dacs -
* i.e. those changed when muting */
stac9460_dac_mute_all(ice, 1, &changed);
- mutex_unlock(&spec->mute_mutex);
}
@@ -491,7 +475,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
/*
* Control tabs
*/
-static struct snd_kcontrol_new stac9640_controls[] = {
+static const struct snd_kcontrol_new stac9640_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -581,12 +565,12 @@ static int wtm_add_controls(struct snd_ice1712 *ice)
static int wtm_init(struct snd_ice1712 *ice)
{
- static unsigned short stac_inits_wtm[] = {
+ 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*/
@@ -613,7 +597,7 @@ static int wtm_init(struct snd_ice1712 *ice)
}
-static unsigned char wtm_eeprom[] = {
+static const unsigned char wtm_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401,
4ADC, 8DAC */
[ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 4c24346340f4..3b53c5e63c29 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -1,29 +1,14 @@
+// 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 <linux/io.h>
@@ -38,38 +23,10 @@
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/initval.h>
-/* for 440MX workaround */
-#include <asm/pgtable.h>
-#ifdef CONFIG_X86
-#include <asm/set_memory.h>
-#endif
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 */
@@ -86,7 +43,7 @@ MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
module_param(ac97_clock, int, 0444);
-MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = 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);
@@ -122,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 */
@@ -351,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) */
@@ -374,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;
};
@@ -414,14 +371,14 @@ 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 */
};
@@ -581,7 +538,8 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
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));
@@ -602,7 +560,8 @@ static void snd_intel8x0_codec_read_test(struct intel8x0 *chip,
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));
@@ -677,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);
@@ -724,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
*/
@@ -750,52 +690,51 @@ static void fill_nocache(void *buf, int size, int nocache)
static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ichdev)
{
unsigned long port = ichdev->reg_offset;
- unsigned long flags;
int status, civ, i, step;
int ack = 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- status = igetbyte(chip, port + ichdev->roff_sr);
- civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
- if (!(status & ICH_BCIS)) {
- step = 0;
- } else if (civ == ichdev->civ) {
- // snd_printd("civ same %d\n", civ);
- step = 1;
- ichdev->civ++;
- ichdev->civ &= ICH_REG_LVI_MASK;
- } else {
- step = civ - ichdev->civ;
- if (step < 0)
- step += ICH_REG_LVI_MASK + 1;
- // if (step != 1)
- // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
- ichdev->civ = civ;
- }
-
- ichdev->position += step * ichdev->fragsize1;
- if (! chip->in_measurement)
- ichdev->position %= ichdev->size;
- ichdev->lvi += step;
- ichdev->lvi &= ICH_REG_LVI_MASK;
- iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
- for (i = 0; i < step; i++) {
- ichdev->lvi_frag++;
- ichdev->lvi_frag %= ichdev->frags;
- ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+ if (!(ichdev->prepared || chip->in_measurement) || ichdev->suspended)
+ return;
+
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ status = igetbyte(chip, port + ichdev->roff_sr);
+ civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
+ if (!(status & ICH_BCIS)) {
+ step = 0;
+ } else if (civ == ichdev->civ) {
+ step = 1;
+ ichdev->civ++;
+ ichdev->civ &= ICH_REG_LVI_MASK;
+ } else {
+ step = civ - ichdev->civ;
+ if (step < 0)
+ step += ICH_REG_LVI_MASK + 1;
+ ichdev->civ = civ;
+ }
+
+ ichdev->position += step * ichdev->fragsize1;
+ if (! chip->in_measurement)
+ ichdev->position %= ichdev->size;
+ ichdev->lvi += step;
+ ichdev->lvi &= ICH_REG_LVI_MASK;
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+ for (i = 0; i < step; i++) {
+ ichdev->lvi_frag++;
+ ichdev->lvi_frag %= ichdev->frags;
+ ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
#if 0
- 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));
+ 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));
#endif
- if (--ichdev->ack == 0) {
- ichdev->ack = ichdev->ack_reload;
- ack = 1;
+ if (--ichdev->ack == 0) {
+ ichdev->ack = ichdev->ack_reload;
+ ack = 1;
+ }
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (ack && ichdev->substream) {
snd_pcm_period_elapsed(ichdev->substream);
}
@@ -850,7 +789,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;
@@ -858,7 +797,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;
@@ -883,7 +822,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;
@@ -892,7 +831,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) {
@@ -909,7 +848,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 */
@@ -938,26 +877,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),
@@ -974,18 +900,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,
@@ -994,7 +916,7 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
unsigned int cnt;
int dbl = runtime->rate > 48000;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
switch (chip->device_type) {
case DEVICE_ALI:
cnt = igetdword(chip, ICHREG(ALI_SCR));
@@ -1040,7 +962,6 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
iputdword(chip, ICHREG(GLOB_CNT), cnt);
break;
}
- spin_unlock_irq(&chip->reg_lock);
}
static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
@@ -1058,6 +979,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;
}
@@ -1069,7 +991,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
int civ, timeout = 10;
unsigned int position;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
do {
civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
@@ -1109,7 +1031,6 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
}
}
ichdev->last_pos = ptr;
- spin_unlock(&chip->reg_lock);
if (ptr >= ichdev->size)
return 0;
return bytes_to_frames(substream->runtime, ptr);
@@ -1180,7 +1101,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;
@@ -1310,12 +1232,12 @@ static int snd_intel8x0_ali_ac97spdifout_open(struct snd_pcm_substream *substrea
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
unsigned int val;
- spin_lock_irq(&chip->reg_lock);
- val = igetdword(chip, ICHREG(ALI_INTERFACECR));
- val |= ICH_ALI_IF_AC97SP;
- iputdword(chip, ICHREG(ALI_INTERFACECR), val);
- /* also needs to set ALI_SC_CODEC_SPDF correctly */
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+ val |= ICH_ALI_IF_AC97SP;
+ iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+ /* also needs to set ALI_SC_CODEC_SPDF correctly */
+ }
return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
}
@@ -1326,11 +1248,10 @@ static int snd_intel8x0_ali_ac97spdifout_close(struct snd_pcm_substream *substre
unsigned int val;
chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
val = igetdword(chip, ICHREG(ALI_INTERFACECR));
val &= ~ICH_ALI_IF_AC97SP;
iputdword(chip, ICHREG(ALI_INTERFACECR), val);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1370,7 +1291,6 @@ static int snd_intel8x0_ali_spdifout_close(struct snd_pcm_substream *substream)
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,
@@ -1381,7 +1301,6 @@ static const struct snd_pcm_ops snd_intel8x0_playback_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,
@@ -1392,7 +1311,6 @@ static const struct snd_pcm_ops snd_intel8x0_capture_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,
@@ -1403,7 +1321,6 @@ static const struct snd_pcm_ops snd_intel8x0_capture_mic_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,
@@ -1414,7 +1331,6 @@ static const struct snd_pcm_ops snd_intel8x0_capture_mic2_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,
@@ -1425,7 +1341,6 @@ static const struct snd_pcm_ops snd_intel8x0_capture2_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,
@@ -1436,7 +1351,6 @@ static const struct snd_pcm_ops snd_intel8x0_spdif_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,
@@ -1447,7 +1361,6 @@ static const struct snd_pcm_ops snd_intel8x0_ali_playback_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,
@@ -1458,7 +1371,6 @@ static const struct snd_pcm_ops snd_intel8x0_ali_capture_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,
@@ -1469,7 +1381,6 @@ static const struct snd_pcm_ops snd_intel8x0_ali_capture_mic_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,
@@ -1481,7 +1392,6 @@ static const 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,
@@ -1492,7 +1402,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,
@@ -1510,8 +1419,11 @@ struct ich_pcm_table {
int ac97_idx;
};
+#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,
- struct ich_pcm_table *rec)
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -1520,7 +1432,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
if (rec->suffix)
sprintf(name, "Intel ICH - %s", rec->suffix);
else
- strcpy(name, "Intel ICH");
+ strscpy(name, "Intel ICH");
err = snd_pcm_new(chip->card, name, device,
rec->playback_ops ? 1 : 0,
rec->capture_ops ? 1 : 0, &pcm);
@@ -1537,12 +1449,12 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
if (rec->suffix)
sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
else
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(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) {
@@ -1566,7 +1478,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
return 0;
}
-static struct ich_pcm_table intel_pcms[] = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1603,7 +1515,7 @@ static struct ich_pcm_table intel_pcms[] = {
},
};
-static struct ich_pcm_table nforce_pcms[] = {
+static const struct ich_pcm_table nforce_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1626,7 +1538,7 @@ static struct ich_pcm_table nforce_pcms[] = {
},
};
-static struct ich_pcm_table ali_pcms[] = {
+static const struct ich_pcm_table ali_pcms[] = {
{
.playback_ops = &snd_intel8x0_ali_playback_ops,
.capture_ops = &snd_intel8x0_ali_capture_ops,
@@ -1661,7 +1573,7 @@ static struct ich_pcm_table ali_pcms[] = {
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:
@@ -2206,12 +2118,12 @@ static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
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,
};
@@ -2274,7 +2186,8 @@ static int 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)
@@ -2290,7 +2203,8 @@ static int 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)
dev_err(chip->card->dev,
"Unable to initialize codec #%d\n", i);
@@ -2331,7 +2245,7 @@ static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
for (i = 1; i < 4; i++) {
if (pcm->r[0].codec[i]) {
- tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT;
+ tmp |= chip->ac97_sdin[pcm->r[0].codec[i]->num] << ICH_DI2L_SHIFT;
break;
}
}
@@ -2396,7 +2310,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 */
};
@@ -2575,11 +2489,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;
}
@@ -2605,8 +2521,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)
@@ -2629,22 +2546,8 @@ 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_SLEEP
/*
* power management
*/
@@ -2655,19 +2558,6 @@ static int intel8x0_suspend(struct device *dev)
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)
@@ -2676,6 +2566,7 @@ static int intel8x0_suspend(struct device *dev)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
return 0;
}
@@ -2696,7 +2587,7 @@ static int intel8x0_resume(struct device *dev)
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) {
@@ -2708,25 +2599,9 @@ static int intel8x0_resume(struct device *dev)
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];
@@ -2745,11 +2620,7 @@ static int intel8x0_resume(struct device *dev)
return 0;
}
-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 */
+static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
@@ -2764,6 +2635,8 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
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;
@@ -2785,53 +2658,53 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
}
snd_intel8x0_setup_periods(chip, ichdev);
port = ichdev->reg_offset;
- spin_lock_irq(&chip->reg_lock);
- chip->in_measurement = 1;
- /* trigger */
- if (chip->device_type != DEVICE_ALI)
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
- else {
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
- iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->in_measurement = 1;
+ /* trigger */
+ if (chip->device_type != DEVICE_ALI)
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
+ else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+ }
+ start_time = ktime_get();
}
- start_time = ktime_get();
- spin_unlock_irq(&chip->reg_lock);
msleep(50);
- spin_lock_irq(&chip->reg_lock);
- /* check the position */
- do {
- civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
- pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
- if (pos1 == 0) {
- udelay(10);
- continue;
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ /* check the position */
+ do {
+ civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
+ pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
+ if (pos1 == 0) {
+ udelay(10);
+ continue;
+ }
+ if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
+ pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+ break;
+ } while (timeout--);
+ if (pos1 == 0) { /* oops, this value is not reliable */
+ pos = 0;
+ } else {
+ pos = ichdev->fragsize1;
+ pos -= pos1 << ichdev->pos_shift;
+ pos += ichdev->position;
}
- if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
- pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
- break;
- } while (timeout--);
- if (pos1 == 0) { /* oops, this value is not reliable */
- pos = 0;
- } else {
- pos = ichdev->fragsize1;
- pos -= pos1 << ichdev->pos_shift;
- pos += ichdev->position;
- }
- chip->in_measurement = 0;
- stop_time = ktime_get();
- /* stop */
- if (chip->device_type == DEVICE_ALI) {
- iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
- iputbyte(chip, port + ICH_REG_OFF_CR, 0);
- while (igetbyte(chip, port + ICH_REG_OFF_CR))
- ;
- } else {
- iputbyte(chip, port + ICH_REG_OFF_CR, 0);
- while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
- ;
+ chip->in_measurement = 0;
+ stop_time = ktime_get();
+ /* stop */
+ if (chip->device_type == DEVICE_ALI) {
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (igetbyte(chip, port + ICH_REG_OFF_CR))
+ ;
+ } else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
+ ;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
}
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
- spin_unlock_irq(&chip->reg_lock);
if (pos == 0) {
dev_err(chip->card->dev,
@@ -2873,7 +2746,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
}
-static struct snd_pci_quirk intel8x0_clock_list[] = {
+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),
@@ -2891,7 +2764,7 @@ static int intel8x0_in_clock_list(struct intel8x0 *chip)
wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list);
if (!wl)
return 0;
- dev_info(chip->card->dev, "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;
@@ -2933,16 +2806,8 @@ static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
static void 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);
-}
-
-static int snd_intel8x0_dev_free(struct snd_device *device)
-{
- 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 {
@@ -2950,10 +2815,10 @@ 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
};
@@ -2988,28 +2853,24 @@ fini:
return result;
}
-static int snd_intel8x0_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0 **r_intel8x0)
+static int snd_intel8x0_init(struct snd_card *card,
+ struct pci_dev *pci,
+ unsigned long device_type)
{
- struct intel8x0 *chip;
+ 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 },
@@ -3017,13 +2878,13 @@ static int 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 },
@@ -3031,18 +2892,12 @@ static int snd_intel8x0_create(struct snd_card *card,
{ ALI_INT_SPDIFIN, 0xa0 },
{ ALI_INT_SPDIFOUT, 0xb0 },
};
- struct ich_reg_info *tbl;
+ const struct ich_reg_info *tbl;
- *r_intel8x0 = 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->device_type = device_type;
chip->card = card;
@@ -3057,42 +2912,34 @@ static int snd_intel8x0_create(struct snd_card *card,
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 = pcim_request_all_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) {
- dev_err(card->dev, "AC'97 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);
}
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
- chip->bmaddr = pci_iomap(pci, 3, 0);
- else
- chip->bmaddr = pci_iomap(pci, 1, 0);
- port_inited:
- if (!chip->bmaddr) {
- dev_err(card->dev, "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
chip->bdbars_count = bdbars[device_type];
/* initialize offsets */
@@ -3128,24 +2975,20 @@ static int 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);
- dev_err(card->dev, "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;
}
@@ -3178,26 +3021,25 @@ static int 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, KBUILD_MODNAME, chip)) {
dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0_free(chip);
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;
- }
+ card->private_free = snd_intel8x0_free;
- *r_intel8x0 = chip;
return 0;
}
@@ -3230,12 +3072,12 @@ static struct shortname_table {
{ 0, NULL },
};
-static struct snd_pci_quirk spdif_aclink_defaults[] = {
+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 */
+/* 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;
@@ -3255,36 +3097,38 @@ static int check_default_spdif_aclink(struct pci_dev *pci)
return 0;
}
-static int 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_new(&pci->dev, 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);
- strcpy(card->driver, "ICH");
+ strscpy(card->driver, "ICH");
if (!spdif_aclink) {
switch (pci_id->driver_data) {
case DEVICE_NFORCE:
- strcpy(card->driver, "NFORCE");
+ strscpy(card->driver, "NFORCE");
break;
case DEVICE_INTEL_ICH4:
- strcpy(card->driver, "ICH4");
+ strscpy(card->driver, "ICH4");
}
}
- strcpy(card->shortname, "Intel ICH");
+ strscpy(card->shortname, "Intel ICH");
for (name = shortnames; name->id; name++) {
if (pci->device == name->id) {
- strcpy(card->shortname, name->s);
+ strscpy(card->shortname, name->s);
break;
}
}
@@ -3299,21 +3143,16 @@ static int 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);
@@ -3330,26 +3169,26 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0_probe(pci, pci_id));
}
static struct pci_driver intel8x0_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_intel8x0_ids,
.probe = snd_intel8x0_probe,
- .remove = snd_intel8x0_remove,
.driver = {
- .pm = INTEL8X0_PM_OPS,
+ .pm = &intel8x0_pm,
},
};
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 3a4769a97d29..84e1b7ea34e2 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,22 +6,6 @@
*
* 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 <linux/io.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 */
@@ -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,7 +182,7 @@ 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 */
@@ -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))
@@ -372,7 +342,8 @@ static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97,
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));
@@ -395,7 +366,7 @@ static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97,
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);
@@ -452,7 +423,6 @@ static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *i
civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
if (civ == ichdev->civ) {
- // snd_printd("civ same %d\n", civ);
step = 1;
ichdev->civ++;
ichdev->civ &= ICH_REG_LVI_MASK;
@@ -460,8 +430,6 @@ static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *i
step = civ - ichdev->civ;
if (step < 0)
step += ICH_REG_LVI_MASK + 1;
- // if (step != 1)
- // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
ichdev->civ = civ;
}
@@ -503,16 +471,13 @@ static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id)
unsigned int status;
unsigned int i;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
status = igetdword(chip, chip->int_sta_reg);
- if (status == 0xffffffff) { /* we are not yet resumed */
- spin_unlock(&chip->reg_lock);
+ if (status == 0xffffffff) /* we are not yet resumed */
return IRQ_NONE;
- }
if ((status & chip->int_sta_mask) == 0) {
if (status)
iputdword(chip, chip->int_sta_reg, status);
- spin_unlock(&chip->reg_lock);
return IRQ_NONE;
}
@@ -524,7 +489,6 @@ static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id)
/* ack them */
iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -568,17 +532,6 @@ static int snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cm
return 0;
}
-static int snd_intel8x0m_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_intel8x0m_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer(struct snd_pcm_substream *substream)
{
struct intel8x0m *chip = snd_pcm_substream_chip(substream);
@@ -688,9 +641,6 @@ static int snd_intel8x0m_capture_close(struct snd_pcm_substream *substream)
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_intel8x0m_hw_params,
- .hw_free = snd_intel8x0m_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
.trigger = snd_intel8x0m_pcm_trigger,
.pointer = snd_intel8x0m_pcm_pointer,
@@ -699,9 +649,6 @@ static const struct snd_pcm_ops snd_intel8x0m_playback_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_intel8x0m_hw_params,
- .hw_free = snd_intel8x0m_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
.trigger = snd_intel8x0m_pcm_trigger,
.pointer = snd_intel8x0m_pcm_pointer,
@@ -718,7 +665,7 @@ struct ich_pcm_table {
};
static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
- struct ich_pcm_table *rec)
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -727,7 +674,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
if (rec->suffix)
sprintf(name, "Intel ICH - %s", rec->suffix);
else
- strcpy(name, "Intel ICH");
+ strscpy(name, "Intel ICH");
err = snd_pcm_new(chip->card, name, device,
rec->playback_ops ? 1 : 0,
rec->capture_ops ? 1 : 0, &pcm);
@@ -745,18 +692,18 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
if (rec->suffix)
sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
else
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(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[] = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.suffix = "Modem",
.playback_ops = &snd_intel8x0m_playback_ops,
@@ -769,7 +716,7 @@ static struct ich_pcm_table intel_pcms[] = {
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;
@@ -833,7 +780,7 @@ static int snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
struct snd_ac97 *x97;
int err;
unsigned int glob_sta = 0;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_intel8x0m_codec_write,
.read = snd_intel8x0m_codec_read,
};
@@ -847,7 +794,8 @@ static int snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
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_intel8x0m_mixer_free_ac97_bus;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
@@ -856,7 +804,8 @@ static int snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
ac97.pci = chip->pci;
ac97.num = glob_sta & ICH_SCR ? 1 : 0;
- if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) {
+ 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)
@@ -962,7 +911,7 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
}
if (chip->device_type == DEVICE_SIS) {
- /* unmute the output on SIS7012 */
+ /* unmute the output on SIS7013 */
iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
}
@@ -974,7 +923,8 @@ 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 */
@@ -990,8 +940,9 @@ static int snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing)
return 0;
}
-static int snd_intel8x0m_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)
@@ -1005,19 +956,8 @@ static int snd_intel8x0m_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_SLEEP
/*
* power management
*/
@@ -1025,15 +965,13 @@ static int intel8x0m_suspend(struct device *dev)
{
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;
}
return 0;
}
@@ -1052,6 +990,7 @@ static int intel8x0m_resume(struct device *dev)
return -EIO;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
snd_intel8x0m_chip_init(chip, 0);
snd_ac97_resume(chip->ac97);
@@ -1059,11 +998,7 @@ static int intel8x0m_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
@@ -1087,16 +1022,8 @@ static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
static void 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);
-}
-
-static int snd_intel8x0m_dev_free(struct snd_device *device)
-{
- struct intel8x0m *chip = device->device_data;
- return snd_intel8x0m_free(chip);
+ snd_card_ro_proc_new(chip->card, "intel8x0m", chip,
+ snd_intel8x0m_proc_read);
}
struct ich_reg_info {
@@ -1104,83 +1031,49 @@ struct ich_reg_info {
unsigned int offset;
};
-static int snd_intel8x0m_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0m **r_intel8x0m)
+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_intel8x0m_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_intel8x0m = 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 = pcim_request_all_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) {
- dev_err(card->dev, "AC'97 space ioremap problem\n");
- snd_intel8x0m_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) {
- dev_err(card->dev, "Controller space ioremap problem\n");
- snd_intel8x0m_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_intel8x0m_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, chip)) {
- dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0m_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;
@@ -1206,35 +1099,44 @@ static int 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_intel8x0m_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_intel8x0m_chip_init(chip, 1)) < 0) {
- snd_intel8x0m_free(chip);
- return err;
- }
+ pci_set_master(pci);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_intel8x0m_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;
+
+ card->private_free = snd_intel8x0m_free;
- *r_intel8x0m = chip;
return 0;
}
@@ -1264,68 +1166,65 @@ static struct shortname_table {
{ 0 },
};
-static int 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_new(&pci->dev, 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");
+ strscpy(card->driver, "ICH-MODEM");
+ strscpy(card->shortname, "Intel ICH");
for (name = shortnames; name->id; name++) {
if (pci->device == name->id) {
- strcpy(card->shortname, name->s);
+ strscpy(card->shortname, name->s);
break;
}
}
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_intel8x0m_mixer(chip, ac97_clock)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_mixer(chip, ac97_clock);
+ if (err < 0)
return err;
- }
- if ((err = snd_intel8x0m_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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0m_probe(pci, pci_id));
}
static struct pci_driver intel8x0m_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_intel8x0m_ids,
.probe = snd_intel8x0m_probe,
- .remove = snd_intel8x0m_remove,
.driver = {
- .pm = INTEL8X0M_PM_OPS,
+ .pm = &intel8x0m_pm,
},
};
diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile
index f11ce1b1b3d4..ab0186ffbd58 100644
--- a/sound/pci/korg1212/Makefile
+++ b/sound/pci/korg1212/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-korg1212-objs := korg1212.o
+snd-korg1212-y := korg1212.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 4206ba44d8bb..d16acf83668a 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>
@@ -42,14 +28,14 @@
// ----------------------------------------------------------------------------
#define K1212_DEBUG_LEVEL 0
#if K1212_DEBUG_LEVEL > 0
-#define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args)
+#define K1212_DEBUG_PRINTK(fmt, args...) pr_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)
+#define K1212_DEBUG_PRINTK_VERBOSE(fmt, args...) pr_debug(fmt, ##args)
#else
-#define K1212_DEBUG_PRINTK_VERBOSE(fmt,...)
+#define K1212_DEBUG_PRINTK_VERBOSE(fmt, ...)
#endif
// ----------------------------------------------------------------------------
@@ -322,9 +308,6 @@ struct snd_korg1212 {
spinlock_t lock;
struct mutex open_mutex;
- struct timer_list timer; /* timer callback for checking ack of stop request */
- int stop_pending_cnt; /* counter for stop pending check */
-
wait_queue_head_t wait;
unsigned long iomem;
@@ -334,10 +317,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;
@@ -396,13 +379,12 @@ struct snd_korg1212 {
unsigned long totalerrorcnt; // Total Error Count
int dsp_is_loaded;
- int dsp_stop_is_processed;
+ int dsp_stop_processing;
};
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 */
@@ -429,7 +411,7 @@ static const struct pci_device_id 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",
@@ -469,7 +451,7 @@ static const char * const 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
@@ -580,66 +562,25 @@ static int snd_korg1212_Send1212Command(struct snd_korg1212 *korg1212,
/* spinlock already held */
static void snd_korg1212_SendStop(struct snd_korg1212 *korg1212)
{
- if (! korg1212->stop_pending_cnt) {
- korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
- /* program the timer */
- korg1212->stop_pending_cnt = HZ;
- mod_timer(&korg1212->timer, jiffies + 1);
- }
+ korg1212->dsp_stop_processing = 1;
+ korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
}
static void snd_korg1212_SendStopAndWait(struct snd_korg1212 *korg1212)
{
- unsigned long flags;
- spin_lock_irqsave(&korg1212->lock, flags);
- korg1212->dsp_stop_is_processed = 0;
- snd_korg1212_SendStop(korg1212);
- spin_unlock_irqrestore(&korg1212->lock, flags);
- wait_event_timeout(korg1212->wait, korg1212->dsp_stop_is_processed, (HZ * 3) / 2);
-}
-
-/* timer callback for checking the ack of stop request */
-static void snd_korg1212_timer_func(struct timer_list *t)
-{
- struct snd_korg1212 *korg1212 = from_timer(korg1212, t, timer);
- unsigned long flags;
-
- spin_lock_irqsave(&korg1212->lock, flags);
- if (korg1212->sharedBufferPtr->cardCommand == 0) {
- /* ack'ed */
- korg1212->stop_pending_cnt = 0;
- korg1212->dsp_stop_is_processed = 1;
- wake_up(&korg1212->wait);
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: Stop ack'ed [%s]\n",
- stateName[korg1212->cardState]);
- } else {
- if (--korg1212->stop_pending_cnt > 0) {
- /* reprogram timer */
- mod_timer(&korg1212->timer, jiffies + 1);
- } else {
- snd_printd("korg1212_timer_func timeout\n");
- korg1212->sharedBufferPtr->cardCommand = 0;
- korg1212->dsp_stop_is_processed = 1;
- wake_up(&korg1212->wait);
- K1212_DEBUG_PRINTK("K1212_DEBUG: Stop timeout [%s]\n",
- stateName[korg1212->cardState]);
- }
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ snd_korg1212_SendStop(korg1212);
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ wait_event_timeout(korg1212->wait, !korg1212->dsp_stop_processing, HZ);
}
static int snd_korg1212_TurnOnIdleMonitor(struct snd_korg1212 *korg1212)
{
- unsigned long flags;
- int rc;
-
udelay(INTERCOMMAND_DELAY);
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
korg1212->idleMonitorOn = 1;
- rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
- K1212_MODE_MonitorOn, 0, 0, 0);
- spin_unlock_irqrestore(&korg1212->lock, flags);
- return rc;
+ return snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_MonitorOn, 0, 0, 0);
}
static void snd_korg1212_TurnOffIdleMonitor(struct snd_korg1212 *korg1212)
@@ -659,13 +600,12 @@ static int snd_korg1212_OpenCard(struct snd_korg1212 * korg1212)
{
K1212_DEBUG_PRINTK("K1212_DEBUG: OpenCard [%s] %d\n",
stateName[korg1212->cardState], korg1212->opencnt);
- mutex_lock(&korg1212->open_mutex);
+ guard(mutex)(&korg1212->open_mutex);
if (korg1212->opencnt++ == 0) {
snd_korg1212_TurnOffIdleMonitor(korg1212);
snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
}
- mutex_unlock(&korg1212->open_mutex);
return 1;
}
@@ -674,11 +614,9 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard [%s] %d\n",
stateName[korg1212->cardState], korg1212->opencnt);
- mutex_lock(&korg1212->open_mutex);
- if (--(korg1212->opencnt)) {
- mutex_unlock(&korg1212->open_mutex);
+ guard(mutex)(&korg1212->open_mutex);
+ if (--(korg1212->opencnt))
return 0;
- }
if (korg1212->cardState == K1212_STATE_SETUP) {
int rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
@@ -686,10 +624,8 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard - RC = %d [%s]\n",
rc, stateName[korg1212->cardState]);
- if (rc != K1212_CMDRET_Success) {
- mutex_unlock(&korg1212->open_mutex);
+ if (rc != K1212_CMDRET_Success)
return 0;
- }
} else if (korg1212->cardState > K1212_STATE_SETUP) {
snd_korg1212_SendStopAndWait(korg1212);
}
@@ -699,7 +635,6 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
}
- mutex_unlock(&korg1212->open_mutex);
return 0;
}
@@ -827,12 +762,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
@@ -897,7 +832,6 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
u16 controlValue; // this keeps the current value to be written to
// the card's eeprom control register.
u16 count;
- unsigned long flags;
K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity [%s]\n",
stateName[korg1212->cardState]);
@@ -918,7 +852,7 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
} else
monModeSet = 0;
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
// ----------------------------------------------------------------------------
// we are about to send new values to the card, so clear the new values queued
@@ -1027,8 +961,6 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
rc, stateName[korg1212->cardState]);
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
-
return 1;
}
@@ -1120,7 +1052,7 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
if (!doorbellValue)
return IRQ_NONE;
- spin_lock(&korg1212->lock);
+ guard(spinlock)(&korg1212->lock);
writel(doorbellValue, korg1212->inDoorbellPtr);
@@ -1146,11 +1078,13 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: IRQ DMAE count - %ld, %x, [%s].\n",
korg1212->irqcount, doorbellValue,
stateName[korg1212->cardState]);
- snd_printk(KERN_ERR "korg1212: DMA Error\n");
+ dev_err(korg1212->card->dev, "korg1212: DMA Error\n");
korg1212->errorcnt++;
korg1212->totalerrorcnt++;
korg1212->sharedBufferPtr->cardCommand = 0;
+ korg1212->dsp_stop_processing = 0;
snd_korg1212_setCardState(korg1212, K1212_STATE_ERRORSTOP);
+ wake_up(&korg1212->wait);
break;
// ------------------------------------------------------------------------
@@ -1162,6 +1096,8 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
korg1212->irqcount, doorbellValue,
stateName[korg1212->cardState]);
korg1212->sharedBufferPtr->cardCommand = 0;
+ korg1212->dsp_stop_processing = 0;
+ wake_up(&korg1212->wait);
break;
default:
@@ -1194,8 +1130,6 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
korg1212->inIRQ--;
- spin_unlock(&korg1212->lock);
-
return IRQ_HANDLED;
}
@@ -1215,8 +1149,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]);
@@ -1287,8 +1221,8 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
#if K1212_DEBUG_LEVEL > 0
if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
(void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_silence KERNEL EFAULT dst=%p iter=%d\n",
- dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT dst=%p iter=%d\n",
+ __func__, dst, i);
return -EFAULT;
}
#endif
@@ -1300,8 +1234,7 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
}
static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
- void __user *dst, int pos, int count,
- bool in_kernel)
+ struct iov_iter *dst, int pos, int count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1321,24 +1254,21 @@ static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
#if K1212_DEBUG_LEVEL > 0
if ( (void *) src < (void *) korg1212->recordDataBufsPtr ||
(void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n",
+ __func__, src, dst->kvec.iov_base, i);
return -EFAULT;
}
#endif
- if (in_kernel)
- memcpy((void *)dst, src, size);
- else if (copy_to_user(dst, src, size))
+ if (copy_to_iter(src, size, dst) != size)
return -EFAULT;
src++;
- dst += size;
}
return 0;
}
static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
- void __user *src, int pos, int count,
- bool in_kernel)
+ struct iov_iter *src, int pos, int count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1350,8 +1280,8 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
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);
+ K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: %s pos=%d size=%d count=%d\n",
+ __func__, pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
return -EINVAL;
@@ -1360,16 +1290,14 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
#if K1212_DEBUG_LEVEL > 0
if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
(void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n",
+ __func__, src->kvec.iov_base, dst, i);
return -EFAULT;
}
#endif
- if (in_kernel)
- memcpy((void *)dst, src, size);
- else if (copy_from_user(dst, src, size))
+ if (copy_from_iter(dst, size, src) != size)
return -EFAULT;
dst++;
- src += size;
}
return 0;
@@ -1387,7 +1315,6 @@ static void snd_korg1212_free_pcm(struct snd_pcm *pcm)
static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1397,17 +1324,15 @@ 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);
-
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->playback_substream = substream;
- korg1212->playback_pid = current->pid;
- korg1212->periodsize = K1212_PERIODS;
- korg1212->channels = K1212_CHANNELS;
- korg1212->errorcnt = 0;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_play);
+
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->playback_substream = substream;
+ korg1212->playback_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+ korg1212->errorcnt = 0;
+ }
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
kPlayBufferFrames);
@@ -1418,7 +1343,6 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1428,16 +1352,14 @@ 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);
-
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->capture_substream = substream;
- korg1212->capture_pid = current->pid;
- korg1212->periodsize = K1212_PERIODS;
- korg1212->channels = K1212_CHANNELS;
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_rec);
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->capture_substream = substream;
+ korg1212->capture_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+ }
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
kPlayBufferFrames);
@@ -1446,7 +1368,6 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_close [%s]\n",
@@ -1454,13 +1375,11 @@ static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
snd_korg1212_silence(korg1212, 0, K1212_MAX_SAMPLES, 0, korg1212->channels * 2);
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->playback_pid = -1;
- korg1212->playback_substream = NULL;
- korg1212->periodsize = 0;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->playback_pid = -1;
+ korg1212->playback_substream = NULL;
+ korg1212->periodsize = 0;
+ }
snd_korg1212_CloseCard(korg1212);
return 0;
@@ -1468,19 +1387,16 @@ static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
static int snd_korg1212_capture_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_close [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->capture_pid = -1;
- korg1212->capture_substream = NULL;
- korg1212->periodsize = 0;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->capture_pid = -1;
+ korg1212->capture_substream = NULL;
+ korg1212->periodsize = 0;
+ }
snd_korg1212_CloseCard(korg1212);
return 0;
@@ -1506,7 +1422,6 @@ static int snd_korg1212_ioctl(struct snd_pcm_substream *substream,
static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
int err;
pid_t this_pid;
@@ -1515,7 +1430,7 @@ static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_hw_params [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
this_pid = korg1212->playback_pid;
@@ -1533,28 +1448,31 @@ static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
*/
if ((int)params_rate(params) != korg1212->clkRate) {
- spin_unlock_irqrestore(&korg1212->lock, flags);
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return -EBUSY;
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
return 0;
}
- if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ err = snd_korg1212_SetRate(korg1212, params_rate(params));
+ if (err < 0)
return err;
- }
korg1212->channels = params_channels(params);
korg1212->periodsize = K1212_PERIOD_BYTES;
- spin_unlock_irqrestore(&korg1212->lock, flags);
-
return 0;
}
+static int snd_korg1212_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+
+ wait_event_timeout(korg1212->wait, !korg1212->dsp_stop_processing, HZ);
+ return 0;
+}
+
static int snd_korg1212_prepare(struct snd_pcm_substream *substream)
{
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1563,27 +1481,13 @@ static int snd_korg1212_prepare(struct snd_pcm_substream *substream)
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irq(&korg1212->lock);
-
- /* FIXME: we should wait for ack! */
- if (korg1212->stop_pending_cnt > 0) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare - Stop is pending... [%s]\n",
- stateName[korg1212->cardState]);
- spin_unlock_irq(&korg1212->lock);
- return -EAGAIN;
- /*
- korg1212->sharedBufferPtr->cardCommand = 0;
- del_timer(&korg1212->timer);
- korg1212->stop_pending_cnt = 0;
- */
- }
+ guard(spinlock_irq)(&korg1212->lock);
+ korg1212->dsp_stop_processing = 0;
rc = snd_korg1212_SetupForPlay(korg1212);
korg1212->currentBuffer = 0;
- spin_unlock_irq(&korg1212->lock);
-
return rc ? -EINVAL : 0;
}
@@ -1596,7 +1500,7 @@ static int snd_korg1212_trigger(struct snd_pcm_substream *substream,
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger [%s] cmd=%d\n",
stateName[korg1212->cardState], cmd);
- spin_lock(&korg1212->lock);
+ guard(spinlock)(&korg1212->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/*
@@ -1624,7 +1528,6 @@ static int snd_korg1212_trigger(struct snd_pcm_substream *substream,
rc = 1;
break;
}
- spin_unlock(&korg1212->lock);
return rc ? -EINVAL : 0;
}
@@ -1656,17 +1559,9 @@ 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, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
- 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);
+ return snd_korg1212_copy_from(substream, src, pos, count);
}
static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
@@ -1684,17 +1579,9 @@ static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
- return snd_korg1212_copy_to(substream, dst, pos, count, false);
-}
-
-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);
+ return snd_korg1212_copy_to(substream, dst, pos, count);
}
static const struct snd_pcm_ops snd_korg1212_playback_ops = {
@@ -1704,9 +1591,9 @@ static const struct snd_pcm_ops snd_korg1212_playback_ops = {
.hw_params = snd_korg1212_hw_params,
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
+ .sync_stop = snd_korg1212_sync_stop,
.pointer = snd_korg1212_playback_pointer,
- .copy_user = snd_korg1212_playback_copy,
- .copy_kernel = snd_korg1212_playback_copy_kernel,
+ .copy = snd_korg1212_playback_copy,
.fill_silence = snd_korg1212_playback_silence,
};
@@ -1717,9 +1604,9 @@ static const struct snd_pcm_ops snd_korg1212_capture_ops = {
.hw_params = snd_korg1212_hw_params,
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
+ .sync_stop = snd_korg1212_sync_stop,
.pointer = snd_korg1212_capture_pointer,
- .copy_user = snd_korg1212_capture_copy,
- .copy_kernel = snd_korg1212_capture_copy_kernel,
+ .copy = snd_korg1212_capture_copy,
};
/*
@@ -1740,15 +1627,13 @@ static int snd_korg1212_control_phase_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i = kcontrol->private_value;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
u->value.integer.value[0] = korg1212->volumePhase[i];
if (i >= 8)
u->value.integer.value[1] = korg1212->volumePhase[i+1];
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1759,7 +1644,7 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
int change = 0;
int i, val;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1785,8 +1670,6 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
@@ -1806,7 +1689,7 @@ static int snd_korg1212_control_volume_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]);
@@ -1814,8 +1697,6 @@ static int snd_korg1212_control_volume_get(struct snd_kcontrol *kcontrol,
if (i >= 8)
u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]);
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1827,7 +1708,7 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
int i;
int val;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1853,8 +1734,6 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
@@ -1872,7 +1751,7 @@ static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i];
@@ -1880,8 +1759,6 @@ static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
if (i >= 8)
u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1];
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1891,7 +1768,7 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int change = 0, i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1911,8 +1788,6 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
@@ -1931,13 +1806,11 @@ static int snd_korg1212_control_get(struct snd_kcontrol *kcontrol,
{
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
u->value.integer.value[0] = korg1212->leftADCInSens;
u->value.integer.value[1] = korg1212->rightADCInSens;
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1947,22 +1820,20 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int change = 0;
- spin_lock_irq(&korg1212->lock);
-
- if (u->value.integer.value[0] >= k1212MinADCSens &&
- u->value.integer.value[0] <= k1212MaxADCSens &&
- u->value.integer.value[0] != korg1212->leftADCInSens) {
- korg1212->leftADCInSens = u->value.integer.value[0];
- change = 1;
- }
- if (u->value.integer.value[1] >= k1212MinADCSens &&
- u->value.integer.value[1] <= k1212MaxADCSens &&
- u->value.integer.value[1] != korg1212->rightADCInSens) {
- korg1212->rightADCInSens = u->value.integer.value[1];
- change = 1;
- }
-
- spin_unlock_irq(&korg1212->lock);
+ scoped_guard(spinlock_irq, &korg1212->lock) {
+ if (u->value.integer.value[0] >= k1212MinADCSens &&
+ u->value.integer.value[0] <= k1212MaxADCSens &&
+ u->value.integer.value[0] != korg1212->leftADCInSens) {
+ korg1212->leftADCInSens = u->value.integer.value[0];
+ change = 1;
+ }
+ if (u->value.integer.value[1] >= k1212MinADCSens &&
+ u->value.integer.value[1] <= k1212MaxADCSens &&
+ u->value.integer.value[1] != korg1212->rightADCInSens) {
+ korg1212->rightADCInSens = u->value.integer.value[1];
+ change = 1;
+ }
+ }
if (change)
snd_korg1212_WriteADCSensitivity(korg1212);
@@ -1981,11 +1852,9 @@ static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol,
{
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
ucontrol->value.enumerated.item[0] = korg1212->clkSource;
-
- spin_unlock_irq(&korg1212->lock);
return 0;
}
@@ -1997,10 +1866,9 @@ static int snd_korg1212_control_sync_put(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
change = val != korg1212->clkSource;
snd_korg1212_SetClockSource(korg1212, val);
- spin_unlock_irq(&korg1212->lock);
return change;
}
@@ -2033,7 +1901,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"),
@@ -2090,106 +1958,40 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry,
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;
- }
-
-#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;
-}
+ struct snd_korg1212 *korg1212 = card->private_data;
-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 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);
- timer_setup(&korg1212->timer, snd_korg1212_timer_func, 0);
korg1212->irq = -1;
korg1212->clkSource = K1212_CLKIDX_Local;
@@ -2211,11 +2013,9 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
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_request_all_regions(pci, "korg1212");
+ if (err < 0)
return err;
- }
korg1212->iomem = pci_resource_start(korg1212->pci, 0);
korg1212->ioport = pci_resource_start(korg1212->pci, 1);
@@ -2235,24 +2035,22 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
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(pci, 0, 0);
+ if (!korg1212->iobase)
+ return -ENOMEM;
- err = request_irq(pci->irq, snd_korg1212_interrupt,
+ err = devm_request_irq(&pci->dev, pci->irq, snd_korg1212_interrupt,
IRQF_SHARED,
KBUILD_MODNAME, korg1212);
if (err) {
- snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq);
- snd_korg1212_free(korg1212);
+ dev_err(&pci->dev, "korg1212: unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
korg1212->irq = pci->irq;
+ card->sync_irq = korg1212->irq;
+ card->private_free = snd_korg1212_free;
pci_set_master(korg1212->pci);
@@ -2291,41 +2089,36 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
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);
@@ -2348,24 +2141,22 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev);
if (err < 0) {
- snd_printk(KERN_ERR "firmware not available\n");
- snd_korg1212_free(korg1212);
+ dev_err(&pci->dev, "firmware not available\n");
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);
@@ -2374,11 +2165,6 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
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);
@@ -2399,12 +2185,13 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
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;
korg1212->pcm->private_free = snd_korg1212_free_pcm;
- strcpy(korg1212->pcm->name, "korg1212");
+ strscpy(korg1212->pcm->name, "korg1212");
snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
@@ -2419,10 +2206,8 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
}
snd_korg1212_proc_init(korg1212);
-
- * rchip = korg1212;
- return 0;
+ return 0;
}
/*
@@ -2445,42 +2230,39 @@ snd_korg1212_probe(struct pci_dev *pci,
dev++;
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "korg1212");
+ strscpy(card->shortname, "korg1212");
sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
korg1212->iomem, korg1212->irq);
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 snd_korg1212_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
+ error:
+ snd_card_free(card);
+ return err;
}
static struct pci_driver korg1212_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_korg1212_ids,
.probe = snd_korg1212_probe,
- .remove = snd_korg1212_remove,
};
module_pci_driver(korg1212_driver);
diff --git a/sound/pci/lola/Makefile b/sound/pci/lola/Makefile
index 8178a2a59d00..8fdb5e5a776b 100644
--- a/sound/pci/lola/Makefile
+++ b/sound/pci/lola/Makefile
@@ -1,3 +1,4 @@
+# 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
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
index 9ff600084973..34a3ba17deb4 100644
--- a/sound/pci/lola/lola.c
+++ b/sound/pci/lola/lola.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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/kernel.h>
@@ -67,7 +54,6 @@ MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
*/
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram, Lola}}");
MODULE_DESCRIPTION("Digigram Lola driver");
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
@@ -88,7 +74,6 @@ 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;
@@ -97,7 +82,7 @@ static int corb_send_verb(struct lola *chip, unsigned int nid,
chip->last_extdata = extdata;
data |= (nid << 20) | (verb << 8);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
unsigned int wp = chip->corb.wp + 1;
wp %= LOLA_CORB_ENTRIES;
@@ -109,7 +94,6 @@ static int corb_send_verb(struct lola *chip, unsigned int nid,
smp_wmb();
ret = 0;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return ret;
}
@@ -358,20 +342,18 @@ static void lola_irq_disable(struct lola *chip)
static int setup_corb_rirb(struct lola *chip)
{
- int err;
unsigned char tmp;
unsigned long end_time;
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &chip->rb);
- if (err < 0)
- return err;
+ 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 = (u32 *)chip->rb.area;
- chip->rirb.addr = chip->rb.addr + 2048;
- chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+ 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);
@@ -543,56 +525,32 @@ static void lola_stop_hw(struct lola *chip)
lola_irq_disable(chip);
}
-static void lola_free(struct lola *chip)
+static void lola_free(struct snd_card *card)
{
+ struct lola *chip = card->private_data;
+
if (chip->initialized)
lola_stop_hw(chip);
- lola_free_pcm(chip);
lola_free_mixer(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
- iounmap(chip->bar[0].remap_addr);
- iounmap(chip->bar[1].remap_addr);
- if (chip->rb.area)
- snd_dma_free_pages(&chip->rb);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
-}
-
-static int lola_dev_free(struct snd_device *device)
-{
- lola_free(device->device_data);
- return 0;
}
-static int lola_create(struct snd_card *card, struct pci_dev *pci,
- int dev, struct lola **rchip)
+static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev)
{
- struct lola *chip;
+ struct lola *chip = card->private_data;
int err;
unsigned int dever;
- static struct snd_device_ops ops = {
- .dev_free = lola_dev_free,
- };
-
- *rchip = NULL;
+ void __iomem *iomem;
- 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;
- }
-
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) {
@@ -621,37 +579,33 @@ static int lola_create(struct snd_card *card, struct pci_dev *pci,
chip->sample_rate_min = 16000;
}
- err = pci_request_regions(pci, DRVNAME);
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
+ iomem = pcim_iomap_region(pci, 0, DRVNAME);
+ if (IS_ERR(iomem))
+ return PTR_ERR(iomem);
+ chip->bar[0].remap_addr = iomem;
chip->bar[0].addr = pci_resource_start(pci, 0);
- chip->bar[0].remap_addr = pci_ioremap_bar(pci, 0);
+
+ iomem = pcim_iomap_region(pci, 2, DRVNAME);
+ if (IS_ERR(iomem))
+ return PTR_ERR(iomem);
+
+ chip->bar[1].remap_addr = iomem;
chip->bar[1].addr = pci_resource_start(pci, 2);
- chip->bar[1].remap_addr = pci_ioremap_bar(pci, 2);
- if (!chip->bar[0].remap_addr || !chip->bar[1].remap_addr) {
- dev_err(chip->card->dev, "ioremap error\n");
- err = -ENXIO;
- goto errout;
- }
pci_set_master(pci);
err = reset_controller(chip);
if (err < 0)
- goto errout;
+ return err;
- if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, chip)) {
+ 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);
- err = -EBUSY;
- goto errout;
+ return -EBUSY;
}
chip->irq = pci->irq;
- synchronize_irq(chip->irq);
+ card->sync_irq = chip->irq;
dever = lola_readl(chip, BAR1, DEVER);
chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
@@ -667,40 +621,28 @@ static int lola_create(struct snd_card *card, struct pci_dev *pci,
(!chip->pcm[CAPT].num_streams &&
!chip->pcm[PLAY].num_streams)) {
dev_err(chip->card->dev, "invalid DEVER = %x\n", dever);
- err = -EINVAL;
- goto errout;
+ return -EINVAL;
}
err = setup_corb_rirb(chip);
if (err < 0)
- goto errout;
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- dev_err(chip->card->dev, "Error creating device [card]!\n");
- goto errout;
- }
+ return err;
- strcpy(card->driver, "Lola");
- strlcpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ strscpy(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);
+ strscpy(card->mixername, card->shortname);
lola_irq_enable(chip);
chip->initialized = 1;
- *rchip = chip;
return 0;
-
- errout:
- lola_free(chip);
- return err;
}
-static int lola_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -714,47 +656,45 @@ static int lola_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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) {
dev_err(&pci->dev, "Error creating card!\n");
return err;
}
+ chip = card->private_data;
- err = lola_create(card, pci, dev, &chip);
+ err = lola_create(card, pci, dev);
if (err < 0)
- goto out_free;
- card->private_data = chip;
+ return err;
err = lola_parse_tree(chip);
if (err < 0)
- goto out_free;
+ return err;
err = lola_create_pcm(chip);
if (err < 0)
- goto out_free;
+ return err;
err = lola_create_mixer(chip);
if (err < 0)
- goto out_free;
+ return err;
lola_proc_debug_new(chip);
err = snd_card_register(card);
if (err < 0)
- goto out_free;
+ return err;
pci_set_drvdata(pci, card);
dev++;
- return err;
-out_free:
- snd_card_free(card);
- return err;
+ return 0;
}
-static void lola_remove(struct pci_dev *pci)
+static int lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
+ return snd_card_free_on_error(&pci->dev, __lola_probe(pci, pci_id));
}
/* PCI IDs */
@@ -769,7 +709,6 @@ static struct pci_driver lola_driver = {
.name = KBUILD_MODNAME,
.id_table = lola_ids,
.probe = lola_probe,
- .remove = lola_remove,
};
module_pci_driver(lola_driver);
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
index f0b100059efd..25f72f9e3f9b 100644
--- a/sound/pci/lola/lola.h
+++ b/sound/pci/lola/lola.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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 _LOLA_H
@@ -220,7 +207,7 @@ struct lola_bar {
/* CORB/RIRB */
struct lola_rb {
- u32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ __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 */
@@ -275,7 +262,7 @@ struct lola_mixer_array {
struct lola_mixer_widget {
unsigned int nid;
unsigned int caps;
- struct lola_mixer_array __user *array;
+ struct lola_mixer_array __iomem *array;
struct lola_mixer_array *array_saved;
unsigned int src_stream_outs;
unsigned int src_phys_ins;
@@ -316,7 +303,7 @@ struct lola_stream {
struct lola_pcm {
unsigned int num_streams;
- struct snd_dma_buffer bdl; /* BDL buffer */
+ struct snd_dma_buffer *bdl; /* BDL buffer */
struct lola_stream streams[MAX_STREAM_COUNT];
};
@@ -341,7 +328,7 @@ struct lola {
unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
/* CORB/RIRB buffers */
- struct snd_dma_buffer rb;
+ struct snd_dma_buffer *rb;
/* unsolicited events */
unsigned int last_unsol_res;
@@ -493,7 +480,6 @@ int lola_codec_flush(struct lola *chip);
/* PCM */
int lola_create_pcm(struct lola *chip);
-void lola_free_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);
@@ -513,8 +499,6 @@ 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 */
diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c
index 2bef6b412aee..2e73fbf335ed 100644
--- a/sound/pci/lola/lola_clock.c
+++ b/sound/pci/lola/lola_clock.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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/kernel.h>
@@ -48,7 +35,7 @@ unsigned int lola_sample_rate_convert(unsigned int coded)
default: return 0; /* error */
}
- /* ajustement */
+ /* adjustement */
switch (coded & 0x60) {
case (0 << 5): break;
case (1 << 5): freq = (freq * 999) / 1000; break;
@@ -148,7 +135,7 @@ int lola_init_clock_widget(struct lola *chip, int nid)
}
nitems = chip->clock.items;
- nb_verbs = (nitems + 3) / 4;
+ nb_verbs = DIV_ROUND_UP(nitems, 4);
idx = 0;
idx_list = 0;
for (i = 0; i < nb_verbs; i++) {
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
index cb25acf7bc49..9cb26a8a4e1a 100644
--- a/sound/pci/lola/lola_mixer.c
+++ b/sound/pci/lola/lola_mixer.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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/kernel.h>
@@ -134,6 +121,8 @@ int lola_init_mixer_widget(struct lola *chip, int nid)
/* 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;
@@ -347,49 +336,6 @@ int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
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);
- }
-}
-
/*
*/
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
index 310b26a756c9..6c046ecd6e08 100644
--- a/sound/pci/lola/lola_pcm.c
+++ b/sound/pci/lola/lola_pcm.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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/kernel.h>
@@ -227,11 +214,9 @@ static int lola_pcm_open(struct snd_pcm_substream *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);
+ guard(mutex)(&chip->open_mutex);
+ if (str->opened)
return -EBUSY;
- }
str->substream = substream;
str->master = NULL;
str->opened = 1;
@@ -252,7 +237,6 @@ static int lola_pcm_open(struct snd_pcm_substream *substream)
chip->granularity);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
chip->granularity);
- mutex_unlock(&chip->open_mutex);
return 0;
}
@@ -274,7 +258,7 @@ 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);
+ guard(mutex)(&chip->open_mutex);
if (str->substream == substream) {
str->substream = NULL;
str->opened = 0;
@@ -283,7 +267,6 @@ static int lola_pcm_close(struct snd_pcm_substream *substream)
/* release sample rate */
chip->sample_rate = 0;
}
- mutex_unlock(&chip->open_mutex);
return 0;
}
@@ -295,8 +278,7 @@ static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
str->bufsize = 0;
str->period_bytes = 0;
str->format_verb = 0;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -305,21 +287,20 @@ static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
struct lola_pcm *pcm = lola_get_pcm(substream);
struct lola_stream *str = lola_get_stream(substream);
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
lola_stream_reset(chip, str);
lola_cleanup_slave_streams(pcm, str);
- mutex_unlock(&chip->open_mutex);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/*
* set up a BDL entry
*/
static int setup_bdle(struct snd_pcm_substream *substream,
- struct lola_stream *str, u32 **bdlp,
+ struct lola_stream *str, __le32 **bdlp,
int ofs, int size)
{
- u32 *bdl = *bdlp;
+ __le32 *bdl = *bdlp;
while (size > 0) {
dma_addr_t addr;
@@ -355,14 +336,14 @@ static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
struct snd_pcm_substream *substream,
struct lola_stream *str)
{
- u32 *bdl;
+ __le32 *bdl;
int i, ofs, periods, period_bytes;
period_bytes = str->period_bytes;
periods = str->bufsize / period_bytes;
/* program the initial BDL entries */
- bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
+ bdl = (__le32 *)(pcm->bdl->area + LOLA_BDL_ENTRY_SIZE * str->index);
ofs = 0;
str->frags = 0;
for (i = 0; i < periods; i++) {
@@ -447,7 +428,7 @@ static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
return -EINVAL;
/* set up BDL */
- bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ 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 */
@@ -471,18 +452,16 @@ static int lola_pcm_prepare(struct snd_pcm_substream *substream)
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;
+ scoped_guard(mutex, &chip->open_mutex) {
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams)
+ 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);
@@ -544,7 +523,7 @@ static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
*/
sync_streams = (start && snd_pcm_stream_linked(substream));
tstamp = lola_get_tstamp(chip, !sync_streams);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
snd_pcm_group_for_each_entry(s, substream) {
if (s->pcm->card != substream->pcm->card)
continue;
@@ -557,7 +536,6 @@ static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
str->paused = !start;
snd_pcm_trigger_done(s, substream);
}
- spin_unlock(&chip->reg_lock);
return 0;
}
@@ -575,8 +553,9 @@ static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
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 < pcm->num_streams; i++) {
+ for (i = 0; bits && i < num_streams; i++) {
if (bits & (1 << i)) {
struct lola_stream *str = &pcm->streams[i];
if (str->substream && str->running)
@@ -589,13 +568,11 @@ void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
static const struct snd_pcm_ops lola_pcm_ops = {
.open = lola_pcm_open,
.close = lola_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = lola_pcm_hw_params,
.hw_free = lola_pcm_hw_free,
.prepare = lola_pcm_prepare,
.trigger = lola_pcm_trigger,
.pointer = lola_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
int lola_create_pcm(struct lola *chip)
@@ -604,11 +581,11 @@ int lola_create_pcm(struct lola *chip)
int i, err;
for (i = 0; i < 2; i++) {
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &chip->pcm[i].bdl);
- if (err < 0)
- return err;
+ 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,
@@ -617,25 +594,19 @@ int lola_create_pcm(struct lola *chip)
&pcm);
if (err < 0)
return err;
- strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ 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_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 1024 * 64, 32 * 1024 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 1024 * 64, 32 * 1024 * 1024);
return 0;
}
-void lola_free_pcm(struct lola *chip)
-{
- snd_dma_free_pages(&chip->pcm[0].bdl);
- snd_dma_free_pages(&chip->pcm[1].bdl);
-}
-
/*
*/
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
index c241dc06dd92..a166672e22cb 100644
--- a/sound/pci/lola/lola_proc.c
+++ b/sound/pci/lola/lola_proc.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 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/kernel.h>
@@ -208,15 +195,9 @@ static void lola_proc_regs_read(struct snd_info_entry *entry,
void lola_proc_debug_new(struct lola *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "codec", &entry))
- snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
- if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
- snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = lola_proc_codec_rw_write;
- }
- if (!snd_card_proc_new(chip->card, "regs", &entry))
- snd_info_set_text_ops(entry, chip, lola_proc_regs_read);
+ 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..2b3047c7a388 100644
--- a/sound/pci/lx6464es/Makefile
+++ b/sound/pci/lx6464es/Makefile
@@ -1,2 +1,3 @@
-snd-lx6464es-objs := lx6464es.o lx_core.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-lx6464es-y := 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 9655b08a1c52..96df00db51d5 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,8 +21,6 @@
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;
@@ -65,6 +47,14 @@ static const struct pci_device_id snd_lx6464es_ids[] = {
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, },
};
@@ -217,7 +207,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
int board_rate;
dev_dbg(chip->card->dev, "->lx_pcm_open\n");
- mutex_lock(&chip->setup_mutex);
+ guard(mutex)(&chip->setup_mutex);
/* copy the struct snd_pcm_hardware struct */
runtime->hw = lx_caps;
@@ -228,7 +218,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIODS);
if (err < 0) {
dev_warn(chip->card->dev, "could not constrain periods\n");
- goto exit;
+ return err;
}
#endif
@@ -239,7 +229,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
if (err < 0) {
dev_warn(chip->card->dev, "could not constrain periods\n");
- goto exit;
+ return err;
}
/* constrain period size */
@@ -250,7 +240,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
if (err < 0) {
dev_warn(chip->card->dev,
"could not constrain period size\n");
- goto exit;
+ return err;
}
snd_pcm_hw_constraint_step(runtime, 0,
@@ -259,19 +249,16 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
err = 0;
-exit:
runtime->private_data = chip;
- mutex_unlock(&chip->setup_mutex);
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;
dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n");
- return err;
+ return 0;
}
static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
@@ -286,9 +273,8 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
pos = lx_stream->frame_pos * substream->runtime->period_size;
- mutex_unlock(&chip->lock);
dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
return pos;
@@ -302,21 +288,21 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
dev_dbg(chip->card->dev, "->lx_pcm_prepare\n");
- mutex_lock(&chip->setup_mutex);
+ guard(mutex)(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
}
@@ -325,14 +311,14 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
if (err < 0) {
dev_err(chip->card->dev, "failed to open hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_start(chip, substream);
if (err < 0) {
dev_err(chip->card->dev, "failed to start hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
chip->hardware_running[is_capture] = 1;
@@ -342,8 +328,6 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
chip->board_sample_rate = substream->runtime->rate;
}
-exit:
- mutex_unlock(&chip->setup_mutex);
return err;
}
@@ -351,23 +335,17 @@ 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;
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));
+ guard(mutex)(&chip->setup_mutex);
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,36 +367,32 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n");
- mutex_lock(&chip->setup_mutex);
+ guard(mutex)(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
chip->hardware_running[is_capture] = 0;
}
- err = snd_pcm_lib_free_pages(substream);
-
if (is_capture)
chip->capture_stream.stream = NULL;
else
chip->playback_stream.stream = NULL;
-exit:
- mutex_unlock(&chip->setup_mutex);
- return err;
+ return 0;
}
static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
@@ -504,9 +478,7 @@ static void lx_trigger_dispatch_stream(struct lx6464es *chip,
static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
struct lx_stream *lx_stream, int cmd)
{
- int err = 0;
-
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
@@ -517,16 +489,13 @@ static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
break;
default:
- err = -EINVAL;
- goto exit;
+ return -EINVAL;
}
lx_trigger_dispatch_stream(chip, &chip->capture_stream);
lx_trigger_dispatch_stream(chip, &chip->playback_stream);
-exit:
- mutex_unlock(&chip->lock);
- return err;
+ return 0;
}
@@ -542,29 +511,11 @@ static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
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)
{
- dev_dbg(chip->card->dev, "->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 */
@@ -807,7 +758,6 @@ mac_ready:
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,
@@ -818,7 +768,6 @@ static const struct snd_pcm_ops lx_ops_playback = {
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,
@@ -852,13 +801,10 @@ static int lx_pcm_create(struct lx6464es *chip)
pcm->info_flags = 0;
pcm->nonatomic = true;
- strcpy(pcm->name, card_name);
+ strscpy(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;
@@ -948,33 +894,20 @@ static void lx_proc_levels_read(struct snd_info_entry *entry,
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 snd_lx6464es_create(struct snd_card *card,
- struct pci_dev *pci,
- struct lx6464es **rchip)
+ 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,
- };
-
dev_dbg(card->dev, "->snd_lx6464es_create\n");
- *rchip = NULL;
-
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
@@ -985,16 +918,9 @@ static int snd_lx6464es_create(struct snd_card *card,
if (err < 0) {
dev_err(card->dev,
"architecture does not support 32bit PCI busmaster DMA\n");
- pci_disable_device(pci);
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;
@@ -1005,29 +931,32 @@ static int snd_lx6464es_create(struct snd_card *card,
mutex_init(&chip->setup_mutex);
/* request resources */
- err = pci_request_regions(pci, card_name);
+ err = pcim_request_all_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_threaded_irq(pci->irq, lx_interrupt, lx_threaded_irq,
- IRQF_SHARED, KBUILD_MODNAME, chip);
+ err = devm_request_threaded_irq(&pci->dev, pci->irq, lx_interrupt,
+ lx_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, chip);
if (err) {
dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
- goto request_irq_failed;
+ 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) {
@@ -1048,22 +977,7 @@ static int snd_lx6464es_create(struct snd_card *card,
if (err < 0)
return err;
- *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 snd_lx6464es_probe(struct pci_dev *pci,
@@ -1083,18 +997,19 @@ static int snd_lx6464es_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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) {
dev_err(card->dev, "error during snd_lx6464es_create\n");
- goto out_free;
+ goto error;
}
- strcpy(card->driver, "LX6464ES");
+ strscpy(card->driver, "LX6464ES");
sprintf(card->id, "LX6464ES_%02X%02X%02X",
chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
@@ -1108,30 +1023,22 @@ static int snd_lx6464es_probe(struct pci_dev *pci,
err = snd_card_register(card);
if (err < 0)
- goto out_free;
+ goto error;
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 snd_lx6464es_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
-
static struct pci_driver lx6464es_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_lx6464es_ids,
.probe = snd_lx6464es_probe,
- .remove = snd_lx6464es_remove,
};
module_pci_driver(lx6464es_driver);
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
index 1bec187d772f..1cfe10d23fa2 100644
--- a/sound/pci/lx6464es/lx6464es.h
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -1,25 +1,9 @@
+/* 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
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index a80684bdc30d..6f0843cfb3be 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -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.
- *
*/
/* #define RMH_DEBUG 1 */
@@ -176,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") },
@@ -246,14 +231,14 @@ static void lx_message_dump(struct lx_rmh *rmh)
u8 idx = rmh->cmd_idx;
int i;
- snd_printk(LXRMH "command %s\n", dsp_commands[idx].dcOpName);
+ pr_debug(LXRMH "command %s\n", dsp_commands[idx].dcOpName);
for (i = 0; i != rmh->cmd_len; ++i)
- snd_printk(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]);
+ pr_debug(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]);
for (i = 0; i != rmh->stat_len; ++i)
- snd_printk(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]);
- snd_printk("\n");
+ pr_debug(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]);
+ pr_debug("\n");
}
#else
static inline void lx_message_dump(struct lx_rmh *rmh)
@@ -331,26 +316,25 @@ polling_successful:
/* low-level dsp access */
int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
{
- u16 ret;
+ int ret;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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];
- mutex_unlock(&chip->msg_lock);
return ret;
}
int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
{
- u16 ret = 0;
u32 freq_raw = 0;
u32 freq = 0;
u32 frequency = 0;
+ int ret;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
ret = lx_message_send_atomic(chip, &chip->rmh);
@@ -368,8 +352,6 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
frequency = 48000;
}
- mutex_unlock(&chip->msg_lock);
-
*rfreq = frequency * chip->freq_ratio;
return ret;
@@ -396,23 +378,19 @@ int lx_dsp_get_mac(struct lx6464es *chip)
int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
{
- int ret;
-
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->msg_lock);
- return ret;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
{
int ret;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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 */
@@ -422,7 +400,6 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
if (!ret)
memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
- mutex_unlock(&chip->msg_lock);
return ret;
}
@@ -438,14 +415,13 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->msg_lock);
if (err != 0)
dev_err(chip->card->dev, "could not allocate pipe\n");
@@ -455,18 +431,14 @@ 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;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->msg_lock);
-
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
@@ -483,7 +455,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
*r_needed = 0;
*r_freed = 0;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -508,50 +480,40 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
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)
- dev_dbg(chip->card->dev,
- " 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);
}
}
- mutex_unlock(&chip->msg_lock);
return err;
}
int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
{
- int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
-
- mutex_unlock(&chip->msg_lock);
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
{
- int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
-
- mutex_unlock(&chip->msg_lock);
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
@@ -588,7 +550,7 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -605,7 +567,6 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
+ chip->rmh.stat[1]; /* lo part */
}
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -614,7 +575,7 @@ int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -626,7 +587,6 @@ int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
else
*rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -667,33 +627,24 @@ int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture)
int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
int is_capture, enum stream_state_t state)
{
- int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->msg_lock);
-
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
u32 pipe, int is_capture)
{
- int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
u32 channels = runtime->channels;
- if (runtime->channels != channels)
- dev_err(chip->card->dev, "channel count mismatch: %d vs %d",
- runtime->channels, channels);
-
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -708,10 +659,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);
- mutex_unlock(&chip->msg_lock);
-
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
@@ -720,7 +668,7 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -729,7 +677,6 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
*rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -739,7 +686,7 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -750,7 +697,6 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
<< 32) /* hi part */
+ chip->rmh.stat[1]; /* lo part */
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -762,7 +708,7 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -783,7 +729,7 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
if (err == 0) {
*r_buffer_index = chip->rmh.stat[0];
- goto done;
+ return err;
}
if (err == EB_RBUFFERS_TABLE_OVERFLOW)
@@ -798,8 +744,6 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
dev_err(chip->card->dev,
"lx_buffer_give EB_CMD_REFUSED\n");
- done:
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -809,7 +753,7 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -821,26 +765,21 @@ 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;
- mutex_unlock(&chip->msg_lock);
return err;
}
int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
u32 buffer_index)
{
- int err;
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
chip->rmh.cmd[0] |= buffer_index;
- err = lx_message_send_atomic(chip, &chip->rmh);
-
- mutex_unlock(&chip->msg_lock);
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
@@ -851,11 +790,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;
/* bit set to 1: channel muted */
u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
@@ -867,13 +805,10 @@ int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
"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);
-
- mutex_unlock(&chip->msg_lock);
- return err;
+ return lx_message_send_atomic(chip, &chip->rmh);
}
-static u32 peak_map[] = {
+static const u32 peak_map[] = {
0x00000109, /* -90.308dB */
0x0000083B, /* -72.247dB */
0x000020C4, /* -60.205dB */
@@ -898,7 +833,7 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
int err = 0;
int i;
- mutex_lock(&chip->msg_lock);
+ guard(mutex)(&chip->msg_lock);
for (i = 0; i < channels; i += 4) {
u32 s0, s1, s2, s3;
@@ -923,7 +858,6 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
r_levels += 4;
}
- mutex_unlock(&chip->msg_lock);
return err;
}
@@ -1001,8 +935,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
* Stat[8] LSB overrun
* */
- u64 orun_mask;
- u64 urun_mask;
int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
@@ -1025,9 +957,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
*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;
@@ -1058,7 +987,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
dev_dbg(chip->card->dev, "->lx_interrupt_request_new_buffer\n");
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
dev_dbg(chip->card->dev,
@@ -1072,7 +1001,6 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
buffer_index, (unsigned long)buf, period_bytes);
lx_stream->frame_pos = next_pos;
- mutex_unlock(&chip->lock);
return err;
}
diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h
index 0cc140ca98e3..c1113439f7c9 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
@@ -144,21 +129,18 @@ int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
static inline int lx_stream_start(struct lx6464es *chip, u32 pipe,
int is_capture)
{
- snd_printdd("->lx_stream_start\n");
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN);
}
static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe,
int is_capture)
{
- snd_printdd("->lx_stream_pause\n");
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE);
}
static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe,
int is_capture)
{
- snd_printdd("->lx_stream_stop\n");
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP);
}
diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h
index 469bcc685edf..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
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 8f20dec97843..bddf47a1f263 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,26 +7,10 @@
* 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"
@@ -54,11 +39,6 @@
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");
@@ -789,9 +769,7 @@ struct snd_m3 {
unsigned int in_suspend;
-#ifdef CONFIG_PM_SLEEP
u16 *suspend_mem;
-#endif
const struct firmware *assp_kernel_image;
const struct firmware *assp_minisrc_image;
@@ -822,7 +800,7 @@ static const struct pci_device_id snd_m3_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_m3_ids);
-static struct snd_pci_quirk m3_amp_quirk_list[] = {
+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),
@@ -831,7 +809,7 @@ static struct snd_pci_quirk m3_amp_quirk_list[] = {
{ } /* END */
};
-static struct snd_pci_quirk m3_irda_quirk_list[] = {
+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),
@@ -839,7 +817,7 @@ static struct snd_pci_quirk m3_irda_quirk_list[] = {
};
/* hardware volume quirks */
-static struct snd_pci_quirk m3_hv_quirk_list[] = {
+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),
@@ -917,7 +895,7 @@ static struct snd_pci_quirk m3_hv_quirk_list[] = {
};
/* HP Omnibook quirks */
-static struct snd_pci_quirk m3_omnibook_quirk_list[] = {
+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 */
@@ -1118,7 +1096,7 @@ snd_m3_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
if (snd_BUG_ON(!s))
return -ENXIO;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -1139,7 +1117,6 @@ snd_m3_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
}
break;
}
- spin_unlock(&chip->reg_lock);
return err;
}
@@ -1260,7 +1237,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--;
@@ -1396,10 +1373,7 @@ 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) {
@@ -1416,7 +1390,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;
}
@@ -1438,7 +1411,7 @@ snd_m3_pcm_prepare(struct snd_pcm_substream *subs)
runtime->rate < 8000)
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_m3_pcm_setup1(chip, s, subs);
@@ -1449,8 +1422,6 @@ snd_m3_pcm_prepare(struct snd_pcm_substream *subs)
snd_m3_pcm_setup2(chip, s, runtime);
- spin_unlock_irq(&chip->reg_lock);
-
return 0;
}
@@ -1492,9 +1463,8 @@ snd_m3_pcm_pointer(struct snd_pcm_substream *subs)
if (snd_BUG_ON(!s))
return 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
ptr = snd_m3_get_pointer(chip, s, subs);
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(subs->runtime, ptr);
}
@@ -1655,13 +1625,12 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
if (ctl & DSP2HOST_REQ_TIMER) {
outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS);
/* update adc/dac info if it was a timer int */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
for (i = 0; i < chip->num_substreams; i++) {
struct m3_dma *s = &chip->substreams[i];
if (s->running)
snd_m3_update_ptr(chip, s);
}
- spin_unlock(&chip->reg_lock);
}
}
}
@@ -1733,18 +1702,16 @@ snd_m3_substream_open(struct snd_m3 *chip, struct snd_pcm_substream *subs)
int i;
struct m3_dma *s;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < chip->num_substreams; i++) {
s = &chip->substreams[i];
if (! s->opened)
goto __found;
}
- spin_unlock_irq(&chip->reg_lock);
return -ENOMEM;
__found:
s->opened = 1;
s->running = 0;
- spin_unlock_irq(&chip->reg_lock);
subs->runtime->private_data = s;
s->substream = subs;
@@ -1768,7 +1735,7 @@ snd_m3_substream_close(struct snd_m3 *chip, struct snd_pcm_substream *subs)
if (s == NULL)
return; /* not opened properly */
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
if (s->substream && s->running)
snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */
if (s->in_lists) {
@@ -1779,7 +1746,6 @@ snd_m3_substream_close(struct snd_m3 *chip, struct snd_pcm_substream *subs)
}
s->running = 0;
s->opened = 0;
- spin_unlock_irq(&chip->reg_lock);
}
static int
@@ -1789,7 +1755,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;
@@ -1813,7 +1780,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;
@@ -1837,7 +1805,6 @@ snd_m3_capture_close(struct snd_pcm_substream *subs)
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,
@@ -1848,7 +1815,6 @@ static const struct snd_pcm_ops snd_m3_playback_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,
@@ -1872,11 +1838,11 @@ snd_m3_pcm(struct snd_m3 * chip, int device)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->driver);
+ strscpy(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;
}
@@ -2053,21 +2019,20 @@ static int snd_m3_mixer(struct snd_m3 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- 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.. */
@@ -2076,14 +2041,10 @@ static int snd_m3_mixer(struct snd_m3 *chip)
snd_ac97_write(chip->ac97, AC97_PCM, 0);
#ifndef CONFIG_SND_MAESTRO3_INPUT
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+ chip->master_switch = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Switch");
+ chip->master_volume = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Volume");
#endif
return 0;
@@ -2103,7 +2064,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++)
@@ -2121,7 +2082,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,
@@ -2134,7 +2095,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]));
@@ -2361,55 +2322,36 @@ 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;
cancel_work_sync(&chip->hwvol_work);
-#ifdef CONFIG_SND_MAESTRO3_INPUT
- if (chip->input_dev)
- input_unregister_device(chip->input_dev);
-#endif
if (chip->substreams) {
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < chip->num_substreams; i++) {
s = &chip->substreams[i];
/* check surviving pcms; this should not happen though.. */
if (s->substream && s->running)
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_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_SLEEP
static int m3_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -2422,7 +2364,6 @@ static int m3_suspend(struct device *dev)
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.. */
@@ -2484,11 +2425,7 @@ static int m3_resume(struct device *dev)
return 0;
}
-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 */
+static DEFINE_SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume);
#ifdef CONFIG_SND_MAESTRO3_INPUT
static int snd_m3_input_register(struct snd_m3 *chip)
@@ -2496,7 +2433,7 @@ 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;
@@ -2516,10 +2453,8 @@ static int 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;
@@ -2529,45 +2464,25 @@ static int 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
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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ 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");
- pci_disable_device(pci);
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
switch (pci->device) {
@@ -2583,6 +2498,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
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)
@@ -2612,27 +2528,24 @@ 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)
- goto free_chip;
+ return err;
err = request_firmware(&chip->assp_minisrc_image,
"ess/maestro3_assp_minisrc.fw", &pci->dev);
if (err < 0)
- goto free_chip;
+ return err;
- err = pci_request_regions(pci, card->driver);
+ err = pcim_request_all_regions(pci, card->driver);
if (err < 0)
- goto free_chip;
+ return err;
chip->iobase = pci_resource_start(pci, 0);
@@ -2648,34 +2561,36 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_hv_init(chip);
- if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- err = -ENOMEM;
- goto free_chip;
+ return -ENOMEM;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+
+ if (IS_ENABLED(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)
+ dev_warn(card->dev, "can't allocate apm buffer\n");
+ }
-#ifdef CONFIG_PM_SLEEP
- chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
- if (chip->suspend_mem == NULL)
- dev_warn(card->dev, "can't allocate apm buffer\n");
-#endif
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_m3_mixer(chip);
if (err < 0)
- goto free_chip;
-
- if ((err = snd_m3_mixer(chip)) < 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
@@ -2691,19 +2606,13 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_enable_ints(chip);
snd_m3_assp_continue(chip);
- *chip_ret = chip;
-
return 0;
-
-free_chip:
- snd_m3_free(chip);
- return err;
}
/*
*/
static int
-snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+__snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2721,30 +2630,29 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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:
case PCI_DEVICE_ID_ESS_ALLEGRO_1:
- strcpy(card->driver, "Allegro");
+ strscpy(card->driver, "Allegro");
break;
case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
case PCI_DEVICE_ID_ESS_CANYON3D_2:
- strcpy(card->driver, "Canyon3D-2");
+ strscpy(card->driver, "Canyon3D-2");
break;
default:
- strcpy(card->driver, "Maestro3");
+ strscpy(card->driver, "Maestro3");
break;
}
- err = snd_m3_create(card, pci, external_amp[dev], amp_gpio[dev], &chip);
+ err = snd_m3_create(card, pci, external_amp[dev], amp_gpio[dev]);
if (err < 0)
- goto free_card;
-
- card->private_data = chip;
+ return err;
sprintf(card->shortname, "ESS %s PCI", card->driver);
sprintf(card->longname, "%s at 0x%lx, irq %d",
@@ -2752,7 +2660,7 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
err = snd_card_register(card);
if (err < 0)
- goto free_card;
+ return err;
#if 0 /* TODO: not supported yet */
/* TODO enable MIDI IRQ and I/O */
@@ -2767,24 +2675,20 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
pci_set_drvdata(pci, card);
dev++;
return 0;
-
-free_card:
- snd_card_free(card);
- return err;
}
-static void 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));
+ return snd_card_free_on_error(&pci->dev, __snd_m3_probe(pci, pci_id));
}
static struct pci_driver m3_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_m3_ids,
.probe = snd_m3_probe,
- .remove = snd_m3_remove,
.driver = {
- .pm = M3_PM_OPS,
+ .pm = &m3_pm,
},
};
diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
index cce159ec5624..b803e5e72791 100644
--- a/sound/pci/mixart/Makefile
+++ b/sound/pci/mixart/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
+snd-mixart-y := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
obj-$(CONFIG_SND_MIXART) += snd-mixart.o
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index a74f1ad7e7b8..c6319e75beab 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
*/
@@ -45,7 +32,6 @@
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 */
@@ -112,7 +98,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
memset(&group_state, 0, sizeof(group_state));
group_state.pipe_count = 1;
- group_state.pipe_uid[0] = pipe->group_uid;
+ group_state.pipe_uid = pipe->group_uid;
if(start)
request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
@@ -182,6 +168,7 @@ 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 */
@@ -198,7 +185,7 @@ static int mixart_set_clock(struct mixart_mgr *mgr,
clock_properties.clock_mode = CM_STANDALONE;
clock_properties.frequency = rate;
clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
- clock_properties.uid_caller[0] = pipe->group_uid;
+ clock_properties.uid_caller = pipe->group_uid;
dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate);
@@ -578,8 +565,8 @@ static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t form
stream_param.pipe_count = 1; /* set to 1 */
stream_param.stream_count = 1; /* set to 1 */
- stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
- stream_param.stream_desc[0].stream_idx = stream->substream->number;
+ stream_param.stream_desc.uid_pipe = stream->pipe->group_uid;
+ stream_param.stream_desc.stream_idx = stream->substream->number;
request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
request.uid = (struct mixart_uid){0,0};
@@ -616,7 +603,7 @@ static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
/* set up format for the stream */
format = params_format(hw);
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
/* update the stream levels */
if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
@@ -631,15 +618,10 @@ static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
/* set the format to the board */
err = mixart_set_format(stream, format);
- if(err < 0) {
- mutex_unlock(&mgr->setup_mutex);
+ if (err < 0)
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 ) {
@@ -657,15 +639,13 @@ static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
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;
}
@@ -729,7 +709,7 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
int err = 0;
int pcm_number;
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
if ( pcm == chip->pcm ) {
pcm_number = MIXART_PCM_ANALOG;
@@ -751,25 +731,21 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
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;
+ return -EBUSY;
}
/* get pipe pointer (out pipe) */
pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
- if (pipe == NULL) {
- err = -EINVAL;
- goto _exit_open;
- }
+ if (pipe == NULL)
+ return -EINVAL;
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
dev_err(chip->card->dev, "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
- err = -EINVAL;
- goto _exit_open;
+ return -EINVAL;
}
stream->pipe = pipe;
@@ -790,10 +766,7 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
}
}
- _exit_open:
- mutex_unlock(&mgr->setup_mutex);
-
- return err;
+ return 0;
}
@@ -808,7 +781,7 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
int err = 0;
int pcm_number;
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
if ( pcm == chip->pcm ) {
pcm_number = MIXART_PCM_ANALOG;
@@ -832,25 +805,21 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
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;
+ return -EBUSY;
}
/* get pipe pointer (in pipe) */
pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
- if (pipe == NULL) {
- err = -EINVAL;
- goto _exit_open;
- }
+ if (pipe == NULL)
+ return -EINVAL;
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
dev_err(chip->card->dev, "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
- err = -EINVAL;
- goto _exit_open;
+ return -EINVAL;
}
stream->pipe = pipe;
@@ -871,10 +840,7 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
}
}
- _exit_open:
- mutex_unlock(&mgr->setup_mutex);
-
- return err;
+ return 0;
}
@@ -885,7 +851,7 @@ static int snd_mixart_close(struct snd_pcm_substream *subs)
struct mixart_mgr *mgr = chip->mgr;
struct mixart_stream *stream = subs->runtime->private_data;
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n",
chip->chip_idx, stream->pcm_number, subs->number);
@@ -907,7 +873,6 @@ static int snd_mixart_close(struct snd_pcm_substream *subs)
stream->status = MIXART_STREAM_STATUS_FREE;
stream->substream = NULL;
- mutex_unlock(&mgr->setup_mutex);
return 0;
}
@@ -925,7 +890,6 @@ static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *sub
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,
@@ -936,7 +900,6 @@ static const struct snd_pcm_ops snd_mixart_playback_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,
@@ -959,8 +922,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);
}
/*
@@ -972,9 +936,10 @@ 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) {
+ 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;
@@ -987,7 +952,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip)
pcm->info_flags = 0;
pcm->nonatomic = true;
- strcpy(pcm->name, name);
+ strscpy(pcm->name, name);
preallocate_buffers(chip, pcm);
@@ -1005,9 +970,10 @@ 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) {
+ 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;
@@ -1020,7 +986,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip)
pcm->info_flags = 0;
pcm->nonatomic = true;
- strcpy(pcm->name, name);
+ strscpy(pcm->name, name);
preallocate_buffers(chip, pcm);
@@ -1047,7 +1013,7 @@ static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int
{
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,
};
@@ -1058,8 +1024,10 @@ static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int
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;
}
@@ -1169,11 +1137,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,
};
@@ -1219,10 +1187,8 @@ 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;
@@ -1262,7 +1228,8 @@ static int 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);
@@ -1286,7 +1253,8 @@ static int 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;
@@ -1344,14 +1312,15 @@ static int snd_mixart_probe(struct pci_dev *pci,
return err;
}
- strcpy(card->driver, CARD_NAME);
+ strscpy(card->driver, CARD_NAME);
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;
@@ -1362,7 +1331,8 @@ static int 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;
}
@@ -1374,7 +1344,7 @@ static int 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;
@@ -1385,7 +1355,7 @@ static int 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;
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
index 69b3ece099ad..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
@@ -82,7 +69,7 @@ struct mixart_mgr {
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 tasklet */
+ atomic_t msg_processed; /* number of messages to be processed in irq thread */
struct mutex lock; /* interrupt lock */
struct mutex msg_lock; /* mailbox lock */
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index 8bf2ce32d4a8..f7396ee4f6d7 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -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
*/
#include <linux/interrupt.h>
@@ -36,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) */
@@ -83,7 +68,6 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
unsigned int i;
#endif
- mutex_lock(&mgr->msg_lock);
err = 0;
/* copy message descriptor from miXart to driver */
@@ -107,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
@@ -132,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:
- mutex_unlock(&mgr->msg_lock);
-
return err;
}
@@ -244,17 +226,16 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
init_waitqueue_entry(&wait, current);
- 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) {
- mutex_unlock(&mgr->msg_lock);
- return err;
+ scoped_guard(mutex, &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)
+ return err;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
}
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&mgr->msg_sleep, &wait);
- mutex_unlock(&mgr->msg_lock);
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
remove_wait_queue(&mgr->msg_sleep, &wait);
@@ -271,7 +252,9 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
resp.data = resp_data;
resp.size = max_resp_size;
- err = get_msg(mgr, &resp, msg_frame);
+ scoped_guard(mutex, &mgr->msg_lock) {
+ err = get_msg(mgr, &resp, msg_frame);
+ }
if( request->message_id != resp.message_id )
dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n");
@@ -296,17 +279,16 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
init_waitqueue_entry(&wait, current);
- 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) {
- mutex_unlock(&mgr->msg_lock);
- return err;
+ scoped_guard(mutex, &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)
+ return err;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
}
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&mgr->msg_sleep, &wait);
- mutex_unlock(&mgr->msg_lock);
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
remove_wait_queue(&mgr->msg_sleep, &wait);
@@ -327,9 +309,8 @@ int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *requ
int err;
/* just send the message (do not mark it as a pending one) */
- mutex_lock(&mgr->msg_lock);
+ guard(mutex)(&mgr->msg_lock);
err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
- mutex_unlock(&mgr->msg_lock);
/* the answer will be handled by snd_struct mixart_msgasklet() */
atomic_inc(&mgr->msg_processed);
@@ -436,7 +417,7 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
struct mixart_msg resp;
u32 msg;
- mutex_lock(&mgr->lock);
+ guard(mutex)(&mgr->lock);
/* process interrupt */
while (retrieve_msg_frame(mgr, &msg)) {
@@ -458,6 +439,9 @@ irqreturn_t snd_mixart_threaded_irq(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;
@@ -519,7 +503,7 @@ irqreturn_t snd_mixart_threaded_irq(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;
@@ -540,22 +524,22 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
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*/
- mutex_lock(&mgr->msg_lock);
- if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
- wake_up(&mgr->msg_sleep);
- mgr->pending_event = 0;
- }
- /* answer to a message we did't want to wait for */
- else {
- mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
- mgr->msg_fifo_writeptr++;
- mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
- snd_mixart_process_msg(mgr);
+ scoped_guard(mutex, &mgr->msg_lock) {
+ if ((msg & ~MSG_TYPE_MASK) == mgr->pending_event) {
+ wake_up(&mgr->msg_sleep);
+ mgr->pending_event = 0;
+ }
+ /* answer to a message we did't want to wait for */
+ else {
+ mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
+ mgr->msg_fifo_writeptr++;
+ mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
+ snd_mixart_process_msg(mgr);
+ }
}
- mutex_unlock(&mgr->msg_lock);
break;
case MSG_TYPE_REQUEST:
default:
@@ -569,8 +553,6 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
/* allow interrupt again */
writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
- mutex_unlock(&mgr->lock);
-
return IRQ_HANDLED;
}
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
index d1722e575409..7c9a9d82d66e 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
{
@@ -80,7 +68,7 @@ struct mixart_enum_connector_resp
u32 uid_count;
u32 current_uid_index;
struct mixart_uid uid[MIXART_MAX_PHYS_CONNECTORS];
-} __attribute__((packed));
+} __packed;
/* used for following struct */
@@ -93,7 +81,7 @@ struct mixart_audio_info_req
u32 line_max_level; /* float */
u32 micro_max_level; /* float */
u32 cd_max_level; /* float */
-} __attribute__((packed));
+} __packed;
struct mixart_analog_hw_info
{
@@ -105,7 +93,7 @@ struct mixart_analog_hw_info
u32 step_var_level; /* float */
u32 fix_gain; /* float */
u32 zero_var; /* float */
-} __attribute__((packed));
+} __packed;
struct mixart_digital_hw_info
{
@@ -113,7 +101,7 @@ struct mixart_digital_hw_info
u32 presence;
u32 clock;
u32 reserved;
-} __attribute__((packed));
+} __packed;
struct mixart_analog_info
{
@@ -122,27 +110,27 @@ struct mixart_analog_info
struct mixart_analog_hw_info line_info;
struct mixart_analog_hw_info cd_info;
u32 analog_level_present;
-} __attribute__((packed));
+} __packed;
struct mixart_digital_info
{
u32 type_mask;
struct mixart_digital_hw_info aes_info;
struct mixart_digital_hw_info adat_info;
-} __attribute__((packed));
+} __packed;
struct mixart_audio_info
{
u32 clock_type_mask;
struct mixart_analog_info analog_info;
struct mixart_digital_info digital_info;
-} __attribute__((packed));
+} __packed;
struct mixart_audio_info_resp
{
u32 txx_status;
struct mixart_audio_info info;
-} __attribute__((packed));
+} __packed;
/* used for nb_bytes_max_per_sample */
@@ -154,7 +142,7 @@ struct mixart_stream_info
u32 size_max_byte_frame;
u32 size_max_sample_frame;
u32 nb_bytes_max_per_sample; /* float */
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_ADD_INPUT_GROUP */
/* MSG_STREAM_ADD_OUTPUT_GROUP */
@@ -169,13 +157,13 @@ struct mixart_streaming_group_req
struct mixart_stream_info stream_info[32];
struct mixart_uid connector;
u32 flow_entry[32];
-} __attribute__((packed));
+} __packed;
struct mixart_stream_desc
{
struct mixart_uid stream_uid;
u32 stream_desc;
-} __attribute__((packed));
+} __packed;
struct mixart_streaming_group
{
@@ -184,7 +172,7 @@ struct mixart_streaming_group
u32 pipe_desc;
u32 stream_count;
struct mixart_stream_desc stream[32];
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_DELETE_GROUP */
@@ -194,7 +182,7 @@ struct mixart_delete_group_resp
{
u32 status;
u32 unused[2];
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7,
@@ -207,7 +195,7 @@ struct mixart_fx_couple_uid
{
struct mixart_uid uid_fx_code;
struct mixart_uid uid_fx_data;
-} __attribute__((packed));
+} __packed;
struct mixart_txx_stream_desc
{
@@ -215,14 +203,14 @@ struct mixart_txx_stream_desc
u32 stream_idx;
u32 fx_number;
struct mixart_fx_couple_uid uid_fx[4];
-} __attribute__((packed));
+} __packed;
struct mixart_flow_info
{
struct mixart_txx_stream_desc stream_desc;
u32 flow_entry;
u32 flow_phy_addr;
-} __attribute__((packed));
+} __packed;
struct mixart_stream_state_req
{
@@ -231,7 +219,7 @@ struct mixart_stream_state_req
u32 reserved4np[3];
u32 stream_count; /* set to 1 for instance */
struct mixart_flow_info stream_info; /* could be an array[stream_count] */
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6
MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9
@@ -243,14 +231,14 @@ struct mixart_group_state_req
u64 scheduler;
u32 reserved4np[2];
u32 pipe_count; /* set to 1 for instance */
- struct mixart_uid pipe_uid[1]; /* could be an array[pipe_count] */
-} __attribute__((packed));
+ struct mixart_uid pipe_uid; /* could be an array[pipe_count], in theory */
+} __packed;
struct mixart_group_state_resp
{
u32 txx_status;
u64 scheduler;
-} __attribute__((packed));
+} __packed;
@@ -262,13 +250,20 @@ struct mixart_sample_pos
u32 validity;
u32 sample_pos_high_part;
u32 sample_pos_low_part;
-} __attribute__((packed));
+} __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];
-} __attribute__((packed));
+ struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS];
+} __packed;
/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
@@ -280,7 +275,7 @@ struct mixart_return_uid
{
u32 error_code;
struct mixart_uid uid;
-} __attribute__((packed));
+} __packed;
/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
MSG_CLOCK_SET_PROPERTIES = 0x200002,
@@ -319,14 +314,14 @@ struct mixart_clock_properties
u32 format;
u32 board_mask;
u32 nb_callers; /* set to 1 (see below) */
- struct mixart_uid uid_caller[1];
-} __attribute__((packed));
+ struct mixart_uid uid_caller;
+} __packed;
struct mixart_clock_properties_resp
{
u32 status;
u32 clock_mode;
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */
@@ -406,9 +401,8 @@ struct mixart_stream_param_desc
u32 reserved4np[3];
u32 pipe_count; /* set to 1 (array size !) */
u32 stream_count; /* set to 1 (array size !) */
- struct mixart_txx_stream_desc stream_desc[1]; /* only one stream per command, but this could be an array */
-
-} __attribute__((packed));
+ struct mixart_txx_stream_desc stream_desc; /* only one stream per command, but this could be an array, in theory */
+} __packed;
/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
@@ -424,7 +418,7 @@ struct mixart_get_out_audio_level
u32 mute;
u32 monitor_mute1;
u32 monitor_mute2;
-} __attribute__((packed));
+} __packed;
/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
@@ -451,7 +445,7 @@ struct mixart_set_out_audio_level
u32 monitor_mute1;
u32 monitor_mute2;
u32 reserved4np;
-} __attribute__((packed));
+} __packed;
/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
@@ -466,7 +460,7 @@ struct mixart_uid_enumeration
u32 nb_uid;
u32 current_uid_index;
struct mixart_uid uid[MIXART_MAX_PHYS_IO];
-} __attribute__((packed));
+} __packed;
/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
@@ -477,13 +471,13 @@ struct mixart_io_channel_level
{
u32 analog_level; /* float */
u32 unused[2];
-} __attribute__((packed));
+} __packed;
struct mixart_io_level
{
s32 channel; /* 0=left, 1=right, -1=both, -2=both same */
struct mixart_io_channel_level level[2];
-} __attribute__((packed));
+} __packed;
/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
@@ -496,7 +490,7 @@ struct mixart_in_audio_level_info
u32 valid_mask2;
u32 digital_level;
u32 analog_level;
-} __attribute__((packed));
+} __packed;
struct mixart_set_in_audio_level_req
{
@@ -505,7 +499,7 @@ struct mixart_set_in_audio_level_req
u32 audio_count; /* set to <= 2 */
u32 reserved4np;
struct mixart_in_audio_level_info level[2];
-} __attribute__((packed));
+} __packed;
/* response is a 32 bit status */
@@ -535,13 +529,13 @@ struct mixart_out_stream_level_info
u32 digital_level2;
u32 mute1;
u32 mute2;
-} __attribute__((packed));
+} __packed;
struct mixart_set_out_stream_level
{
struct mixart_txx_stream_desc desc;
struct mixart_out_stream_level_info out_level;
-} __attribute__((packed));
+} __packed;
struct mixart_set_out_stream_level_req
{
@@ -550,7 +544,7 @@ struct mixart_set_out_stream_level_req
u32 reserved4np[2];
u32 nb_of_stream; /* set to 1 */
struct mixart_set_out_stream_level stream_level; /* could be an array */
-} __attribute__((packed));
+} __packed;
/* response to this request is a u32 status value */
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index 5bfd3ac80db5..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>
@@ -35,7 +22,8 @@
/**
- * 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
*
* @mgr: pointer to miXart manager structure
* @offset: unsigned pseudo_register base + offset of value
@@ -73,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 )
@@ -318,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 */
@@ -540,15 +532,18 @@ 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;
}
@@ -561,7 +556,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
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];
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 2b9496a66c77..f4081d323340 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 */
@@ -357,7 +344,8 @@ static int mixart_analog_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int mixart_analog_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if(kcontrol->private_value == 0) { /* playback */
ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
@@ -365,7 +353,6 @@ static int mixart_analog_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -375,7 +362,7 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int changed = 0;
int is_capture, i;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
is_capture = (kcontrol->private_value != 0);
for (i = 0; i < 2; i++) {
int new_volume = ucontrol->value.integer.value[i];
@@ -398,7 +385,6 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
if (changed)
mixart_update_analog_audio_level(chip, is_capture);
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -422,10 +408,9 @@ static int mixart_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -433,7 +418,8 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int i, changed = 0;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
if (chip->analog_playback_active[i] !=
ucontrol->value.integer.value[i]) {
@@ -444,7 +430,6 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
}
if (changed) /* update playback levels */
mixart_update_analog_audio_level(chip, 0);
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -456,7 +441,7 @@ static const 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) */
@@ -838,7 +823,8 @@ static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int *stored_volume;
int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if(is_capture) {
if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
@@ -849,7 +835,6 @@ static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
}
ucontrol->value.integer.value[0] = stored_volume[0];
ucontrol->value.integer.value[1] = stored_volume[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -862,7 +847,8 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
int* stored_volume;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (is_capture) {
if (is_aes) /* AES capture */
stored_volume = chip->digital_capture_volume[1];
@@ -891,7 +877,6 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
else
mixart_update_playback_stream_level(chip, is_aes, idx);
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -916,12 +901,12 @@ static int mixart_pcm_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */
idx += MIXART_PLAYBACK_STREAMS;
ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -933,7 +918,8 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int i, j;
snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
j = idx;
if (is_aes)
j += MIXART_PLAYBACK_STREAMS;
@@ -947,7 +933,6 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
}
if (changed)
mixart_update_playback_stream_level(chip, is_aes, idx);
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -998,10 +983,10 @@ static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
static int mixart_monitor_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -1010,7 +995,8 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
if (chip->monitoring_volume[i] !=
ucontrol->value.integer.value[i]) {
@@ -1020,7 +1006,6 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
changed = 1;
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -1042,10 +1027,10 @@ static const struct snd_kcontrol_new mixart_control_monitor_vol = {
static int mixart_monitor_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_active[0];
ucontrol->value.integer.value[1] = chip->monitoring_active[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -1054,7 +1039,8 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
if (chip->monitoring_active[i] !=
ucontrol->value.integer.value[i]) {
@@ -1087,7 +1073,6 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return (changed != 0);
}
@@ -1127,10 +1112,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 !*/
@@ -1138,7 +1125,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;
}
@@ -1146,45 +1134,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..7d55fe774d20 100644
--- a/sound/pci/nm256/Makefile
+++ b/sound/pci/nm256/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-nm256-objs := nm256.o
+snd-nm256-y := nm256.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_NM256) += snd-nm256.o
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index b97f4ea6b56c..da74b923bc88 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,21 +8,6 @@
* 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 <linux/io.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.
@@ -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 */
@@ -313,7 +295,7 @@ 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
@@ -464,33 +446,32 @@ snd_nm256_set_format(struct nm256 *chip, struct nm256_stream *s,
/* acquire interrupt */
static int snd_nm256_acquire_irq(struct nm256 *chip)
{
- mutex_lock(&chip->irq_mutex);
+ guard(mutex)(&chip->irq_mutex);
if (chip->irq < 0) {
if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED,
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);
return 0;
}
/* release interrupt */
static void snd_nm256_release_irq(struct nm256 *chip)
{
- mutex_lock(&chip->irq_mutex);
+ guard(mutex)(&chip->irq_mutex);
if (chip->irq_acks > 0)
chip->irq_acks--;
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);
}
/*
@@ -563,16 +544,15 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct nm256 *chip = snd_pcm_substream_chip(substream);
struct nm256_stream *s = substream->runtime->private_data;
- int err = 0;
if (snd_BUG_ON(!s))
return -ENXIO;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
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);
@@ -581,7 +561,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);
@@ -589,11 +569,9 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
}
break;
default:
- err = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return err;
+ return 0;
}
static int
@@ -601,12 +579,11 @@ snd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct nm256 *chip = snd_pcm_substream_chip(substream);
struct nm256_stream *s = substream->runtime->private_data;
- int err = 0;
if (snd_BUG_ON(!s))
return -ENXIO;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -623,11 +600,9 @@ snd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)
}
break;
default:
- err = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return err;
+ return 0;
}
@@ -647,10 +622,9 @@ static int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream)
s->periods = substream->runtime->periods;
s->cur_period = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
s->running = 0;
snd_nm256_set_format(chip, s, substream);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -707,56 +681,32 @@ snd_nm256_playback_silence(struct snd_pcm_substream *substream,
static int
snd_nm256_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- if (copy_from_user_toio(s->bufptr + pos, src, count))
+ if (copy_from_iter_toio(s->bufptr + pos, count, 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, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- if (copy_to_user_fromio(dst, s->bufptr + pos, count))
+ if (copy_to_iter_fromio(s->bufptr + pos, count, dst) != 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__ */
@@ -797,7 +747,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 |
@@ -816,7 +766,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 |
@@ -850,7 +800,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;
@@ -920,14 +870,12 @@ snd_nm256_capture_close(struct snd_pcm_substream *substream)
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_user = snd_nm256_playback_copy,
- .copy_kernel = snd_nm256_playback_copy_kernel,
+ .copy = snd_nm256_playback_copy,
.fill_silence = snd_nm256_playback_silence,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
@@ -936,14 +884,12 @@ static const struct snd_pcm_ops snd_nm256_playback_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_user = snd_nm256_capture_copy,
- .copy_kernel = snd_nm256_capture_copy_kernel,
+ .copy = snd_nm256_capture_copy,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1043,7 +989,7 @@ snd_nm256_interrupt(int irq, void *dev_id)
/* Rather boring; check for individual interrupts and process them. */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (status & NM_PLAYBACK_INT) {
status &= ~NM_PLAYBACK_INT;
NM_ACK_INT(chip, NM_PLAYBACK_INT);
@@ -1082,7 +1028,6 @@ snd_nm256_interrupt(int irq, void *dev_id)
NM_ACK_INT(chip, status);
}
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -1109,7 +1054,7 @@ snd_nm256_interrupt_zx(int irq, void *dev_id)
/* Rather boring; check for individual interrupts and process them. */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (status & NM2_PLAYBACK_INT) {
status &= ~NM2_PLAYBACK_INT;
NM2_ACK_INT(chip, NM2_PLAYBACK_INT);
@@ -1147,7 +1092,6 @@ snd_nm256_interrupt_zx(int irq, void *dev_id)
NM2_ACK_INT(chip, status);
}
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -1193,7 +1137,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 },
@@ -1323,18 +1267,20 @@ 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));
@@ -1367,7 +1313,7 @@ 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) {
dev_err(chip->card->dev,
"Unable to scan for card signature in video RAM\n");
@@ -1402,7 +1348,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
/*
* APM event handler, so the card is properly reinitialized after a power
* event.
@@ -1413,7 +1358,6 @@ static int nm256_suspend(struct device *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;
return 0;
@@ -1436,9 +1380,8 @@ static int nm256_resume(struct device *dev)
for (i = 0; i < 2; i++) {
struct nm256_stream *s = &chip->streams[i];
if (s->substream && s->suspended) {
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_nm256_set_format(chip, s, s->substream);
- spin_unlock_irq(&chip->reg_lock);
}
}
@@ -1447,61 +1390,29 @@ static int nm256_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume);
-static int snd_nm256_free(struct nm256 *chip)
+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);
-
- iounmap(chip->cport);
- 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
-snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
- struct nm256 **chip_ret)
+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;
@@ -1523,22 +1434,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 = pcim_request_all_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) {
- dev_err(card->dev, "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) {
+ 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);
- err = -ENOMEM;
- goto __error;
+ return -ENOMEM;
}
if (!strcmp(card->driver, "NM256AV")) {
@@ -1554,8 +1460,7 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
" force_ac97=1\n");
dev_err(card->dev,
"or try sb16, opl3sa2, or cs423x drivers instead.\n");
- err = -ENXIO;
- goto __error;
+ return -ENXIO;
}
}
chip->buffer_end = 2560 * 1024;
@@ -1585,8 +1490,9 @@ 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;
@@ -1595,21 +1501,12 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
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) {
- dev_err(card->dev, "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;
+ 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);
- goto __error;
+ return -ENOMEM;
}
/* set offsets */
@@ -1634,24 +1531,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;
-
- *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[] = {
+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),
@@ -1673,36 +1561,37 @@ static int snd_nm256_probe(struct pci_dev *pci,
dev_dbg(&pci->dev, "Enabled quirk for %s.\n",
snd_pci_quirk_name(q));
switch (q->value) {
- case NM_BLACKLISTED:
+ case NM_IGNORED:
dev_info(&pci->dev,
- "The device is blacklisted. Loading stopped\n");
+ "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_new(&pci->dev, 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:
- strcpy(card->driver, "NM256AV");
+ strscpy(card->driver, "NM256AV");
break;
case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO:
- strcpy(card->driver, "NM256ZX");
+ strscpy(card->driver, "NM256ZX");
break;
case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO:
- strcpy(card->driver, "NM256XL+");
+ strscpy(card->driver, "NM256XL+");
break;
default:
dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device);
- snd_card_free(card);
return -EINVAL;
}
@@ -1717,11 +1606,9 @@ static int 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) {
dev_dbg(&pci->dev, "reset_workaround activated\n");
@@ -1733,39 +1620,33 @@ static int snd_nm256_probe(struct pci_dev *pci,
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 snd_nm256_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
-
static struct pci_driver nm256_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_nm256_ids,
.probe = snd_nm256_probe,
- .remove = snd_nm256_remove,
.driver = {
- .pm = NM256_PM_OPS,
+ .pm = &nm256_pm,
},
};
diff --git a/sound/pci/nm256/nm256_coef.c b/sound/pci/nm256/nm256_coef.c
index c757252119b1..f0599dbe8492 100644
--- a/sound/pci/nm256/nm256_coef.c
+++ b/sound/pci/nm256/nm256_coef.c
@@ -1,7 +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,
@@ -4598,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 0dfc4f840992..cc0c24694750 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
-snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.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 \
+snd-oxygen-lib-y := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
+snd-oxygen-y := oxygen.o xonar_dg_mixer.o xonar_dg.o
+snd-se6x-y := se6x.o
+snd-virtuoso-y := 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
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index e36ed8af55ad..e6f869cf8ca2 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -1,20 +1,8 @@
+// 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
*/
/*
@@ -68,9 +56,6 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
- ",{C-Media,CMI8787}"
- ",{C-Media,CMI8788}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -465,7 +450,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
int changed;
u8 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->ak4396_regs[0][AK4396_CONTROL_2];
if (value->value.enumerated.item[0])
reg |= AK4396_SLOW;
@@ -476,7 +461,6 @@ static int rolloff_put(struct snd_kcontrol *ctl,
for (i = 0; i < data->dacs; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -514,14 +498,13 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
unsigned int reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
if (value->value.enumerated.item[0])
reg |= WM8785_HPFR | WM8785_HPFL;
changed = reg != data->wm8785_regs[WM8785_R2];
if (changed)
wm8785_write(chip, WM8785_R2, reg);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -578,7 +561,7 @@ static int meridian_dig_source_put(struct snd_kcontrol *ctl,
u16 old_reg, new_reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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)
@@ -588,7 +571,6 @@ static int meridian_dig_source_put(struct snd_kcontrol *ctl,
changed = new_reg != old_reg;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -599,7 +581,7 @@ static int claro_dig_source_put(struct snd_kcontrol *ctl,
u16 old_reg, new_reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
if (value->value.enumerated.item[0])
@@ -607,7 +589,6 @@ static int claro_dig_source_put(struct snd_kcontrol *ctl,
changed = new_reg != old_reg;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -869,12 +850,9 @@ static struct pci_driver oxygen_driver = {
.name = KBUILD_MODNAME,
.id_table = oxygen_ids,
.probe = generic_oxygen_probe,
- .remove = oxygen_pci_remove,
-#ifdef CONFIG_PM_SLEEP
.driver = {
- .pm = &oxygen_pci_pm,
+ .pm = pm_sleep_ptr(&oxygen_pci_pm),
},
-#endif
};
module_pci_driver(oxygen_driver);
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 06bf7e5744d0..820026daf838 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -161,10 +161,7 @@ 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_SLEEP
extern const struct dev_pm_ops oxygen_pci_pm;
-#endif
void oxygen_pci_shutdown(struct pci_dev *pci);
/* oxygen_mixer.c */
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index c7851da37749..af8f495dee0c 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -1,20 +1,8 @@
+// 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>
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index b4ef5804212d..6b096d654f9f 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>
@@ -71,36 +59,34 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
if (!status)
return IRQ_NONE;
- spin_lock(&chip->reg_lock);
-
- clear = status & (OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_B |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH |
- OXYGEN_CHANNEL_AC97 |
- OXYGEN_INT_SPDIF_IN_DETECT |
- OXYGEN_INT_GPIO |
- OXYGEN_INT_AC97);
- if (clear) {
- if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
- chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
- oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
- chip->interrupt_mask & ~clear);
- oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
- chip->interrupt_mask);
- }
-
- elapsed_streams = status & chip->pcm_running;
+ scoped_guard(spinlock, &chip->reg_lock) {
+ clear = status & (OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_B |
+ OXYGEN_CHANNEL_C |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH |
+ OXYGEN_CHANNEL_AC97 |
+ OXYGEN_INT_SPDIF_IN_DETECT |
+ OXYGEN_INT_GPIO |
+ OXYGEN_INT_AC97);
+ if (clear) {
+ if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
+ chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask & ~clear);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask);
+ }
- spin_unlock(&chip->reg_lock);
+ elapsed_streams = status & chip->pcm_running;
+ }
for (i = 0; i < PCM_COUNT; ++i)
if ((elapsed_streams & (1 << i)) && chip->streams[i])
snd_pcm_period_elapsed(chip->streams[i]);
if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
OXYGEN_SPDIF_RATE_INT)) {
@@ -108,7 +94,6 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
schedule_work(&chip->spdif_input_bits_work);
}
- spin_unlock(&chip->reg_lock);
}
if (status & OXYGEN_INT_GPIO)
@@ -139,45 +124,45 @@ static void oxygen_spdif_input_bits_changed(struct work_struct *work)
* changes.
*/
msleep(1);
- spin_lock_irq(&chip->reg_lock);
- reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
- if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
- OXYGEN_SPDIF_LOCK_STATUS))
- == OXYGEN_SPDIF_SENSE_STATUS) {
- /*
- * If we detect activity on the SPDIF input but cannot lock to
- * a signal, the clock bit is likely to be wrong.
- */
- reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
- oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
- spin_unlock_irq(&chip->reg_lock);
- msleep(1);
- spin_lock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
OXYGEN_SPDIF_LOCK_STATUS))
== OXYGEN_SPDIF_SENSE_STATUS) {
- /* nothing detected with either clock; give up */
- if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
- == OXYGEN_SPDIF_IN_CLOCK_192) {
- /*
- * Reset clock to <= 96 kHz because this is
- * more likely to be received next time.
- */
- reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
- reg |= OXYGEN_SPDIF_IN_CLOCK_96;
- oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+ /*
+ * If we detect activity on the SPDIF input but cannot lock to
+ * a signal, the clock bit is likely to be wrong.
+ */
+ reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+ spin_unlock_irq(&chip->reg_lock);
+ msleep(1);
+ spin_lock_irq(&chip->reg_lock);
+ reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+ OXYGEN_SPDIF_LOCK_STATUS))
+ == OXYGEN_SPDIF_SENSE_STATUS) {
+ /* nothing detected with either clock; give up */
+ if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
+ == OXYGEN_SPDIF_IN_CLOCK_192) {
+ /*
+ * Reset clock to <= 96 kHz because this is
+ * more likely to be received next time.
+ */
+ reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
+ reg |= OXYGEN_SPDIF_IN_CLOCK_96;
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+ }
}
}
}
- spin_unlock_irq(&chip->reg_lock);
if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
- spin_lock_irq(&chip->reg_lock);
- chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
- oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
- chip->interrupt_mask);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask);
+ }
/*
* We don't actually know that any channel status bits have
@@ -244,10 +229,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
static void oxygen_proc_init(struct oxygen *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "oxygen", &entry))
- snd_info_set_text_ops(entry, chip, oxygen_proc_read);
+ snd_card_ro_proc_new(chip->card, "oxygen", chip, oxygen_proc_read);
}
static const struct pci_device_id *
@@ -373,7 +355,7 @@ 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;
@@ -572,12 +554,11 @@ static void oxygen_init(struct oxygen *chip)
static void oxygen_shutdown(struct oxygen *chip)
{
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->interrupt_mask = 0;
chip->pcm_running = 0;
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
- spin_unlock_irq(&chip->reg_lock);
}
static void oxygen_card_free(struct snd_card *card)
@@ -585,18 +566,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_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,
@@ -609,8 +585,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_new(&pci->dev, index, id, owner,
- sizeof(*chip), &card);
+ err = snd_devm_card_new(&pci->dev, index, id, owner,
+ sizeof(*chip), &card);
if (err < 0)
return err;
@@ -625,41 +601,38 @@ 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);
+ err = pcim_request_all_regions(pci, DRIVER);
if (err < 0) {
dev_err(card->dev, "cannot reserve PCI resources\n");
- goto err_pci_enable;
+ return err;
}
if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
dev_err(card->dev, "invalid PCI I/O range\n");
- err = -ENXIO;
- goto err_pci_regions;
+ 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);
@@ -669,28 +642,29 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
oxygen_init(chip);
chip->model.init(chip);
- err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, chip);
+ err = devm_request_irq(&pci->dev, pci->irq, oxygen_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
if (err < 0) {
dev_err(card->dev, "cannot grab interrupt %d\n", pci->irq);
- goto err_card;
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
- strcpy(card->driver, chip->model.chip);
- strcpy(card->shortname, chip->model.shortname);
+ strscpy(card->driver, chip->model.chip);
+ strscpy(card->shortname, chip->model.shortname);
sprintf(card->longname, "%s at %#lx, irq %i",
chip->model.longname, chip->addr, chip->irq);
- strcpy(card->mixername, chip->model.chip);
+ strscpy(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 =
@@ -703,65 +677,56 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
chip->addr + OXYGEN_MPU401,
info_flags, -1, &chip->midi);
if (err < 0)
- goto err_card;
+ return err;
}
oxygen_proc_init(chip);
- spin_lock_irq(&chip->reg_lock);
- if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
- chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
- if (chip->has_ac97_0 | chip->has_ac97_1)
- chip->interrupt_mask |= OXYGEN_INT_AC97;
- oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+ if (chip->has_ac97_0 | chip->has_ac97_1)
+ chip->interrupt_mask |= OXYGEN_INT_AC97;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+ }
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));
+ 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_SLEEP
static int oxygen_pci_suspend(struct device *dev)
{
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)
- snd_pcm_suspend(chip->streams[i]);
-
if (chip->model.suspend)
chip->model.suspend(chip);
- spin_lock_irq(&chip->reg_lock);
- saved_interrupt_mask = chip->interrupt_mask;
- chip->interrupt_mask = 0;
- oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
- oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ saved_interrupt_mask = chip->interrupt_mask;
+ chip->interrupt_mask = 0;
+ oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+ }
- synchronize_irq(chip->irq);
flush_work(&chip->spdif_input_bits_work);
flush_work(&chip->gpio_work);
chip->interrupt_mask = saved_interrupt_mask;
@@ -819,9 +784,7 @@ static int oxygen_pci_resume(struct device *dev)
return 0;
}
-SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume);
-EXPORT_SYMBOL(oxygen_pci_pm);
-#endif /* CONFIG_PM_SLEEP */
+EXPORT_SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume);
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 4ca12665ff73..256a601d7811 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>
@@ -43,10 +31,9 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
unsigned int i;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
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;
}
@@ -58,7 +45,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
int changed;
changed = 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
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];
@@ -66,7 +53,6 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
}
if (changed)
chip->model.update_dac_volume(chip);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -75,9 +61,8 @@ static int dac_mute_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.integer.value[0] = !chip->dac_mute;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -87,13 +72,12 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = (!value->value.integer.value[0]) != chip->dac_mute;
if (changed) {
chip->dac_mute = !value->value.integer.value[0];
chip->model.update_dac_mute(chip);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -126,9 +110,8 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.enumerated.item[0] = chip->dac_routing;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -200,13 +183,12 @@ static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
if (value->value.enumerated.item[0] >= count)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = value->value.enumerated.item[0] != chip->dac_routing;
if (changed) {
chip->dac_routing = value->value.enumerated.item[0];
oxygen_update_dac_routing(chip);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -215,9 +197,8 @@ static int spdif_switch_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.integer.value[0] = chip->spdif_playback_enable;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -291,7 +272,7 @@ static int spdif_switch_put(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = value->value.integer.value[0] != chip->spdif_playback_enable;
if (changed) {
chip->spdif_playback_enable = !!value->value.integer.value[0];
@@ -299,7 +280,6 @@ static int spdif_switch_put(struct snd_kcontrol *ctl,
oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -348,9 +328,8 @@ static int spdif_default_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oxygen_to_iec958(chip->spdif_bits, value);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -362,14 +341,13 @@ static int spdif_default_put(struct snd_kcontrol *ctl,
int changed;
new_bits = iec958_to_oxygen(value);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = new_bits != chip->spdif_bits;
if (changed) {
chip->spdif_bits = new_bits;
if (!(chip->pcm_active & (1 << PCM_SPDIF)))
write_spdif_bits(chip, new_bits);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -388,9 +366,8 @@ static int spdif_pcm_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oxygen_to_iec958(chip->spdif_pcm_bits, value);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -402,14 +379,13 @@ static int spdif_pcm_put(struct snd_kcontrol *ctl,
int changed;
new_bits = iec958_to_oxygen(value);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = new_bits != chip->spdif_pcm_bits;
if (changed) {
chip->spdif_pcm_bits = new_bits;
if (chip->pcm_active & (1 << PCM_SPDIF))
write_spdif_bits(chip, new_bits);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -456,7 +432,7 @@ static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
u32 oldreg, newreg;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (value->value.integer.value[0])
newreg = oldreg | bit;
@@ -465,7 +441,6 @@ static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
changed = newreg != oldreg;
if (changed)
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
@@ -500,7 +475,7 @@ static int monitor_put(struct snd_kcontrol *ctl,
u8 oldreg, newreg;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR);
if ((!!value->value.integer.value[0] ^ !!invert) != 0)
newreg = oldreg | bit;
@@ -509,7 +484,6 @@ static int monitor_put(struct snd_kcontrol *ctl,
changed = newreg != oldreg;
if (changed)
oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg);
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
@@ -523,9 +497,8 @@ static int ac97_switch_get(struct snd_kcontrol *ctl,
int invert = ctl->private_value & (1 << 16);
u16 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = oxygen_read_ac97(chip, codec, index);
- mutex_unlock(&chip->mutex);
if (!(reg & (1 << bitnr)) ^ !invert)
value->value.integer.value[0] = 1;
else
@@ -562,7 +535,7 @@ static int ac97_switch_put(struct snd_kcontrol *ctl,
u16 oldreg, newreg;
int change;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oldreg = oxygen_read_ac97(chip, codec, index);
newreg = oldreg;
if (!value->value.integer.value[0] ^ !invert)
@@ -591,7 +564,6 @@ static int ac97_switch_put(struct snd_kcontrol *ctl,
CM9780_GPO0, CM9780_GPO0);
}
}
- mutex_unlock(&chip->mutex);
return change;
}
@@ -616,9 +588,8 @@ static int ac97_volume_get(struct snd_kcontrol *ctl,
unsigned int index = ctl->private_value & 0xff;
u16 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = oxygen_read_ac97(chip, codec, index);
- mutex_unlock(&chip->mutex);
if (!stereo) {
value->value.integer.value[0] = 31 - (reg & 0x1f);
} else {
@@ -638,7 +609,7 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
u16 oldreg, newreg;
int change;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oldreg = oxygen_read_ac97(chip, codec, index);
if (!stereo) {
newreg = oldreg & ~0x1f;
@@ -651,7 +622,6 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, codec, index, newreg);
- mutex_unlock(&chip->mutex);
return change;
}
@@ -668,10 +638,9 @@ static int mic_fmic_source_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.enumerated.item[0] =
!!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -682,7 +651,7 @@ static int mic_fmic_source_put(struct snd_kcontrol *ctl,
u16 oldreg, newreg;
int change;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK);
if (value->value.enumerated.item[0])
newreg = oldreg | CM9780_FMIC2MIC;
@@ -691,7 +660,6 @@ static int mic_fmic_source_put(struct snd_kcontrol *ctl,
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, 0, CM9780_JACK, newreg);
- mutex_unlock(&chip->mutex);
return change;
}
@@ -711,9 +679,8 @@ static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
u16 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
- mutex_unlock(&chip->mutex);
value->value.integer.value[0] = reg & 7;
value->value.integer.value[1] = (reg >> 8) & 7;
return 0;
@@ -726,15 +693,14 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
u16 oldreg, newreg;
int change;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
newreg = oldreg & ~0x0707;
newreg = newreg | (value->value.integer.value[0] & 7);
- newreg = newreg | ((value->value.integer.value[0] & 7) << 8);
+ newreg = newreg | ((value->value.integer.value[1] & 7) << 8);
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg);
- mutex_unlock(&chip->mutex);
return change;
}
@@ -1052,10 +1018,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];
@@ -1086,11 +1052,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;
}
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index 042a2439fea5..b716356010b8 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>
@@ -149,7 +137,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_64000);
runtime->hw.rate_min = 44100;
}
- /* fall through */
+ fallthrough;
case PCM_A:
case PCM_B:
runtime->hw.fifo_size = 0;
@@ -183,7 +171,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
snd_pcm_set_sync(substream);
chip->streams[channel] = substream;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
chip->pcm_active |= 1 << channel;
if (channel == PCM_SPDIF) {
chip->spdif_pcm_bits = chip->spdif_bits;
@@ -193,7 +181,6 @@ static int oxygen_open(struct snd_pcm_substream *substream,
SNDRV_CTL_EVENT_MASK_INFO,
&chip->controls[CONTROL_SPDIF_PCM]->id);
}
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -233,7 +220,7 @@ static int oxygen_close(struct snd_pcm_substream *substream)
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
chip->pcm_active &= ~(1 << channel);
if (channel == PCM_SPDIF) {
chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
@@ -244,7 +231,6 @@ static int oxygen_close(struct snd_pcm_substream *substream)
}
if (channel == PCM_SPDIF || channel == PCM_MULTICH)
oxygen_update_spdif_source(chip);
- mutex_unlock(&chip->mutex);
chip->streams[channel] = NULL;
return 0;
@@ -316,12 +302,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);
@@ -369,24 +349,23 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- spin_lock_irq(&chip->reg_lock);
- oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
- oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
- OXYGEN_REC_FORMAT_A_MASK);
- oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
- oxygen_rate(hw_params) |
- 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 |
- OXYGEN_I2S_MCLK_MASK |
- OXYGEN_I2S_BITS_MASK);
- spin_unlock_irq(&chip->reg_lock);
-
- mutex_lock(&chip->mutex);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
+ OXYGEN_REC_FORMAT_A_MASK);
+ oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
+ oxygen_rate(hw_params) |
+ 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 |
+ OXYGEN_I2S_MCLK_MASK |
+ OXYGEN_I2S_BITS_MASK);
+ }
+
+ guard(mutex)(&chip->mutex);
chip->model.set_adc_params(chip, hw_params);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -404,26 +383,25 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
is_ac97 = chip->has_ac97_1 &&
(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
- spin_lock_irq(&chip->reg_lock);
- oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
- oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
- OXYGEN_REC_FORMAT_B_MASK);
- if (!is_ac97)
- oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
- oxygen_rate(hw_params) |
- 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);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
+ OXYGEN_REC_FORMAT_B_MASK);
+ if (!is_ac97)
+ oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
+ oxygen_rate(hw_params) |
+ 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);
+ }
if (!is_ac97) {
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
chip->model.set_adc_params(chip, hw_params);
- mutex_unlock(&chip->mutex);
}
return 0;
}
@@ -441,26 +419,25 @@ static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
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);
+ scoped_guard(spinlock_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);
+ }
if (!is_spdif) {
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
chip->model.set_adc_params(chip, hw_params);
- mutex_unlock(&chip->mutex);
}
return 0;
}
@@ -475,8 +452,8 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- mutex_lock(&chip->mutex);
- spin_lock_irq(&chip->reg_lock);
+ guard(mutex)(&chip->mutex);
+ guard(spinlock_irq)(&chip->reg_lock);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE);
oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
@@ -486,8 +463,6 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
OXYGEN_SPDIF_OUT_RATE_MASK);
oxygen_update_spdif_source(chip);
- spin_unlock_irq(&chip->reg_lock);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -501,29 +476,28 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- mutex_lock(&chip->mutex);
- spin_lock_irq(&chip->reg_lock);
- oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
- oxygen_play_channels(hw_params),
- OXYGEN_PLAY_CHANNELS_MASK);
- oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
- oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
- OXYGEN_MULTICH_FORMAT_MASK);
- oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
- oxygen_rate(hw_params) |
- chip->model.dac_i2s_format |
- get_mclk(chip, PCM_MULTICH, hw_params) |
- oxygen_i2s_bits(hw_params),
- OXYGEN_I2S_RATE_MASK |
- OXYGEN_I2S_FORMAT_MASK |
- OXYGEN_I2S_MCLK_MASK |
- OXYGEN_I2S_BITS_MASK);
- oxygen_update_spdif_source(chip);
- spin_unlock_irq(&chip->reg_lock);
+ guard(mutex)(&chip->mutex);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
+ oxygen_play_channels(hw_params),
+ OXYGEN_PLAY_CHANNELS_MASK);
+ oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
+ OXYGEN_MULTICH_FORMAT_MASK);
+ oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+ oxygen_rate(hw_params) |
+ chip->model.dac_i2s_format |
+ get_mclk(chip, PCM_MULTICH, hw_params) |
+ oxygen_i2s_bits(hw_params),
+ OXYGEN_I2S_RATE_MASK |
+ OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_MCLK_MASK |
+ OXYGEN_I2S_BITS_MASK);
+ oxygen_update_spdif_source(chip);
+ }
chip->model.set_dac_params(chip, hw_params);
oxygen_update_dac_routing(chip);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -533,25 +507,24 @@ static int oxygen_hw_free(struct snd_pcm_substream *substream)
unsigned int channel = oxygen_substream_channel(substream);
unsigned int channel_mask = 1 << channel;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->interrupt_mask &= ~channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
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)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
- oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
- OXYGEN_SPDIF_OUT_ENABLE);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_OUT_ENABLE);
+ }
return oxygen_hw_free(substream);
}
@@ -561,7 +534,7 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
unsigned int channel = oxygen_substream_channel(substream);
unsigned int channel_mask = 1 << channel;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
@@ -570,7 +543,6 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
else
chip->interrupt_mask |= channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -602,7 +574,7 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
}
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (!pausing) {
if (cmd == SNDRV_PCM_TRIGGER_START)
chip->pcm_running |= mask;
@@ -615,7 +587,6 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
else
oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
}
- spin_unlock(&chip->reg_lock);
return 0;
}
@@ -634,7 +605,6 @@ static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
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,
@@ -645,7 +615,6 @@ static const struct snd_pcm_ops oxygen_rec_a_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,
@@ -656,7 +625,6 @@ static const struct snd_pcm_ops oxygen_rec_b_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,
@@ -667,7 +635,6 @@ static const struct snd_pcm_ops oxygen_rec_c_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,
@@ -678,7 +645,6 @@ static const struct snd_pcm_ops oxygen_spdif_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,
@@ -689,7 +655,6 @@ static const struct snd_pcm_ops oxygen_multich_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,
@@ -721,19 +686,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;
- strcpy(pcm->name, "Multichannel");
+ strscpy(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);
@@ -749,11 +714,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;
- 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);
+ strscpy(pcm->name, "Digital");
+ 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) {
@@ -779,11 +744,11 @@ int oxygen_pcm_init(struct oxygen *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_b_ops);
pcm->private_data = chip;
- strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- DEFAULT_BUFFER_BYTES,
- BUFFER_BYTES_MAX);
+ strscpy(pcm->name, outs ? "Front Panel" : "Analog 2");
+ 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);
@@ -797,11 +762,11 @@ int oxygen_pcm_init(struct oxygen *chip)
OXYGEN_REC_C_ROUTE_I2S_ADC_3,
OXYGEN_REC_C_ROUTE_MASK);
pcm->private_data = chip;
- strcpy(pcm->name, "Analog 3");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- DEFAULT_BUFFER_BYTES,
- BUFFER_BYTES_MAX);
+ strscpy(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/pcm1796.h b/sound/pci/oxygen/pcm1796.h
index 34d07dd2d22e..d5dcb09e44cd 100644
--- a/sound/pci/oxygen/pcm1796.h
+++ b/sound/pci/oxygen/pcm1796.h
@@ -10,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
index f70d514c1084..9d009015d97e 100644
--- a/sound/pci/oxygen/se6x.c
+++ b/sound/pci/oxygen/se6x.c
@@ -1,18 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8787 driver for the Studio Evolution SE6X
*
* 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/>.
*/
/*
@@ -39,7 +29,6 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Studio Evolution SE6X driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Studio Evolution,SE6X}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -148,12 +137,9 @@ static struct pci_driver se6x_driver = {
.name = KBUILD_MODNAME,
.id_table = se6x_ids,
.probe = se6x_probe,
- .remove = oxygen_pci_remove,
-#ifdef CONFIG_PM_SLEEP
.driver = {
- .pm = &oxygen_pci_pm,
+ .pm = pm_sleep_ptr(&oxygen_pci_pm),
},
-#endif
.shutdown = oxygen_pci_shutdown,
};
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 83de6fb01021..ded62199da7f 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -1,20 +1,8 @@
+// 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>
@@ -28,7 +16,6 @@
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;
@@ -95,12 +82,9 @@ static struct pci_driver xonar_driver = {
.name = KBUILD_MODNAME,
.id_table = xonar_ids,
.probe = xonar_probe,
- .remove = oxygen_pci_remove,
-#ifdef CONFIG_PM_SLEEP
.driver = {
- .pm = &oxygen_pci_pm,
+ .pm = pm_sleep_ptr(&oxygen_pci_pm),
},
-#endif
.shutdown = oxygen_pci_shutdown,
};
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/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c
index d231b93d6ab5..47b2758653e4 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/>.
*/
/*
@@ -320,7 +309,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
int changed;
u8 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->cs4398_regs[7];
if (value->value.enumerated.item[0])
reg |= CS4398_FILT_SEL;
@@ -335,7 +324,6 @@ static int rolloff_put(struct snd_kcontrol *ctl,
reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
cs4362a_write(chip, 0x04, reg);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -351,11 +339,10 @@ static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
unsigned int reg, unsigned int mute)
{
if (reg == AC97_LINE) {
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
mute ? GPIO_D1_INPUT_ROUTE : 0,
GPIO_D1_INPUT_ROUTE);
- spin_unlock_irq(&chip->reg_lock);
}
}
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
index 4cf3200e988b..b90421a1d909 100644
--- a/sound/pci/oxygen/xonar_dg.c
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -1,19 +1,9 @@
+// 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>
- *
- * 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/>.
*/
/*
@@ -39,7 +29,7 @@
* 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 and the first
+ * GPIO 7 -> switch green rear output jack between CS4245 and the first
* channel of CS4361 (mechanical relay)
* GPIO 8 -> enable output to speakers
*
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c
index d22fbe8aebd0..2179ff8e4d86 100644
--- a/sound/pci/oxygen/xonar_dg_mixer.c
+++ b/sound/pci/oxygen/xonar_dg_mixer.c
@@ -1,19 +1,9 @@
+// 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>
- *
- * 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>
@@ -72,9 +62,8 @@ static int output_select_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.enumerated.item[0] = data->output_sel;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -87,14 +76,13 @@ static int output_select_put(struct snd_kcontrol *ctl,
int changed = 0;
int ret;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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;
}
@@ -118,12 +106,11 @@ static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
struct dg *data = chip->model_data;
unsigned int tmp;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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;
}
@@ -140,7 +127,7 @@ static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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;
@@ -150,7 +137,6 @@ static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
changed = ret >= 0 ? 1 : ret;
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -163,10 +149,9 @@ static int hp_mute_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
val->value.integer.value[0] =
!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -180,13 +165,12 @@ static int hp_mute_put(struct snd_kcontrol *ctl,
if (val->value.integer.value[0] > 1)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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;
}
@@ -222,10 +206,9 @@ static int input_vol_get(struct snd_kcontrol *ctl,
struct dg *data = chip->model_data;
unsigned int idx = ctl->private_value;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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;
}
@@ -243,7 +226,7 @@ static int input_vol_put(struct snd_kcontrol *ctl,
value->value.integer.value[1] < 2 * -12 ||
value->value.integer.value[1] > 2 * 12)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&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) {
@@ -256,7 +239,6 @@ static int input_vol_put(struct snd_kcontrol *ctl,
}
changed = ret >= 0 ? 1 : ret;
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -292,9 +274,8 @@ static int input_sel_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.enumerated.item[0] = data->input_sel;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -309,7 +290,7 @@ static int input_sel_put(struct snd_kcontrol *ctl,
if (value->value.enumerated.item[0] > 3)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = value->value.enumerated.item[0] != data->input_sel;
if (changed) {
data->input_sel = value->value.enumerated.item[0];
@@ -321,7 +302,6 @@ static int input_sel_put(struct snd_kcontrol *ctl,
data->input_vol[data->input_sel][1]);
changed = ret >= 0 ? 1 : ret;
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -351,7 +331,7 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
u8 reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
if (value->value.enumerated.item[0])
reg |= CS4245_HPF_FREEZE;
@@ -360,7 +340,6 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
cs4245_write_spi(chip, CS4245_ADC_CTRL);
}
- mutex_unlock(&chip->mutex);
return changed;
}
diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c
index 91d92bc32b75..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/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>
diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c
index 706b1a42163f..0edf67ce37d1 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>
@@ -120,7 +109,7 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
u16 old_bits, new_bits;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
if (!!value->value.integer.value[0] ^ invert)
new_bits = old_bits | bit;
@@ -129,6 +118,5 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
changed = new_bits != old_bits;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index 24109d37ca09..837a9505382a 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/>.
*/
/*
@@ -331,7 +320,7 @@ static void pcm1796_init(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->pcm1796_regs[0][18 - PCM1796_REG_BASE] =
- PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
+ 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] =
@@ -471,7 +460,7 @@ static void xonar_st_init(struct oxygen *chip)
data->generic.anti_pop_delay = 100;
data->h6 = chip->model.dac_channels_mixer > 2;
- data->has_cs2000 = 1;
+ data->has_cs2000 = true;
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
data->broken_i2c = true;
@@ -513,7 +502,7 @@ static void xonar_xense_init(struct oxygen *chip)
xonar_init_ext_power(chip);
data->generic.anti_pop_delay = 100;
- data->has_cs2000 = 1;
+ data->has_cs2000 = true;
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
@@ -621,6 +610,23 @@ static void update_pcm1796_oversampling(struct oxygen *chip)
pcm1796_write_cached(chip, i, 20, reg);
}
+static void update_pcm1796_deemph(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ u8 reg;
+
+ 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, 18, reg);
+}
+
static void set_pcm1796_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -629,6 +635,7 @@ static void set_pcm1796_params(struct oxygen *chip,
msleep(1);
data->current_rate = params_rate(params);
update_pcm1796_oversampling(chip);
+ update_pcm1796_deemph(chip);
}
static void update_pcm1796_volume(struct oxygen *chip)
@@ -653,9 +660,11 @@ static void update_pcm1796_mute(struct oxygen *chip)
unsigned int i;
u8 value;
- value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | 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);
}
@@ -753,7 +762,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
int changed;
u8 reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->pcm1796_regs[0][19 - PCM1796_REG_BASE];
reg &= ~PCM1796_FLT_MASK;
if (!value->value.enumerated.item[0])
@@ -765,7 +774,6 @@ static int rolloff_put(struct snd_kcontrol *ctl,
for (i = 0; i < data->dacs; ++i)
pcm1796_write(chip, i, 19, reg);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -777,6 +785,48 @@ static const struct snd_kcontrol_new rolloff_control = {
.put = rolloff_put,
};
+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.integer.value[0] =
+ !!(data->pcm1796_regs[0][18 - PCM1796_REG_BASE] & PCM1796_DME);
+ return 0;
+}
+
+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;
+
+ guard(mutex)(&chip->mutex);
+ 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) {
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write(chip, i, 18, reg);
+ }
+ return changed;
+}
+
+static const struct snd_kcontrol_new deemph_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .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",
@@ -820,7 +870,7 @@ static int st_output_switch_put(struct snd_kcontrol *ctl,
struct xonar_pcm179x *data = chip->model_data;
u16 gpio_old, gpio;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
gpio = gpio_old;
switch (value->value.enumerated.item[0]) {
@@ -837,7 +887,6 @@ static int st_output_switch_put(struct snd_kcontrol *ctl,
oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
data->hp_active = gpio & GPIO_ST_HP;
update_pcm1796_volume(chip);
- mutex_unlock(&chip->mutex);
return gpio != gpio_old;
}
@@ -857,7 +906,7 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (data->hp_gain_offset < 2*-12)
value->value.enumerated.item[0] = 0;
else if (data->hp_gain_offset < 2*-6)
@@ -866,7 +915,6 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
value->value.enumerated.item[0] = 2;
else
value->value.enumerated.item[0] = 3;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -883,13 +931,12 @@ static int st_hp_volume_offset_put(struct snd_kcontrol *ctl,
if (value->value.enumerated.item[0] > 3)
return -EINVAL;
offset = offsets[value->value.enumerated.item[0]];
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = offset != data->hp_gain_offset;
if (changed) {
data->hp_gain_offset = offset;
update_pcm1796_volume(chip);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -933,7 +980,7 @@ static int xense_output_switch_put(struct snd_kcontrol *ctl,
struct xonar_pcm179x *data = chip->model_data;
u16 gpio_old, gpio;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
gpio = gpio_old;
switch (value->value.enumerated.item[0]) {
@@ -950,7 +997,6 @@ static int xense_output_switch_put(struct snd_kcontrol *ctl,
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;
}
@@ -975,11 +1021,10 @@ static void xonar_line_mic_ac97_switch(struct oxygen *chip,
unsigned int reg, unsigned int mute)
{
if (reg == AC97_LINE) {
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
mute ? GPIO_INPUT_ROUTE : 0,
GPIO_INPUT_ROUTE);
- spin_unlock_irq(&chip->reg_lock);
}
}
@@ -1011,6 +1056,10 @@ static int add_pcm1796_controls(struct oxygen *chip)
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;
}
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index 90ac479f389f..7d92e6e20c39 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* card driver for models with WM8776/WM8766 DACs (Xonar DS/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/>.
*/
/*
@@ -127,7 +116,8 @@ static void wm8776_write(struct oxygen *chip,
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;
}
@@ -155,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;
@@ -246,7 +237,7 @@ static void xonar_ds_handle_hp_jack(struct oxygen *chip)
bool hp_plugged;
unsigned int reg;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
GPIO_DS_HP_DETECT);
@@ -261,8 +252,6 @@ static void xonar_ds_handle_hp_jack(struct oxygen *chip)
wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
-
- mutex_unlock(&chip->mutex);
}
static void xonar_ds_init(struct oxygen *chip)
@@ -530,14 +519,13 @@ static int wm8776_bit_switch_put(struct snd_kcontrol *ctl,
bool invert = (ctl->private_value >> 24) & 1;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg_value = data->wm8776_regs[reg_index] & ~bit;
if (value->value.integer.value[0] ^ invert)
reg_value |= bit;
changed = reg_value != data->wm8776_regs[reg_index];
if (changed)
wm8776_write(chip, reg_index, reg_value);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -657,13 +645,12 @@ static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value)
max = (ctl->private_value >> 12) & 0xf;
if (value < min || value > max)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = value != (ctl->private_value & 0xf);
if (changed) {
ctl->private_value = (ctl->private_value & ~0xf) | value;
wm8776_field_set_from_ctl(ctl);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -709,12 +696,11 @@ static int wm8776_hp_vol_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct xonar_wm87x6 *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.integer.value[0] =
data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK;
value->value.integer.value[1] =
data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -725,7 +711,7 @@ static int wm8776_hp_vol_put(struct snd_kcontrol *ctl,
struct xonar_wm87x6 *data = chip->model_data;
u8 to_update;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
to_update = (value->value.integer.value[0] !=
(data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK))
<< 0;
@@ -753,7 +739,6 @@ static int wm8776_hp_vol_put(struct snd_kcontrol *ctl,
value->value.integer.value[1] |
WM8776_HPZCEN | WM8776_UPDATE);
}
- mutex_unlock(&chip->mutex);
return to_update != 0;
}
@@ -779,7 +764,7 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
u16 reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->wm8776_regs[WM8776_ADCMUX];
if (value->value.integer.value[0]) {
reg |= mux_bit;
@@ -803,7 +788,6 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
GPIO_DS_INPUT_ROUTE);
wm8776_write(chip, WM8776_ADCMUX, reg);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -823,12 +807,11 @@ static int wm8776_input_vol_get(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
struct xonar_wm87x6 *data = chip->model_data;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
value->value.integer.value[0] =
data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK;
value->value.integer.value[1] =
data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK;
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -839,7 +822,7 @@ static int wm8776_input_vol_put(struct snd_kcontrol *ctl,
struct xonar_wm87x6 *data = chip->model_data;
int changed = 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = (value->value.integer.value[0] !=
(data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) ||
(value->value.integer.value[1] !=
@@ -848,7 +831,6 @@ static int wm8776_input_vol_put(struct snd_kcontrol *ctl,
value->value.integer.value[0] | WM8776_ZCA);
wm8776_write_cached(chip, WM8776_ADCRVOL,
value->value.integer.value[1] | WM8776_ZCA);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -904,7 +886,7 @@ static int wm8776_level_control_put(struct snd_kcontrol *ctl,
if (value->value.enumerated.item[0] >= 3)
return -EINVAL;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = value->value.enumerated.item[0] != ctl->private_value;
if (changed) {
ctl->private_value = value->value.enumerated.item[0];
@@ -935,7 +917,6 @@ static int wm8776_level_control_put(struct snd_kcontrol *ctl,
for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i)
activate_control(chip, data->lc_controls[i], mode);
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -965,14 +946,13 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
unsigned int reg;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD;
if (!value->value.enumerated.item[0])
reg |= WM8776_ADCHPD;
changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL];
if (changed)
wm8776_write(chip, WM8776_ADCIFCTRL, reg);
- mutex_unlock(&chip->mutex);
return changed;
}
diff --git a/sound/pci/pcxhr/Makefile b/sound/pci/pcxhr/Makefile
index b06128e918ca..0ea1e5ccb56f 100644
--- a/sound/pci/pcxhr/Makefile
+++ b/sound/pci/pcxhr/Makefile
@@ -1,2 +1,3 @@
-snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-pcxhr-y := 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 f9ae72f28ddc..83066d08367e 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
*/
@@ -48,7 +35,6 @@ 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 */
@@ -149,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 },
@@ -380,7 +366,7 @@ static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
mgr->codec_speed = speed; /* save new codec speed */
}
- dev_dbg(&mgr->pci->dev, "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;
}
@@ -513,7 +499,7 @@ static int pcxhr_set_stream_state(struct snd_pcxhr *chip,
else {
if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
dev_err(chip->card->dev,
- "pcxhr_set_stream_state CANNOT be stopped\n");
+ "%s CANNOT be stopped\n", __func__);
return -EINVAL;
}
start = 0;
@@ -538,7 +524,7 @@ static int pcxhr_set_stream_state(struct snd_pcxhr *chip,
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
dev_err(chip->card->dev,
- "ERROR pcxhr_set_stream_state err=%x;\n", err);
+ "ERROR %s err=%x;\n", __func__, err);
stream->status =
start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
return err;
@@ -584,7 +570,7 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
break;
default:
dev_err(chip->card->dev,
- "error pcxhr_set_format() : unknown format\n");
+ "error %s() : unknown format\n", __func__);
return -EINVAL;
}
@@ -629,7 +615,7 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
dev_err(chip->card->dev,
- "ERROR pcxhr_set_format err=%x;\n", err);
+ "ERROR %s err=%x;\n", __func__, err);
return err;
}
@@ -644,7 +630,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
stream_num = is_capture ? 0 : subs->number;
dev_dbg(chip->card->dev,
- "pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",
+ "%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);
@@ -715,7 +701,7 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
start_time = ktime_get();
#endif
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
/* check the pipes concerned and build pipe_array */
for (i = 0; i < mgr->num_cards; i++) {
@@ -734,22 +720,19 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
}
}
if (capture_mask == 0 && playback_mask == 0) {
- mutex_unlock(&mgr->setup_mutex);
- dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : no pipes\n");
+ dev_err(&mgr->pci->dev, "%s : no pipes\n", __func__);
return;
}
- dev_dbg(&mgr->pci->dev, "pcxhr_start_linked_stream : "
- "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);
- dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
+ dev_err(&mgr->pci->dev, "%s : "
"error stop pipes (P%x C%x)\n",
- playback_mask, capture_mask);
+ __func__, playback_mask, capture_mask);
return;
}
@@ -791,17 +774,16 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
/* synchronous start of all the pipes concerned */
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
if (err) {
- mutex_unlock(&mgr->setup_mutex);
- dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
+ 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)
*/
- mutex_lock(&mgr->lock);
+ guard(mutex)(&mgr->lock);
for ( i =0; i < mgr->num_cards; i++) {
struct pcxhr_stream *stream;
chip = mgr->chip[i];
@@ -819,9 +801,6 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
}
}
}
- mutex_unlock(&mgr->lock);
-
- mutex_unlock(&mgr->setup_mutex);
#ifdef CONFIG_SND_DEBUG_VERBOSE
stop_time = ktime_get();
@@ -903,7 +882,7 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
}
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
- dev_err(&mgr->pci->dev, "error pcxhr_hardware_timer err(%x)\n",
+ dev_err(&mgr->pci->dev, "error %s err(%x)\n", __func__,
err);
return err;
}
@@ -918,11 +897,11 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
int err = 0;
dev_dbg(chip->card->dev,
- "pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
+ "%s : period_size(%lx) periods(%x) buffer_size(%lx)\n", __func__,
subs->runtime->period_size, subs->runtime->periods,
subs->runtime->buffer_size);
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
do {
/* only the first stream can choose the sample rate */
@@ -938,8 +917,6 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
}
} while(0); /* do only once (so we can use break instead of goto) */
- mutex_unlock(&mgr->setup_mutex);
-
return err;
}
@@ -953,32 +930,14 @@ 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);
+ guard(mutex)(&mgr->setup_mutex);
- mutex_lock(&mgr->setup_mutex);
-
- stream->channels = channels;
- stream->format = format;
-
- /* allocate buffer */
- err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
-
- mutex_unlock(&mgr->setup_mutex);
-
- return err;
-}
+ /* set up channels */
+ stream->channels = params_channels(hw);
+ /* set up format for the stream */
+ stream->format = params_format(hw);
-static int pcxhr_hw_free(struct snd_pcm_substream *subs)
-{
- snd_pcm_lib_free_pages(subs);
return 0;
}
@@ -1021,18 +980,18 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
struct pcxhr_stream *stream;
int err;
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
/* copy the struct snd_pcm_hardware struct */
runtime->hw = pcxhr_caps;
if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
- dev_dbg(chip->card->dev, "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 {
- dev_dbg(chip->card->dev, "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
@@ -1041,9 +1000,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
}
if (stream->status != PCXHR_STREAM_STATUS_FREE){
/* streams in use */
- dev_err(chip->card->dev, "pcxhr_open chip%d subs%d in use\n",
- chip->chip_idx, subs->number);
- mutex_unlock(&mgr->setup_mutex);
+ dev_err(chip->card->dev, "%s chip%d subs%d in use\n",
+ __func__, chip->chip_idx, subs->number);
return -EBUSY;
}
@@ -1054,10 +1012,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
/* buffer-size should better be multiple of period-size */
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- mutex_unlock(&mgr->setup_mutex);
+ if (err < 0)
return err;
- }
/* if a sample rate is already used or fixed by external clock,
* the stream cannot change
@@ -1071,7 +1027,6 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
&external_rate) ||
external_rate == 0) {
/* cannot detect the external clock rate */
- mutex_unlock(&mgr->setup_mutex);
return -EBUSY;
}
runtime->hw.rate_min = external_rate;
@@ -1094,7 +1049,6 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
mgr->ref_count_rate++;
- mutex_unlock(&mgr->setup_mutex);
return 0;
}
@@ -1105,9 +1059,9 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
struct pcxhr_mgr *mgr = chip->mgr;
struct pcxhr_stream *stream = subs->runtime->private_data;
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
- dev_dbg(chip->card->dev, "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 */
@@ -1119,8 +1073,6 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
stream->status = PCXHR_STREAM_STATUS_FREE;
stream->substream = NULL;
- mutex_unlock(&mgr->setup_mutex);
-
return 0;
}
@@ -1133,14 +1085,12 @@ static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
struct snd_pcm_runtime *runtime = subs->runtime;
struct pcxhr_stream *stream = runtime->private_data;
- mutex_lock(&chip->mgr->lock);
+ guard(mutex)(&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;
- mutex_unlock(&chip->mgr->lock);
-
return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
timer_period_frag);
}
@@ -1149,10 +1099,8 @@ static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
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,
};
@@ -1166,9 +1114,10 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
char name[32];
snprintf(name, sizeof(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) {
+ 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;
}
@@ -1181,11 +1130,11 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
pcm->info_flags = 0;
pcm->nonatomic = true;
- strcpy(pcm->name, name);
+ strscpy(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;
}
@@ -1210,7 +1159,7 @@ static int pcxhr_create(struct pcxhr_mgr *mgr,
{
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,
};
@@ -1221,6 +1170,7 @@ static int pcxhr_create(struct pcxhr_mgr *mgr,
chip->card = card;
chip->chip_idx = idx;
chip->mgr = mgr;
+ card->sync_irq = mgr->irq;
if (idx < mgr->playback_chips)
/* stereo or mono streams */
@@ -1233,7 +1183,8 @@ static int 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;
}
@@ -1454,21 +1405,14 @@ static void pcxhr_proc_ltc(struct snd_info_entry *entry,
static void pcxhr_proc_init(struct snd_pcxhr *chip)
{
- struct snd_info_entry *entry;
-
- 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);
+ 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 (!snd_card_proc_new(chip->card, "ltc", &entry))
- snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc);
+ 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 */
@@ -1530,7 +1474,8 @@ static int 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);
@@ -1575,7 +1520,8 @@ static int 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;
@@ -1606,7 +1552,7 @@ static int pcxhr_probe(struct pci_dev *pci,
/* init setup mutex*/
mutex_init(&mgr->setup_mutex);
- 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);
@@ -1640,13 +1586,14 @@ static int pcxhr_probe(struct pci_dev *pci,
return err;
}
- strcpy(card->driver, DRIVER_NAME);
+ strscpy(card->driver, DRIVER_NAME);
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;
@@ -1656,7 +1603,8 @@ static int 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;
}
@@ -1664,7 +1612,7 @@ static int 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;
diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h
index d799cbd37301..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
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index d7e71f309299..b3b9ab4f303e 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -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 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>
@@ -65,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
@@ -479,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 },
@@ -510,7 +497,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
};
#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",
@@ -767,12 +754,8 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
*/
int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
{
- int err;
-
- mutex_lock(&mgr->msg_lock);
- err = pcxhr_send_msg_nolock(mgr, rmh);
- mutex_unlock(&mgr->msg_lock);
- return err;
+ guard(mutex)(&mgr->msg_lock);
+ return pcxhr_send_msg_nolock(mgr, rmh);
}
static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
@@ -975,14 +958,13 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
struct pcxhr_rmh rmh;
int err;
- mutex_lock(&mgr->msg_lock);
+ guard(mutex)(&mgr->msg_lock);
if ((mgr->io_num_reg_cont & mask) == value) {
dev_dbg(&mgr->pci->dev,
"IO_NUM_REG_CONT mask %x already is set to %x\n",
mask, value);
if (changed)
*changed = 0;
- mutex_unlock(&mgr->msg_lock);
return 0; /* already programmed */
}
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
@@ -997,7 +979,6 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
if (changed)
*changed = 1;
}
- mutex_unlock(&mgr->msg_lock);
return err;
}
@@ -1019,7 +1000,7 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
enum pcxhr_async_err_src err_src, int pipe,
int is_capture)
{
- static char* err_src_name[] = {
+ static const char * const err_src_name[] = {
[PCXHR_ERR_PIPE] = "Pipe",
[PCXHR_ERR_STREAM] = "Stream",
[PCXHR_ERR_AUDIO] = "Audio"
@@ -1282,7 +1263,7 @@ irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
int i, j;
struct snd_pcxhr *chip;
- mutex_lock(&mgr->lock);
+ guard(mutex)(&mgr->lock);
if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) {
/* is a 24 bit counter */
int dsp_time_new =
@@ -1341,6 +1322,5 @@ irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
}
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 dc267e4c1074..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
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index a99808ab01fe..249805065f61 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -1,23 +1,10 @@
+// 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>
@@ -335,14 +322,17 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
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);
@@ -361,7 +351,7 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
*/
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",
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 8b4d0282efb8..80d22e22ea30 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.c
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -1,23 +1,10 @@
+// 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>
@@ -548,7 +535,7 @@ int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
{
dev_dbg(chip->card->dev,
"hr222_update_analog_audio_level(%s chan=%d)\n",
- is_capture ? "capture" : "playback", channel);
+ snd_pcm_direction_name(is_capture), channel);
if (is_capture) {
int level_l, level_r, level_mic;
/* we have to update all levels */
@@ -723,9 +710,9 @@ static int hr222_mic_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->mic_volume;
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -734,13 +721,13 @@ static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (chip->mic_volume != ucontrol->value.integer.value[0]) {
changed = 1;
chip->mic_volume = ucontrol->value.integer.value[0];
hr222_update_analog_audio_level(chip, 1, 0);
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -773,9 +760,9 @@ static int hr222_mic_boost_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->mic_boost;
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -784,13 +771,13 @@ static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (chip->mic_boost != ucontrol->value.integer.value[0]) {
changed = 1;
chip->mic_boost = ucontrol->value.integer.value[0];
hr222_micro_boost(chip->mgr, chip->mic_boost);
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -813,9 +800,9 @@ static int hr222_phantom_power_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->phantom_power;
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -825,14 +812,13 @@ static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int power, changed = 0;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
power = !!ucontrol->value.integer.value[0];
if (chip->phantom_power != power) {
hr222_phantom_power(chip->mgr, power);
chip->phantom_power = power;
changed = 1;
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
index 5971b9933f41..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
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index d9a1c6c50a87..03daa2832b1b 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>
@@ -123,7 +110,8 @@ static int pcxhr_analog_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (kcontrol->private_value == 0) { /* playback */
ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
@@ -131,7 +119,6 @@ static int pcxhr_analog_vol_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -142,7 +129,7 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
int changed = 0;
int is_capture, i;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
is_capture = (kcontrol->private_value != 0);
for (i = 0; i < 2; i++) {
int new_volume = ucontrol->value.integer.value[i];
@@ -181,7 +168,6 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
is_capture, i);
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -205,10 +191,9 @@ static int pcxhr_audio_sw_get(struct snd_kcontrol *kcontrol,
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -217,7 +202,8 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int i, changed = 0;
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
for(i = 0; i < 2; i++) {
if (chip->analog_playback_active[i] !=
ucontrol->value.integer.value[i]) {
@@ -231,7 +217,6 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
pcxhr_update_analog_audio_level(chip, 0, i);
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -365,14 +350,13 @@ static int pcxhr_pcm_vol_get(struct snd_kcontrol *kcontrol,
int *stored_volume;
int is_capture = kcontrol->private_value;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (is_capture) /* digital capture */
stored_volume = chip->digital_capture_volume;
else /* digital playback */
stored_volume = chip->digital_playback_volume[idx];
ucontrol->value.integer.value[0] = stored_volume[0];
ucontrol->value.integer.value[1] = stored_volume[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -386,7 +370,7 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
int *stored_volume;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (is_capture) /* digital capture */
stored_volume = chip->digital_capture_volume;
else /* digital playback */
@@ -405,7 +389,6 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
}
if (!is_capture && changed) /* update playback volume */
pcxhr_update_playback_stream_level(chip, idx);
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -429,10 +412,9 @@ static int pcxhr_pcm_sw_get(struct snd_kcontrol *kcontrol,
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -444,7 +426,7 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int i, j;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
j = idx;
for (i = 0; i < 2; i++) {
if (chip->digital_playback_active[j][i] !=
@@ -456,7 +438,6 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
}
if (changed)
pcxhr_update_playback_stream_level(chip, idx);
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -478,10 +459,10 @@ static int pcxhr_monitor_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -492,7 +473,7 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
int changed = 0;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
if (chip->monitoring_volume[i] !=
ucontrol->value.integer.value[i]) {
@@ -505,7 +486,6 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
changed = 1;
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -528,10 +508,10 @@ static int pcxhr_monitor_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mgr->mixer_mutex);
+
+ guard(mutex)(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_active[0];
ucontrol->value.integer.value[1] = chip->monitoring_active[1];
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -542,7 +522,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
int changed = 0;
int i;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
if (chip->monitoring_active[i] !=
ucontrol->value.integer.value[i]) {
@@ -558,7 +538,6 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
/* update right monitoring volume and mute */
pcxhr_update_audio_pipe_level(chip, 0, 1);
- mutex_unlock(&chip->mgr->mixer_mutex);
return (changed != 0);
}
@@ -684,7 +663,7 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
}
if (ucontrol->value.enumerated.item[0] >= i)
return -EINVAL;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
chip->audio_capture_source = ucontrol->value.enumerated.item[0];
if (chip->mgr->is_hr_stereo)
@@ -693,7 +672,6 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
pcxhr_set_audio_source(chip);
ret = 1;
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return ret;
}
@@ -773,9 +751,9 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
}
if (ucontrol->value.enumerated.item[0] >= clock_items)
return -EINVAL;
- mutex_lock(&mgr->mixer_mutex);
+ guard(mutex)(&mgr->mixer_mutex);
if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
- mutex_lock(&mgr->setup_mutex);
+ guard(mutex)(&mgr->setup_mutex);
mgr->use_clock_type = ucontrol->value.enumerated.item[0];
rate = 0;
if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) {
@@ -791,10 +769,8 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
if (mgr->sample_rate)
mgr->sample_rate = rate;
}
- mutex_unlock(&mgr->setup_mutex);
ret = 1; /* return 1 even if the set was not done. ok ? */
}
- mutex_unlock(&mgr->mixer_mutex);
return ret;
}
@@ -827,7 +803,7 @@ static int pcxhr_clock_rate_get(struct snd_kcontrol *kcontrol,
struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
int i, err, rate;
- mutex_lock(&mgr->mixer_mutex);
+ guard(mutex)(&mgr->mixer_mutex);
for(i = 0; i < 3 + mgr->capture_chips; i++) {
if (i == PCXHR_CLOCK_TYPE_INTERNAL)
rate = mgr->sample_rate_real;
@@ -838,7 +814,6 @@ static int pcxhr_clock_rate_get(struct snd_kcontrol *kcontrol,
}
ucontrol->value.integer.value[i] = rate;
}
- mutex_unlock(&mgr->mixer_mutex);
return 0;
}
@@ -931,7 +906,7 @@ static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol,
unsigned char aes_bits;
int i, err;
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
for(i = 0; i < 5; i++) {
if (kcontrol->private_value == 0) /* playback */
aes_bits = chip->aes_bits[i];
@@ -947,7 +922,6 @@ static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol,
}
ucontrol->value.iec958.status[i] = aes_bits;
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
}
@@ -1001,7 +975,7 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
int i, changed = 0;
/* playback */
- mutex_lock(&chip->mgr->mixer_mutex);
+ guard(mutex)(&chip->mgr->mixer_mutex);
for (i = 0; i < 5; i++) {
if (ucontrol->value.iec958.status[i] != chip->aes_bits[i]) {
if (chip->mgr->is_hr_stereo)
@@ -1013,7 +987,6 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
changed = 1;
}
}
- mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
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..9b4e9595859a 100644
--- a/sound/pci/riptide/Makefile
+++ b/sound/pci/riptide/Makefile
@@ -1,3 +1,4 @@
-snd-riptide-objs := riptide.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-riptide-y := riptide.o
obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 44f3b48d47b1..e983cd657e28 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:
@@ -117,7 +103,6 @@
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;
@@ -375,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 {
@@ -395,6 +380,7 @@ struct riptideport {
};
struct cmdif {
+ struct device *dev;
struct riptideport *hwport;
spinlock_t lock;
unsigned int cmdcnt; /* cmd statistics */
@@ -459,26 +445,23 @@ 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_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;
@@ -531,7 +514,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,
@@ -571,63 +554,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,
@@ -745,20 +728,20 @@ static int loadfirmware(struct cmdif *cif, const unsigned char *img,
}
}
}
- snd_printdd("load firmware return %d\n", err);
+ dev_dbg(cif->dev, "load firmware return %d\n", err);
return err;
}
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;
sink = *path & (~SPLIT_PATH);
if (sink != E2SINK_MAX) {
- snd_printdd("alloc path 0x%x->0x%x\n", source, sink);
+ dev_dbg(cif->dev, "alloc path 0x%x->0x%x\n", source, sink);
SEND_PSEL(cif, source, sink);
source = lbusin2out[sink][0];
type = lbusin2out[sink][1];
@@ -779,7 +762,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++;
@@ -789,19 +772,19 @@ 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;
sink = *path & (~SPLIT_PATH);
if (sink != E2SINK_MAX) {
- snd_printdd("free path 0x%x->0x%x\n", source, sink);
+ dev_dbg(cif->dev, "free path 0x%x->0x%x\n", source, sink);
SEND_PCLR(cif, source, sink);
source = lbusin2out[sink][0];
}
if (*path++ & SPLIT_PATH) {
- unsigned char *npath = path;
+ const unsigned char *npath = path;
while (*npath != 0xff)
npath++;
@@ -829,8 +812,8 @@ static int writearm(struct cmdif *cif, u32 addr, u32 data, u32 mask)
} else
rptr.retlongs[0] &= ~mask;
}
- snd_printdd("send arm 0x%x 0x%x 0x%x return %d\n", addr, data, mask,
- flag);
+ dev_dbg(cif->dev, "send arm 0x%x 0x%x 0x%x return %d\n", addr, data, mask,
+ flag);
return flag;
}
@@ -850,14 +833,14 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,
hwport = cif->hwport;
if (cif->errcnt > MAX_ERROR_COUNT) {
if (cif->is_reset) {
- snd_printk(KERN_ERR
- "Riptide: Too many failed cmds, reinitializing\n");
+ dev_err(cif->dev,
+ "Riptide: Too many failed cmds, reinitializing\n");
if (riptide_reset(cif, NULL) == 0) {
cif->errcnt = 0;
return -EIO;
}
}
- snd_printk(KERN_ERR "Riptide: Initialization failed.\n");
+ dev_err(cif->dev, "Riptide: Initialization failed.\n");
return -EINVAL;
}
if (ret) {
@@ -917,21 +900,21 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,
if (time < cif->cmdtimemin)
cif->cmdtimemin = time;
if ((cif->cmdcnt) % 1000 == 0)
- snd_printdd
- ("send cmd %d time: %d mintime: %d maxtime %d err: %d\n",
- cif->cmdcnt, cif->cmdtime, cif->cmdtimemin,
- cif->cmdtimemax, cif->errcnt);
+ dev_dbg(cif->dev,
+ "send cmd %d time: %d mintime: %d maxtime %d err: %d\n",
+ cif->cmdcnt, cif->cmdtime, cif->cmdtimemin,
+ cif->cmdtimemax, cif->errcnt);
return 0;
errout:
cif->errcnt++;
spin_unlock_irqrestore(&cif->lock, irqflags);
- snd_printdd
- ("send cmd %d hw: 0x%x flag: 0x%x cmd: 0x%x parm: 0x%x ret: 0x%x 0x%x CMDE: %d DATF: %d failed %d\n",
- cif->cmdcnt, (int)((void *)&(cmdport->stat) - (void *)hwport),
- flags, cmd, parm, ret ? ret->retlongs[0] : 0,
- ret ? ret->retlongs[1] : 0, IS_CMDE(cmdport), IS_DATF(cmdport),
- err);
+ dev_dbg(cif->dev,
+ "send cmd %d hw: 0x%x flag: 0x%x cmd: 0x%x parm: 0x%x ret: 0x%x 0x%x CMDE: %d DATF: %d failed %d\n",
+ cif->cmdcnt, (int)((void *)&(cmdport->stat) - (void *)hwport),
+ flags, cmd, parm, ret ? ret->retlongs[0] : 0,
+ ret ? ret->retlongs[1] : 0, IS_CMDE(cmdport), IS_DATF(cmdport),
+ err);
return err;
}
@@ -941,14 +924,14 @@ 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%x 0x%x\n", num, rval, lval);
+ dev_dbg(cif->dev, "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);
if (rptr.retwords[0] == lval && rptr.retwords[1] == rval)
return 0;
} while (i++ < MAX_WRITE_RETRY);
- snd_printdd("sent mixer failed\n");
+ dev_dbg(cif->dev, "sent mixer failed\n");
return -EIO;
}
@@ -979,7 +962,7 @@ getsourcesink(struct cmdif *cif, unsigned char source, unsigned char sink,
return -EIO;
*a = rptr.retbytes[0];
*b = rptr.retbytes[1];
- snd_printdd("getsourcesink 0x%x 0x%x\n", *a, *b);
+ dev_dbg(cif->dev, "%s 0x%x 0x%x\n", __func__, *a, *b);
return 0;
}
@@ -1006,24 +989,24 @@ getsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int *rate)
}
if (p[0]) {
if (p[1] != p[0])
- snd_printdd("rates differ %d %d\n", p[0], p[1]);
+ dev_dbg(cif->dev, "rates differ %d %d\n", p[0], p[1]);
*rate = (unsigned int)p[0];
} else
*rate = (unsigned int)p[1];
- snd_printdd("getsampleformat %d %d %d\n", intdec[0], intdec[1], *rate);
+ dev_dbg(cif->dev, "getsampleformat %d %d %d\n", intdec[0], intdec[1], *rate);
return 0;
}
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;
- snd_printdd
- ("setsampleformat mixer: %d id: %d channels: %d format: %d\n",
- mixer, id, channels, format);
+ dev_dbg(cif->dev,
+ "%s mixer: %d id: %d channels: %d format: %d\n",
+ __func__, mixer, id, channels, format);
ch = channels == 1;
w = snd_pcm_format_width(format) == 8;
sig = snd_pcm_format_unsigned(format) != 0;
@@ -1031,7 +1014,7 @@ setsampleformat(struct cmdif *cif,
if (SEND_SETF(cif, mixer, w, ch, order, sig, id) &&
SEND_SETF(cif, mixer, w, ch, order, sig, id)) {
- snd_printdd("setsampleformat failed\n");
+ dev_dbg(cif->dev, "%s failed\n", __func__);
return -EIO;
}
return 0;
@@ -1044,8 +1027,8 @@ setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate)
union cmdret rptr = CMDRET_ZERO;
int i;
- snd_printdd("setsamplerate intdec: %d,%d rate: %d\n", intdec[0],
- intdec[1], rate);
+ dev_dbg(cif->dev, "%s intdec: %d,%d rate: %d\n", __func__,
+ intdec[0], intdec[1], rate);
D = 48000;
M = ((rate == 48000) ? 47999 : rate) * 65536;
N = M % D;
@@ -1060,8 +1043,8 @@ setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate)
rptr.retwords[3] != N &&
i++ < MAX_WRITE_RETRY);
if (i > MAX_WRITE_RETRY) {
- snd_printdd("sent samplerate %d: %d failed\n",
- *intdec, rate);
+ dev_dbg(cif->dev, "sent samplerate %d: %d failed\n",
+ *intdec, rate);
return -EIO;
}
}
@@ -1080,13 +1063,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%x 0x%x\n", num, *rval, *lval);
+ dev_dbg(cif->dev, "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;
@@ -1097,15 +1080,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];
@@ -1117,8 +1106,8 @@ static void riptide_handleirq(unsigned long dev_id)
if ((flag & EOS_STATUS)
&& (data->state == ST_PLAY)) {
data->state = ST_STOP;
- snd_printk(KERN_ERR
- "Riptide: DMA stopped unexpectedly\n");
+ dev_err(cif->dev,
+ "Riptide: DMA stopped unexpectedly\n");
}
c->dwStat_Ctl =
cpu_to_le32(flag &
@@ -1131,11 +1120,11 @@ static void riptide_handleirq(unsigned long dev_id)
period_bytes =
frames_to_bytes(runtime,
runtime->period_size);
- snd_printdd
- ("interrupt 0x%x after 0x%lx of 0x%lx frames in period\n",
- READ_AUDIO_STATUS(cif->hwport),
- bytes_to_frames(runtime, pos),
- runtime->period_size);
+ dev_dbg(cif->dev,
+ "interrupt 0x%x after 0x%lx of 0x%lx frames in period\n",
+ READ_AUDIO_STATUS(cif->hwport),
+ bytes_to_frames(runtime, pos),
+ runtime->period_size);
j = 0;
if (pos >= period_bytes) {
j++;
@@ -1148,9 +1137,10 @@ static void riptide_handleirq(unsigned long dev_id)
}
}
}
+
+ return IRQ_HANDLED;
}
-#ifdef CONFIG_PM_SLEEP
static int riptide_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -1158,7 +1148,6 @@ static int riptide_suspend(struct device *dev)
chip->in_suspend = 1;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
return 0;
}
@@ -1175,11 +1164,7 @@ static int riptide_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume);
static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
{
@@ -1200,23 +1185,23 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
break;
}
if (!timeout) {
- snd_printk(KERN_ERR
- "Riptide: device not ready, audio status: 0x%x "
- "ready: %d gerr: %d\n",
- READ_AUDIO_STATUS(cif->hwport),
- IS_READY(cif->hwport), IS_GERR(cif->hwport));
+ dev_err(cif->dev,
+ "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n",
+ READ_AUDIO_STATUS(cif->hwport),
+ IS_READY(cif->hwport), IS_GERR(cif->hwport));
return -EIO;
} else {
- snd_printdd
- ("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
- READ_AUDIO_STATUS(cif->hwport),
- IS_READY(cif->hwport), IS_GERR(cif->hwport));
+ dev_dbg(cif->dev,
+ "Riptide: audio status: 0x%x ready: %d gerr: %d\n",
+ READ_AUDIO_STATUS(cif->hwport),
+ IS_READY(cif->hwport), IS_GERR(cif->hwport));
}
SEND_GETV(cif, &firmware.ret);
- snd_printdd("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
- firmware.firmware.ASIC, firmware.firmware.CODEC,
- firmware.firmware.AUXDSP, firmware.firmware.PROG);
+ dev_dbg(cif->dev,
+ "Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
+ firmware.firmware.ASIC, firmware.firmware.CODEC,
+ firmware.firmware.AUXDSP, firmware.firmware.PROG);
if (!chip)
return 1;
@@ -1227,20 +1212,20 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
}
- snd_printdd("Writing Firmware\n");
+ dev_dbg(cif->dev, "Writing Firmware\n");
if (!chip->fw_entry) {
err = request_firmware(&chip->fw_entry, "riptide.hex",
&chip->pci->dev);
if (err) {
- snd_printk(KERN_ERR
- "Riptide: Firmware not available %d\n", err);
+ dev_err(cif->dev,
+ "Riptide: Firmware not available %d\n", err);
return -EIO;
}
}
err = loadfirmware(cif, chip->fw_entry->data, chip->fw_entry->size);
if (err) {
- snd_printk(KERN_ERR
- "Riptide: Could not load firmware %d\n", err);
+ dev_err(cif->dev,
+ "Riptide: Could not load firmware %d\n", err);
return err;
}
@@ -1273,7 +1258,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
SEND_SACR(cif, 0, AC97_RESET);
SEND_RACR(cif, AC97_RESET, &rptr);
- snd_printdd("AC97: 0x%x 0x%x\n", rptr.retlongs[0], rptr.retlongs[1]);
+ dev_dbg(cif->dev, "AC97: 0x%x 0x%x\n", rptr.retlongs[0], rptr.retlongs[1]);
SEND_PLST(cif, 0);
SEND_SLST(cif, 0);
@@ -1366,11 +1351,11 @@ static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream
SEND_GPOS(cif, 0, data->id, &rptr);
if (data->size && runtime->period_size) {
- snd_printdd
- ("pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n",
- data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size,
- bytes_to_frames(runtime, rptr.retlongs[1]),
- bytes_to_frames(runtime,
+ dev_dbg(cif->dev,
+ "pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n",
+ data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size,
+ bytes_to_frames(runtime, rptr.retlongs[1]),
+ bytes_to_frames(runtime,
rptr.retlongs[1]) % runtime->period_size);
if (rptr.retlongs[1] > data->pointer)
ret =
@@ -1381,8 +1366,9 @@ static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream
bytes_to_frames(runtime,
data->pointer % data->size);
} else {
- snd_printdd("stream not started or strange parms (%d %ld)\n",
- data->size, runtime->period_size);
+ dev_dbg(cif->dev,
+ "stream not started or strange parms (%d %ld)\n",
+ data->size, runtime->period_size);
ret = bytes_to_frames(runtime, 0);
}
return ret;
@@ -1426,7 +1412,7 @@ static int snd_riptide_trigger(struct snd_pcm_substream *substream, int cmd)
udelay(1);
} while (i != rptr.retlongs[1] && j++ < MAX_WRITE_RETRY);
if (j > MAX_WRITE_RETRY)
- snd_printk(KERN_ERR "Riptide: Could not stop stream!");
+ dev_err(cif->dev, "Riptide: Could not stop stream!");
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (!(data->state & ST_PAUSE)) {
@@ -1456,7 +1442,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;
@@ -1464,8 +1450,8 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
if (snd_BUG_ON(!cif || !data))
return -EINVAL;
- snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id,
- runtime->channels, runtime->format, runtime->rate);
+ dev_dbg(cif->dev, "prepare id %d ch: %d f:0x%x r:%d\n", data->id,
+ runtime->channels, runtime->format, runtime->rate);
spin_lock_irq(&chip->lock);
channels = runtime->channels;
@@ -1485,8 +1471,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
lbuspath = data->paths.stereo;
break;
}
- snd_printdd("use sgdlist at 0x%p\n",
- data->sgdlist.area);
+ dev_dbg(cif->dev, "use sgdlist at 0x%p\n", data->sgdlist.area);
if (data->sgdlist.area) {
unsigned int i, j, size, pages, f, pt, period;
struct sgd *c, *p = NULL;
@@ -1499,9 +1484,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
pages = DIV_ROUND_UP(size, f);
data->size = size;
data->pages = pages;
- snd_printdd
- ("create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n",
- size, pages, f, period);
+ dev_dbg(cif->dev,
+ "create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n",
+ size, pages, f, period);
pt = 0;
j = 0;
for (i = 0; i < pages; i++) {
@@ -1559,22 +1544,22 @@ snd_riptide_hw_params(struct snd_pcm_substream *substream,
struct snd_dma_buffer *sgdlist = &data->sgdlist;
int err;
- snd_printdd("hw params id %d (sgdlist: 0x%p 0x%lx %d)\n", data->id,
- sgdlist->area, (unsigned long)sgdlist->addr,
- (int)sgdlist->bytes);
+ dev_dbg(chip->card->dev, "hw params id %d (sgdlist: 0x%p 0x%lx %d)\n",
+ data->id, sgdlist->area, (unsigned long)sgdlist->addr,
+ (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) {
- snd_printk(KERN_ERR "Riptide: failed to alloc %d dma bytes\n",
- (int)sizeof(struct sgd) * (DESC_MAX_MASK + 1));
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ sizeof(struct sgd) * (DESC_MAX_MASK + 1),
+ sgdlist);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "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)
@@ -1596,7 +1581,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)
@@ -1672,22 +1657,18 @@ static int snd_riptide_capture_close(struct snd_pcm_substream *substream)
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 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,
};
@@ -1697,9 +1678,9 @@ static int snd_riptide_pcm(struct snd_riptide *chip, int device)
struct snd_pcm *pcm;
int err;
- 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);
@@ -1707,11 +1688,10 @@ static int snd_riptide_pcm(struct snd_riptide *chip, int device)
&snd_riptide_capture_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "RIPTIDE");
+ strscpy(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev, 64 * 1024, 128 * 1024);
return 0;
}
@@ -1720,13 +1700,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++;
@@ -1735,7 +1716,7 @@ snd_riptide_interrupt(int irq, void *dev_id)
}
SET_AIACK(cif->hwport);
}
- return IRQ_HANDLED;
+ return ret;
}
static void
@@ -1750,13 +1731,13 @@ snd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg,
if (snd_BUG_ON(!cif))
return;
- snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val);
+ dev_dbg(cif->dev, "Write AC97 reg 0x%x 0x%x\n", reg, val);
do {
SEND_SACR(cif, val, reg);
SEND_RACR(cif, reg, &rptr);
} while (rptr.retwords[1] != val && i++ < MAX_WRITE_RETRY);
if (i > MAX_WRITE_RETRY)
- snd_printdd("Write AC97 reg failed\n");
+ dev_dbg(cif->dev, "Write AC97 reg failed\n");
}
static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97,
@@ -1771,7 +1752,7 @@ static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97,
if (SEND_RACR(cif, reg, &rptr) != 0)
SEND_RACR(cif, reg, &rptr);
- snd_printdd("Read AC97 reg 0x%x got 0x%x\n", reg, rptr.retwords[1]);
+ dev_dbg(cif->dev, "Read AC97 reg 0x%x got 0x%x\n", reg, rptr.retwords[1]);
return rptr.retwords[1];
}
@@ -1786,74 +1767,58 @@ 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->dev = chip->card->dev;
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) {
case 0x4310:
case 0x4320:
case 0x4330:
- snd_printdd("Modem enable?\n");
+ dev_dbg(cif->dev, "Modem enable?\n");
SEND_SETDPLL(cif);
break;
}
- snd_printdd("Enabling MPU IRQs\n");
+ dev_dbg(cif->dev, "Enabling MPU IRQs\n");
if (chip->rmidi)
SET_EMPUIRQ(cif->hwport);
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);
release_firmware(chip->fw_entry);
- release_and_free_resource(chip->res_port);
- kfree(chip);
- return 0;
-}
-
-static int snd_riptide_dev_free(struct snd_device *device)
-{
- struct snd_riptide *chip = device->device_data;
-
- return snd_riptide_free(chip);
}
static int
-snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_riptide **rchip)
+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;
@@ -1864,40 +1829,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);
-
- 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;
- }
+ card->private_free = snd_riptide_free;
+
+ err = pcim_request_all_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,
- KBUILD_MODNAME, chip)) {
- snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n",
- pci->irq);
- snd_riptide_free(chip);
+ if (devm_request_threaded_irq(&pci->dev, pci->irq,
+ snd_riptide_interrupt,
+ riptide_handleirq, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(&pci->dev, "Riptide: unable to grab IRQ %d\n",
+ pci->irq);
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;
- }
- *rchip = chip;
return 0;
}
@@ -1922,7 +1877,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,
@@ -1941,10 +1897,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,
@@ -1953,15 +1910,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);
@@ -1974,10 +1932,8 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
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 snd_riptide_mixer(struct snd_riptide *chip)
@@ -1985,7 +1941,7 @@ 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,
};
@@ -1994,12 +1950,14 @@ static int 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;
}
@@ -2032,9 +1990,9 @@ snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
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]);
+ dev_err(&pci->dev,
+ "Riptide: cannot grab gameport 0x%x\n",
+ joystick_port[dev]);
gameport_free_port(gameport);
ret = -EBUSY;
goto inc_dev;
@@ -2061,7 +2019,7 @@ static void snd_riptide_joystick_remove(struct pci_dev *pci)
#endif
static int
-snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+__snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2076,20 +2034,20 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
+ 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,9 +2067,9 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
val, MPU401_INFO_IRQ_HOOK, -1,
&chip->rmidi);
if (err < 0)
- snd_printk(KERN_WARNING
- "Riptide: Can't Allocate MPU at 0x%x\n",
- val);
+ dev_warn(&pci->dev,
+ "Riptide: Can't Allocate MPU at 0x%x\n",
+ val);
else
chip->mpuaddr = val;
}
@@ -2121,15 +2079,15 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
err = snd_opl3_create(card, val, val + 2,
OPL3_HW_RIPTIDE, 0, &chip->opl3);
if (err < 0)
- snd_printk(KERN_WARNING
- "Riptide: Can't Allocate OPL3 at 0x%x\n",
- val);
+ dev_warn(&pci->dev,
+ "Riptide: Can't Allocate OPL3 at 0x%x\n",
+ val);
else {
chip->opladdr = val;
err = snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL);
if (err < 0)
- snd_printk(KERN_WARNING
- "Riptide: Can't Allocate OPL3-HWDEP\n");
+ dev_warn(&pci->dev,
+ "Riptide: Can't Allocate OPL3-HWDEP\n");
}
}
#ifdef SUPPORT_JOYSTICK
@@ -2140,44 +2098,40 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
}
#endif
- strcpy(card->driver, "RIPTIDE");
- strcpy(card->shortname, "Riptide");
+ strscpy(card->driver, "RIPTIDE");
+ strscpy(card->shortname, "Riptide");
#ifdef SUPPORT_JOYSTICK
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x",
- card->shortname, chip->port, chip->irq, chip->mpuaddr,
- chip->opladdr, chip->gameaddr);
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x",
+ card->shortname, chip->port, chip->irq, chip->mpuaddr,
+ chip->opladdr, chip->gameaddr);
#else
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x",
- card->shortname, chip->port, chip->irq, chip->mpuaddr,
- chip->opladdr);
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x",
+ card->shortname, chip->port, chip->irq, chip->mpuaddr,
+ chip->opladdr);
#endif
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_card_riptide_probe(pci, pci_id));
}
static struct pci_driver driver = {
.name = KBUILD_MODNAME,
.id_table = snd_riptide_ids,
.probe = snd_card_riptide_probe,
- .remove = snd_card_riptide_remove,
.driver = {
- .pm = RIPTIDE_PM_OPS,
+ .pm = &riptide_pm,
},
};
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index f0906ba416d4..ca9bbf554650 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.
- *
*
* ****************************************************************************
*
@@ -102,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
@@ -267,50 +252,29 @@ static int snd_rme32_playback_silence(struct snd_pcm_substream *substream,
/* copy callback for halfduplex mode */
static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
- src, count))
+ if (copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ 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, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- if (copy_to_user_fromio(dst,
- rme32->iobase + RME32_IO_DATA_BUFFER + pos,
- count))
+ if (copy_to_iter_fromio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ count, dst) != count)
return -EFAULT;
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)
*/
@@ -319,7 +283,8 @@ static const struct snd_pcm_hardware snd_rme32_spdif_info = {
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 |
@@ -346,7 +311,8 @@ static const struct snd_pcm_hardware snd_rme32_adat_info =
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),
@@ -370,7 +336,8 @@ static const struct snd_pcm_hardware snd_rme32_spdif_fd_info = {
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 |
@@ -397,7 +364,8 @@ static const struct snd_pcm_hardware snd_rme32_adat_fd_info =
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),
@@ -478,7 +446,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 */
@@ -672,40 +639,34 @@ 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;
runtime->dma_bytes = RME32_BUFFER_SIZE;
}
- spin_lock_irq(&rme32->lock);
- if ((rme32->rcreg & RME32_RCR_KMODE) &&
- (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ guard(spinlock_irq)(&rme32->lock);
+ 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);
+ if ((int)params_rate(params) != rate)
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)
+ return err;
}
- if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
- spin_unlock_irq(&rme32->lock);
+ err = snd_rme32_setformat(rme32, params_format(params));
+ if (err < 0)
return err;
- }
snd_rme32_setframelog(rme32, params_channels(params), 1);
if (rme32->capture_periodsize != 0) {
- if (params_period_size(params) << rme32->playback_frlog != rme32->capture_periodsize) {
- spin_unlock_irq(&rme32->lock);
+ if (params_period_size(params) << rme32->playback_frlog != rme32->capture_periodsize)
return -EBUSY;
- }
}
rme32->playback_periodsize = params_period_size(params) << rme32->playback_frlog;
/* S/PDIF setup */
@@ -714,7 +675,6 @@ snd_rme32_playback_hw_params(struct snd_pcm_substream *substream,
rme32->wcreg |= rme32->wcreg_spdif_stream;
writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
}
- spin_unlock_irq(&rme32->lock);
return 0;
}
@@ -727,40 +687,31 @@ 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;
runtime->dma_bytes = RME32_BUFFER_SIZE;
}
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
/* enable AutoSync for record-preparing */
rme32->wcreg |= RME32_WCR_AUTOSYNC;
writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
- spin_unlock_irq(&rme32->lock);
+ err = snd_rme32_setformat(rme32, params_format(params));
+ if (err < 0)
return err;
- }
- if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
- spin_unlock_irq(&rme32->lock);
+ err = snd_rme32_playback_setrate(rme32, params_rate(params));
+ if (err < 0)
return err;
- }
- if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
- if ((int)params_rate(params) != rate) {
- spin_unlock_irq(&rme32->lock);
+ rate = snd_rme32_capture_getrate(rme32, &isadat);
+ if (rate > 0) {
+ if ((int)params_rate(params) != rate)
return -EIO;
- }
if ((isadat && runtime->hw.channels_min == 2) ||
- (!isadat && runtime->hw.channels_min == 8)) {
- spin_unlock_irq(&rme32->lock);
+ (!isadat && runtime->hw.channels_min == 8))
return -EIO;
- }
}
/* AutoSync off for recording */
rme32->wcreg &= ~RME32_WCR_AUTOSYNC;
@@ -769,26 +720,15 @@ snd_rme32_capture_hw_params(struct snd_pcm_substream *substream,
snd_rme32_setframelog(rme32, params_channels(params), 0);
if (rme32->playback_periodsize != 0) {
if (params_period_size(params) << rme32->capture_frlog !=
- rme32->playback_periodsize) {
- spin_unlock_irq(&rme32->lock);
+ rme32->playback_periodsize)
return -EBUSY;
- }
}
rme32->capture_periodsize =
params_period_size(params) << rme32->capture_frlog;
- spin_unlock_irq(&rme32->lock);
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) {
@@ -864,15 +804,13 @@ static int snd_rme32_playback_spdif_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme32->lock);
- if (rme32->playback_substream != NULL) {
- spin_unlock_irq(&rme32->lock);
- return -EBUSY;
+ scoped_guard(spinlock_irq, &rme32->lock) {
+ if (rme32->playback_substream != NULL)
+ return -EBUSY;
+ rme32->wcreg &= ~RME32_WCR_ADAT;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ rme32->playback_substream = substream;
}
- rme32->wcreg &= ~RME32_WCR_ADAT;
- writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- rme32->playback_substream = substream;
- spin_unlock_irq(&rme32->lock);
if (rme32->fullduplex_mode)
runtime->hw = snd_rme32_spdif_fd_info;
@@ -882,8 +820,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;
@@ -907,13 +847,11 @@ static int snd_rme32_capture_spdif_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme32->lock);
- if (rme32->capture_substream != NULL) {
- spin_unlock_irq(&rme32->lock);
- return -EBUSY;
- }
- rme32->capture_substream = substream;
- spin_unlock_irq(&rme32->lock);
+ scoped_guard(spinlock_irq, &rme32->lock) {
+ if (rme32->capture_substream != NULL)
+ return -EBUSY;
+ rme32->capture_substream = substream;
+ }
if (rme32->fullduplex_mode)
runtime->hw = snd_rme32_spdif_fd_info;
@@ -923,7 +861,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;
}
@@ -946,22 +885,22 @@ snd_rme32_playback_adat_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme32->lock);
- if (rme32->playback_substream != NULL) {
- spin_unlock_irq(&rme32->lock);
- return -EBUSY;
- }
- rme32->wcreg |= RME32_WCR_ADAT;
- writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- rme32->playback_substream = substream;
- spin_unlock_irq(&rme32->lock);
+ scoped_guard(spinlock_irq, &rme32->lock) {
+ if (rme32->playback_substream != NULL)
+ return -EBUSY;
+ rme32->wcreg |= RME32_WCR_ADAT;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ rme32->playback_substream = substream;
+ }
if (rme32->fullduplex_mode)
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;
@@ -983,7 +922,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;
}
@@ -994,13 +934,11 @@ snd_rme32_capture_adat_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme32->lock);
- if (rme32->capture_substream != NULL) {
- spin_unlock_irq(&rme32->lock);
- return -EBUSY;
- }
- rme32->capture_substream = substream;
- spin_unlock_irq(&rme32->lock);
+ scoped_guard(spinlock_irq, &rme32->lock) {
+ if (rme32->capture_substream != NULL)
+ return -EBUSY;
+ rme32->capture_substream = substream;
+ }
snd_rme32_set_buffer_constraint(rme32, runtime);
return 0;
@@ -1011,11 +949,11 @@ static int snd_rme32_playback_close(struct snd_pcm_substream *substream)
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
int spdif = 0;
- spin_lock_irq(&rme32->lock);
- rme32->playback_substream = NULL;
- rme32->playback_periodsize = 0;
- spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0;
- spin_unlock_irq(&rme32->lock);
+ scoped_guard(spinlock_irq, &rme32->lock) {
+ rme32->playback_substream = NULL;
+ rme32->playback_periodsize = 0;
+ spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0;
+ }
if (spdif) {
rme32->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE |
@@ -1029,10 +967,9 @@ static int snd_rme32_capture_close(struct snd_pcm_substream *substream)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
rme32->capture_substream = NULL;
rme32->capture_periodsize = 0;
- spin_unlock_irq(&rme32->lock);
return 0;
}
@@ -1040,7 +977,7 @@ static int snd_rme32_playback_prepare(struct snd_pcm_substream *substream)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
if (rme32->fullduplex_mode) {
memset(&rme32->playback_pcm, 0, sizeof(rme32->playback_pcm));
rme32->playback_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
@@ -1051,7 +988,6 @@ static int snd_rme32_playback_prepare(struct snd_pcm_substream *substream)
if (rme32->wcreg & RME32_WCR_SEL)
rme32->wcreg &= ~RME32_WCR_MUTE;
writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- spin_unlock_irq(&rme32->lock);
return 0;
}
@@ -1059,7 +995,7 @@ static int snd_rme32_capture_prepare(struct snd_pcm_substream *substream)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
if (rme32->fullduplex_mode) {
memset(&rme32->capture_pcm, 0, sizeof(rme32->capture_pcm));
rme32->capture_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
@@ -1068,7 +1004,6 @@ static int snd_rme32_capture_prepare(struct snd_pcm_substream *substream)
} else {
writel(0, rme32->iobase + RME32_IO_RESET_POS);
}
- spin_unlock_irq(&rme32->lock);
return 0;
}
@@ -1078,7 +1013,7 @@ snd_rme32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
- spin_lock(&rme32->lock);
+ guard(spinlock)(&rme32->lock);
snd_pcm_group_for_each_entry(s, substream) {
if (s != rme32->playback_substream &&
s != rme32->capture_substream)
@@ -1104,16 +1039,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))
@@ -1132,7 +1057,6 @@ snd_rme32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_rme32_pcm_start(rme32, 1);
break;
}
- spin_unlock(&rme32->lock);
return 0;
}
@@ -1168,11 +1092,11 @@ static int snd_rme32_playback_fd_ack(struct snd_pcm_substream *substream)
rec = &rme32->playback_pcm;
cprec = &rme32->capture_pcm;
- spin_lock(&rme32->lock);
- rec->hw_queue_size = RME32_BUFFER_SIZE;
- if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE))
- rec->hw_queue_size -= cprec->hw_ready;
- spin_unlock(&rme32->lock);
+ scoped_guard(spinlock, &rme32->lock) {
+ rec->hw_queue_size = RME32_BUFFER_SIZE;
+ if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE))
+ rec->hw_queue_size -= cprec->hw_ready;
+ }
return snd_pcm_indirect_playback_transfer(substream, rec,
snd_rme32_pb_trans_copy);
}
@@ -1213,14 +1137,11 @@ snd_rme32_capture_fd_pointer(struct snd_pcm_substream *substream)
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_user = snd_rme32_playback_copy,
- .copy_kernel = snd_rme32_playback_copy_kernel,
+ .copy = snd_rme32_playback_copy,
.fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1228,27 +1149,22 @@ static const struct snd_pcm_ops snd_rme32_playback_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_user = snd_rme32_capture_copy,
- .copy_kernel = snd_rme32_capture_copy_kernel,
+ .copy = snd_rme32_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
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_user = snd_rme32_playback_copy,
- .copy_kernel = snd_rme32_playback_copy_kernel,
+ .copy = snd_rme32_playback_copy,
.fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1256,13 +1172,11 @@ static const struct snd_pcm_ops snd_rme32_playback_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_user = snd_rme32_capture_copy,
- .copy_kernel = snd_rme32_capture_copy_kernel,
+ .copy = snd_rme32_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1270,9 +1184,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_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,
@@ -1282,9 +1194,7 @@ static const struct snd_pcm_ops snd_rme32_playback_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,
@@ -1294,7 +1204,6 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_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,
@@ -1305,7 +1214,6 @@ static const struct snd_pcm_ops snd_rme32_playback_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,
@@ -1313,27 +1221,10 @@ static const 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)
@@ -1357,46 +1248,48 @@ static int 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 = pcim_request_all_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) {
dev_err(rme32->card->dev,
"unable to remap memory region 0x%lx-0x%lx\n",
- rme32->port, rme32->port + RME32_IO_SIZE - 1);
+ rme32->port, rme32->port + RME32_IO_SIZE - 1);
return -ENOMEM;
}
- if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, rme32)) {
+ 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");
+ strscpy(rme32->spdif_pcm->name, "Digi32 IEC958");
if (rme32->fullduplex_mode) {
snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
&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,
@@ -1413,22 +1306,21 @@ static int 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");
+ strscpy(rme32->adat_pcm->name, "Digi32 ADAT");
if (rme32->fullduplex_mode) {
snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK,
&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,
@@ -1460,9 +1352,9 @@ static int 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);
@@ -1574,10 +1466,7 @@ snd_rme32_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
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);
}
/*
@@ -1592,10 +1481,9 @@ snd_rme32_get_loopback_control(struct snd_kcontrol *kcontrol,
{
struct rme32 *rme32 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
ucontrol->value.integer.value[0] =
rme32->wcreg & RME32_WCR_SEL ? 0 : 1;
- spin_unlock_irq(&rme32->lock);
return 0;
}
static int
@@ -1607,7 +1495,7 @@ snd_rme32_put_loopback_control(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.integer.value[0] ? 0 : RME32_WCR_SEL;
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
val = (rme32->wcreg & ~RME32_WCR_SEL) | val;
change = val != rme32->wcreg;
if (ucontrol->value.integer.value[0])
@@ -1616,7 +1504,6 @@ snd_rme32_put_loopback_control(struct snd_kcontrol *kcontrol,
val |= RME32_WCR_MUTE;
rme32->wcreg = val;
writel(val, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- spin_unlock_irq(&rme32->lock);
return change;
}
@@ -1651,7 +1538,7 @@ snd_rme32_get_inputtype_control(struct snd_kcontrol *kcontrol,
struct rme32 *rme32 = snd_kcontrol_chip(kcontrol);
unsigned int items = 3;
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
ucontrol->value.enumerated.item[0] = snd_rme32_getinputtype(rme32);
switch (rme32->pci->device) {
@@ -1670,7 +1557,6 @@ snd_rme32_get_inputtype_control(struct snd_kcontrol *kcontrol,
ucontrol->value.enumerated.item[0] = items - 1;
}
- spin_unlock_irq(&rme32->lock);
return 0;
}
static int
@@ -1695,10 +1581,9 @@ snd_rme32_put_inputtype_control(struct snd_kcontrol *kcontrol,
}
val = ucontrol->value.enumerated.item[0] % items;
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
change = val != (unsigned int)snd_rme32_getinputtype(rme32);
snd_rme32_setinputtype(rme32, val);
- spin_unlock_irq(&rme32->lock);
return change;
}
@@ -1719,9 +1604,8 @@ snd_rme32_get_clockmode_control(struct snd_kcontrol *kcontrol,
{
struct rme32 *rme32 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
ucontrol->value.enumerated.item[0] = snd_rme32_getclockmode(rme32);
- spin_unlock_irq(&rme32->lock);
return 0;
}
static int
@@ -1733,10 +1617,9 @@ snd_rme32_put_clockmode_control(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
change = val != (unsigned int)snd_rme32_getclockmode(rme32);
snd_rme32_setclockmode(rme32, val);
- spin_unlock_irq(&rme32->lock);
return change;
}
@@ -1786,10 +1669,9 @@ static int snd_rme32_control_spdif_put(struct snd_kcontrol *kcontrol,
u32 val;
val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
change = val != rme32->wcreg_spdif;
rme32->wcreg_spdif = val;
- spin_unlock_irq(&rme32->lock);
return change;
}
@@ -1821,13 +1703,12 @@ static int snd_rme32_control_spdif_stream_put(struct snd_kcontrol *kcontrol,
u32 val;
val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme32->lock);
+ guard(spinlock_irq)(&rme32->lock);
change = val != rme32->wcreg_spdif_stream;
rme32->wcreg_spdif_stream = val;
rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP);
rme32->wcreg |= val;
writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- spin_unlock_irq(&rme32->lock);
return change;
}
@@ -1847,7 +1728,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),
@@ -1908,7 +1789,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;
@@ -1927,7 +1810,7 @@ static void snd_rme32_card_free(struct snd_card *card)
}
static int
-snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+__snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct rme32 *rme32;
@@ -1942,8 +1825,8 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
@@ -1952,45 +1835,43 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
rme32->pci = pci;
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");
+ strscpy(card->driver, "Digi32");
switch (rme32->pci->device) {
case PCI_DEVICE_ID_RME_DIGI32:
- strcpy(card->shortname, "RME Digi32");
+ strscpy(card->shortname, "RME Digi32");
break;
case PCI_DEVICE_ID_RME_DIGI32_8:
- strcpy(card->shortname, "RME Digi32/8");
+ strscpy(card->shortname, "RME Digi32/8");
break;
case PCI_DEVICE_ID_RME_DIGI32_PRO:
- strcpy(card->shortname, "RME Digi32 PRO");
+ strscpy(card->shortname, "RME Digi32 PRO");
break;
}
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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_rme32_probe(pci, pci_id));
}
static struct pci_driver rme32_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme32_ids,
.probe = snd_rme32_probe,
- .remove = snd_rme32_remove,
};
module_pci_driver(rme32_driver);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index dcfa4d7a73e2..58b8ebf1a24e 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,21 +7,6 @@
*
* 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>
@@ -45,11 +31,6 @@ 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 */
@@ -239,12 +220,10 @@ 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;
@@ -339,45 +318,26 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream,
static int
snd_rme96_playback_copy(struct snd_pcm_substream *substream,
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 iov_iter *src, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count);
+ if (copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+ count, src) != count)
+ return -EFAULT;
return 0;
}
static int
snd_rme96_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
-{
- struct rme96 *rme96 = snd_pcm_substream_chip(substream);
-
- 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 iov_iter *dst, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count);
+ if (copy_to_iter_fromio(rme96->iobase + RME96_IO_REC_BUFFER + pos,
+ count, dst) != count)
+ return -EFAULT;
return 0;
}
@@ -685,12 +645,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) {
@@ -1002,47 +964,45 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
runtime->dma_addr = rme96->port + RME96_IO_PLAY_BUFFER;
runtime->dma_bytes = RME96_BUFFER_SIZE;
- spin_lock_irq(&rme96->lock);
- if (!(rme96->wcreg & RME96_WCR_MASTER) &&
- snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
- {
- /* slave clock */
- if ((int)params_rate(params) != rate) {
- err = -EIO;
- goto error;
+ scoped_guard(spinlock_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);
+ if (rate > 0) {
+ /* slave clock */
+ if ((int)params_rate(params) != rate)
+ return -EIO;
+ } else {
+ err = snd_rme96_playback_setrate(rme96, params_rate(params));
+ if (err < 0)
+ return err;
+ apply_dac_volume = err > 0; /* need to restore volume later? */
}
- } 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)
- {
- err = -EBUSY;
+ 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) {
+ err = -EBUSY;
+ goto error;
+ }
+ }
+ rme96->playback_periodsize =
+ params_period_size(params) << rme96->playback_frlog;
+ snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
+ /* S/PDIF setup */
+ if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
+ rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+ writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
- }
- rme96->playback_periodsize =
- params_period_size(params) << rme96->playback_frlog;
- snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
- /* S/PDIF setup */
- if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
- 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;
+ err = 0;
+ }
error:
- spin_unlock_irq(&rme96->lock);
if (apply_dac_volume) {
usleep_range(3000, 10000);
snd_rme96_apply_dac_volume(rme96);
@@ -1064,43 +1024,33 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
runtime->dma_addr = rme96->port + RME96_IO_REC_BUFFER;
runtime->dma_bytes = RME96_BUFFER_SIZE;
- spin_lock_irq(&rme96->lock);
- if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
+ err = snd_rme96_capture_setformat(rme96, params_format(params));
+ if (err < 0)
return err;
- }
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
- if ((err = snd_rme96_capture_analog_setrate(rme96,
- params_rate(params))) < 0)
- {
- spin_unlock_irq(&rme96->lock);
+ err = snd_rme96_capture_analog_setrate(rme96, params_rate(params));
+ if (err < 0)
return err;
+ } else {
+ rate = snd_rme96_capture_getrate(rme96, &isadat);
+ if (rate > 0) {
+ if ((int)params_rate(params) != rate)
+ return -EIO;
+ if ((isadat && runtime->hw.channels_min == 2) ||
+ (!isadat && runtime->hw.channels_min == 8))
+ return -EIO;
}
- } 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;
- }
}
snd_rme96_setframelog(rme96, params_channels(params), 0);
if (rme96->playback_periodsize != 0) {
if (params_period_size(params) << rme96->capture_frlog !=
rme96->playback_periodsize)
- {
- spin_unlock_irq(&rme96->lock);
return -EBUSY;
- }
}
rme96->capture_periodsize =
params_period_size(params) << rme96->capture_frlog;
snd_rme96_set_period_properties(rme96, rme96->capture_periodsize);
- spin_unlock_irq(&rme96->lock);
return 0;
}
@@ -1179,8 +1129,10 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
RME96_BUFFER_SIZE);
- if ((size = rme96->playback_periodsize) != 0 ||
- (size = rme96->capture_periodsize) != 0)
+ 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);
@@ -1198,25 +1150,24 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme96->lock);
- if (rme96->playback_substream) {
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
- }
- rme96->wcreg &= ~RME96_WCR_ADAT;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
- rme96->playback_substream = substream;
- spin_unlock_irq(&rme96->lock);
+ scoped_guard(spinlock_irq, &rme96->lock) {
+ if (rme96->playback_substream)
+ return -EBUSY;
+ rme96->wcreg &= ~RME96_WCR_ADAT;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ rme96->playback_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);
@@ -1236,24 +1187,22 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
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) {
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
- }
- rme96->capture_substream = substream;
- spin_unlock_irq(&rme96->lock);
+ scoped_guard(spinlock_irq, &rme96->lock) {
+ if (rme96->capture_substream)
+ return -EBUSY;
+ rme96->capture_substream = substream;
+ }
rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
@@ -1267,26 +1216,26 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
- spin_lock_irq(&rme96->lock);
- if (rme96->playback_substream) {
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
- }
- rme96->wcreg |= RME96_WCR_ADAT;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
- rme96->playback_substream = substream;
- spin_unlock_irq(&rme96->lock);
+ scoped_guard(spinlock_irq, &rme96->lock) {
+ if (rme96->playback_substream)
+ return -EBUSY;
+ rme96->wcreg |= RME96_WCR_ADAT;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ rme96->playback_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;
}
@@ -1305,7 +1254,8 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
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;
}
@@ -1314,13 +1264,11 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
runtime->hw.rate_max = rate;
}
- spin_lock_irq(&rme96->lock);
- if (rme96->capture_substream) {
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
- }
- rme96->capture_substream = substream;
- spin_unlock_irq(&rme96->lock);
+ scoped_guard(spinlock_irq, &rme96->lock) {
+ if (rme96->capture_substream)
+ return -EBUSY;
+ rme96->capture_substream = substream;
+ }
rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
@@ -1332,14 +1280,13 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
int spdif = 0;
- spin_lock_irq(&rme96->lock);
- if (RME96_ISPLAYING(rme96)) {
- snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
+ scoped_guard(spinlock_irq, &rme96->lock) {
+ if (RME96_ISPLAYING(rme96))
+ snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
+ rme96->playback_substream = NULL;
+ rme96->playback_periodsize = 0;
+ spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
}
- rme96->playback_substream = NULL;
- rme96->playback_periodsize = 0;
- spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
- spin_unlock_irq(&rme96->lock);
if (spdif) {
rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
@@ -1353,13 +1300,12 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
rme96->capture_substream = NULL;
rme96->capture_periodsize = 0;
- spin_unlock_irq(&rme96->lock);
return 0;
}
@@ -1368,12 +1314,11 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
if (RME96_ISPLAYING(rme96)) {
snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
}
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
- spin_unlock_irq(&rme96->lock);
return 0;
}
@@ -1382,12 +1327,11 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
- spin_unlock_irq(&rme96->lock);
return 0;
}
@@ -1522,13 +1466,11 @@ snd_rme96_capture_pointer(struct snd_pcm_substream *substream)
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_user = snd_rme96_playback_copy,
- .copy_kernel = snd_rme96_playback_copy_kernel,
+ .copy = snd_rme96_playback_copy,
.fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1536,26 +1478,22 @@ static const struct snd_pcm_ops snd_rme96_playback_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_user = snd_rme96_capture_copy,
- .copy_kernel = snd_rme96_capture_copy_kernel,
+ .copy = snd_rme96_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
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_user = snd_rme96_playback_copy,
- .copy_kernel = snd_rme96_playback_copy_kernel,
+ .copy = snd_rme96_playback_copy,
.fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1563,44 +1501,24 @@ static const struct snd_pcm_ops snd_rme96_playback_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_user = snd_rme96_capture_copy,
- .copy_kernel = snd_rme96_capture_copy_kernel,
+ .copy = snd_rme96_capture_copy,
.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)
- return;
-
if (rme96->irq >= 0) {
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;
- }
-#ifdef CONFIG_PM_SLEEP
vfree(rme96->playback_suspend_buffer);
vfree(rme96->capture_suspend_buffer);
-#endif
- pci_disable_device(rme96->pci);
}
static void
@@ -1626,40 +1544,43 @@ 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 = pcim_request_all_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) {
dev_err(rme96->card->dev,
"unable to remap memory region 0x%lx-0x%lx\n",
rme96->port, rme96->port + RME96_IO_SIZE - 1);
- return -ENOMEM;
+ return -EBUSY;
}
- if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, rme96)) {
+ 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");
+ strscpy(rme96->spdif_pcm->name, "Digi96 IEC958");
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
@@ -1670,14 +1591,13 @@ 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");
+ strscpy(rme96->adat_pcm->name, "Digi96 ADAT");
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
@@ -1723,9 +1643,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);
@@ -1868,10 +1788,7 @@ snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer
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);
}
/*
@@ -1885,9 +1802,8 @@ snd_rme96_get_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1;
- spin_unlock_irq(&rme96->lock);
return 0;
}
static int
@@ -1898,12 +1814,11 @@ snd_rme96_put_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
int change;
val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL;
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
val = (rme96->wcreg & ~RME96_WCR_SEL) | val;
change = val != rme96->wcreg;
rme96->wcreg = val;
writel(val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -1949,7 +1864,7 @@ snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
unsigned int items = 3;
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96);
switch (rme96->pci->device) {
@@ -1979,7 +1894,6 @@ snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
ucontrol->value.enumerated.item[0] = items - 1;
}
- spin_unlock_irq(&rme96->lock);
return 0;
}
static int
@@ -2017,10 +1931,9 @@ snd_rme96_put_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
}
}
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = (int)val != snd_rme96_getinputtype(rme96);
snd_rme96_setinputtype(rme96, val);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2036,9 +1949,8 @@ snd_rme96_get_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96);
- spin_unlock_irq(&rme96->lock);
return 0;
}
static int
@@ -2049,10 +1961,9 @@ snd_rme96_put_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
int change;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = (int)val != snd_rme96_getclockmode(rme96);
snd_rme96_setclockmode(rme96, val);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2070,9 +1981,8 @@ snd_rme96_get_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96);
- spin_unlock_irq(&rme96->lock);
return 0;
}
static int
@@ -2083,11 +1993,10 @@ snd_rme96_put_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_
int change;
val = ucontrol->value.enumerated.item[0] % 4;
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = (int)val != snd_rme96_getattenuation(rme96);
snd_rme96_setattenuation(rme96, val);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2103,9 +2012,8 @@ snd_rme96_get_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96);
- spin_unlock_irq(&rme96->lock);
return 0;
}
static int
@@ -2116,10 +2024,9 @@ snd_rme96_put_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
int change;
val = ucontrol->value.enumerated.item[0] % 4;
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = (int)val != snd_rme96_getmontracks(rme96);
snd_rme96_setmontracks(rme96, val);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2167,10 +2074,9 @@ static int snd_rme96_control_spdif_put(struct snd_kcontrol *kcontrol, struct snd
u32 val;
val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = val != rme96->wcreg_spdif;
rme96->wcreg_spdif = val;
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2196,13 +2102,12 @@ static int snd_rme96_control_spdif_stream_put(struct snd_kcontrol *kcontrol, str
u32 val;
val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
change = val != rme96->wcreg_spdif_stream;
rme96->wcreg_spdif_stream = val;
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
rme96->wcreg |= val;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
- spin_unlock_irq(&rme96->lock);
return change;
}
@@ -2236,10 +2141,9 @@ snd_rme96_dac_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
u->value.integer.value[0] = rme96->vol[0];
u->value.integer.value[1] = rme96->vol[1];
- spin_unlock_irq(&rme96->lock);
return 0;
}
@@ -2255,7 +2159,7 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
if (!RME96_HAS_ANALOG_OUT(rme96))
return -EINVAL;
maxvol = RME96_185X_MAX_OUT(rme96);
- spin_lock_irq(&rme96->lock);
+ guard(spinlock_irq)(&rme96->lock);
vol = u->value.integer.value[0];
if (vol != rme96->vol[0] && vol <= maxvol) {
rme96->vol[0] = vol;
@@ -2268,12 +2172,11 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
}
if (change)
snd_rme96_apply_dac_volume(rme96);
- spin_unlock_irq(&rme96->lock);
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),
@@ -2361,16 +2264,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;
@@ -2380,16 +2287,12 @@ 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);
- snd_pcm_suspend(rme96->playback_substream);
- snd_pcm_suspend(rme96->capture_substream);
/* save capture & playback pointers */
rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
@@ -2445,11 +2348,7 @@ static int rme96_resume(struct device *dev)
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 DEFINE_SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume);
static void snd_rme96_card_free(struct snd_card *card)
{
@@ -2457,8 +2356,8 @@ static void snd_rme96_card_free(struct snd_card *card)
}
static int
-snd_rme96_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+__snd_rme96_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct rme96 *rme96;
@@ -2473,8 +2372,8 @@ snd_rme96_probe(struct pci_dev *pci,
dev++;
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
@@ -2483,38 +2382,34 @@ snd_rme96_probe(struct pci_dev *pci,
rme96->pci = pci;
err = snd_rme96_create(rme96);
if (err)
- goto free_card;
+ return err;
-#ifdef CONFIG_PM_SLEEP
- rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
- if (!rme96->playback_suspend_buffer) {
- err = -ENOMEM;
- goto free_card;
+ if (IS_ENABLED(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;
}
- rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
- if (!rme96->capture_suspend_buffer) {
- err = -ENOMEM;
- goto free_card;
- }
-#endif
- strcpy(card->driver, "Digi96");
+ strscpy(card->driver, "Digi96");
switch (rme96->pci->device) {
case PCI_DEVICE_ID_RME_DIGI96:
- strcpy(card->shortname, "RME Digi96");
+ strscpy(card->shortname, "RME Digi96");
break;
case PCI_DEVICE_ID_RME_DIGI96_8:
- strcpy(card->shortname, "RME Digi96/8");
+ strscpy(card->shortname, "RME Digi96/8");
break;
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
- strcpy(card->shortname, "RME Digi96/8 PRO");
+ strscpy(card->shortname, "RME Digi96/8 PRO");
break;
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
pci_read_config_byte(rme96->pci, 8, &val);
if (val < 5) {
- strcpy(card->shortname, "RME Digi96/8 PAD");
+ strscpy(card->shortname, "RME Digi96/8 PAD");
} else {
- strcpy(card->shortname, "RME Digi96/8 PST");
+ strscpy(card->shortname, "RME Digi96/8 PST");
}
break;
}
@@ -2522,28 +2417,25 @@ snd_rme96_probe(struct pci_dev *pci,
rme96->port, rme96->irq);
err = snd_card_register(card);
if (err)
- goto free_card;
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-free_card:
- snd_card_free(card);
- return err;
}
-static void 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));
+ return snd_card_free_on_error(&pci->dev, __snd_rme96_probe(pci, pci_id));
}
static struct pci_driver rme96_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme96_ids,
.probe = snd_rme96_probe,
- .remove = snd_rme96_remove,
.driver = {
- .pm = RME96_PM_OPS,
+ .pm = &rme96_pm,
},
};
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile
index a3351447ddc0..cc99ae892211 100644
--- a/sound/pci/rme9652/Makefile
+++ b/sound/pci/rme9652/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-rme9652-objs := rme9652.o
-snd-hdsp-objs := hdsp.o
-snd-hdspm-objs := hdspm.o
+snd-rme9652-y := rme9652.o
+snd-hdsp-y := hdsp.o
+snd-hdspm-y := hdspm.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_RME9652) += snd-rme9652.o
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 1bff4b1b39cd..31cc2d91c8d2 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>
@@ -30,6 +16,7 @@
#include <linux/math64.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -57,9 +44,6 @@ 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}}");
MODULE_FIRMWARE("rpm_firmware.bin");
MODULE_FIRMWARE("multiface_firmware.bin");
MODULE_FIRMWARE("multiface_firmware_rev11.bin");
@@ -305,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)
@@ -449,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;
@@ -460,8 +444,8 @@ 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 */
@@ -482,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 */
@@ -492,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;
@@ -514,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 */
@@ -529,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 */
@@ -538,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 */
@@ -552,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 */
@@ -566,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,
@@ -580,23 +568,12 @@ 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_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)
- snd_dma_free_pages(dmab);
+ return snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, size);
}
-
static const struct pci_device_id snd_hdsp_ids[] = {
{
.vendor = PCI_VENDOR_ID_XILINX,
@@ -714,7 +691,6 @@ static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
int i;
- unsigned long flags;
const u32 *cache;
if (hdsp->fw_uploaded)
@@ -769,9 +745,8 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
if (hdsp->state & HDSP_InitializationComplete) {
dev_info(hdsp->card->dev,
"firmware loaded from cache, restoring defaults\n");
- spin_lock_irqsave(&hdsp->lock, flags);
+ guard(spinlock_irqsave)(&hdsp->lock);
snd_hdsp_set_defaults(hdsp);
- spin_unlock_irqrestore(&hdsp->lock, flags);
}
hdsp->state |= HDSP_FirmwareLoaded;
@@ -962,14 +937,12 @@ static int hdsp_write_gain(struct hdsp *hdsp, unsigned int addr, unsigned short
static int snd_hdsp_use_is_exclusive(struct hdsp *hdsp)
{
- unsigned long flags;
int ret = 1;
- spin_lock_irqsave(&hdsp->lock, flags);
+ guard(spinlock_irqsave)(&hdsp->lock);
if ((hdsp->playback_pid != hdsp->capture_pid) &&
(hdsp->playback_pid >= 0) && (hdsp->capture_pid >= 0))
ret = 0;
- spin_unlock_irqrestore(&hdsp->lock, flags);
return ret;
}
@@ -1086,8 +1059,6 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames)
{
int n;
- spin_lock_irq(&s->lock);
-
frames >>= 7;
n = 0;
while (frames) {
@@ -1102,8 +1073,6 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames)
hdsp_compute_period_size(s);
- spin_unlock_irq(&s->lock);
-
return 0;
}
@@ -1321,13 +1290,14 @@ static int snd_hdsp_midi_output_possible (struct hdsp *hdsp, int id)
static void snd_hdsp_flush_midi_input (struct hdsp *hdsp, int id)
{
- while (snd_hdsp_midi_input_available (hdsp, id))
- snd_hdsp_midi_read_byte (hdsp, id);
+ int count = 256;
+
+ while (snd_hdsp_midi_input_available(hdsp, id) && --count)
+ snd_hdsp_midi_read_byte(hdsp, id);
}
static int snd_hdsp_midi_output_write (struct hdsp_midi *hmidi)
{
- unsigned long flags;
int n_pending;
int to_write;
int i;
@@ -1335,53 +1305,54 @@ static int snd_hdsp_midi_output_write (struct hdsp_midi *hmidi)
/* Output is not interrupt driven */
- spin_lock_irqsave (&hmidi->lock, flags);
+ guard(spinlock_irqsave)(&hmidi->lock);
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]);
}
}
}
}
- spin_unlock_irqrestore (&hmidi->lock, flags);
return 0;
}
static int snd_hdsp_midi_input_read (struct hdsp_midi *hmidi)
{
unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */
- unsigned long flags;
int n_pending;
int i;
- spin_lock_irqsave (&hmidi->lock, flags);
- if ((n_pending = snd_hdsp_midi_input_available (hmidi->hdsp, hmidi->id)) > 0) {
- if (hmidi->input) {
- if (n_pending > (int)sizeof (buf))
- n_pending = sizeof (buf);
- for (i = 0; i < n_pending; ++i)
- buf[i] = snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id);
- if (n_pending)
- snd_rawmidi_receive (hmidi->input, buf, n_pending);
- } else {
- /* flush the MIDI input FIFO */
- while (--n_pending)
- snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id);
+ scoped_guard(spinlock_irqsave, &hmidi->lock) {
+ 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);
+ for (i = 0; i < n_pending; ++i)
+ buf[i] = snd_hdsp_midi_read_byte(hmidi->hdsp, hmidi->id);
+ if (n_pending)
+ snd_rawmidi_receive(hmidi->input, buf, n_pending);
+ } else {
+ /* flush the MIDI input FIFO */
+ while (--n_pending)
+ snd_hdsp_midi_read_byte(hmidi->hdsp, hmidi->id);
+ }
}
+ hmidi->pending = 0;
+ if (hmidi->id)
+ hmidi->hdsp->control_register |= HDSP_Midi1InterruptEnable;
+ else
+ hmidi->hdsp->control_register |= HDSP_Midi0InterruptEnable;
+ hdsp_write(hmidi->hdsp, HDSP_controlRegister, hmidi->hdsp->control_register);
}
- hmidi->pending = 0;
- if (hmidi->id)
- hmidi->hdsp->control_register |= HDSP_Midi1InterruptEnable;
- else
- hmidi->hdsp->control_register |= HDSP_Midi0InterruptEnable;
- hdsp_write(hmidi->hdsp, HDSP_controlRegister, hmidi->hdsp->control_register);
- spin_unlock_irqrestore (&hmidi->lock, flags);
return snd_hdsp_midi_output_write (hmidi);
}
@@ -1389,13 +1360,12 @@ static void snd_hdsp_midi_input_trigger(struct snd_rawmidi_substream *substream,
{
struct hdsp *hdsp;
struct hdsp_midi *hmidi;
- unsigned long flags;
u32 ie;
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
hdsp = hmidi->hdsp;
ie = hmidi->id ? HDSP_Midi1InterruptEnable : HDSP_Midi0InterruptEnable;
- spin_lock_irqsave (&hdsp->lock, flags);
+ guard(spinlock_irqsave)(&hdsp->lock);
if (up) {
if (!(hdsp->control_register & ie)) {
snd_hdsp_flush_midi_input (hdsp, hmidi->id);
@@ -1403,20 +1373,17 @@ 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(struct timer_list *t)
{
- struct hdsp_midi *hmidi = from_timer(hmidi, t, timer);
- unsigned long flags;
+ struct hdsp_midi *hmidi = timer_container_of(hmidi, t, timer);
snd_hdsp_midi_output_write(hmidi);
- spin_lock_irqsave (&hmidi->lock, flags);
+ guard(spinlock_irqsave)(&hmidi->lock);
/* this does not bump hmidi->istimer, because the
kernel automatically removed the timer when it
@@ -1426,29 +1393,26 @@ static void snd_hdsp_midi_output_timer(struct timer_list *t)
if (hmidi->istimer)
mod_timer(&hmidi->timer, 1 + jiffies);
-
- spin_unlock_irqrestore (&hmidi->lock, flags);
}
static void snd_hdsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct hdsp_midi *hmidi;
- unsigned long flags;
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
- spin_lock_irqsave (&hmidi->lock, flags);
- if (up) {
- if (!hmidi->istimer) {
- timer_setup(&hmidi->timer, snd_hdsp_midi_output_timer,
- 0);
- mod_timer(&hmidi->timer, 1 + jiffies);
- hmidi->istimer++;
+ scoped_guard(spinlock_irqsave, &hmidi->lock) {
+ if (up) {
+ if (!hmidi->istimer) {
+ timer_setup(&hmidi->timer, snd_hdsp_midi_output_timer,
+ 0);
+ mod_timer(&hmidi->timer, 1 + jiffies);
+ hmidi->istimer++;
+ }
+ } else {
+ if (hmidi->istimer && --hmidi->istimer <= 0)
+ timer_delete(&hmidi->timer);
}
- } else {
- if (hmidi->istimer && --hmidi->istimer <= 0)
- del_timer (&hmidi->timer);
}
- spin_unlock_irqrestore (&hmidi->lock, flags);
if (up)
snd_hdsp_midi_output_write(hmidi);
}
@@ -1458,10 +1422,9 @@ static int snd_hdsp_midi_input_open(struct snd_rawmidi_substream *substream)
struct hdsp_midi *hmidi;
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
snd_hdsp_flush_midi_input (hmidi->hdsp, hmidi->id);
hmidi->input = substream;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -1471,9 +1434,8 @@ static int snd_hdsp_midi_output_open(struct snd_rawmidi_substream *substream)
struct hdsp_midi *hmidi;
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->output = substream;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -1485,9 +1447,8 @@ static int snd_hdsp_midi_input_close(struct snd_rawmidi_substream *substream)
snd_hdsp_midi_input_trigger (substream, 0);
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->input = NULL;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -1499,9 +1460,8 @@ static int snd_hdsp_midi_output_close(struct snd_rawmidi_substream *substream)
snd_hdsp_midi_output_trigger (substream, 0);
hmidi = (struct hdsp_midi *) substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->output = NULL;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -1598,10 +1558,9 @@ static int snd_hdsp_control_spdif_put(struct snd_kcontrol *kcontrol, struct snd_
u32 val;
val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = val != hdsp->creg_spdif;
hdsp->creg_spdif = val;
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -1627,12 +1586,11 @@ static int snd_hdsp_control_spdif_stream_put(struct snd_kcontrol *kcontrol, stru
u32 val;
val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = val != hdsp->creg_spdif_stream;
hdsp->creg_spdif_stream = val;
hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis);
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -1698,11 +1656,10 @@ static int snd_hdsp_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0] % ((hdsp->io_type == H9632) ? 4 : 3);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = val != hdsp_spdif_in(hdsp);
if (change)
hdsp_set_spdif_input(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -1739,9 +1696,8 @@ static int snd_hdsp_get_toggle_setting(struct snd_kcontrol *kcontrol,
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
u32 regmask = kcontrol->private_value;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
ucontrol->value.integer.value[0] = hdsp_toggle_setting(hdsp, regmask);
- spin_unlock_irq(&hdsp->lock);
return 0;
}
@@ -1756,11 +1712,10 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol,
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = (int) val != hdsp_toggle_setting(hdsp, regmask);
if (change)
hdsp_set_toggle_setting(hdsp, regmask, val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2066,12 +2021,11 @@ static int snd_hdsp_put_clock_source(struct snd_kcontrol *kcontrol, struct snd_c
if (val > 6)
val = 6;
}
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (val != hdsp_clock_source(hdsp))
change = (hdsp_set_clock_source(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2166,12 +2120,11 @@ static int snd_hdsp_put_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_el
val = ucontrol->value.enumerated.item[0];
if (val < 0) val = 0;
if (val > 2) val = 2;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (val != hdsp_da_gain(hdsp))
change = (hdsp_set_da_gain(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2245,12 +2198,11 @@ static int snd_hdsp_put_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_el
val = ucontrol->value.enumerated.item[0];
if (val < 0) val = 0;
if (val > 2) val = 2;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (val != hdsp_ad_gain(hdsp))
change = (hdsp_set_ad_gain(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2324,12 +2276,11 @@ static int snd_hdsp_put_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl
val = ucontrol->value.enumerated.item[0];
if (val < 0) val = 0;
if (val > 2) val = 2;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (val != hdsp_phone_gain(hdsp))
change = (hdsp_set_phone_gain(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2455,10 +2406,9 @@ static int snd_hdsp_put_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_
}
val = ucontrol->value.enumerated.item[0] % max;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = (int)val != hdsp_pref_sync_ref(hdsp);
hdsp_set_pref_sync_ref(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2538,9 +2488,8 @@ static int snd_hdsp_get_precise_pointer(struct snd_kcontrol *kcontrol, struct sn
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
ucontrol->value.integer.value[0] = hdsp->precise_ptr;
- spin_unlock_irq(&hdsp->lock);
return 0;
}
@@ -2553,44 +2502,42 @@ static int snd_hdsp_put_precise_pointer(struct snd_kcontrol *kcontrol, struct sn
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = (int)val != hdsp->precise_ptr;
hdsp_set_precise_pointer(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
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;
- spin_unlock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
+ ucontrol->value.integer.value[0] = hdsp->use_midi_work;
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;
@@ -2599,10 +2546,9 @@ static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct s
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->use_midi_tasklet;
- hdsp_set_use_midi_tasklet(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
+ change = (int)val != hdsp->use_midi_work;
+ hdsp_set_use_midi_work(hdsp, val);
return change;
}
@@ -2643,9 +2589,8 @@ static int snd_hdsp_get_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
else
addr = hdsp_input_to_output_key(hdsp,source, destination);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
ucontrol->value.integer.value[2] = hdsp_read_gain (hdsp, addr);
- spin_unlock_irq(&hdsp->lock);
return 0;
}
@@ -2671,11 +2616,10 @@ static int snd_hdsp_put_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
gain = ucontrol->value.integer.value[2];
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = gain != hdsp_read_gain(hdsp, addr);
if (change)
hdsp_write_gain(hdsp, addr, gain);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2888,16 +2832,15 @@ 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.integer.value[0];
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (val != hdsp_dds_offset(hdsp))
change = (hdsp_set_dds_offset(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdsp->lock);
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),
@@ -2905,7 +2848,7 @@ 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),
@@ -2968,7 +2911,7 @@ HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0),
HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 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),
};
@@ -3037,12 +2980,11 @@ static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ct
val = 0;
if (val > 4)
val = 4;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_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;
}
@@ -3122,12 +3064,11 @@ static int snd_hdsp_put_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ct
val = 0;
if (val > 4)
val = 4;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_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;
}
@@ -3168,10 +3109,9 @@ static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = (int)val != hdsp_rpm_bypass(hdsp);
hdsp_set_rpm_bypass(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -3220,10 +3160,9 @@ static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
change = (int)val != hdsp_rpm_disconnect(hdsp);
hdsp_set_rpm_disconnect(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -3234,7 +3173,7 @@ static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct sn
return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
-static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
+static const struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "RPM Bypass",
@@ -3267,11 +3206,65 @@ static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
HDSP_MIXER("Mixer", 0)
};
-static struct snd_kcontrol_new snd_hdsp_96xx_aeb =
+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;
@@ -3281,7 +3274,7 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
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, kctl = snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
if (err < 0)
return err;
}
@@ -3289,7 +3282,9 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
}
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;
@@ -3298,12 +3293,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;
}
}
@@ -3311,15 +3310,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;
@@ -3371,7 +3385,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
return;
}
} else {
- int err = -EINVAL;
+ int err;
+
err = hdsp_request_fw_loader(hdsp);
if (err < 0) {
snd_iprintf(buffer,
@@ -3387,7 +3402,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", str_on_off(hdsp->use_midi_work));
snd_iprintf(buffer, "\n");
@@ -3395,8 +3410,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "Buffer Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes);
snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp));
- snd_iprintf(buffer, "Precise pointer: %s\n", hdsp->precise_ptr ? "on" : "off");
- snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off");
+ snd_iprintf(buffer, "Precise pointer: %s\n", str_on_off(hdsp->precise_ptr));
+ snd_iprintf(buffer, "Line out: %s\n", str_on_off(hdsp->control_register & HDSP_LineOut));
snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2);
@@ -3693,8 +3708,8 @@ 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_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ?
- "yes" : "no");
+ str_yes_no(hdsp_toggle_setting(hdsp,
+ HDSP_XLRBreakoutCable)));
if (hdsp->control_register & HDSP_AnalogExtensionBoard)
snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
@@ -3707,43 +3722,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);
-}
-
-static void snd_hdsp_free_buffers(struct hdsp *hdsp)
-{
- snd_hammerfall_free_buffer(&hdsp->capture_dma_buf, hdsp->pci);
- snd_hammerfall_free_buffer(&hdsp->playback_dma_buf, hdsp->pci);
+ snd_card_ro_proc_new(hdsp->card, "hdsp", hdsp, snd_hdsp_proc_read);
}
static int snd_hdsp_initialize_memory(struct hdsp *hdsp)
{
- unsigned long pb_bus, cb_bus;
+ struct snd_dma_buffer *capture_dma, *playback_dma;
- 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);
+ 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;
}
@@ -3811,9 +3820,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]);
@@ -3858,7 +3867,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);
@@ -3869,7 +3878,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
}
}
if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) {
- if (hdsp->use_midi_tasklet) {
+ 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);
@@ -3879,8 +3888,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;
}
@@ -3890,7 +3899,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)
@@ -3900,7 +3909,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)
@@ -3911,10 +3921,10 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp,
static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *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))
return -EINVAL;
@@ -3922,31 +3932,17 @@ static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream,
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, src, count))
+ if (copy_from_iter(channel_buf + pos, count, src) != count)
return -EFAULT;
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);
- 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, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *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))
return -EINVAL;
@@ -3954,31 +3950,17 @@ static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream,
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, count))
+ if (copy_to_iter(channel_buf + pos, count, dst) != count)
return -EFAULT;
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);
- 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, 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))
@@ -4027,7 +4009,7 @@ static int snd_hdsp_hw_params(struct snd_pcm_substream *substream,
if (hdsp_check_for_firmware(hdsp, 1))
return -EIO;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis);
@@ -4047,40 +4029,34 @@ static int snd_hdsp_hw_params(struct snd_pcm_substream *substream,
*/
if (params_rate(params) != hdsp->system_sample_rate) {
- spin_unlock_irq(&hdsp->lock);
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return -EBUSY;
}
if (params_period_size(params) != hdsp->period_bytes / 4) {
- spin_unlock_irq(&hdsp->lock);
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return -EBUSY;
}
/* We're fine. */
- spin_unlock_irq(&hdsp->lock);
return 0;
- } else {
- spin_unlock_irq(&hdsp->lock);
}
/* how to make sure that the rate matches an externally-set one ?
*/
- spin_lock_irq(&hdsp->lock);
if (! hdsp->clock_source_locked) {
- if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) {
- spin_unlock_irq(&hdsp->lock);
+ err = hdsp_set_rate(hdsp, params_rate(params), 0);
+ if (err < 0) {
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return err;
}
}
- 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;
}
@@ -4092,15 +4068,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;
@@ -4133,7 +4110,7 @@ static int snd_hdsp_trigger(struct snd_pcm_substream *substream, int cmd)
if (hdsp_check_for_firmware(hdsp, 0)) /* no auto-loading in trigger */
return -EIO;
- spin_lock(&hdsp->lock);
+ guard(spinlock)(&hdsp->lock);
running = hdsp->running;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -4144,7 +4121,6 @@ static int snd_hdsp_trigger(struct snd_pcm_substream *substream, int cmd)
break;
default:
snd_BUG();
- spin_unlock(&hdsp->lock);
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -4184,7 +4160,6 @@ static int snd_hdsp_trigger(struct snd_pcm_substream *substream, int cmd)
else if (hdsp->running && !running)
hdsp_stop_audio(hdsp);
hdsp->running = running;
- spin_unlock(&hdsp->lock);
return 0;
}
@@ -4200,10 +4175,9 @@ static int snd_hdsp_prepare(struct snd_pcm_substream *substream)
if (hdsp_check_for_firmware(hdsp, 1))
return -EIO;
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
if (!hdsp->running)
hdsp_reset_hw_pointer(hdsp);
- spin_unlock_irq(&hdsp->lock);
return result;
}
@@ -4274,14 +4248,6 @@ static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes
.mask = 0
};
-static const unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 };
-
-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
-};
-
static int snd_hdsp_hw_rule_in_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
@@ -4454,18 +4420,15 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
if (hdsp_check_for_firmware(hdsp, 1))
return -EIO;
- spin_lock_irq(&hdsp->lock);
-
- snd_pcm_set_sync(substream);
+ scoped_guard(spinlock_irq, &hdsp->lock) {
+ snd_pcm_set_sync(substream);
- runtime->hw = snd_hdsp_playback_subinfo;
- runtime->dma_area = hdsp->playback_buffer;
- runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+ runtime->hw = snd_hdsp_playback_subinfo;
+ snd_pcm_set_runtime_buffer(substream, &hdsp->playback_dma_buf);
- hdsp->playback_pid = current->pid;
- hdsp->playback_substream = substream;
-
- spin_unlock_irq(&hdsp->lock);
+ hdsp->playback_pid = current->pid;
+ hdsp->playback_substream = substream;
+ }
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes);
@@ -4473,8 +4436,9 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
runtime->hw.rate_min = runtime->hw.rate_max = hdsp->system_sample_rate;
} else if (hdsp->io_type == H9632) {
runtime->hw.rate_max = 192000;
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates);
+ runtime->hw.rates |= (SNDRV_PCM_RATE_128000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000);
}
if (hdsp->io_type == H9632) {
runtime->hw.channels_min = hdsp->qs_out_channels;
@@ -4504,12 +4468,10 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- spin_lock_irq(&hdsp->lock);
-
- hdsp->playback_pid = -1;
- hdsp->playback_substream = NULL;
-
- spin_unlock_irq(&hdsp->lock);
+ scoped_guard(spinlock_irq, &hdsp->lock) {
+ hdsp->playback_pid = -1;
+ hdsp->playback_substream = NULL;
+ }
if (RPM != hdsp->io_type) {
hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
@@ -4531,18 +4493,15 @@ static int snd_hdsp_capture_open(struct snd_pcm_substream *substream)
if (hdsp_check_for_firmware(hdsp, 1))
return -EIO;
- spin_lock_irq(&hdsp->lock);
-
- snd_pcm_set_sync(substream);
-
- runtime->hw = snd_hdsp_capture_subinfo;
- runtime->dma_area = hdsp->capture_buffer;
- runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+ scoped_guard(spinlock_irq, &hdsp->lock) {
+ snd_pcm_set_sync(substream);
- hdsp->capture_pid = current->pid;
- hdsp->capture_substream = substream;
+ runtime->hw = snd_hdsp_capture_subinfo;
+ snd_pcm_set_runtime_buffer(substream, &hdsp->capture_dma_buf);
- spin_unlock_irq(&hdsp->lock);
+ hdsp->capture_pid = current->pid;
+ hdsp->capture_substream = substream;
+ }
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes);
@@ -4550,8 +4509,9 @@ static int snd_hdsp_capture_open(struct snd_pcm_substream *substream)
runtime->hw.channels_min = hdsp->qs_in_channels;
runtime->hw.channels_max = hdsp->ss_in_channels;
runtime->hw.rate_max = 192000;
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates);
+ runtime->hw.rates |= (SNDRV_PCM_RATE_128000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000);
}
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdsp_hw_rule_in_channels, hdsp,
@@ -4569,12 +4529,11 @@ static int snd_hdsp_capture_release(struct snd_pcm_substream *substream)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- spin_lock_irq(&hdsp->lock);
+ guard(spinlock_irq)(&hdsp->lock);
hdsp->capture_pid = -1;
hdsp->capture_substream = NULL;
- spin_unlock_irq(&hdsp->lock);
return 0;
}
@@ -4737,7 +4696,6 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
}
case SNDRV_HDSP_IOCTL_GET_CONFIG_INFO: {
struct hdsp_config_info info;
- unsigned long flags;
int i;
err = hdsp_check_for_iobox(hdsp);
@@ -4749,48 +4707,48 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
return err;
memset(&info, 0, sizeof(info));
- spin_lock_irqsave(&hdsp->lock, flags);
- info.pref_sync_ref = (unsigned char)hdsp_pref_sync_ref(hdsp);
- info.wordclock_sync_check = (unsigned char)hdsp_wc_sync_check(hdsp);
- 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 != 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_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_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_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);
+ scoped_guard(spinlock_irqsave, &hdsp->lock) {
+ info.pref_sync_ref = (unsigned char)hdsp_pref_sync_ref(hdsp);
+ info.wordclock_sync_check = (unsigned char)hdsp_wc_sync_check(hdsp);
+ 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 != 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_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_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_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_toggle_setting(hdsp,
+ HDSP_AnalogExtensionBoard);
}
- if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
- 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;
break;
@@ -4811,18 +4769,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;
@@ -4835,10 +4794,9 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
dev_info(hdsp->card->dev,
"initializing firmware upload\n");
- firmware = (struct hdsp_firmware __user *)argp;
-
- if (get_user(firmware_data, &firmware->firmware_data))
+ 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;
@@ -4858,17 +4816,20 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
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) {
+ err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp);
+ if (err < 0) {
dev_err(hdsp->card->dev,
"error creating alsa devices\n");
return err;
@@ -4896,8 +4857,7 @@ static const struct snd_pcm_ops snd_hdsp_playback_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy_user = snd_hdsp_playback_copy,
- .copy_kernel = snd_hdsp_playback_copy_kernel,
+ .copy = snd_hdsp_playback_copy,
.fill_silence = snd_hdsp_hw_silence,
};
@@ -4909,8 +4869,7 @@ static const struct snd_pcm_ops snd_hdsp_capture_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy_user = snd_hdsp_capture_copy,
- .copy_kernel = snd_hdsp_capture_copy_kernel,
+ .copy = snd_hdsp_capture_copy,
};
static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
@@ -4918,12 +4877,13 @@ 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;
hw->private_data = hdsp;
- strcpy(hw->name, "HDSP hwdep interface");
+ strscpy(hw->name, "HDSP hwdep interface");
hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl;
@@ -4936,12 +4896,13 @@ 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;
pcm->private_data = hdsp;
- strcpy(pcm->name, hdsp->card_name);
+ strscpy(pcm->name, hdsp->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops);
@@ -4977,7 +4938,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:
@@ -5004,6 +4965,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:
@@ -5036,28 +5003,32 @@ 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) {
+ 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) {
+ 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) {
+ 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) {
+ err = snd_hdsp_create_controls(card, hdsp);
+ if (err < 0) {
dev_err(card->dev,
"Error creating ctl interface\n");
return err;
@@ -5071,18 +5042,20 @@ 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) {
+ err = snd_hdsp_set_defaults(hdsp);
+ if (err < 0) {
dev_err(card->dev,
"Error setting default values\n");
return err;
}
if (!(hdsp->state & HDSP_InitializationComplete)) {
- strcpy(card->shortname, "Hammerfall DSP");
+ strscpy(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) {
+ err = snd_card_register(card);
+ if (err < 0) {
dev_err(card->dev,
"error registering card\n");
return err;
@@ -5103,7 +5076,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;
@@ -5149,21 +5123,25 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
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) {
+ 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) {
+ err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp);
+ if (err < 0) {
dev_err(hdsp->card->dev,
"error creating alsa devices\n");
return err;
@@ -5202,7 +5180,7 @@ static int 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;
@@ -5216,8 +5194,8 @@ static int snd_hdsp_create(struct snd_card *card,
*/
pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF);
- strcpy(card->driver, "H-DSP");
- strcpy(card->mixername, "Xilinx FPGA");
+ strscpy(card->driver, "H-DSP");
+ strscpy(card->mixername, "Xilinx FPGA");
if (hdsp->firmware_rev < 0xa)
return -ENODEV;
@@ -5232,32 +5210,37 @@ static int 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 = pcim_request_all_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) {
+ 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,
- KBUILD_MODNAME, hdsp)) {
+ 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) {
@@ -5269,7 +5252,8 @@ static int snd_hdsp_create(struct snd_card *card,
return err;
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
- 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
@@ -5282,7 +5266,8 @@ static int snd_hdsp_create(struct snd_card *card,
/* we defer initialization */
dev_info(hdsp->card->dev,
"card initialization pending : waiting for firmware\n");
- if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0)
+ err = snd_hdsp_create_hwdep(card, hdsp);
+ if (err < 0)
return err;
return 0;
} else {
@@ -5297,7 +5282,8 @@ static int snd_hdsp_create(struct snd_card *card,
}
}
- if ((err = snd_hdsp_enable_io(hdsp)) != 0)
+ err = snd_hdsp_enable_io(hdsp);
+ if (err)
return err;
if (is_9652)
@@ -5306,7 +5292,8 @@ static int 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);
@@ -5314,43 +5301,26 @@ static int 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);
-
release_firmware(hdsp->firmware);
vfree(hdsp->fw_uploaded);
- iounmap(hdsp->iobase);
-
- if (hdsp->port)
- pci_release_regions(hdsp->pci);
-
- pci_disable_device(hdsp->pci);
- return 0;
-}
-
-static void snd_hdsp_card_free(struct snd_card *card)
-{
- struct hdsp *hdsp = card->private_data;
-
- if (hdsp)
- snd_hdsp_free(hdsp);
}
static int snd_hdsp_probe(struct pci_dev *pci,
@@ -5368,8 +5338,8 @@ static int snd_hdsp_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
@@ -5379,32 +5349,27 @@ static int snd_hdsp_probe(struct pci_dev *pci,
hdsp->pci = pci;
err = snd_hdsp_create(card, hdsp);
if (err)
- goto free_card;
+ goto error;
- strcpy(card->shortname, "Hammerfall DSP");
+ strscpy(card->shortname, "Hammerfall DSP");
sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
hdsp->port, hdsp->irq);
err = snd_card_register(card);
- if (err) {
-free_card:
- snd_card_free(card);
- return err;
- }
+ if (err)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void snd_hdsp_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
+ error:
+ snd_card_free(card);
+ return err;
}
static struct pci_driver hdsp_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_hdsp_ids,
.probe = snd_hdsp_probe,
- .remove = snd_hdsp_remove,
};
module_pci_driver(hdsp_driver);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 4c59983158e0..3ba5bdc96d9d 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)
*
@@ -23,20 +24,8 @@
*
* Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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 2019-05-23 fix AIO single speed ADAT capture and playback
+ * by Philippe.Bekaert@uhasselt.be
*/
/* ************* Register Documentation *******************************************************
@@ -137,6 +126,7 @@
#include <linux/pci.h>
#include <linux/math64.h>
#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -175,7 +165,6 @@ MODULE_AUTHOR
);
MODULE_DESCRIPTION("RME HDSPM");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* --- Write registers. ---
These are defined as byte-offsets from the iobase value. */
@@ -647,7 +636,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#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",
@@ -694,7 +683,7 @@ static const char *const texts_freq[] = {
"192 kHz"
};
-static char *texts_ports_madi[] = {
+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",
@@ -709,7 +698,7 @@ static char *texts_ports_madi[] = {
};
-static char *texts_ports_raydat_ss[] = {
+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",
@@ -720,7 +709,7 @@ static char *texts_ports_raydat_ss[] = {
"SPDIF.L", "SPDIF.R"
};
-static char *texts_ports_raydat_ds[] = {
+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",
@@ -729,7 +718,7 @@ static char *texts_ports_raydat_ds[] = {
"SPDIF.L", "SPDIF.R"
};
-static char *texts_ports_raydat_qs[] = {
+static const char * const texts_ports_raydat_qs[] = {
"ADAT1.1", "ADAT1.2",
"ADAT2.1", "ADAT2.2",
"ADAT3.1", "ADAT3.2",
@@ -739,7 +728,7 @@ static char *texts_ports_raydat_qs[] = {
};
-static char *texts_ports_aio_in_ss[] = {
+static const char * const texts_ports_aio_in_ss[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -748,7 +737,7 @@ static char *texts_ports_aio_in_ss[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aio_out_ss[] = {
+static const char * const texts_ports_aio_out_ss[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -758,7 +747,7 @@ static char *texts_ports_aio_out_ss[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aio_in_ds[] = {
+static const char * const texts_ports_aio_in_ds[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -766,7 +755,7 @@ static char *texts_ports_aio_in_ds[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aio_out_ds[] = {
+static const char * const texts_ports_aio_out_ds[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -775,7 +764,7 @@ static char *texts_ports_aio_out_ds[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aio_in_qs[] = {
+static const char * const texts_ports_aio_in_qs[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -783,7 +772,7 @@ static char *texts_ports_aio_in_qs[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aio_out_qs[] = {
+static const char * const texts_ports_aio_out_qs[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
@@ -792,7 +781,7 @@ static char *texts_ports_aio_out_qs[] = {
"AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
-static char *texts_ports_aes32[] = {
+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"
@@ -806,7 +795,7 @@ static char *texts_ports_aes32[] = {
where the data for that channel can be read/written from/to.
*/
-static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
+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,
@@ -817,7 +806,7 @@ static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
56, 57, 58, 59, 60, 61, 62, 63
};
-static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
+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 */
@@ -830,7 +819,7 @@ static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
-static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
+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 */
@@ -845,7 +834,7 @@ static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
-static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
4, 5, /* ADAT 1 */
6, 7, /* ADAT 2 */
8, 9, /* ADAT 3 */
@@ -861,7 +850,7 @@ static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
-static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
0, 1, /* line in */
8, 9, /* aes in, */
10, 11, /* spdif in */
@@ -875,7 +864,7 @@ static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
-static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
0, 1, /* line out */
8, 9, /* aes out */
10, 11, /* spdif out */
@@ -890,7 +879,7 @@ static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
-static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
0, 1, /* line in */
8, 9, /* aes in */
10, 11, /* spdif in */
@@ -905,7 +894,7 @@ static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
0, 1, /* line out */
8, 9, /* aes out */
10, 11, /* spdif out */
@@ -920,7 +909,7 @@ static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
0, 1, /* line in */
8, 9, /* aes in */
10, 11, /* spdif in */
@@ -935,7 +924,7 @@ static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
+static const char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
0, 1, /* line out */
8, 9, /* aes out */
10, 11, /* spdif out */
@@ -951,7 +940,7 @@ static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
+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,
@@ -1007,7 +996,7 @@ struct hdspm {
u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */
struct hdspm_midi midi[4];
- struct tasklet_struct midi_tasklet;
+ struct work_struct midi_work;
size_t period_bytes;
unsigned char ss_in_channels;
@@ -1020,17 +1009,21 @@ struct hdspm {
unsigned char max_channels_in;
unsigned char max_channels_out;
- signed char *channel_map_in;
- signed char *channel_map_out;
+ const signed char *channel_map_in;
+ const signed char *channel_map_out;
- signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
- signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
+ 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;
- char **port_names_in;
- char **port_names_out;
+ const char * const *port_names_in;
+ const char * const *port_names_out;
- char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs;
- char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs;
+ 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 */
@@ -1104,9 +1097,9 @@ 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_sgbuf(struct hdspm *hdspm,
- struct snd_pcm_substream *substream,
- unsigned int reg, int channels);
+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);
@@ -1211,19 +1204,14 @@ static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v)
/* check if same process is writing and reading */
static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
{
- unsigned long flags;
- int ret = 1;
-
- spin_lock_irqsave(&hdspm->lock, flags);
+ guard(spinlock_irqsave)(&hdspm->lock);
if ((hdspm->playback_pid != hdspm->capture_pid) &&
- (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
- ret = 0;
- }
- spin_unlock_irqrestore(&hdspm->lock, flags);
- return ret;
+ (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0))
+ return 0;
+ return 1;
}
-/* round arbitary sample rates to commonly known rates */
+/* round arbitrary sample rates to commonly known rates */
static int hdspm_round_frequency(int rate)
{
if (rate < 38050)
@@ -1534,7 +1522,7 @@ static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
{
int n;
- spin_lock_irq(&s->lock);
+ guard(spinlock_irq)(&s->lock);
if (32 == frames) {
/* Special case for new RME cards like RayDAT/AIO which
@@ -1564,8 +1552,6 @@ static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
hdspm_compute_period_size(s);
- spin_unlock_irq(&s->lock);
-
return 0;
}
@@ -1845,13 +1831,14 @@ static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
{
- while (snd_hdspm_midi_input_available (hdspm, id))
- snd_hdspm_midi_read_byte (hdspm, id);
+ int count = 256;
+
+ while (snd_hdspm_midi_input_available(hdspm, id) && --count)
+ snd_hdspm_midi_read_byte(hdspm, id);
}
static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
{
- unsigned long flags;
int n_pending;
int to_write;
int i;
@@ -1859,7 +1846,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
/* Output is not interrupt driven */
- spin_lock_irqsave (&hmidi->lock, flags);
+ guard(spinlock_irqsave)(&hmidi->lock);
if (hmidi->output &&
!snd_rawmidi_transmit_empty (hmidi->output)) {
n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm,
@@ -1878,7 +1865,6 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
}
}
}
- spin_unlock_irqrestore (&hmidi->lock, flags);
return 0;
}
@@ -1887,37 +1873,36 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
unsigned char buf[128]; /* this buffer is designed to match the MIDI
* input FIFO size
*/
- unsigned long flags;
int n_pending;
int i;
- spin_lock_irqsave (&hmidi->lock, flags);
- n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id);
- if (n_pending > 0) {
- if (hmidi->input) {
- if (n_pending > (int)sizeof (buf))
- n_pending = sizeof (buf);
- for (i = 0; i < n_pending; ++i)
- buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm,
- hmidi->id);
- if (n_pending)
- snd_rawmidi_receive (hmidi->input, buf,
- n_pending);
- } else {
- /* flush the MIDI input FIFO */
- while (n_pending--)
- snd_hdspm_midi_read_byte (hmidi->hdspm,
- hmidi->id);
+ scoped_guard(spinlock_irqsave, &hmidi->lock) {
+ n_pending = snd_hdspm_midi_input_available(hmidi->hdspm, hmidi->id);
+ if (n_pending > 0) {
+ if (hmidi->input) {
+ if (n_pending > (int)sizeof(buf))
+ n_pending = sizeof(buf);
+ for (i = 0; i < n_pending; ++i)
+ buf[i] = snd_hdspm_midi_read_byte(hmidi->hdspm,
+ hmidi->id);
+ if (n_pending)
+ snd_rawmidi_receive(hmidi->input, buf,
+ n_pending);
+ } else {
+ /* flush the MIDI input FIFO */
+ while (n_pending--)
+ snd_hdspm_midi_read_byte(hmidi->hdspm,
+ hmidi->id);
+ }
}
+ hmidi->pending = 0;
}
- hmidi->pending = 0;
- 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->hdspm->lock, flags);
+ scoped_guard(spinlock_irqsave, &hmidi->hdspm->lock) {
+ hmidi->hdspm->control_register |= hmidi->ie;
+ hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
+ hmidi->hdspm->control_register);
+ }
return snd_hdspm_midi_output_write (hmidi);
}
@@ -1927,12 +1912,11 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct hdspm *hdspm;
struct hdspm_midi *hmidi;
- unsigned long flags;
hmidi = substream->rmidi->private_data;
hdspm = hmidi->hdspm;
- spin_lock_irqsave (&hdspm->lock, flags);
+ guard(spinlock_irqsave)(&hdspm->lock);
if (up) {
if (!(hdspm->control_register & hmidi->ie)) {
snd_hdspm_flush_midi_input (hdspm, hmidi->id);
@@ -1943,16 +1927,14 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
- spin_unlock_irqrestore (&hdspm->lock, flags);
}
static void snd_hdspm_midi_output_timer(struct timer_list *t)
{
- struct hdspm_midi *hmidi = from_timer(hmidi, t, timer);
- unsigned long flags;
+ struct hdspm_midi *hmidi = timer_container_of(hmidi, t, timer);
snd_hdspm_midi_output_write(hmidi);
- spin_lock_irqsave (&hmidi->lock, flags);
+ guard(spinlock_irqsave)(&hmidi->lock);
/* this does not bump hmidi->istimer, because the
kernel automatically removed the timer when it
@@ -1962,30 +1944,27 @@ static void snd_hdspm_midi_output_timer(struct timer_list *t)
if (hmidi->istimer)
mod_timer(&hmidi->timer, 1 + jiffies);
-
- spin_unlock_irqrestore (&hmidi->lock, flags);
}
static void
snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct hdspm_midi *hmidi;
- unsigned long flags;
hmidi = substream->rmidi->private_data;
- spin_lock_irqsave (&hmidi->lock, flags);
- if (up) {
- if (!hmidi->istimer) {
- timer_setup(&hmidi->timer,
- snd_hdspm_midi_output_timer, 0);
- mod_timer(&hmidi->timer, 1 + jiffies);
- hmidi->istimer++;
+ scoped_guard(spinlock_irqsave, &hmidi->lock) {
+ if (up) {
+ if (!hmidi->istimer) {
+ timer_setup(&hmidi->timer,
+ snd_hdspm_midi_output_timer, 0);
+ mod_timer(&hmidi->timer, 1 + jiffies);
+ hmidi->istimer++;
+ }
+ } else {
+ if (hmidi->istimer && --hmidi->istimer <= 0)
+ timer_delete(&hmidi->timer);
}
- } else {
- if (hmidi->istimer && --hmidi->istimer <= 0)
- del_timer (&hmidi->timer);
}
- spin_unlock_irqrestore (&hmidi->lock, flags);
if (up)
snd_hdspm_midi_output_write(hmidi);
}
@@ -1995,10 +1974,9 @@ static int snd_hdspm_midi_input_open(struct snd_rawmidi_substream *substream)
struct hdspm_midi *hmidi;
hmidi = substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
hmidi->input = substream;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -2008,9 +1986,8 @@ static int snd_hdspm_midi_output_open(struct snd_rawmidi_substream *substream)
struct hdspm_midi *hmidi;
hmidi = substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->output = substream;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -2022,9 +1999,8 @@ static int snd_hdspm_midi_input_close(struct snd_rawmidi_substream *substream)
snd_hdspm_midi_input_trigger (substream, 0);
hmidi = substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->input = NULL;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -2036,9 +2012,8 @@ static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
snd_hdspm_midi_output_trigger (substream, 0);
hmidi = substream->rmidi->private_data;
- spin_lock_irq (&hmidi->lock);
+ guard(spinlock_irq)(&hmidi->lock);
hmidi->output = NULL;
- spin_unlock_irq (&hmidi->lock);
return 0;
}
@@ -2175,9 +2150,9 @@ static int snd_hdspm_create_midi(struct snd_card *card,
}
-static void hdspm_midi_tasklet(unsigned long arg)
+static void hdspm_midi_work(struct work_struct *work)
{
- struct hdspm *hdspm = (struct hdspm *)arg;
+ struct hdspm *hdspm = container_of(work, struct hdspm, midi_work);
int i = 0;
while (i < hdspm->midiPorts) {
@@ -2292,7 +2267,6 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
case AIO:
status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
return (status >> 16) & 0xF;
- break;
case AES32:
status = hdspm_read(hdspm, HDSPM_statusRegister);
return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
@@ -2318,7 +2292,6 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
case AIO:
status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
return (status >> 20) & 0xF;
- break;
case AES32:
status = hdspm_read(hdspm, HDSPM_statusRegister);
return (status >> 1) & 0xF;
@@ -2344,7 +2317,6 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
case AIO:
status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
return (status >> 12) & 0xF;
- break;
default:
break;
}
@@ -2364,7 +2336,6 @@ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
case AES32:
timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
return (timecode >> (4*index)) & 0xF;
- break;
default:
break;
}
@@ -2680,12 +2651,11 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
val = 0;
if (val > 9)
val = 9;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
if (val != hdspm_clock_source(hdspm))
change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
else
change = 0;
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3008,11 +2978,10 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
else if (val >= hdspm->texts_autosync_items)
val = hdspm->texts_autosync_items-1;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
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;
}
@@ -3033,8 +3002,8 @@ static int hdspm_autosync_ref(struct hdspm *hdspm)
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
- if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) &&
- (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) {
+ /* 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;
@@ -3092,7 +3061,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
-#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
+#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ |\
@@ -3138,7 +3107,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
-#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
+#define HDSPM_TCO_LTC_FRAMES(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ |\
@@ -3248,9 +3217,8 @@ static int snd_hdspm_get_toggle_setting(struct snd_kcontrol *kcontrol,
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
u32 regmask = kcontrol->private_value;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.integer.value[0] = hdspm_toggle_setting(hdspm, regmask);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3265,10 +3233,9 @@ static int snd_hdspm_put_toggle_setting(struct snd_kcontrol *kcontrol,
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = (int) val != hdspm_toggle_setting(hdspm, regmask);
hdspm_set_toggle_setting(hdspm, regmask, val);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3310,9 +3277,8 @@ static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3326,10 +3292,9 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = (int) val != hdspm_input_select(hdspm);
hdspm_set_input_select(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3372,9 +3337,8 @@ static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3388,10 +3352,9 @@ static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = (int) val != hdspm_ds_wire(hdspm);
hdspm_set_ds_wire(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3445,9 +3408,8 @@ static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3465,10 +3427,9 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
val = 0;
if (val > 2)
val = 2;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = val != hdspm_qs_wire(hdspm);
hdspm_set_qs_wire(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3521,9 +3482,8 @@ static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
u32 regmask = kcontrol->private_value;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3543,10 +3503,9 @@ static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
if (val > 2)
val = 2;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = val != hdspm_tristate(hdspm, regmask);
hdspm_set_tristate(hdspm, val, regmask);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3599,9 +3558,8 @@ static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3619,10 +3577,9 @@ static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
val = 0;
if (val > 2)
val = 2;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change = val != hdspm_madi_speedmode(hdspm);
hdspm_set_madi_speedmode(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3668,7 +3625,7 @@ static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
else if (destination >= HDSPM_MAX_CHANNELS)
destination = HDSPM_MAX_CHANNELS - 1;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
ucontrol->value.integer.value[2] =
hdspm_read_pb_gain(hdspm, destination,
@@ -3677,8 +3634,6 @@ static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[2] =
hdspm_read_in_gain(hdspm, destination, source);
- spin_unlock_irq(&hdspm->lock);
-
return 0;
}
@@ -3704,7 +3659,7 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
gain = ucontrol->value.integer.value[2];
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
change = gain != hdspm_read_pb_gain(hdspm, destination,
@@ -3723,7 +3678,6 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
hdspm_write_in_gain(hdspm, destination, source,
gain);
}
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3764,10 +3718,9 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
return -EINVAL;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
ucontrol->value.integer.value[0] =
(hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
- spin_unlock_irq(&hdspm->lock);
return 0;
}
@@ -3790,14 +3743,13 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
change =
gain != hdspm_read_pb_gain(hdspm, channel,
channel);
if (change)
hdspm_write_pb_gain(hdspm, channel, channel,
gain);
- spin_unlock_irq(&hdspm->lock);
return change;
}
@@ -3851,7 +3803,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm)
return 1;
}
return 0;
- break;
case MADI:
status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
@@ -3862,7 +3813,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm)
return 1;
}
return 0;
- break;
case RayDAT:
case AIO:
@@ -3874,8 +3824,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm)
return 1;
return 0;
- break;
-
case MADIface:
break;
}
@@ -4488,7 +4436,7 @@ static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
-static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
+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),
@@ -4510,7 +4458,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
};
-static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
+static const struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
HDSPM_MIXER("Mixer", 0),
HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
@@ -4523,7 +4471,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
};
-static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
+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),
@@ -4564,7 +4512,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
*/
};
-static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
+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),
@@ -4592,7 +4540,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
};
-static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
+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),
@@ -4634,7 +4582,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
/* Control elements for the optional TCO module */
-static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
+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),
@@ -4643,8 +4591,8 @@ static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
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)
+ HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate"),
+ HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format")
};
@@ -4681,7 +4629,7 @@ static int snd_hdspm_create_controls(struct snd_card *card,
unsigned int idx, limit;
int err;
struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *list = NULL;
+ const struct snd_kcontrol_new *list = NULL;
switch (hdspm->io_type) {
case MADI:
@@ -4940,14 +4888,14 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
x, (unsigned long) hdspm->period_bytes);
snd_iprintf(buffer, "Line out: %s\n",
- (hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
+ str_on_off(hdspm->control_register & HDSPM_LineOut));
snd_iprintf(buffer,
"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
"Auto Input %s\n",
- (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
+ str_on_off(hdspm->control_register & HDSPM_clr_tms),
(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
- (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
+ str_on_off(hdspm->control_register & HDSPM_AutoInp));
if (!(hdspm->control_register & HDSPM_ClockModeMaster))
@@ -5101,12 +5049,9 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
snd_iprintf(buffer,
"ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
- (hdspm->
- control_register & HDSPM_clr_tms) ? "on" : "off",
- (hdspm->
- control_register & HDSPM_Emphasis) ? "on" : "off",
- (hdspm->
- control_register & HDSPM_Dolby) ? "on" : "off");
+ str_on_off(hdspm->control_register & HDSPM_clr_tms),
+ str_on_off(hdspm->control_register & HDSPM_Emphasis),
+ str_on_off(hdspm->control_register & HDSPM_Dolby));
pref_syncref = hdspm_pref_sync_ref(hdspm);
@@ -5286,44 +5231,35 @@ static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
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;
- if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) {
- switch (hdspm->io_type) {
- case AES32:
- snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read_aes32);
- break;
- case MADI:
- snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read_madi);
- break;
- case MADIface:
- /* snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read_madiface); */
- break;
- case RayDAT:
- snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read_raydat);
- break;
- case AIO:
- break;
- }
- }
-
- if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) {
- snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in);
+ 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;
}
- if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) {
- snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out);
- }
+ 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);
#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
}
@@ -5464,7 +5400,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
}
if (schedule)
- tasklet_hi_schedule(&hdspm->midi_tasklet);
+ queue_work(system_highpri_wq, &hdspm->midi_work);
}
return IRQ_HANDLED;
@@ -5521,53 +5457,50 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
pid_t this_pid;
pid_t other_pid;
- spin_lock_irq(&hdspm->lock);
+ scoped_guard(spinlock_irq, &hdspm->lock) {
- if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- this_pid = hdspm->playback_pid;
- other_pid = hdspm->capture_pid;
- } else {
- this_pid = hdspm->capture_pid;
- other_pid = hdspm->playback_pid;
- }
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ this_pid = hdspm->playback_pid;
+ other_pid = hdspm->capture_pid;
+ } else {
+ this_pid = hdspm->capture_pid;
+ other_pid = hdspm->playback_pid;
+ }
- if (other_pid > 0 && this_pid != other_pid) {
+ if (other_pid > 0 && this_pid != other_pid) {
- /* The other stream is open, and not by the same
- task as this one. Make sure that the parameters
- that matter are the same.
- */
+ /* 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);
- return -EBUSY;
- }
+ if (params_rate(params) != hdspm->system_sample_rate) {
+ _snd_pcm_hw_param_setempty(params,
+ 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);
- return -EBUSY;
- }
+ if (params_period_size(params) != hdspm->period_bytes / 4) {
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return -EBUSY;
+ }
+ }
}
/* We're fine. */
- spin_unlock_irq(&hdspm->lock);
/* how to make sure that the rate matches an externally-set one ? */
- 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);
- return err;
+ scoped_guard(spinlock_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);
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ return err;
+ }
}
- spin_unlock_irq(&hdspm->lock);
err = hdspm_set_interrupt_interval(hdspm,
params_period_size(params));
@@ -5596,11 +5529,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
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;
@@ -5608,11 +5546,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
"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;
@@ -5624,15 +5567,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
/*
dev_dbg(hdspm->card->dev,
"Allocated sample buffer for %s at 0x%08X\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- "playback" : "capture",
+ snd_pcm_direction_name(substream->stream),
snd_pcm_sgbuf_get_addr(substream, 0));
*/
/*
dev_dbg(hdspm->card->dev,
"set_hwparams: %s %d Hz, %d channels, bs = %d\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- "playback" : "capture",
+ snd_pcm_direction_name(substream->stream),
params_rate(params), params_channels(params),
params_buffer_size(params));
*/
@@ -5673,19 +5614,17 @@ 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 */
- for (i = 0; i < hdspm->max_channels_out; ++i)
+ /* 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);
hdspm->playback_buffer = NULL;
} else {
- for (i = 0; i < hdspm->max_channels_in; ++i)
+ for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
snd_hdspm_enable_in(hdspm, i, 0);
hdspm->capture_buffer = NULL;
-
}
snd_pcm_lib_free_pages(substream);
@@ -5698,40 +5637,43 @@ static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_channel_info *info)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ unsigned int channel = info->channel;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
+ 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",
- info->channel);
+ channel);
return -EINVAL;
}
- if (hdspm->channel_map_out[info->channel] < 0) {
+ 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",
- info->channel);
+ channel);
return -EINVAL;
}
- info->offset = hdspm->channel_map_out[info->channel] *
+ info->offset = hdspm->channel_map_out[channel] *
HDSPM_CHANNEL_BUFFER_BYTES;
} else {
- if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
+ 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",
- info->channel);
+ channel);
return -EINVAL;
}
- if (hdspm->channel_map_in[info->channel] < 0) {
+ 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",
- info->channel);
+ channel);
return -EINVAL;
}
- info->offset = hdspm->channel_map_in[info->channel] *
+ info->offset = hdspm->channel_map_in[channel] *
HDSPM_CHANNEL_BUFFER_BYTES;
}
@@ -5766,7 +5708,7 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_substream *other;
int running;
- spin_lock(&hdspm->lock);
+ guard(spinlock)(&hdspm->lock);
running = hdspm->running;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -5777,7 +5719,6 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
break;
default:
snd_BUG();
- spin_unlock(&hdspm->lock);
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -5818,7 +5759,6 @@ _ok:
else if (hdspm->running && !running)
hdspm_stop_audio(hdspm);
hdspm->running = running;
- spin_unlock(&hdspm->lock);
return 0;
}
@@ -5828,7 +5768,7 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-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 |
@@ -5853,7 +5793,7 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
.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 |
@@ -6045,45 +5985,32 @@ static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
return snd_interval_list(c, 3, list, 0);
}
-
-static const unsigned int hdspm_aes32_sample_rates[] = {
- 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
-};
-
-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_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;
+ scoped_guard(spinlock_irq, &hdspm->lock) {
+ snd_pcm_set_sync(substream);
+ runtime->hw = (playback) ? snd_hdspm_playback_subinfo :
+ snd_hdspm_capture_subinfo;
- if (playback) {
- if (!hdspm->capture_substream)
- hdspm_stop_audio(hdspm);
+ if (playback) {
+ if (!hdspm->capture_substream)
+ 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;
+ } else {
+ if (!hdspm->playback_substream)
+ hdspm_stop_audio(hdspm);
- hdspm->capture_pid = current->pid;
- hdspm->capture_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);
@@ -6109,9 +6036,7 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
}
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);
+ runtime->hw.rates |= SNDRV_PCM_RATE_128000;
} else {
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
(playback ?
@@ -6138,7 +6063,7 @@ static int snd_hdspm_release(struct snd_pcm_substream *substream)
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- spin_lock_irq(&hdspm->lock);
+ guard(spinlock_irq)(&hdspm->lock);
if (playback) {
hdspm->playback_pid = -1;
@@ -6148,8 +6073,6 @@ static int snd_hdspm_release(struct snd_pcm_substream *substream)
hdspm->capture_substream = NULL;
}
- spin_unlock_irq(&hdspm->lock);
-
return 0;
}
@@ -6159,12 +6082,6 @@ static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
return 0;
}
-static inline int copy_u32_le(void __user *dest, void __iomem *src)
-{
- u32 val = readl(src);
- return copy_to_user(dest, &val, 4);
-}
-
static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -6278,19 +6195,19 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
case SNDRV_HDSPM_IOCTL_GET_CONFIG:
memset(&info, 0, sizeof(info));
- spin_lock_irq(&hdspm->lock);
- info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
- info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
-
- info.system_sample_rate = hdspm->system_sample_rate;
- info.autosync_sample_rate =
- 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_toggle_setting(hdspm, HDSPM_LineOut);
- info.passthru = 0;
- spin_unlock_irq(&hdspm->lock);
+ scoped_guard(spinlock_irq, &hdspm->lock) {
+ info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
+ info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
+
+ info.system_sample_rate = hdspm->system_sample_rate;
+ info.autosync_sample_rate =
+ 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_toggle_setting(hdspm, HDSPM_LineOut);
+ info.passthru = 0;
+ }
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
break;
@@ -6325,6 +6242,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
(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;
@@ -6340,7 +6258,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
memset(&hdspm_version, 0, sizeof(hdspm_version));
hdspm_version.card_type = hdspm->io_type;
- strlcpy(hdspm_version.cardname, hdspm->card_name,
+ strscpy(hdspm_version.cardname, hdspm->card_name,
sizeof(hdspm_version.cardname));
hdspm_version.serial = hdspm->serial;
hdspm_version.firmware_rev = hdspm->firmware_rev;
@@ -6376,7 +6294,6 @@ static const struct snd_pcm_ops snd_hdspm_ops = {
.prepare = snd_hdspm_prepare,
.trigger = snd_hdspm_trigger,
.pointer = snd_hdspm_hw_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
static int snd_hdspm_create_hwdep(struct snd_card *card,
@@ -6391,7 +6308,7 @@ static int snd_hdspm_create_hwdep(struct snd_card *card,
hdspm->hwdep = hw;
hw->private_data = hdspm;
- strcpy(hw->name, "HDSPM hwdep interface");
+ strscpy(hw->name, "HDSPM hwdep interface");
hw->ops.open = snd_hdspm_hwdep_dummy_op;
hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
@@ -6407,7 +6324,6 @@ static int snd_hdspm_create_hwdep(struct snd_card *card,
------------------------------------------------------------*/
static int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
{
- int err;
struct snd_pcm *pcm;
size_t wanted;
@@ -6415,35 +6331,24 @@ static int 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) {
- dev_dbg(hdspm->card->dev,
- "Could not preallocate %zd Bytes\n", wanted);
-
- return err;
- } else
- dev_dbg(hdspm->card->dev,
- " 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;
- /* continuous memory segment */
- 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));
+ snd_pcm_sgbuf_get_addr(substream, 4096 * i));
}
@@ -6460,7 +6365,7 @@ static int snd_hdspm_create_pcm(struct snd_card *card,
hdspm->pcm = pcm;
pcm->private_data = hdspm;
- strcpy(pcm->name, hdspm->card_name);
+ strscpy(pcm->name, hdspm->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_hdspm_ops);
@@ -6530,7 +6435,7 @@ static int snd_hdspm_create_alsa_devices(struct snd_card *card,
dev_dbg(card->dev, "Update mixer controls...\n");
hdspm_update_simple_mixer_controls(hdspm);
- dev_dbg(card->dev, "Initializeing complete ???\n");
+ dev_dbg(card->dev, "Initializing complete?\n");
err = snd_card_register(card);
if (err < 0) {
@@ -6555,12 +6460,13 @@ static int snd_hdspm_create(struct snd_card *card,
hdspm->card = card;
spin_lock_init(&hdspm->lock);
+ INIT_WORK(&hdspm->midi_work, hdspm_midi_work);
pci_read_config_word(hdspm->pci,
PCI_CLASS_REVISION, &hdspm->firmware_rev);
- strcpy(card->mixername, "Xilinx FPGA");
- strcpy(card->driver, "HDSPM");
+ strscpy(card->mixername, "Xilinx FPGA");
+ strscpy(card->driver, "HDSPM");
switch (hdspm->firmware_rev) {
case HDSPM_RAYDAT_REV:
@@ -6599,34 +6505,24 @@ static int snd_hdspm_create(struct snd_card *card,
}
}
- 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");
- if (err < 0)
- return err;
+ hdspm->iobase = pcim_iomap_region(pci, 0, "hdspm");
+ if (IS_ERR(hdspm->iobase))
+ return PTR_ERR(hdspm->iobase);
hdspm->port = pci_resource_start(pci, 0);
io_extent = pci_resource_len(pci, 0);
-
- dev_dbg(card->dev, "grabbed memory region 0x%lx-0x%lx\n",
- hdspm->port, hdspm->port + io_extent - 1);
-
- hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
- if (!hdspm->iobase) {
- dev_err(card->dev, "unable to remap region 0x%lx-0x%lx\n",
- hdspm->port, hdspm->port + io_extent - 1);
- return -EBUSY;
- }
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 (request_irq(pci->irq, snd_hdspm_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
+ 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;
}
@@ -6634,10 +6530,11 @@ static int snd_hdspm_create(struct snd_card *card,
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 = kzalloc(sizeof(*hdspm->mixer), GFP_KERNEL);
+ hdspm->mixer = devm_kzalloc(&pci->dev, sizeof(*hdspm->mixer), GFP_KERNEL);
if (!hdspm->mixer)
return -ENOMEM;
@@ -6852,10 +6749,6 @@ static int snd_hdspm_create(struct snd_card *card,
}
- tasklet_init(&hdspm->midi_tasklet,
- hdspm_midi_tasklet, (unsigned long) hdspm);
-
-
if (hdspm->io_type != MADIface) {
hdspm->serial = (hdspm_read(hdspm,
HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
@@ -6886,10 +6779,12 @@ static int snd_hdspm_create(struct snd_card *card,
}
-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 &=
@@ -6899,27 +6794,6 @@ static int snd_hdspm_free(struct hdspm * hdspm)
hdspm_write(hdspm, HDSPM_controlRegister,
hdspm->control_register);
}
-
- if (hdspm->irq >= 0)
- free_irq(hdspm->irq, (void *) hdspm);
-
- kfree(hdspm->mixer);
- 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);
}
@@ -6938,8 +6812,8 @@ static int snd_hdspm_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, index[dev], id[dev],
- THIS_MODULE, sizeof(*hdspm), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev],
+ THIS_MODULE, sizeof(*hdspm), &card);
if (err < 0)
return err;
@@ -6950,7 +6824,7 @@ static int snd_hdspm_probe(struct pci_dev *pci,
err = snd_hdspm_create(card, hdspm);
if (err < 0)
- goto free_card;
+ goto error;
if (hdspm->io_type != MADIface) {
snprintf(card->shortname, sizeof(card->shortname), "%s_%x",
@@ -6969,28 +6843,22 @@ static int snd_hdspm_probe(struct pci_dev *pci,
err = snd_card_register(card);
if (err < 0)
- goto free_card;
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-free_card:
+ error:
snd_card_free(card);
return err;
}
-static void snd_hdspm_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
static struct pci_driver hdspm_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_hdspm_ids,
.probe = snd_hdspm_probe,
- .remove = snd_hdspm_remove,
};
module_pci_driver(hdspm_driver);
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index df648b1d9217..3be30cb57a2e 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -1,23 +1,9 @@
+// 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>
@@ -26,6 +12,7 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -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,7 +269,7 @@ 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 */
@@ -290,23 +278,12 @@ static char channel_map_9636_ds[26] = {
-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)
+static struct snd_dma_buffer *
+snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size)
{
- dmab->dev.type = SNDRV_DMA_TYPE_DEV;
- dmab->dev.dev = snd_dma_pci_data(pci);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- size, dmab) < 0)
- return -ENOMEM;
- return 0;
+ return snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, size);
}
-static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
-{
- if (dmab->area)
- snd_dma_free_pages(dmab);
-}
-
-
static const struct pci_device_id snd_rme9652_ids[] = {
{
.vendor = 0x10ee,
@@ -331,16 +308,11 @@ static inline unsigned int rme9652_read(struct snd_rme9652 *rme9652, int reg)
static inline int snd_rme9652_use_is_exclusive(struct snd_rme9652 *rme9652)
{
- unsigned long flags;
- int ret = 1;
-
- spin_lock_irqsave(&rme9652->lock, flags);
+ guard(spinlock_irqsave)(&rme9652->lock);
if ((rme9652->playback_pid != rme9652->capture_pid) &&
- (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) {
- ret = 0;
- }
- spin_unlock_irqrestore(&rme9652->lock, flags);
- return ret;
+ (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0))
+ return 0;
+ return 1;
}
static inline int rme9652_adat_sample_rate(struct snd_rme9652 *rme9652)
@@ -451,11 +423,11 @@ static int rme9652_set_interrupt_interval(struct snd_rme9652 *s,
int restart = 0;
int n;
- spin_lock_irq(&s->lock);
+ guard(spinlock_irq)(&s->lock);
- if ((restart = s->running)) {
+ restart = s->running;
+ if (restart)
rme9652_stop(s);
- }
frames >>= 7;
n = 0;
@@ -474,8 +446,6 @@ static int rme9652_set_interrupt_interval(struct snd_rme9652 *s,
if (restart)
rme9652_start(s);
- spin_unlock_irq(&s->lock);
-
return 0;
}
@@ -500,7 +470,7 @@ static int rme9652_set_rate(struct snd_rme9652 *rme9652, int rate)
is to flag rate changes in the read/write routines.
*/
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
xrate = rme9652_adat_sample_rate(rme9652);
switch (rate) {
@@ -529,25 +499,21 @@ static int rme9652_set_rate(struct snd_rme9652 *rme9652, int rate)
rate = RME9652_DS | RME9652_freq;
break;
default:
- spin_unlock_irq(&rme9652->lock);
return -EINVAL;
}
- if (reject_if_open && (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) {
- spin_unlock_irq(&rme9652->lock);
+ if (reject_if_open && (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0))
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) {
@@ -563,7 +529,6 @@ static int rme9652_set_rate(struct snd_rme9652 *rme9652, int rate)
}
}
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -750,34 +715,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:
dev_err(s->card->dev,
"%s: unknown S/PDIF input rate (bits = 0x%x)\n",
s->card_name, rate_bits);
return 0;
- break;
}
}
@@ -829,10 +787,9 @@ static int snd_rme9652_control_spdif_put(struct snd_kcontrol *kcontrol, struct s
u32 val;
val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = val != rme9652->creg_spdif;
rme9652->creg_spdif = val;
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -858,12 +815,11 @@ static int snd_rme9652_control_spdif_stream_put(struct snd_kcontrol *kcontrol, s
u32 val;
val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = val != rme9652->creg_spdif_stream;
rme9652->creg_spdif_stream = val;
rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -905,15 +861,14 @@ 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;
}
@@ -929,9 +884,8 @@ static int snd_rme9652_get_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ct
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -944,11 +898,10 @@ static int snd_rme9652_put_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ct
if (!snd_rme9652_use_is_exclusive(rme9652))
return -EBUSY;
val = ucontrol->value.enumerated.item[0] % 2;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = val != rme9652_adat1_in(rme9652);
if (change)
rme9652_set_adat1_input(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -970,15 +923,14 @@ 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;
}
@@ -994,9 +946,8 @@ static int snd_rme9652_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ct
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1009,11 +960,10 @@ static int snd_rme9652_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ct
if (!snd_rme9652_use_is_exclusive(rme9652))
return -EBUSY;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = val != rme9652_spdif_in(rme9652);
if (change)
rme9652_set_spdif_input(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -1037,15 +987,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;
}
@@ -1056,9 +1005,8 @@ static int snd_rme9652_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_c
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1071,10 +1019,9 @@ static int snd_rme9652_put_spdif_out(struct snd_kcontrol *kcontrol, struct snd_c
if (!snd_rme9652_use_is_exclusive(rme9652))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = (int)val != rme9652_spdif_out(rme9652);
rme9652_set_spdif_output(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -1113,15 +1060,14 @@ 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;
}
@@ -1139,9 +1085,8 @@ static int snd_rme9652_get_sync_mode(struct snd_kcontrol *kcontrol, struct snd_c
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1152,10 +1097,9 @@ static int snd_rme9652_put_sync_mode(struct snd_kcontrol *kcontrol, struct snd_c
unsigned int val;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = (int)val != rme9652_sync_mode(rme9652);
rme9652_set_sync_mode(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -1200,15 +1144,14 @@ 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;
}
@@ -1229,9 +1172,8 @@ static int snd_rme9652_get_sync_pref(struct snd_kcontrol *kcontrol, struct snd_c
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1245,10 +1187,9 @@ static int snd_rme9652_put_sync_pref(struct snd_kcontrol *kcontrol, struct snd_c
return -EBUSY;
max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
val = ucontrol->value.enumerated.item[0] % max;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = (int)val != rme9652_sync_pref(rme9652);
rme9652_set_sync_pref(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return change;
}
@@ -1289,7 +1230,7 @@ static int snd_rme9652_put_thru(struct snd_kcontrol *kcontrol, struct snd_ctl_el
thru_bits |= 1 << chn;
}
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = thru_bits ^ rme9652->thru_bits;
if (change) {
for (chn = 0; chn < rme9652->ss_channels; ++chn) {
@@ -1298,7 +1239,6 @@ static int snd_rme9652_put_thru(struct snd_kcontrol *kcontrol, struct snd_ctl_el
rme9652_set_thru(rme9652,chn,thru_bits&(1<<chn));
}
}
- spin_unlock_irq(&rme9652->lock);
return !!change;
}
@@ -1314,9 +1254,8 @@ static int snd_rme9652_get_passthru(struct snd_kcontrol *kcontrol, struct snd_ct
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.integer.value[0] = rme9652->passthru;
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1331,11 +1270,10 @@ static int snd_rme9652_put_passthru(struct snd_kcontrol *kcontrol, struct snd_ct
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
change = (ucontrol->value.integer.value[0] != rme9652->passthru);
if (change)
err = rme9652_set_passthru(rme9652, val);
- spin_unlock_irq(&rme9652->lock);
return err ? err : change;
}
@@ -1360,9 +1298,8 @@ static int snd_rme9652_get_spdif_rate(struct snd_kcontrol *kcontrol, struct snd_
{
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652);
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -1472,7 +1409,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),
@@ -1527,10 +1464,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)
@@ -1540,19 +1477,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;
}
@@ -1589,8 +1534,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
x, (unsigned long) rme9652->period_bytes);
snd_iprintf(buffer, "Hardware pointer (frames): %ld\n",
rme9652_hw_pointer(rme9652));
- snd_iprintf(buffer, "Passthru: %s\n",
- rme9652->passthru ? "yes" : "no");
+ snd_iprintf(buffer, "Passthru: %s\n", str_yes_no(rme9652->passthru));
if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) {
snd_iprintf(buffer, "Clock mode: autosync\n");
@@ -1713,7 +1657,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, "Timecode signal: %s\n",
- (status & RME9652_tc_valid) ? "yes" : "no");
+ str_yes_no(status & RME9652_tc_valid));
/* thru modes */
@@ -1736,59 +1680,46 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
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);
- iounmap(rme9652->iobase);
- if (rme9652->port)
- pci_release_regions(rme9652->pci);
-
- pci_disable_device(rme9652->pci);
- return 0;
}
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);
+ 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;
}
@@ -1860,7 +1791,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)
@@ -1870,9 +1801,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 +
@@ -1885,10 +1816,10 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *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))
return -EINVAL;
@@ -1898,33 +1829,17 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream,
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_from_user(channel_buf + pos, src, count))
+ if (copy_from_iter(channel_buf + pos, count, src) != count)
return -EFAULT;
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);
- 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, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *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))
return -EINVAL;
@@ -1934,33 +1849,17 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream,
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_to_user(dst, channel_buf + pos, count))
+ if (copy_to_iter(channel_buf + pos, count, dst) != count)
return -EFAULT;
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);
- 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, 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,
@@ -2005,56 +1904,52 @@ static int snd_rme9652_hw_params(struct snd_pcm_substream *substream,
pid_t this_pid;
pid_t other_pid;
- spin_lock_irq(&rme9652->lock);
-
- if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
- rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream);
- this_pid = rme9652->playback_pid;
- other_pid = rme9652->capture_pid;
- } else {
- this_pid = rme9652->capture_pid;
- other_pid = rme9652->playback_pid;
- }
-
- if ((other_pid > 0) && (this_pid != other_pid)) {
-
- /* The other stream is open, and not by the same
- task as this one. Make sure that the parameters
- that matter are the same.
- */
+ scoped_guard(spinlock_irq, &rme9652->lock) {
- if ((int)params_rate(params) !=
- rme9652_adat_sample_rate(rme9652)) {
- spin_unlock_irq(&rme9652->lock);
- _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
- return -EBUSY;
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream);
+ this_pid = rme9652->playback_pid;
+ other_pid = rme9652->capture_pid;
+ } else {
+ this_pid = rme9652->capture_pid;
+ other_pid = rme9652->playback_pid;
}
- if (params_period_size(params) != rme9652->period_bytes / 4) {
- spin_unlock_irq(&rme9652->lock);
- _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
- return -EBUSY;
- }
+ if ((other_pid > 0) && (this_pid != other_pid)) {
- /* We're fine. */
+ /* The other stream is open, and not by the same
+ task as this one. Make sure that the parameters
+ that matter are the same.
+ */
- spin_unlock_irq(&rme9652->lock);
- return 0;
+ if ((int)params_rate(params) !=
+ rme9652_adat_sample_rate(rme9652)) {
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return -EBUSY;
+ }
- } else {
- spin_unlock_irq(&rme9652->lock);
+ if (params_period_size(params) != rme9652->period_bytes / 4) {
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return -EBUSY;
+ }
+
+ /* We're fine. */
+ return 0;
+ }
}
/* 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;
}
@@ -2071,9 +1966,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;
@@ -2112,7 +2008,8 @@ static int snd_rme9652_trigger(struct snd_pcm_substream *substream,
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *other;
int running;
- spin_lock(&rme9652->lock);
+
+ guard(spinlock)(&rme9652->lock);
running = rme9652->running;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -2123,7 +2020,6 @@ static int snd_rme9652_trigger(struct snd_pcm_substream *substream,
break;
default:
snd_BUG();
- spin_unlock(&rme9652->lock);
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -2163,7 +2059,6 @@ static int snd_rme9652_trigger(struct snd_pcm_substream *substream,
else if (rme9652->running && !running)
rme9652_stop(rme9652);
rme9652->running = running;
- spin_unlock(&rme9652->lock);
return 0;
}
@@ -2171,14 +2066,11 @@ static int snd_rme9652_trigger(struct snd_pcm_substream *substream,
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);
+ guard(spinlock_irqsave)(&rme9652->lock);
if (!rme9652->running)
rme9652_reset_hw_pointer(rme9652);
- spin_unlock_irqrestore(&rme9652->lock, flags);
- return result;
+ return 0;
}
static const struct snd_pcm_hardware snd_rme9652_playback_subinfo =
@@ -2298,24 +2190,21 @@ static int snd_rme9652_playback_open(struct snd_pcm_substream *substream)
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irq(&rme9652->lock);
+ scoped_guard(spinlock_irq, &rme9652->lock) {
+ snd_pcm_set_sync(substream);
- snd_pcm_set_sync(substream);
+ runtime->hw = snd_rme9652_playback_subinfo;
+ snd_pcm_set_runtime_buffer(substream, &rme9652->playback_dma_buf);
- runtime->hw = snd_rme9652_playback_subinfo;
- runtime->dma_area = rme9652->playback_buffer;
- runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+ if (rme9652->capture_substream == NULL) {
+ rme9652_stop(rme9652);
+ rme9652_set_thru(rme9652, -1, 0);
+ }
- if (rme9652->capture_substream == NULL) {
- rme9652_stop(rme9652);
- rme9652_set_thru(rme9652, -1, 0);
+ rme9652->playback_pid = current->pid;
+ rme9652->playback_substream = substream;
}
- rme9652->playback_pid = current->pid;
- rme9652->playback_substream = substream;
-
- spin_unlock_irq(&rme9652->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);
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
@@ -2339,12 +2228,10 @@ static int snd_rme9652_playback_release(struct snd_pcm_substream *substream)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme9652->lock);
-
- rme9652->playback_pid = -1;
- rme9652->playback_substream = NULL;
-
- spin_unlock_irq(&rme9652->lock);
+ scoped_guard(spinlock_irq, &rme9652->lock) {
+ rme9652->playback_pid = -1;
+ rme9652->playback_substream = NULL;
+ }
rme9652->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
@@ -2358,24 +2245,21 @@ static int snd_rme9652_capture_open(struct snd_pcm_substream *substream)
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irq(&rme9652->lock);
+ scoped_guard(spinlock_irq, &rme9652->lock) {
+ snd_pcm_set_sync(substream);
- snd_pcm_set_sync(substream);
+ runtime->hw = snd_rme9652_capture_subinfo;
+ snd_pcm_set_runtime_buffer(substream, &rme9652->capture_dma_buf);
- runtime->hw = snd_rme9652_capture_subinfo;
- runtime->dma_area = rme9652->capture_buffer;
- runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+ if (rme9652->playback_substream == NULL) {
+ rme9652_stop(rme9652);
+ rme9652_set_thru(rme9652, -1, 0);
+ }
- if (rme9652->playback_substream == NULL) {
- rme9652_stop(rme9652);
- rme9652_set_thru(rme9652, -1, 0);
+ rme9652->capture_pid = current->pid;
+ rme9652->capture_substream = substream;
}
- rme9652->capture_pid = current->pid;
- rme9652->capture_substream = substream;
-
- spin_unlock_irq(&rme9652->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);
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
@@ -2394,12 +2278,11 @@ static int snd_rme9652_capture_release(struct snd_pcm_substream *substream)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- spin_lock_irq(&rme9652->lock);
+ guard(spinlock_irq)(&rme9652->lock);
rme9652->capture_pid = -1;
rme9652->capture_substream = NULL;
- spin_unlock_irq(&rme9652->lock);
return 0;
}
@@ -2411,8 +2294,7 @@ static const struct snd_pcm_ops snd_rme9652_playback_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy_user = snd_rme9652_playback_copy,
- .copy_kernel = snd_rme9652_playback_copy_kernel,
+ .copy = snd_rme9652_playback_copy,
.fill_silence = snd_rme9652_hw_silence,
};
@@ -2424,8 +2306,7 @@ static const struct snd_pcm_ops snd_rme9652_capture_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy_user = snd_rme9652_capture_copy,
- .copy_kernel = snd_rme9652_capture_copy_kernel,
+ .copy = snd_rme9652_capture_copy,
};
static int snd_rme9652_create_pcm(struct snd_card *card,
@@ -2434,15 +2315,13 @@ static int snd_rme9652_create_pcm(struct snd_card *card,
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;
- strcpy(pcm->name, rme9652->card_name);
+ strscpy(pcm->name, rme9652->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops);
@@ -2478,27 +2357,30 @@ static int 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 = pcim_request_all_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) {
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,
- KBUILD_MODNAME, rme9652)) {
+ 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
@@ -2522,7 +2404,7 @@ static int snd_rme9652_create(struct snd_card *card,
switch (rev) {
case 8: /* original eprom */
- strcpy(card->driver, "RME9636");
+ strscpy(card->driver, "RME9636");
if (rme9652->hw_rev == 15) {
rme9652->card_name = "RME Digi9636 (Rev 1.5)";
} else {
@@ -2531,17 +2413,17 @@ static int snd_rme9652_create(struct snd_card *card,
rme9652->ss_channels = RME9636_NCHANNELS;
break;
case 9: /* W36_G EPROM */
- strcpy(card->driver, "RME9636");
+ strscpy(card->driver, "RME9636");
rme9652->card_name = "RME Digi9636 (Rev G)";
rme9652->ss_channels = RME9636_NCHANNELS;
break;
case 4: /* W52_G EPROM */
- strcpy(card->driver, "RME9652");
+ strscpy(card->driver, "RME9652");
rme9652->card_name = "RME Digi9652 (Rev G)";
rme9652->ss_channels = RME9652_NCHANNELS;
break;
case 3: /* original eprom */
- strcpy(card->driver, "RME9652");
+ strscpy(card->driver, "RME9652");
if (rme9652->hw_rev == 15) {
rme9652->card_name = "RME Digi9652 (Rev 1.5)";
} else {
@@ -2555,17 +2437,17 @@ static int 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);
@@ -2585,14 +2467,6 @@ static int 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 snd_rme9652_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -2608,8 +2482,8 @@ static int snd_rme9652_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
@@ -2620,33 +2494,28 @@ static int snd_rme9652_probe(struct pci_dev *pci,
rme9652->pci = pci;
err = snd_rme9652_create(card, rme9652, precise_ptr[dev]);
if (err)
- goto free_card;
+ goto error;
- strcpy(card->shortname, rme9652->card_name);
+ strscpy(card->shortname, rme9652->card_name);
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname, rme9652->port, rme9652->irq);
err = snd_card_register(card);
- if (err) {
-free_card:
- snd_card_free(card);
- return err;
- }
+ if (err)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void snd_rme9652_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
+ error:
+ snd_card_free(card);
+ return err;
}
static struct pci_driver rme9652_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme9652_ids,
.probe = snd_rme9652_probe,
- .remove = snd_rme9652_remove,
};
module_pci_driver(rme9652_driver);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 964acf302479..4be085d27712 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,19 +7,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
*/
#include <linux/init.h>
@@ -36,7 +24,6 @@
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 */
@@ -103,11 +90,7 @@ struct voice {
* we're not doing power management, we still need to allocate a page
* for the silence buffer.
*/
-#ifdef CONFIG_PM_SLEEP
#define SIS_SUSPEND_PAGES 4
-#else
-#define SIS_SUSPEND_PAGES 1
-#endif
struct sis7019 {
unsigned long ioport;
@@ -375,7 +358,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;
}
@@ -400,9 +383,7 @@ static void __sis_unmap_silence(struct sis7019 *sis)
static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
{
- unsigned long flags;
-
- spin_lock_irqsave(&sis->voice_lock, flags);
+ guard(spinlock_irqsave)(&sis->voice_lock);
if (voice->timing) {
__sis_unmap_silence(sis);
voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING |
@@ -410,7 +391,6 @@ static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
voice->timing = NULL;
}
voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
}
static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis)
@@ -434,14 +414,8 @@ found_one:
static struct voice *sis_alloc_playback_voice(struct sis7019 *sis)
{
- struct voice *voice;
- unsigned long flags;
-
- spin_lock_irqsave(&sis->voice_lock, flags);
- voice = __sis_alloc_playback_voice(sis);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
-
- return voice;
+ guard(spinlock_irqsave)(&sis->voice_lock);
+ return __sis_alloc_playback_voice(sis);
}
static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
@@ -451,7 +425,6 @@ static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct voice *voice = runtime->private_data;
unsigned int period_size, buffer_size;
- unsigned long flags;
int needed;
/* If there are one or two periods per buffer, we don't need a
@@ -464,11 +437,11 @@ static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
period_size != (buffer_size / 2));
if (needed && !voice->timing) {
- spin_lock_irqsave(&sis->voice_lock, flags);
- voice->timing = __sis_alloc_playback_voice(sis);
- if (voice->timing)
- __sis_map_silence(sis);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
+ scoped_guard(spinlock_irqsave, &sis->voice_lock) {
+ voice->timing = __sis_alloc_playback_voice(sis);
+ if (voice->timing)
+ __sis_map_silence(sis);
+ }
if (!voice->timing)
return -ENOMEM;
voice->timing->substream = substream;
@@ -511,18 +484,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;
@@ -674,17 +635,16 @@ static int sis_capture_open(struct snd_pcm_substream *substream)
struct sis7019 *sis = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct voice *voice = &sis->capture_voice;
- unsigned long flags;
/* FIXME: The driver only supports recording from one channel
* at the moment, but it could support more.
*/
- spin_lock_irqsave(&sis->voice_lock, flags);
- if (voice->flags & VOICE_IN_USE)
- voice = NULL;
- else
- voice->flags |= VOICE_IN_USE;
- spin_unlock_irqrestore(&sis->voice_lock, flags);
+ scoped_guard(spinlock_irqsave, &sis->voice_lock) {
+ if (voice->flags & VOICE_IN_USE)
+ voice = NULL;
+ else
+ voice->flags |= VOICE_IN_USE;
+ }
if (!voice)
return -EAGAIN;
@@ -713,11 +673,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:
@@ -875,9 +830,6 @@ static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
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,
@@ -886,9 +838,7 @@ static const struct snd_pcm_ops sis_playback_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,
@@ -907,7 +857,7 @@ static int sis_pcm_create(struct sis7019 *sis)
return rc;
pcm->private_data = sis;
- strcpy(pcm->name, "SiS7019");
+ strscpy(pcm->name, "SiS7019");
sis->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops);
@@ -916,8 +866,8 @@ static int 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;
}
@@ -941,7 +891,7 @@ static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
/* Get the AC97 semaphore -- software first, so we don't spin
* pounding out IO reads on the hardware semaphore...
*/
- mutex_lock(&sis->ac97_mutex);
+ guard(mutex)(&sis->ac97_mutex);
count = 0xffff;
while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
@@ -980,8 +930,6 @@ static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
timeout_sema:
outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
timeout:
- mutex_unlock(&sis->ac97_mutex);
-
if (!count) {
dev_err(&sis->pci->dev, "ac97 codec %d timeout cmd 0x%08x\n",
codec, cmd);
@@ -1017,7 +965,7 @@ 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,
};
@@ -1042,16 +990,10 @@ static int 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);
@@ -1063,18 +1005,6 @@ static int sis_chip_free(struct sis7019 *sis)
*/
if (sis->irq >= 0)
free_irq(sis->irq, sis);
-
- 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)
@@ -1205,7 +1135,6 @@ static int sis_chip_init(struct sis7019 *sis)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int sis_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -1214,7 +1143,6 @@ static int sis_suspend(struct device *dev)
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)
@@ -1285,11 +1213,7 @@ error:
return -EIO;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume);
static int sis_alloc_suspend(struct sis7019 *sis)
{
@@ -1301,7 +1225,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;
}
@@ -1315,23 +1240,19 @@ static int sis_chip_create(struct snd_card *card,
{
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;
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");
- goto error_out_enabled;
+ return -ENXIO;
}
- memset(sis, 0, sizeof(*sis));
mutex_init(&sis->ac97_mutex);
spin_lock_init(&sis->voice_lock);
sis->card = card;
@@ -1339,37 +1260,38 @@ static int sis_chip_create(struct snd_card *card,
sis->irq = -1;
sis->ioport = pci_resource_start(pci, 0);
- rc = pci_request_regions(pci, "SiS7019");
+ rc = pcim_request_all_regions(pci, "SiS7019");
if (rc) {
dev_err(&pci->dev, "unable request regions\n");
- goto error_out_enabled;
+ 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) {
dev_err(&pci->dev, "unable to remap MMIO, aborting\n");
- goto error_out_cleanup;
+ return -EIO;
}
rc = sis_alloc_suspend(sis);
if (rc < 0) {
dev_err(&pci->dev, "unable to allocate state storage\n");
- goto error_out_cleanup;
+ return rc;
}
rc = sis_chip_init(sis);
if (rc)
- goto error_out_cleanup;
+ return rc;
+ card->private_free = sis_chip_free;
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);
- goto error_out_cleanup;
+ return rc;
}
sis->irq = pci->irq;
+ card->sync_irq = sis->irq;
pci_set_master(pci);
for (i = 0; i < 64; i++) {
@@ -1384,32 +1306,18 @@ static int 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;
-
return 0;
-
-error_out_cleanup:
- sis_chip_free(sis);
-
-error_out_enabled:
- pci_disable_device(pci);
-
-error_out:
- return rc;
}
-static int 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;
/* 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
@@ -1422,26 +1330,26 @@ static int snd_sis7019_probe(struct pci_dev *pci,
if (!codecs)
codecs = SIS_PRIMARY_CODEC_PRESENT;
- rc = snd_card_new(&pci->dev, index, id, THIS_MODULE,
- sizeof(*sis), &card);
+ 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");
+ strscpy(card->driver, "SiS7019");
+ strscpy(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",
@@ -1450,30 +1358,24 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_sis7019_probe(pci, pci_id));
}
static struct pci_driver sis7019_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_sis7019_ids,
.probe = snd_sis7019_probe,
- .remove = snd_sis7019_remove,
.driver = {
- .pm = SIS_PM_OPS,
+ .pm = &sis_pm,
},
};
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 a8abb15e3c3a..a4c72799d034 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>
@@ -43,7 +29,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("S3 SonicVibes PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}");
#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -318,14 +303,11 @@ static void snd_sonicvibes_out(struct sonicvibes * sonic,
unsigned char reg,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&sonic->reg_lock, flags);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
outb(reg, SV_REG(sonic, INDEX));
udelay(10);
outb(value, SV_REG(sonic, DATA));
udelay(10);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
}
static unsigned char snd_sonicvibes_in1(struct sonicvibes * sonic, unsigned char reg)
@@ -341,15 +323,13 @@ static unsigned char snd_sonicvibes_in1(struct sonicvibes * sonic, unsigned char
static unsigned char snd_sonicvibes_in(struct sonicvibes * sonic, unsigned char reg)
{
- unsigned long flags;
unsigned char value;
- spin_lock_irqsave(&sonic->reg_lock, flags);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
outb(reg, SV_REG(sonic, INDEX));
udelay(10);
value = inb(SV_REG(sonic, DATA));
udelay(10);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
return value;
}
@@ -463,9 +443,7 @@ static void snd_sonicvibes_setfmt(struct sonicvibes * sonic,
unsigned char mask,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&sonic->reg_lock, flags);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX));
if (mask) {
sonic->format = inb(SV_REG(sonic, DATA));
@@ -476,7 +454,6 @@ static void snd_sonicvibes_setfmt(struct sonicvibes * sonic,
udelay(10);
outb(0, SV_REG(sonic, INDEX));
udelay(10);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
}
static void snd_sonicvibes_pll(unsigned int rate,
@@ -521,21 +498,18 @@ static void snd_sonicvibes_setpll(struct sonicvibes * sonic,
unsigned char reg,
unsigned int rate)
{
- unsigned long flags;
unsigned int r, m, n;
snd_sonicvibes_pll(rate, &r, &m, &n);
if (sonic != NULL) {
- spin_lock_irqsave(&sonic->reg_lock, flags);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
snd_sonicvibes_out1(sonic, reg, m);
snd_sonicvibes_out1(sonic, reg + 1, r | n);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
}
}
static void snd_sonicvibes_set_adc_rate(struct sonicvibes * sonic, unsigned int rate)
{
- unsigned long flags;
unsigned int div;
unsigned char clock;
@@ -548,10 +522,9 @@ static void snd_sonicvibes_set_adc_rate(struct sonicvibes * sonic, unsigned int
clock = 0x00;
snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate);
}
- spin_lock_irqsave(&sonic->reg_lock, flags);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4);
snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
}
static int snd_sonicvibes_hw_constraint_dac_rate(struct snd_pcm_hw_params *params,
@@ -582,22 +555,18 @@ static int snd_sonicvibes_hw_constraint_dac_rate(struct snd_pcm_hw_params *param
static void snd_sonicvibes_set_dac_rate(struct sonicvibes * sonic, unsigned int rate)
{
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);
+ guard(spinlock_irqsave)(&sonic->reg_lock);
snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8);
snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div);
- spin_unlock_irqrestore(&sonic->reg_lock, flags);
}
static int snd_sonicvibes_trigger(struct sonicvibes * sonic, int what, int cmd)
{
- int result = 0;
-
- spin_lock(&sonic->reg_lock);
+ guard(spinlock)(&sonic->reg_lock);
if (cmd == SNDRV_PCM_TRIGGER_START) {
if (!(sonic->enable & what)) {
sonic->enable |= what;
@@ -609,10 +578,9 @@ static int snd_sonicvibes_trigger(struct sonicvibes * sonic, int what, int cmd)
snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
}
} else {
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&sonic->reg_lock);
- return result;
+ return 0;
}
static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id)
@@ -643,34 +611,34 @@ static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id)
unsigned char udreg;
int vol, oleft, oright, mleft, mright;
- spin_lock(&sonic->reg_lock);
- udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON);
- vol = udreg & 0x3f;
- if (!(udreg & 0x40))
- vol = -vol;
- oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG);
- oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG);
- oleft &= 0x1f;
- oright &= 0x1f;
- oleft += vol;
- if (oleft < 0)
- oleft = 0;
- if (oleft > 0x1f)
- oleft = 0x1f;
- oright += vol;
- if (oright < 0)
- oright = 0;
- if (oright > 0x1f)
- oright = 0x1f;
- if (udreg & 0x80) {
- mleft ^= 0x80;
- mright ^= 0x80;
+ scoped_guard(spinlock, &sonic->reg_lock) {
+ udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON);
+ vol = udreg & 0x3f;
+ if (!(udreg & 0x40))
+ vol = -vol;
+ oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG);
+ oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG);
+ oleft &= 0x1f;
+ oright &= 0x1f;
+ oleft += vol;
+ if (oleft < 0)
+ oleft = 0;
+ if (oleft > 0x1f)
+ oleft = 0x1f;
+ oright += vol;
+ if (oright < 0)
+ oright = 0;
+ if (oright > 0x1f)
+ oright = 0x1f;
+ if (udreg & 0x80) {
+ mleft ^= 0x80;
+ mright ^= 0x80;
+ }
+ oleft |= mleft & 0x80;
+ oright |= mright & 0x80;
+ snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft);
+ snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright);
}
- oleft |= mleft & 0x80;
- oright |= mright & 0x80;
- snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft);
- snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright);
- spin_unlock(&sonic->reg_lock);
snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id);
snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id);
}
@@ -695,17 +663,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);
@@ -722,11 +679,10 @@ static int snd_sonicvibes_playback_prepare(struct snd_pcm_substream *substream)
fmt |= 2;
snd_sonicvibes_setfmt(sonic, ~3, fmt);
snd_sonicvibes_set_dac_rate(sonic, runtime->rate);
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size);
snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8);
snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count);
- spin_unlock_irq(&sonic->reg_lock);
return 0;
}
@@ -747,11 +703,10 @@ static int snd_sonicvibes_capture_prepare(struct snd_pcm_substream *substream)
fmt |= 0x20;
snd_sonicvibes_setfmt(sonic, ~0x30, fmt);
snd_sonicvibes_set_adc_rate(sonic, runtime->rate);
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size);
snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8);
snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count);
- spin_unlock_irq(&sonic->reg_lock);
return 0;
}
@@ -860,9 +815,6 @@ static int snd_sonicvibes_capture_close(struct snd_pcm_substream *substream)
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,
@@ -871,9 +823,6 @@ static const struct snd_pcm_ops snd_sonicvibes_playback_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,
@@ -884,7 +833,8 @@ 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;
@@ -894,11 +844,11 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device)
pcm->private_data = sonic;
pcm->info_flags = 0;
- strcpy(pcm->name, "S3 SonicVibes");
+ strscpy(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);
return 0;
}
@@ -925,10 +875,9 @@ static int snd_sonicvibes_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_
{
struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
- spin_unlock_irq(&sonic->reg_lock);
return 0;
}
@@ -943,7 +892,7 @@ static int snd_sonicvibes_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_
return -EINVAL;
left = (ucontrol->value.enumerated.item[0] + 1) << 5;
right = (ucontrol->value.enumerated.item[1] + 1) << 5;
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC);
oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC);
left = (oval1 & ~SV_RECSRC_OUT) | left;
@@ -951,7 +900,6 @@ static int snd_sonicvibes_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_
change = left != oval1 || right != oval2;
snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left);
snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right);
- spin_unlock_irq(&sonic->reg_lock);
return change;
}
@@ -980,9 +928,8 @@ static int snd_sonicvibes_get_single(struct snd_kcontrol *kcontrol, struct snd_c
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask;
- spin_unlock_irq(&sonic->reg_lock);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -1002,12 +949,11 @@ static int snd_sonicvibes_put_single(struct snd_kcontrol *kcontrol, struct snd_c
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
oval = snd_sonicvibes_in1(sonic, reg);
val = (oval & ~(mask << shift)) | val;
change = val != oval;
snd_sonicvibes_out1(sonic, reg, val);
- spin_unlock_irq(&sonic->reg_lock);
return change;
}
@@ -1038,10 +984,9 @@ static int snd_sonicvibes_get_double(struct snd_kcontrol *kcontrol, struct snd_c
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask;
ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask;
- spin_unlock_irq(&sonic->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];
@@ -1069,7 +1014,7 @@ static int snd_sonicvibes_put_double(struct snd_kcontrol *kcontrol, struct snd_c
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irq(&sonic->reg_lock);
+ guard(spinlock_irq)(&sonic->reg_lock);
oval1 = snd_sonicvibes_in1(sonic, left_reg);
oval2 = snd_sonicvibes_in1(sonic, right_reg);
val1 = (oval1 & ~(mask << shift_left)) | val1;
@@ -1077,11 +1022,10 @@ static int snd_sonicvibes_put_double(struct snd_kcontrol *kcontrol, struct snd_c
change = val1 != oval1 || val2 != oval2;
snd_sonicvibes_out1(sonic, left_reg, val1);
snd_sonicvibes_out1(sonic, right_reg, val2);
- spin_unlock_irq(&sonic->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_sonicvibes_controls[] = {
+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),
@@ -1122,10 +1066,12 @@ static int snd_sonicvibes_mixer(struct sonicvibes *sonic)
if (snd_BUG_ON(!sonic || !sonic->card))
return -EINVAL;
card = sonic->card;
- strcpy(card->mixername, "S3 SonicVibes");
+ strscpy(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:
@@ -1147,7 +1093,7 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry,
tmp = sonic->srs_space & 0x0f;
snd_iprintf(buffer, "SRS 3D : %s\n",
- sonic->srs_space & 0x80 ? "off" : "on");
+ str_off_on(sonic->srs_space & 0x80));
snd_iprintf(buffer, "SRS Space : %s\n",
tmp == 0x00 ? "100%" :
tmp == 0x01 ? "75%" :
@@ -1164,17 +1110,15 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry,
tmp == 0x00 ? "on-board ROM" :
tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus");
tmp = sonic->mpu_switch;
- snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off");
- snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off");
- snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off");
+ snd_iprintf(buffer, "Onboard synth : %s\n", str_on_off(tmp & 0x01));
+ snd_iprintf(buffer, "Ext. Rx to synth : %s\n", str_on_off(tmp & 0x02));
+ snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", str_on_off(tmp & 0x04));
}
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);
}
/*
@@ -1182,12 +1126,13 @@ static void snd_sonicvibes_proc_init(struct sonicvibes *sonic)
*/
#ifdef SUPPORT_JOYSTICK
-static struct snd_kcontrol_new snd_sonicvibes_game_control =
+static const struct snd_kcontrol_new snd_sonicvibes_game_control =
SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0);
static int snd_sonicvibes_create_gameport(struct sonicvibes *sonic)
{
struct gameport *gp;
+ int err;
sonic->gameport = gp = gameport_allocate_port();
if (!gp) {
@@ -1203,7 +1148,10 @@ static int 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;
}
@@ -1220,68 +1168,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)
-{
- struct sonicvibes *sonic = device->device_data;
- return snd_sonicvibes_free(sonic);
}
static int snd_sonicvibes_create(struct snd_card *card,
struct pci_dev *pci,
int reverb,
- int mge,
- struct sonicvibes **rsonic)
+ int mge)
{
- 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
+ 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");
- pci_disable_device(pci);
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 = pcim_request_all_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);
@@ -1289,13 +1212,14 @@ static int 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,
- KBUILD_MODNAME, 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);
- snd_sonicvibes_free(sonic);
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);
@@ -1319,15 +1243,17 @@ static int snd_sonicvibes_create(struct snd_card *card,
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);
+ 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);
+ 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);
@@ -1388,14 +1314,7 @@ static int 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);
-
- *rsonic = sonic;
return 0;
}
@@ -1403,7 +1322,7 @@ static int snd_sonicvibes_create(struct snd_card *card,
* MIDI section
*/
-static struct snd_kcontrol_new snd_sonicvibes_midi_controls[] = {
+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),
@@ -1429,29 +1348,29 @@ static int snd_sonicvibes_midi(struct sonicvibes *sonic,
{
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 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;
@@ -1460,83 +1379,71 @@ static int snd_sonic_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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");
+ strscpy(card->driver, "SonicVibes");
+ strscpy(card->shortname, "S3 SonicVibes");
sprintf(card->longname, "%s rev %i at 0x%llx, irq %i",
card->shortname,
sonic->revision,
(unsigned long long)pci_resource_start(pci, 1),
sonic->irq);
- if ((err = snd_sonicvibes_pcm(sonic, 0)) < 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 |
- MPU401_INFO_IRQ_HOOK,
- -1, &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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_sonic_probe(pci, pci_id));
}
static struct pci_driver sonicvibes_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_sonic_ids,
.probe = snd_sonic_probe,
- .remove = snd_sonic_remove,
};
module_pci_driver(sonicvibes_driver);
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
index 88676b50f385..476d16abcfc9 100644
--- a/sound/pci/trident/Makefile
+++ b/sound/pci/trident/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-trident-objs := trident.o trident_main.o trident_memory.o
+snd-trident-y := trident.o trident_main.o trident_memory.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index cedf13b64803..ddb6ccc72e44 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -1,24 +1,9 @@
+// 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>
@@ -32,18 +17,6 @@
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 */
@@ -89,20 +62,18 @@ static int snd_trident_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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:
@@ -117,66 +88,56 @@ static int snd_trident_probe(struct pci_dev *pci,
default:
str = "Unknown";
}
- strcpy(card->driver, str);
+ strscpy(card->driver, str);
if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
- strcpy(card->shortname, "SiS ");
+ strscpy(card->shortname, "SiS ");
} else {
- strcpy(card->shortname, "Trident ");
+ strscpy(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++)) < 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++)) < 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++)) < 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 |
- MPU401_INFO_IRQ_HOOK,
- -1, &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 snd_trident_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
static struct pci_driver trident_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_trident_ids,
.probe = snd_trident_probe,
- .remove = snd_trident_remove,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_trident_pm,
diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h
index 9624e5937719..ed2d4eecc704 100644
--- a/sound/pci/trident/trident.h
+++ b/sound/pci/trident/trident.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_TRIDENT_H
#define __SOUND_TRIDENT_H
@@ -5,22 +6,6 @@
* audio@tridentmicro.com
* Fri Feb 19 15:55:28 MST 1999
* Definitions for Trident 4DWave DX/NX chips
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/pcm.h>
@@ -264,12 +249,11 @@ struct snd_trident_memblk_arg {
};
struct snd_trident_tlb {
- unsigned int * entries; /* 16k-aligned TLB table */
+ __le32 *entries; /* 16k-aligned TLB table */
dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */
- unsigned long * shadow_entries; /* shadow entries with virtual addresses */
- struct snd_dma_buffer buffer;
+ struct snd_dma_buffer *buffer;
struct snd_util_memhdr * memhdr; /* page allocation list */
- struct snd_dma_buffer silent_page;
+ struct snd_dma_buffer *silent_page;
};
struct snd_trident_voice {
@@ -416,14 +400,12 @@ 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);
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);
@@ -436,9 +418,5 @@ extern const struct dev_pm_ops snd_trident_pm;
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 eabd84d9ffee..55515c58b8aa 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>
*/
@@ -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
@@ -121,10 +107,9 @@ static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned sho
{
unsigned int data = 0, treg;
unsigned short count = 0xffff;
- unsigned long flags;
struct snd_trident *trident = ac97->private_data;
- spin_lock_irqsave(&trident->reg_lock, flags);
+ guard(spinlock_irqsave)(&trident->reg_lock);
if (trident->device == TRIDENT_DEVICE_ID_DX) {
data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
@@ -161,7 +146,6 @@ static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned sho
data = 0;
}
- spin_unlock_irqrestore(&trident->reg_lock, flags);
return ((unsigned short) (data >> 16));
}
@@ -184,12 +168,11 @@ static void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
{
unsigned int address, data;
unsigned short count = 0xffff;
- unsigned long flags;
struct snd_trident *trident = ac97->private_data;
data = ((unsigned long) wdata) << 16;
- spin_lock_irqsave(&trident->reg_lock, flags);
+ guard(spinlock_irqsave)(&trident->reg_lock);
if (trident->device == TRIDENT_DEVICE_ID_DX) {
address = DX_ACR0_AC97_W;
@@ -227,12 +210,9 @@ static void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
count = 0; /* return */
}
- if (count == 0) {
- spin_unlock_irqrestore(&trident->reg_lock, flags);
+ if (count == 0)
return;
- }
outl(data, TRID_REG(trident, address));
- spin_unlock_irqrestore(&trident->reg_lock, flags);
}
/*---------------------------------------------------------------------------
@@ -692,7 +672,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;
}
@@ -782,29 +762,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
@@ -822,12 +779,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);
@@ -925,7 +879,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;
@@ -952,7 +905,7 @@ static int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
struct snd_trident_voice *evoice = voice->extra;
struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
/* set delta (rate) value */
voice->Delta = snd_trident_convert_rate(runtime->rate);
@@ -1013,8 +966,6 @@ static int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
evoice->ESO = (runtime->period_size * 2) - 1;
}
- spin_unlock_irq(&trident->reg_lock);
-
return 0;
}
@@ -1054,7 +1005,7 @@ static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
struct snd_trident_voice *voice = runtime->private_data;
unsigned int val, ESO_bytes;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
// Initialize the channel and set channel Mode
outb(0, TRID_REG(trident, LEGACY_DMAR15));
@@ -1075,7 +1026,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
@@ -1123,7 +1074,6 @@ static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
snd_trident_write_voice_regs(trident, voice);
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -1142,11 +1092,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);
}
@@ -1168,7 +1113,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;
@@ -1194,7 +1138,7 @@ static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substrea
struct snd_trident_voice *voice = runtime->private_data;
struct snd_trident_voice *evoice = voice->extra;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
voice->LBA = runtime->dma_addr;
voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
@@ -1243,7 +1187,6 @@ static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substrea
evoice->ESO = (runtime->period_size * 2) - 1;
}
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -1265,7 +1208,7 @@ static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
struct snd_trident_voice *voice = runtime->private_data;
struct snd_trident_voice *evoice = voice->extra;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
/* Set channel buffer Address */
if (voice->memblk)
@@ -1320,7 +1263,6 @@ static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
evoice->ESO = (runtime->period_size * 2) - 1;
}
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -1354,35 +1296,33 @@ static int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream,
}
/* prepare SPDIF channel */
- spin_lock_irq(&trident->reg_lock);
- old_bits = trident->spdif_pcm_bits;
- if (old_bits & IEC958_AES0_PROFESSIONAL)
- trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
- else
- trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
- if (params_rate(hw_params) >= 48000) {
- trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz
- trident->spdif_pcm_bits |=
- trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ scoped_guard(spinlock_irq, &trident->reg_lock) {
+ old_bits = trident->spdif_pcm_bits;
+ if (old_bits & IEC958_AES0_PROFESSIONAL)
+ trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
+ else
+ trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
+ if (params_rate(hw_params) >= 48000) {
+ trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
IEC958_AES0_PRO_FS_48000 :
(IEC958_AES3_CON_FS_48000 << 24);
- }
- else if (params_rate(hw_params) >= 44100) {
- trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz
- trident->spdif_pcm_bits |=
- trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ } else if (params_rate(hw_params) >= 44100) {
+ trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
IEC958_AES0_PRO_FS_44100 :
(IEC958_AES3_CON_FS_44100 << 24);
- }
- else {
- trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz
- trident->spdif_pcm_bits |=
- trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ } else {
+ trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
IEC958_AES0_PRO_FS_32000 :
(IEC958_AES3_CON_FS_32000 << 24);
+ }
+ change = old_bits != trident->spdif_pcm_bits;
}
- change = old_bits != trident->spdif_pcm_bits;
- spin_unlock_irq(&trident->reg_lock);
if (change)
snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
@@ -1411,7 +1351,7 @@ static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
unsigned int RESO, LBAO;
unsigned int temp;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
@@ -1523,8 +1463,6 @@ static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
- spin_unlock_irq(&trident->reg_lock);
-
return 0;
}
@@ -1565,7 +1503,7 @@ static int snd_trident_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
what = whati = capture_flag = spdif_flag = 0;
- spin_lock(&trident->reg_lock);
+ guard(spinlock)(&trident->reg_lock);
val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
snd_pcm_group_for_each_entry(s, substream) {
if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
@@ -1624,7 +1562,6 @@ static int snd_trident_trigger(struct snd_pcm_substream *substream,
if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
}
- spin_unlock(&trident->reg_lock);
return 0;
}
@@ -1649,7 +1586,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *
if (!voice->running)
return 0;
- spin_lock(&trident->reg_lock);
+ guard(spinlock)(&trident->reg_lock);
outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
@@ -1659,8 +1596,6 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *
cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
}
- spin_unlock(&trident->reg_lock);
-
if (cso >= runtime->buffer_size)
cso = 0;
@@ -1913,9 +1848,9 @@ static int snd_trident_spdif_open(struct snd_pcm_substream *substream)
return -EAGAIN;
voice->spdif = 1;
voice->substream = substream;
- spin_lock_irq(&trident->reg_lock);
- trident->spdif_pcm_bits = trident->spdif_bits;
- spin_unlock_irq(&trident->reg_lock);
+ scoped_guard(spinlock_irq, &trident->reg_lock) {
+ trident->spdif_pcm_bits = trident->spdif_bits;
+ }
runtime->private_data = voice;
runtime->private_free = snd_trident_pcm_free_substream;
@@ -1948,22 +1883,22 @@ static int snd_trident_spdif_close(struct snd_pcm_substream *substream)
struct snd_trident *trident = snd_pcm_substream_chip(substream);
unsigned int temp;
- spin_lock_irq(&trident->reg_lock);
- // restore default SPDIF setting
- if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
- outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
- outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
- } else {
- outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
- temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
- if (trident->spdif_ctrl) {
- temp |= SPDIF_EN;
+ scoped_guard(spinlock_irq, &trident->reg_lock) {
+ // restore default SPDIF setting
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
} else {
- temp &= ~SPDIF_EN;
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+ temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ if (trident->spdif_ctrl) {
+ temp |= SPDIF_EN;
+ } else {
+ temp &= ~SPDIF_EN;
+ }
+ outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
- outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
- spin_unlock_irq(&trident->reg_lock);
trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
@@ -2060,9 +1995,8 @@ static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
voice = runtime->private_data;
/* stop capture channel */
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -2073,7 +2007,6 @@ static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
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,
@@ -2084,19 +2017,16 @@ static const struct snd_pcm_ops snd_trident_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 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,
@@ -2107,7 +2037,6 @@ static const struct snd_pcm_ops snd_trident_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,
@@ -2118,7 +2047,6 @@ static const struct snd_pcm_ops snd_trident_si7018_capture_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,
@@ -2129,19 +2057,16 @@ static const struct snd_pcm_ops snd_trident_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 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,
@@ -2152,7 +2077,6 @@ static const struct snd_pcm_ops snd_trident_spdif_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,
@@ -2176,7 +2100,8 @@ int snd_trident_pcm(struct snd_trident *trident, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -2193,21 +2118,23 @@ int snd_trident_pcm(struct snd_trident *trident, int device)
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "Trident 4DWave");
+ strscpy(pcm->name, "Trident 4DWave");
trident->pcm = pcm;
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);
}
return 0;
@@ -2233,7 +2160,8 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, int device)
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;
@@ -2242,25 +2170,27 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, int device)
else
snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
foldback->info_flags = 0;
- strcpy(foldback->name, "Trident 4DWave");
+ strscpy(foldback->name, "Trident 4DWave");
substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
- strcpy(substream->name, "Front Mixer");
+ strscpy(substream->name, "Front Mixer");
substream = substream->next;
- strcpy(substream->name, "Reverb Mixer");
+ strscpy(substream->name, "Reverb Mixer");
substream = substream->next;
- strcpy(substream->name, "Chorus Mixer");
+ strscpy(substream->name, "Chorus Mixer");
if (num_chan == 4) {
substream = substream->next;
- strcpy(substream->name, "Second AC'97 ADC");
+ strscpy(substream->name, "Second AC'97 ADC");
}
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);
return 0;
}
@@ -2281,7 +2211,8 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, int device)
struct snd_pcm *spdif;
int err;
- 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;
@@ -2291,10 +2222,11 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, int device)
snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
}
spdif->info_flags = 0;
- strcpy(spdif->name, "Trident 4DWave IEC958");
+ strscpy(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);
return 0;
}
@@ -2318,10 +2250,9 @@ static int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol,
struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
unsigned char val;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
val = trident->spdif_ctrl;
ucontrol->value.integer.value[0] = val == kcontrol->private_value;
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -2333,7 +2264,7 @@ static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
/* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
change = trident->spdif_ctrl != val;
trident->spdif_ctrl = val;
@@ -2352,7 +2283,6 @@ static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
}
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2385,12 +2315,11 @@ static int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol,
{
struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -2405,7 +2334,7 @@ static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = trident->spdif_bits != val;
trident->spdif_bits = val;
if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
@@ -2415,7 +2344,6 @@ static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
if (trident->spdif == NULL)
outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
}
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2480,12 +2408,11 @@ static int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol,
{
struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -2500,7 +2427,7 @@ static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = trident->spdif_pcm_bits != val;
trident->spdif_pcm_bits = val;
if (trident->spdif != NULL) {
@@ -2510,7 +2437,6 @@ static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
}
}
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2538,10 +2464,9 @@ static int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol,
struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
unsigned char val;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
- spin_unlock_irq(&trident->reg_lock);
return 0;
}
@@ -2552,7 +2477,7 @@ static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
unsigned char val;
int change = 0;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
val &= ~(1 << kcontrol->private_value);
if (ucontrol->value.integer.value[0])
@@ -2560,7 +2485,6 @@ static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
change = val != trident->ac97_ctrl;
trident->ac97_ctrl = val;
outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2611,14 +2535,13 @@ static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
unsigned int val;
int change = 0;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
val = trident->musicvol_wavevol;
val &= ~(0xffff << kcontrol->private_value);
val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
change = val != trident->musicvol_wavevol;
outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2691,12 +2614,11 @@ static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
} else {
val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
}
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = val != mix->vol;
mix->vol = val;
if (mix->voice != NULL)
snd_trident_write_vol_reg(trident, mix->voice, val);
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2755,12 +2677,11 @@ static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
val = ucontrol->value.integer.value[0] & 0x3f;
else
val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = val != mix->pan;
mix->pan = val;
if (mix->voice != NULL)
snd_trident_write_pan_reg(trident, mix->voice, val);
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2810,12 +2731,11 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
int change = 0;
val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = val != mix->rvol;
mix->rvol = val;
if (mix->voice != NULL)
snd_trident_write_rvol_reg(trident, mix->voice, val);
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2868,12 +2788,11 @@ static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
int change = 0;
val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
- spin_lock_irq(&trident->reg_lock);
+ guard(spinlock_irq)(&trident->reg_lock);
change = val != mix->cvol;
mix->cvol = val;
if (mix->voice != NULL)
snd_trident_write_cvol_reg(trident, mix->voice, val);
- spin_unlock_irq(&trident->reg_lock);
return change;
}
@@ -2964,7 +2883,7 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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,
};
@@ -2973,7 +2892,8 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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));
@@ -2981,9 +2901,11 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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;
@@ -3014,10 +2936,14 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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));
@@ -3031,28 +2957,38 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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);
}
@@ -3068,7 +3004,8 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
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);
@@ -3079,7 +3016,8 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
}
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);
@@ -3089,7 +3027,8 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
}
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);
@@ -3099,7 +3038,8 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
}
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;
}
@@ -3306,9 +3246,9 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count);
snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
- snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
+ snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", str_on_off(trident->spdif_ctrl == 0x28));
if (trident->device == TRIDENT_DEVICE_ID_NX) {
- snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
+ snd_iprintf(buffer, "Rear Speakers : %s\n", str_on_off(trident->ac97_ctrl & 0x00000010));
if (trident->tlb.entries) {
snd_iprintf(buffer,"\nVirtual Memory\n");
snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
@@ -3320,19 +3260,11 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
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);
}
/*---------------------------------------------------------------------------
@@ -3354,29 +3286,27 @@ static int snd_trident_tlb_alloc(struct snd_trident *trident)
/* 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) {
+ 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 = (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)
- 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) {
+ 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);
@@ -3501,7 +3431,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);
@@ -3532,36 +3463,24 @@ 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 (dma_set_mask(&pci->dev, DMA_BIT_MASK(30)) < 0 ||
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(30)) < 0) {
+ 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");
- pci_disable_device(pci);
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;
@@ -3577,33 +3496,30 @@ int 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 = pcim_request_all_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,
- KBUILD_MODNAME, 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);
- snd_trident_free(trident);
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;
@@ -3623,17 +3539,11 @@ int snd_trident_create(struct snd_card *card,
snd_BUG();
break;
}
- if (err < 0) {
- snd_trident_free(trident);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
- snd_trident_free(trident);
+ if (err < 0)
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 */
@@ -3654,7 +3564,6 @@ int snd_trident_create(struct snd_card *card,
snd_trident_enable_eso(trident);
snd_trident_proc_init(trident);
- *rtrident = trident;
return 0;
}
@@ -3664,14 +3573,16 @@ int 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
@@ -3680,20 +3591,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));
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;
+ }
}
/*---------------------------------------------------------------------------
@@ -3726,79 +3627,76 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (audio_int & ADDRESS_IRQ) {
// get interrupt status for all channels
- spin_lock(&trident->reg_lock);
- stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
- chn_int = inl(TRID_REG(trident, T4D_AINT_A));
- if (chn_int == 0)
- goto __skip1;
- outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */
- __skip1:
- chn_int = inl(TRID_REG(trident, T4D_AINT_B));
- if (chn_int == 0)
- goto __skip2;
- for (channel = 63; channel >= 32; channel--) {
- mask = 1 << (channel&0x1f);
- if ((chn_int & mask) == 0)
- continue;
- voice = &trident->synth.voices[channel];
- if (!voice->pcm || voice->substream == NULL) {
- outl(mask, TRID_REG(trident, T4D_STOP_B));
- continue;
- }
- delta = (int)stimer - (int)voice->stimer;
- if (delta < 0)
- delta = -delta;
- if ((unsigned int)delta < voice->spurious_threshold) {
- /* do some statistics here */
- trident->spurious_irq_count++;
- if (trident->spurious_irq_max_delta < (unsigned int)delta)
- trident->spurious_irq_max_delta = delta;
- continue;
- }
- voice->stimer = stimer;
- if (voice->isync) {
- if (!voice->isync3) {
- tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
- if (trident->bDMAStart & 0x40)
- tmp >>= 1;
- if (tmp > 0)
- tmp = voice->isync_max - tmp;
- } else {
- tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+ scoped_guard(spinlock, &trident->reg_lock) {
+ stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+ chn_int = inl(TRID_REG(trident, T4D_AINT_A));
+ if (chn_int)
+ outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */
+ chn_int = inl(TRID_REG(trident, T4D_AINT_B));
+ if (chn_int == 0)
+ break;
+ for (channel = 63; channel >= 32; channel--) {
+ mask = 1 << (channel&0x1f);
+ if ((chn_int & mask) == 0)
+ continue;
+ voice = &trident->synth.voices[channel];
+ if (!voice->pcm || voice->substream == NULL) {
+ outl(mask, TRID_REG(trident, T4D_STOP_B));
+ continue;
+ }
+ delta = (int)stimer - (int)voice->stimer;
+ if (delta < 0)
+ delta = -delta;
+ if ((unsigned int)delta < voice->spurious_threshold) {
+ /* do some statistics here */
+ trident->spurious_irq_count++;
+ if (trident->spurious_irq_max_delta < (unsigned int)delta)
+ trident->spurious_irq_max_delta = delta;
+ continue;
}
- if (tmp < voice->isync_mark) {
- if (tmp > 0x10)
- tmp = voice->isync_ESO - 7;
- else
- tmp = voice->isync_ESO + 2;
- /* update ESO for IRQ voice to preserve sync */
+ voice->stimer = stimer;
+ if (voice->isync) {
+ if (!voice->isync3) {
+ tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+ if (trident->bDMAStart & 0x40)
+ tmp >>= 1;
+ if (tmp > 0)
+ tmp = voice->isync_max - tmp;
+ } else {
+ tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+ }
+ if (tmp < voice->isync_mark) {
+ if (tmp > 0x10)
+ tmp = voice->isync_ESO - 7;
+ else
+ tmp = voice->isync_ESO + 2;
+ /* update ESO for IRQ voice to preserve sync */
+ snd_trident_stop_voice(trident, voice->number);
+ snd_trident_write_eso_reg(trident, voice, tmp);
+ snd_trident_start_voice(trident, voice->number);
+ }
+ } else if (voice->isync2) {
+ voice->isync2 = 0;
+ /* write original ESO and update CSO for IRQ voice to preserve sync */
snd_trident_stop_voice(trident, voice->number);
- snd_trident_write_eso_reg(trident, voice, tmp);
+ snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
+ snd_trident_write_eso_reg(trident, voice, voice->ESO);
snd_trident_start_voice(trident, voice->number);
}
- } else if (voice->isync2) {
- voice->isync2 = 0;
- /* write original ESO and update CSO for IRQ voice to preserve sync */
- snd_trident_stop_voice(trident, voice->number);
- snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
- snd_trident_write_eso_reg(trident, voice, voice->ESO);
- snd_trident_start_voice(trident, voice->number);
- }
#if 0
- if (voice->extra) {
- /* update CSO for extra voice to preserve sync */
- snd_trident_stop_voice(trident, voice->extra->number);
- snd_trident_write_cso_reg(trident, voice->extra, 0);
- snd_trident_start_voice(trident, voice->extra->number);
- }
+ if (voice->extra) {
+ /* update CSO for extra voice to preserve sync */
+ snd_trident_stop_voice(trident, voice->extra->number);
+ snd_trident_write_cso_reg(trident, voice->extra, 0);
+ snd_trident_start_voice(trident, voice->extra->number);
+ }
#endif
- spin_unlock(&trident->reg_lock);
- snd_pcm_period_elapsed(voice->substream);
- spin_lock(&trident->reg_lock);
+ spin_unlock(&trident->reg_lock);
+ snd_pcm_period_elapsed(voice->substream);
+ spin_lock(&trident->reg_lock);
+ }
+ outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */
}
- outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */
- __skip2:
- spin_unlock(&trident->reg_lock);
}
if (audio_int & MPU401_IRQ) {
if (trident->rmidi) {
@@ -3814,16 +3712,13 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
{
struct snd_trident_voice *pvoice;
- unsigned long flags;
int idx;
- spin_lock_irqsave(&trident->voice_alloc, flags);
+ guard(spinlock_irqsave)(&trident->voice_alloc);
if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
idx = snd_trident_allocate_pcm_channel(trident);
- if(idx < 0) {
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ if (idx < 0)
return NULL;
- }
pvoice = &trident->synth.voices[idx];
pvoice->use = 1;
pvoice->pcm = 1;
@@ -3831,27 +3726,22 @@ struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident,
pvoice->spdif = 0;
pvoice->memblk = NULL;
pvoice->substream = NULL;
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
return pvoice;
}
if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
idx = snd_trident_allocate_synth_channel(trident);
- if(idx < 0) {
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ if (idx < 0)
return NULL;
- }
pvoice = &trident->synth.voices[idx];
pvoice->use = 1;
pvoice->synth = 1;
pvoice->client = client;
pvoice->port = port;
pvoice->memblk = NULL;
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
return pvoice;
}
if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
}
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
return NULL;
}
@@ -3859,26 +3749,25 @@ EXPORT_SYMBOL(snd_trident_alloc_voice);
void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice)
{
- unsigned long flags;
void (*private_free)(struct snd_trident_voice *);
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;
- voice->private_free = NULL;
- voice->private_data = NULL;
- if (voice->pcm)
- snd_trident_free_pcm_channel(trident, voice->number);
- if (voice->synth)
- snd_trident_free_synth_channel(trident, voice->number);
- voice->use = voice->pcm = voice->synth = voice->midi = 0;
- voice->capture = voice->spdif = 0;
- voice->sample_ops = NULL;
- voice->substream = NULL;
- voice->extra = NULL;
- spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ scoped_guard(spinlock_irqsave, &trident->voice_alloc) {
+ private_free = voice->private_free;
+ voice->private_free = NULL;
+ voice->private_data = NULL;
+ if (voice->pcm)
+ snd_trident_free_pcm_channel(trident, voice->number);
+ if (voice->synth)
+ snd_trident_free_synth_channel(trident, voice->number);
+ voice->use = voice->pcm = voice->synth = voice->midi = 0;
+ voice->capture = voice->spdif = 0;
+ voice->sample_ops = NULL;
+ voice->substream = NULL;
+ voice->extra = NULL;
+ }
if (private_free)
private_free(voice);
}
@@ -3913,10 +3802,6 @@ static int snd_trident_suspend(struct device *dev)
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);
return 0;
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index b9ebb51893c5..81f6348191dc 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,22 +6,6 @@
*
* 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 <linux/io.h>
@@ -34,11 +19,8 @@
/* 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)
@@ -169,14 +137,14 @@ __found_pages:
/*
* check if the given pointer is valid for pages
*/
-static int is_valid_page(unsigned long ptr)
+static int is_valid_page(struct snd_trident *trident, unsigned long ptr)
{
if (ptr & ~0x3fffffffUL) {
- snd_printk(KERN_ERR "max memory size is 1GB!!\n");
+ dev_err(trident->card->dev, "max memory size is 1GB!!\n");
return 0;
}
if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) {
- snd_printk(KERN_ERR "page is not aligned\n");
+ dev_err(trident->card->dev, "page is not aligned\n");
return 0;
}
return 1;
@@ -204,28 +172,22 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,
- mutex_lock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
blk = search_empty(hdr, runtime->dma_bytes);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ if (blk == NULL)
return NULL;
- }
/* set TLB entries */
idx = 0;
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)) {
+ if (!is_valid_page(trident, 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;
}
@@ -241,7 +203,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 *
@@ -251,26 +212,21 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident,
if (snd_BUG_ON(!hdr))
return NULL;
- mutex_lock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
blk = search_empty(hdr, runtime->dma_bytes);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ if (blk == NULL)
return NULL;
- }
/* 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) {
- if (! is_valid_page(addr)) {
+ addr += SNDRV_TRIDENT_PAGE_SIZE) {
+ if (!is_valid_page(trident, 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;
}
@@ -303,12 +259,11 @@ int snd_trident_free_pages(struct snd_trident *trident,
return -EINVAL;
hdr = trident->tlb.memhdr;
- mutex_lock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
/* reset TLB entries */
for (page = firstpg(blk); page <= lastpg(blk); page++)
set_silent_tlb(trident, page);
/* free memory block */
__snd_util_mem_free(hdr, blk);
- mutex_unlock(&hdr->block_mutex);
return 0;
}
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 3a1c0b8b4ea2..2b0f9e38863e 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
- *
*/
/*
@@ -70,7 +56,6 @@
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 IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
@@ -362,13 +347,11 @@ struct via82xx {
unsigned char old_legacy;
unsigned char old_legacy_cfg;
-#ifdef CONFIG_PM_SLEEP
unsigned char legacy_saved;
unsigned char legacy_cfg_saved;
unsigned char spdif_ctrl_saved;
unsigned char capture_src_saved[2];
unsigned int mpu_port_saved;
-#endif
unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */
unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */
@@ -428,18 +411,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 +433,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.
@@ -463,7 +450,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
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) {
@@ -478,7 +465,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
"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;
@@ -526,7 +513,8 @@ 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;
}
dev_err(chip->card->dev, "codec_ready: codec %i is not ready [0x%x]\n",
@@ -554,7 +542,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)
@@ -640,7 +628,7 @@ static irqreturn_t snd_via686_interrupt(int irq, void *dev_id)
}
/* check status for each stream */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
for (i = 0; i < chip->num_devs; i++) {
struct viadev *viadev = &chip->devs[i];
unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
@@ -664,7 +652,6 @@ static irqreturn_t snd_via686_interrupt(int irq, void *dev_id)
}
outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
}
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -679,7 +666,7 @@ static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id)
int irqreturn = 0;
/* check status for each stream */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
status = inl(VIAREG(chip, SGD_SHADOW));
for (i = 0; i < chip->num_devs; i++) {
@@ -718,7 +705,6 @@ static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id)
outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
irqreturn = 1;
}
- spin_unlock(&chip->reg_lock);
return IRQ_RETVAL(irqreturn);
}
@@ -845,7 +831,7 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
return 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
/* The via686a does not have the current index register,
* so we need to calculate the index from CURR_PTR.
@@ -857,7 +843,6 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
res = calc_linear_pos(chip, viadev, idx, count);
viadev->lastpos = res; /* remember the last position */
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, res);
}
@@ -875,7 +860,7 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *subst
if (snd_BUG_ON(!viadev->tbl_entries))
return 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
status = viadev->in_interrupt;
if (!status)
@@ -916,7 +901,6 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *subst
}
unlock:
viadev->lastpos = res;
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, res);
}
@@ -931,18 +915,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));
}
/*
@@ -955,7 +931,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;
}
@@ -1018,7 +993,7 @@ static int via_lock_rate(struct via_rate_lock *rec, int rate)
{
int changed = 0;
- spin_lock_irq(&rec->lock);
+ guard(spinlock_irq)(&rec->lock);
if (rec->rate != rate) {
if (rec->rate && rec->used > 1) /* already set */
changed = -EINVAL;
@@ -1027,7 +1002,6 @@ static int via_lock_rate(struct via_rate_lock *rec, int rate)
changed = 1;
}
}
- spin_unlock_irq(&rec->lock);
return changed;
}
@@ -1043,7 +1017,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,
@@ -1187,37 +1162,38 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
/* set the hw rate condition */
ratep = &chip->rates[viadev->direction];
- spin_lock_irq(&ratep->lock);
- ratep->used++;
- if (chip->spdif_on && viadev->reg_offset == 0x30) {
- /* DXS#3 and spdif is on */
- runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF];
- snd_pcm_limit_hw_rates(runtime);
- } else if (chip->dxs_fixed && viadev->reg_offset < 0x40) {
- /* fixed DXS playback rate */
- runtime->hw.rates = SNDRV_PCM_RATE_48000;
- runtime->hw.rate_min = runtime->hw.rate_max = 48000;
- } else if (chip->dxs_src && viadev->reg_offset < 0x40) {
- /* use full SRC capabilities of DXS */
- runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS |
- 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];
- snd_pcm_limit_hw_rates(runtime);
- } else {
- /* a fixed rate */
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
- runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate;
+ scoped_guard(spinlock_irq, &ratep->lock) {
+ ratep->used++;
+ if (chip->spdif_on && viadev->reg_offset == 0x30) {
+ /* DXS#3 and spdif is on */
+ runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF];
+ snd_pcm_limit_hw_rates(runtime);
+ } else if (chip->dxs_fixed && viadev->reg_offset < 0x40) {
+ /* fixed DXS playback rate */
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ } else if (chip->dxs_src && viadev->reg_offset < 0x40) {
+ /* use full SRC capabilities of DXS */
+ runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS |
+ 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];
+ snd_pcm_limit_hw_rates(runtime);
+ } else {
+ /* a fixed rate */
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate;
+ }
}
- spin_unlock_irq(&ratep->lock);
/* 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) {
@@ -1242,7 +1218,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;
}
@@ -1258,7 +1235,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]) {
@@ -1295,7 +1273,8 @@ static int snd_via8233_multi_open(struct snd_pcm_substream *substream)
.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)
@@ -1327,11 +1306,11 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
/* release the rate lock */
ratep = &chip->rates[viadev->direction];
- spin_lock_irq(&ratep->lock);
- ratep->used--;
- if (! ratep->used)
- ratep->rate = 0;
- spin_unlock_irq(&ratep->lock);
+ scoped_guard(spinlock_irq, &ratep->lock) {
+ ratep->used--;
+ if (!ratep->used)
+ ratep->rate = 0;
+ }
if (! ratep->rate) {
if (! viadev->direction) {
snd_ac97_update_power(chip->ac97,
@@ -1369,65 +1348,55 @@ static int snd_via8233_playback_close(struct snd_pcm_substream *substream)
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 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 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 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 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,
};
@@ -1462,7 +1431,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[0] = pcm;
/* set up playbacks */
for (i = 0; i < 4; i++)
@@ -1470,9 +1439,9 @@ static int 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,
@@ -1487,16 +1456,16 @@ static int snd_via8233_pcm_new(struct via82xx *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[1] = pcm;
/* set up playback */
init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
/* 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,
@@ -1530,16 +1499,16 @@ static int snd_via8233a_pcm_new(struct via82xx *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[0] = pcm;
/* set up playback */
init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
/* 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,
@@ -1558,14 +1527,14 @@ static int snd_via8233a_pcm_new(struct via82xx *chip)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[1] = pcm;
/* 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;
}
@@ -1588,14 +1557,14 @@ static int snd_via686_pcm_new(struct via82xx *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[0] = pcm;
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;
}
@@ -1632,14 +1601,13 @@ static int snd_via8233_capture_source_put(struct snd_kcontrol *kcontrol,
unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL);
u8 val, oval;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oval = inb(port);
val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC;
if (ucontrol->value.enumerated.item[0])
val |= VIA_REG_CAPTURE_CHANNEL_MIC;
if (val != oval)
outb(val, port);
- spin_unlock_irq(&chip->reg_lock);
return val != oval;
}
@@ -1899,13 +1867,14 @@ static int snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_overrid
{
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;
@@ -1915,7 +1884,8 @@ static int snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_overrid
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);
@@ -1933,13 +1903,12 @@ static int snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_overrid
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) {
+ 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;
@@ -1949,7 +1918,6 @@ static int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legac
if (!gp) {
dev_err(chip->card->dev,
"cannot allocate memory for gameport\n");
- release_and_free_resource(r);
return -ENOMEM;
}
@@ -1957,7 +1925,6 @@ static int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legac
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;
@@ -1971,11 +1938,8 @@ static int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legac
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
@@ -2012,11 +1976,7 @@ static int snd_via8233_init_misc(struct via82xx *chip)
/* when no h/w PCM volume control is found, use DXS volume control
* as the PCM vol control
*/
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, "PCM Playback Volume");
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (! snd_ctl_find_id(chip->card, &sid)) {
+ if (!snd_ctl_find_id_mixer(chip->card, "PCM Playback Volume")) {
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));
@@ -2063,9 +2023,7 @@ static int 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_SLEEP
chip->mpu_port_saved = mpu_port;
-#endif
} else {
mpu_port = pci_resource_start(chip->pci, 2);
}
@@ -2084,9 +2042,10 @@ static int 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;
@@ -2116,10 +2075,8 @@ static int snd_via686_init_misc(struct via82xx *chip)
snd_via686_create_gameport(chip, &legacy);
-#ifdef CONFIG_PM_SLEEP
chip->legacy_saved = legacy;
chip->legacy_cfg_saved = legacy_cfg;
-#endif
return 0;
}
@@ -2142,10 +2099,8 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry,
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);
}
/*
@@ -2205,7 +2160,8 @@ 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)
+ 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);
@@ -2218,7 +2174,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;
}
@@ -2265,7 +2222,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
@@ -2276,11 +2232,8 @@ static int snd_via82xx_suspend(struct device *dev)
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 */
@@ -2321,65 +2274,37 @@ static int snd_via82xx_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
-static int snd_via82xx_free(struct via82xx *chip)
+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 snd_via82xx_create(struct snd_card *card,
struct pci_dev *pci,
int chip_type,
int revision,
- unsigned int ac97_clock,
- struct via82xx **r_via)
+ 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;
@@ -2395,42 +2320,32 @@ static int 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 = pcim_request_all_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,
- KBUILD_MODNAME, 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);
- snd_via82xx_free(chip);
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);
-
- *r_via = chip;
return 0;
}
@@ -2439,7 +2354,7 @@ struct via823x_info {
char *name;
int type;
};
-static struct via823x_info via823x_cards[] = {
+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 },
@@ -2453,7 +2368,7 @@ static struct via823x_info via823x_cards[] = {
* auto detection of DXS channel supports.
*/
-static struct snd_pci_quirk dxs_whitelist[] = {
+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),
@@ -2501,9 +2416,9 @@ 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) {
- dev_dbg(&pci->dev, "DXS white list for %s found\n",
+ dev_dbg(&pci->dev, "DXS allow list for %s found\n",
snd_pci_quirk_name(w));
return w->value;
}
@@ -2522,8 +2437,8 @@ static int check_dxs_list(struct pci_dev *pci, int revision)
return VIA_DXS_48K;
};
-static int 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;
@@ -2531,14 +2446,16 @@ static int snd_via82xx_probe(struct pci_dev *pci,
unsigned int i;
int err;
- err = snd_card_new(&pci->dev, 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) {
case TYPE_CARD_VIA686:
- strcpy(card->driver, "VIA686A");
+ strscpy(card->driver, "VIA686A");
sprintf(card->shortname, "VIA 82C686A/B rev%x", pci->revision);
chip_type = TYPE_VIA686;
break;
@@ -2548,7 +2465,7 @@ static int snd_via82xx_probe(struct pci_dev *pci,
for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) {
if (pci->revision == via823x_cards[i].revision) {
chip_type = via823x_cards[i].type;
- strcpy(card->shortname, via823x_cards[i].name);
+ strscpy(card->shortname, via823x_cards[i].name);
break;
}
}
@@ -2564,37 +2481,42 @@ static int snd_via82xx_probe(struct pci_dev *pci,
chip_type = TYPE_VIA8233;
}
if (chip_type == TYPE_VIA8233A)
- strcpy(card->driver, "VIA8233A");
+ strscpy(card->driver, "VIA8233A");
else if (pci->revision >= VIA_REV_8237)
- strcpy(card->driver, "VIA8237"); /* no slog assignment */
+ strscpy(card->driver, "VIA8237"); /* no slog assignment */
else
- strcpy(card->driver, "VIA8233");
+ strscpy(card->driver, "VIA8233");
break;
default:
dev_err(card->dev, "invalid card type %d\n", card_type);
- err = -EINVAL;
- goto __error;
+ 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)
@@ -2604,8 +2526,9 @@ static int 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 */
@@ -2618,30 +2541,25 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_via82xx_probe(pci, pci_id));
}
static struct pci_driver via82xx_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_via82xx_ids,
.probe = snd_via82xx_probe,
- .remove = snd_via82xx_remove,
.driver = {
- .pm = SND_VIA82XX_PM_OPS,
+ .pm = &snd_via82xx_pm,
},
};
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 8a69221c1b86..6ce2cd88cda6 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
- *
*/
/*
@@ -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 */
@@ -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.
@@ -316,7 +305,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
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;
@@ -333,7 +322,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
"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;
@@ -380,7 +369,8 @@ 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;
}
dev_err(chip->card->dev, "codec_ready: codec %i is not ready [0x%x]\n",
@@ -408,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);
@@ -493,7 +483,7 @@ static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id)
// _skip_sgd:
/* check status for each stream */
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
for (i = 0; i < chip->num_devs; i++) {
struct viadev *viadev = &chip->devs[i];
unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
@@ -507,7 +497,6 @@ static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id)
}
outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
}
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -626,7 +615,7 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
return 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
/* The via686a does not have the current index register,
* so we need to calculate the index from CURR_PTR.
@@ -638,7 +627,6 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) %
viadev->tbl_entries;
res = calc_linear_pos(chip, viadev, idx, count);
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, res);
}
@@ -654,9 +642,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));
@@ -679,7 +664,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;
}
@@ -753,13 +737,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;
@@ -807,26 +793,22 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
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 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,
};
@@ -858,16 +840,13 @@ static int snd_via686_pcm_new(struct via82xx_modem *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
pcm->private_data = chip;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcms[0] = pcm;
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;
}
@@ -894,13 +873,14 @@ 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;
@@ -912,7 +892,8 @@ static int 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;
@@ -935,10 +916,8 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry, struct snd_info_
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);
}
/*
@@ -995,7 +974,8 @@ 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)
+ 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);
@@ -1007,7 +987,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;
}
@@ -1025,7 +1006,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
@@ -1036,11 +1016,8 @@ static int snd_via82xx_suspend(struct device *dev)
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);
return 0;
}
@@ -1062,102 +1039,65 @@ static int snd_via82xx_resume(struct device *dev)
return 0;
}
-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 DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
-static int snd_via82xx_free(struct via82xx_modem *chip)
+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)
-{
- struct via82xx_modem *chip = device->device_data;
- return snd_via82xx_free(chip);
}
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 **r_via)
+ unsigned int ac97_clock)
{
- 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 = pcim_request_all_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,
- KBUILD_MODNAME, 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);
- snd_via82xx_free(chip);
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);
-
- *r_via = chip;
return 0;
}
-static int 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;
@@ -1165,31 +1105,34 @@ static int snd_via82xx_probe(struct pci_dev *pci,
unsigned int i;
int err;
- err = snd_card_new(&pci->dev, 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) {
case TYPE_CARD_VIA82XX_MODEM:
- strcpy(card->driver, "VIA82XX-MODEM");
+ strscpy(card->driver, "VIA82XX-MODEM");
sprintf(card->shortname, "VIA 82XX modem");
break;
default:
dev_err(card->dev, "invalid card type %d\n", card_type);
- err = -EINVAL;
- goto __error;
+ 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++)
@@ -1200,30 +1143,25 @@ static int 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 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));
+ return snd_card_free_on_error(&pci->dev, __snd_via82xx_probe(pci, pci_id));
}
static struct pci_driver via82xx_modem_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_via82xx_modem_ids,
.probe = snd_via82xx_probe,
- .remove = snd_via82xx_remove,
.driver = {
- .pm = SND_VIA82XX_PM_OPS,
+ .pm = &snd_via82xx_pm,
},
};
diff --git a/sound/pci/vx222/Makefile b/sound/pci/vx222/Makefile
index a4d08d4de354..6889137eb438 100644
--- a/sound/pci/vx222/Makefile
+++ b/sound/pci/vx222/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vx222-objs := vx222.o vx222_ops.o
+snd-vx222-y := vx222.o vx222_ops.o
obj-$(CONFIG_SND_VX222) += snd-vx222.o
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 55861849d7df..693a4e471cf7 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -1,21 +1,8 @@
+// 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>
@@ -33,7 +20,6 @@
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 */
@@ -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,75 +100,45 @@ static struct snd_vx_hardware vx222_mic_hw = {
/*
*/
-static int snd_vx222_free(struct vx_core *chip)
-{
- struct snd_vx222 *vx = to_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 snd_vx222_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_vx_hardware *hw,
+ 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 = to_vx222(chip);
vx->pci = pci;
- if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
- snd_vx222_free(chip);
+ err = pcim_request_all_regions(pci, KBUILD_MODNAME);
+ if (err < 0)
return err;
- }
for (i = 0; i < 2; i++)
vx->port[i] = pci_resource_start(pci, i + 1);
- if (request_threaded_irq(pci->irq, snd_vx_irq_handler,
- snd_vx_threaded_irq_handler, IRQF_SHARED,
- KBUILD_MODNAME, 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);
- snd_vx222_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_vx222_free(chip);
- return err;
- }
-
+ card->sync_irq = chip->irq;
*rchip = vx;
+
return 0;
}
@@ -192,7 +148,7 @@ static int snd_vx222_probe(struct pci_dev *pci,
{
static int dev;
struct snd_card *card;
- struct snd_vx_hardware *hw;
+ const struct snd_vx_hardware *hw;
struct snd_vx222 *vx;
int err;
@@ -203,8 +159,8 @@ static int snd_vx222_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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,10 +176,9 @@ static int 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];
@@ -236,27 +191,19 @@ static int snd_vx222_probe(struct pci_dev *pci,
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 snd_vx222_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
-}
-
-#ifdef CONFIG_PM_SLEEP
static int snd_vx222_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -273,19 +220,14 @@ static int snd_vx222_resume(struct device *dev)
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 DEFINE_SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume);
static struct pci_driver vx222_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vx222_ids,
.probe = snd_vx222_probe,
- .remove = snd_vx222_remove,
.driver = {
- .pm = SND_VX222_PM_OPS,
+ .pm = pm_ptr(&snd_vx222_pm),
},
};
diff --git a/sound/pci/vx222/vx222.h b/sound/pci/vx222/vx222.h
index cae355c8ed28..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
@@ -44,8 +31,8 @@ struct snd_vx222 {
/* 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 d4298af6d3ee..b6459dbdb1b0 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -1,23 +1,10 @@
+// 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>
@@ -32,7 +19,7 @@
#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,
@@ -91,7 +78,7 @@ static inline unsigned long vx2_reg_addr(struct vx_core *_chip, int 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
*/
@@ -101,7 +88,7 @@ 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
@@ -115,7 +102,7 @@ static void vx2_outb(struct vx_core *chip, int offset, unsigned char val)
}
/**
- * 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
*/
@@ -125,7 +112,7 @@ 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
@@ -226,7 +213,7 @@ static int vx2_test_xilinx(struct vx_core *_chip)
/**
- * 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
*/
@@ -275,7 +262,7 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
length >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
for (; length > 0; length--) {
- outl(cpu_to_le32(*addr), port);
+ outl(*addr, port);
addr++;
}
addr = (u32 *)runtime->dma_area;
@@ -285,7 +272,7 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
count >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
for (; count > 0; count--) {
- outl(cpu_to_le32(*addr), port);
+ outl(*addr, port);
addr++;
}
@@ -313,7 +300,7 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
length >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
for (; length > 0; length--)
- *addr++ = le32_to_cpu(inl(port));
+ *addr++ = inl(port);
addr = (u32 *)runtime->dma_area;
pipe->hw_ptr = 0;
}
@@ -321,7 +308,7 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
count >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
for (; count > 0; count--)
- *addr++ = le32_to_cpu(inl(port));
+ *addr++ = inl(port);
vx2_release_pseudo_dma(chip);
}
@@ -421,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:
@@ -879,10 +868,10 @@ static int vx_input_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
struct snd_vx222 *chip = to_vx222(_chip);
- mutex_lock(&_chip->mixer_mutex);
+
+ guard(mutex)(&_chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->input_level[0];
ucontrol->value.integer.value[1] = chip->input_level[1];
- mutex_unlock(&_chip->mixer_mutex);
return 0;
}
@@ -896,16 +885,14 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
if (ucontrol->value.integer.value[1] < 0 ||
ucontrol->value.integer.value[1] > MIC_LEVEL_MAX)
return -EINVAL;
- mutex_lock(&_chip->mixer_mutex);
+ guard(mutex)(&_chip->mixer_mutex);
if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
chip->input_level[1] != ucontrol->value.integer.value[1]) {
chip->input_level[0] = ucontrol->value.integer.value[0];
chip->input_level[1] = ucontrol->value.integer.value[1];
vx2_set_input_level(chip);
- mutex_unlock(&_chip->mixer_mutex);
return 1;
}
- mutex_unlock(&_chip->mixer_mutex);
return 0;
}
@@ -934,14 +921,12 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if (ucontrol->value.integer.value[0] < 0 ||
ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
return -EINVAL;
- mutex_lock(&_chip->mixer_mutex);
+ guard(mutex)(&_chip->mixer_mutex);
if (chip->mic_level != ucontrol->value.integer.value[0]) {
chip->mic_level = ucontrol->value.integer.value[0];
vx2_set_input_level(chip);
- mutex_unlock(&_chip->mixer_mutex);
return 1;
}
- mutex_unlock(&_chip->mixer_mutex);
return 0;
}
@@ -985,9 +970,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;
@@ -997,7 +984,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,
@@ -1017,7 +1004,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..2d7856403371 100644
--- a/sound/pci/ymfpci/Makefile
+++ b/sound/pci/ymfpci/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ymfpci-objs := ymfpci.o ymfpci_main.o
+snd-ymfpci-y := ymfpci.o ymfpci_main.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index eafdee384059..764ca59e98d1 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -1,22 +1,7 @@
+// 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>
@@ -32,12 +17,6 @@
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 */
@@ -93,7 +72,8 @@ static int 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,7 +82,8 @@ static int 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) {
@@ -117,23 +98,28 @@ static int 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:
- dev_err(chip->card->dev,
- "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"))) {
- dev_err(chip->card->dev,
- "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) {
dev_err(chip->card->dev,
"cannot allocate memory for gameport\n");
- release_and_free_resource(r);
return -ENOMEM;
}
@@ -142,7 +128,6 @@ static int 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);
@@ -158,12 +143,8 @@ static int 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
@@ -171,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 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;
@@ -191,10 +172,11 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, 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;
@@ -206,6 +188,13 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
default: model = str = "???"; break;
}
+ strscpy(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 */
@@ -214,8 +203,10 @@ static int 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]);
}
@@ -223,8 +214,10 @@ static int 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]);
}
@@ -234,10 +227,18 @@ static int 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;
@@ -248,10 +249,18 @@ static int 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;
@@ -265,54 +274,42 @@ static int 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) {
- release_and_free_resource(mpu_res);
- release_and_free_resource(fm_res);
- goto free_card;
- }
- chip->fm_res = fm_res;
- chip->mpu_res = mpu_res;
- card->private_data = chip;
+ err = snd_ymfpci_create(card, pci, old_legacy_ctrl);
+ if (err < 0)
+ return err;
- 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);
err = snd_ymfpci_pcm(chip, 0);
if (err < 0)
- goto free_card;
+ return err;
err = snd_ymfpci_pcm_spdif(chip, 1);
if (err < 0)
- goto free_card;
+ return err;
err = snd_ymfpci_mixer(chip, rear_switch[dev]);
if (err < 0)
- goto free_card;
+ return err;
if (chip->ac97->ext_id & AC97_EI_SDAC) {
err = snd_ymfpci_pcm_4ch(chip, 2);
if (err < 0)
- goto free_card;
+ return err;
err = snd_ymfpci_pcm2(chip, 3);
if (err < 0)
- goto free_card;
+ return err;
}
err = snd_ymfpci_timer(chip, 0);
if (err < 0)
- goto free_card;
-
- if (chip->mpu_res) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
- mpu_port[dev],
- MPU401_INFO_INTEGRATED |
- MPU401_INFO_IRQ_HOOK,
- -1, &chip->rawmidi)) < 0) {
+ return err;
+
+ 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]);
@@ -320,19 +317,23 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
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) {
+ 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) {
- dev_err(card->dev, "cannot create opl3 hwdep\n");
- goto free_card;
+ } else {
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0) {
+ dev_err(card->dev, "cannot create opl3 hwdep\n");
+ return err;
+ }
}
}
@@ -340,32 +341,26 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
err = snd_card_register(card);
if (err < 0)
- goto free_card;
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
-free_card:
- snd_card_free(card);
- return err;
}
-static void snd_card_ymfpci_remove(struct pci_dev *pci)
+static int snd_card_ymfpci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
+ return snd_card_free_on_error(&pci->dev, __snd_card_ymfpci_probe(pci, pci_id));
}
static struct pci_driver ymfpci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ymfpci_ids,
.probe = snd_card_ymfpci_probe,
- .remove = snd_card_ymfpci_remove,
-#ifdef CONFIG_PM_SLEEP
.driver = {
- .pm = &snd_ymfpci_pm,
+ .pm = pm_sleep_ptr(&snd_ymfpci_pm),
},
-#endif
};
module_pci_driver(ymfpci_driver);
diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h
index aa9bb065f385..a408785cfa1b 100644
--- a/sound/pci/ymfpci/ymfpci.h
+++ b/sound/pci/ymfpci/ymfpci.h
@@ -1,25 +1,10 @@
+/* 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
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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/pcm.h>
@@ -185,50 +170,50 @@
*/
struct snd_ymfpci_playback_bank {
- u32 format;
- u32 loop_default;
- u32 base; /* 32-bit address */
- u32 loop_start; /* 32-bit offset */
- u32 loop_end; /* 32-bit offset */
- u32 loop_frac; /* 8-bit fraction - loop_start */
- u32 delta_end; /* pitch delta end */
- u32 lpfK_end;
- u32 eg_gain_end;
- u32 left_gain_end;
- u32 right_gain_end;
- u32 eff1_gain_end;
- u32 eff2_gain_end;
- u32 eff3_gain_end;
- u32 lpfQ;
- u32 status;
- u32 num_of_frames;
- u32 loop_count;
- u32 start;
- u32 start_frac;
- u32 delta;
- u32 lpfK;
- u32 eg_gain;
- u32 left_gain;
- u32 right_gain;
- u32 eff1_gain;
- u32 eff2_gain;
- u32 eff3_gain;
- u32 lpfD1;
- u32 lpfD2;
+ __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 {
- u32 base; /* 32-bit address */
- u32 loop_end; /* 32-bit offset */
- u32 start; /* 32-bit offset */
- u32 num_of_loops; /* counter */
+ __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 {
- u32 base; /* 32-bit address */
- u32 loop_end; /* 32-bit offset */
- u32 start; /* 32-bit offset */
- u32 temp;
+ __le32 base; /* 32-bit address */
+ __le32 loop_end; /* 32-bit offset */
+ __le32 start; /* 32-bit offset */
+ __le32 temp;
};
struct snd_ymfpci_pcm;
@@ -283,6 +268,49 @@ struct snd_ymfpci_pcm {
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;
@@ -290,16 +318,13 @@ struct snd_ymfpci {
unsigned char rev; /* PCI revision */
unsigned long reg_area_phys;
void __iomem *reg_area_virt;
- struct resource *res_reg_area;
- struct resource *fm_res;
- struct resource *mpu_res;
- unsigned short old_legacy_ctrl;
+ u16 old_legacy_ctrl;
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
- struct snd_dma_buffer work_ptr;
+ struct snd_dma_buffer *work_ptr;
unsigned int bank_size_playback;
unsigned int bank_size_capture;
@@ -316,7 +341,7 @@ struct snd_ymfpci {
dma_addr_t work_base_addr;
struct snd_dma_buffer ac3_tmp_base;
- u32 *ctrl_playback;
+ __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];
@@ -363,18 +388,14 @@ struct snd_ymfpci {
const struct firmware *dsp_microcode;
const struct firmware *controller_microcode;
-#ifdef CONFIG_PM_SLEEP
- u32 *saved_regs;
+ u32 saved_regs[YDSXGR_NUM_SAVED_REGS];
u32 saved_ydsxgr_mode;
- u16 saved_dsxg_legacy;
- u16 saved_dsxg_elegacy;
-#endif
+ u16 saved_dsxg_pci_regs[DSXG_PCI_NUM_SAVED_REGS];
};
int snd_ymfpci_create(struct snd_card *card,
struct pci_dev *pci,
- unsigned short old_legacy_ctrl,
- struct snd_ymfpci ** rcodec);
+ u16 old_legacy_ctrl);
void snd_ymfpci_free_gameport(struct snd_ymfpci *chip);
extern const struct dev_pm_ops snd_ymfpci_pm;
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 8ca2e41e5827..eb373d9395e3 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>
@@ -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);
@@ -136,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
};
@@ -159,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
};
@@ -178,26 +159,21 @@ static u32 snd_ymfpci_calc_lpfQ(u32 rate)
static void snd_ymfpci_hw_start(struct snd_ymfpci *chip)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->start_count++ > 0)
- goto __end;
+ return;
snd_ymfpci_writel(chip, YDSXGR_MODE,
snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
- __end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip)
{
- unsigned long flags;
long timeout = 1000;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (--chip->start_count > 0)
- goto __end;
+ return;
snd_ymfpci_writel(chip, YDSXGR_MODE,
snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);
while (timeout-- > 0) {
@@ -208,8 +184,6 @@ static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip)
atomic_set(&chip->interrupt_sleep_count, 0);
wake_up(&chip->interrupt_sleep);
}
- __end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/*
@@ -258,7 +232,6 @@ static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
enum snd_ymfpci_voice_type type, int pair,
struct snd_ymfpci_voice **rvoice)
{
- unsigned long flags;
int result;
if (snd_BUG_ON(!rvoice))
@@ -266,7 +239,7 @@ static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
if (snd_BUG_ON(pair && type != YMFPCI_PCM))
return -EINVAL;
- spin_lock_irqsave(&chip->voice_lock, flags);
+ guard(spinlock_irqsave)(&chip->voice_lock);
for (;;) {
result = voice_alloc(chip, type, pair, rvoice);
if (result == 0 || type != YMFPCI_PCM)
@@ -274,18 +247,15 @@ static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
/* TODO: synth/midi voice deallocation */
break;
}
- spin_unlock_irqrestore(&chip->voice_lock, flags);
return result;
}
static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voice *pvoice)
{
- unsigned long flags;
-
if (snd_BUG_ON(!pvoice))
return -EINVAL;
snd_ymfpci_hw_stop(chip);
- spin_lock_irqsave(&chip->voice_lock, flags);
+ guard(spinlock_irqsave)(&chip->voice_lock);
if (pvoice->number == chip->src441_used) {
chip->src441_used = -1;
pvoice->ypcm->use_441_slot = 0;
@@ -293,7 +263,6 @@ static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voic
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
pvoice->ypcm = NULL;
pvoice->interrupt = NULL;
- spin_unlock_irqrestore(&chip->voice_lock, flags);
return 0;
}
@@ -306,11 +275,12 @@ 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;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (ypcm->running) {
pos = le32_to_cpu(voice->bank[chip->active_bank].start);
if (pos < ypcm->last_pos)
@@ -336,7 +306,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);
@@ -352,7 +322,6 @@ static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_
ypcm->update_pcm_vol--;
}
}
- spin_unlock(&chip->reg_lock);
}
static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream)
@@ -362,7 +331,7 @@ static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream
struct snd_ymfpci *chip = ypcm->chip;
u32 pos, delta;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (ypcm->running) {
pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
if (pos < ypcm->last_pos)
@@ -384,7 +353,6 @@ static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream
spin_lock(&chip->reg_lock);
}
}
- spin_unlock(&chip->reg_lock);
}
static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
@@ -395,11 +363,9 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
struct snd_kcontrol *kctl = NULL;
int result = 0;
- spin_lock(&chip->reg_lock);
- if (ypcm->voices[0] == NULL) {
- result = -EINVAL;
- goto __unlock;
- }
+ guard(spinlock)(&chip->reg_lock);
+ if (ypcm->voices[0] == NULL)
+ return -EINVAL;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -414,7 +380,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;
@@ -423,11 +389,8 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
ypcm->running = 0;
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- __unlock:
- spin_unlock(&chip->reg_lock);
if (kctl)
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
return result;
@@ -440,7 +403,7 @@ static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
int result = 0;
u32 tmp;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -460,7 +423,6 @@ static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
break;
}
- spin_unlock(&chip->reg_lock);
return result;
}
@@ -505,9 +467,8 @@ 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;
if (snd_BUG_ON(!voice))
return;
@@ -527,26 +488,26 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
vol_left = cpu_to_le32(0x40000000);
vol_right = cpu_to_le32(0x40000000);
}
- spin_lock_irqsave(&ypcm->chip->voice_lock, flags);
- format = runtime->channels == 2 ? 0x00010000 : 0;
- if (snd_pcm_format_width(runtime->format) == 8)
- format |= 0x80000000;
- else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
- runtime->rate == 44100 && runtime->channels == 2 &&
- voiceidx == 0 && (ypcm->chip->src441_used == -1 ||
- ypcm->chip->src441_used == voice->number)) {
- ypcm->chip->src441_used = voice->number;
- ypcm->use_441_slot = 1;
- format |= 0x10000000;
- }
- if (ypcm->chip->src441_used == voice->number &&
- (format & 0x10000000) == 0) {
- ypcm->chip->src441_used = -1;
- ypcm->use_441_slot = 0;
+ scoped_guard(spinlock_irqsave, &ypcm->chip->voice_lock) {
+ format = runtime->channels == 2 ? 0x00010000 : 0;
+ if (snd_pcm_format_width(runtime->format) == 8)
+ format |= 0x80000000;
+ else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
+ runtime->rate == 44100 && runtime->channels == 2 &&
+ voiceidx == 0 && (ypcm->chip->src441_used == -1 ||
+ ypcm->chip->src441_used == voice->number)) {
+ ypcm->chip->src441_used = voice->number;
+ ypcm->use_441_slot = 1;
+ format |= 0x10000000;
+ }
+ if (ypcm->chip->src441_used == voice->number &&
+ (format & 0x10000000) == 0) {
+ ypcm->chip->src441_used = -1;
+ ypcm->use_441_slot = 0;
+ }
+ if (runtime->channels == 2 && (voiceidx & 1) != 0)
+ format |= 1;
}
- if (runtime->channels == 2 && (voiceidx & 1) != 0)
- format |= 1;
- spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags);
for (nbank = 0; nbank < 2; nbank++) {
bank = &voice->bank[nbank];
memset(bank, 0, sizeof(*bank));
@@ -601,7 +562,7 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
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;
@@ -614,19 +575,18 @@ static int snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
chip->bank_effect[4][0]->loop_end =
chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int snd_ymfpci_ac3_done(struct snd_ymfpci *chip)
{
- spin_lock_irq(&chip->reg_lock);
- snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
- snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+ snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
+ }
// snd_ymfpci_irq_wait(chip);
if (chip->ac3_tmp_base.area) {
snd_dma_free_pages(&chip->ac3_tmp_base);
@@ -642,9 +602,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;
}
@@ -661,7 +620,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;
@@ -697,19 +655,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)
@@ -804,28 +756,28 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
status = snd_ymfpci_readl(chip, YDSXGR_STATUS);
if (status & 0x80000000) {
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
- spin_lock(&chip->voice_lock);
- for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
- voice = &chip->voices[nvoice];
- if (voice->interrupt)
- voice->interrupt(chip, voice);
- }
- for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
- if (chip->capture_substream[nvoice])
- snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
- }
+ scoped_guard(spinlock, &chip->voice_lock) {
+ for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
+ voice = &chip->voices[nvoice];
+ if (voice->interrupt)
+ voice->interrupt(chip, voice);
+ }
+ for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
+ if (chip->capture_substream[nvoice])
+ snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
+ }
#if 0
- for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
- if (chip->effect_substream[nvoice])
- snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
- }
+ for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
+ if (chip->effect_substream[nvoice])
+ snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
+ }
#endif
- spin_unlock(&chip->voice_lock);
- spin_lock(&chip->reg_lock);
- snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
- mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
- snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
- spin_unlock(&chip->reg_lock);
+ }
+ scoped_guard(spinlock, &chip->reg_lock) {
+ snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
+ mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
+ snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
+ }
if (atomic_read(&chip->interrupt_sleep_count)) {
atomic_set(&chip->interrupt_sleep_count, 0);
@@ -955,18 +907,18 @@ 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;
ypcm->output_rear = chip->mode_dup4ch ? 1 : 0;
ypcm->swap_rear = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
if (ypcm->output_rear) {
ymfpci_open_extension(chip);
chip->rear_opened++;
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -977,20 +929,21 @@ 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;
ypcm->output_rear = 1;
ypcm->swap_rear = 1;
- spin_lock_irq(&chip->reg_lock);
- snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
- snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
- ymfpci_open_extension(chip);
- chip->spdif_pcm_bits = chip->spdif_bits;
- snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
- chip->spdif_opened++;
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+ snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
+ ymfpci_open_extension(chip);
+ chip->spdif_pcm_bits = chip->spdif_bits;
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+ chip->spdif_opened++;
+ }
chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
@@ -1005,16 +958,16 @@ 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;
ypcm->output_rear = 1;
ypcm->swap_rear = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
ymfpci_open_extension(chip);
chip->rear_opened++;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1071,12 +1024,12 @@ static int snd_ymfpci_playback_close(struct snd_pcm_substream *substream)
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
- spin_lock_irq(&chip->reg_lock);
- if (ypcm->output_rear && chip->rear_opened > 0) {
- chip->rear_opened--;
- ymfpci_close_extension(chip);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ if (ypcm->output_rear && chip->rear_opened > 0) {
+ chip->rear_opened--;
+ ymfpci_close_extension(chip);
+ }
}
- spin_unlock_irq(&chip->reg_lock);
return snd_ymfpci_playback_close_1(substream);
}
@@ -1084,13 +1037,13 @@ static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream)
{
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
- chip->spdif_opened = 0;
- ymfpci_close_extension(chip);
- snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
- snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
- snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->spdif_opened = 0;
+ ymfpci_close_extension(chip);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+ snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+ }
chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
@@ -1101,12 +1054,12 @@ static int snd_ymfpci_playback_4ch_close(struct snd_pcm_substream *substream)
{
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
- if (chip->rear_opened > 0) {
- chip->rear_opened--;
- ymfpci_close_extension(chip);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ if (chip->rear_opened > 0) {
+ chip->rear_opened--;
+ ymfpci_close_extension(chip);
+ }
}
- spin_unlock_irq(&chip->reg_lock);
return snd_ymfpci_playback_close_1(substream);
}
@@ -1126,7 +1079,6 @@ static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
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,
@@ -1137,8 +1089,6 @@ static const struct snd_pcm_ops snd_ymfpci_playback_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,
@@ -1150,7 +1100,8 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1159,11 +1110,11 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "YMFPCI");
+ strscpy(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);
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_std_chmaps, 2, 0, NULL);
@@ -1172,8 +1123,6 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
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,
@@ -1185,7 +1134,8 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1197,8 +1147,8 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
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);
return 0;
}
@@ -1206,7 +1156,6 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
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,
@@ -1219,7 +1168,8 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1227,11 +1177,11 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "YMFPCI - IEC958");
+ strscpy(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);
return 0;
}
@@ -1239,7 +1189,6 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
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,
@@ -1260,7 +1209,8 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device)
struct snd_pcm *pcm;
int err;
- 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;
@@ -1268,11 +1218,11 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device)
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "YMFPCI - Rear PCM");
+ strscpy(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);
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
surround_map, 2, 0, NULL);
@@ -1290,11 +1240,10 @@ static int snd_ymfpci_spdif_default_get(struct snd_kcontrol *kcontrol,
{
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff;
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1307,12 +1256,11 @@ static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
(ucontrol->value.iec958.status[1] << 8);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
change = chip->spdif_bits != val;
chip->spdif_bits = val;
if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL)
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1337,10 +1285,9 @@ static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
{
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
ucontrol->value.iec958.status[0] = 0x3e;
ucontrol->value.iec958.status[1] = 0xff;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1365,11 +1312,10 @@ static int snd_ymfpci_spdif_stream_get(struct snd_kcontrol *kcontrol,
{
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff;
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1382,12 +1328,11 @@ static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
(ucontrol->value.iec958.status[1] << 8);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
change = chip->spdif_pcm_bits != val;
chip->spdif_pcm_bits = val;
if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2))
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1413,9 +1358,8 @@ static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
u16 reg;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
- spin_unlock_irq(&chip->reg_lock);
if (!(reg & 0x100))
value->value.enumerated.item[0] = 0;
else
@@ -1428,14 +1372,13 @@ static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
u16 reg, old_reg;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
if (value->value.enumerated.item[0] == 0)
reg = old_reg & ~0x100;
else
reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9);
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg);
- spin_unlock_irq(&chip->reg_lock);
return reg != old_reg;
}
@@ -1495,12 +1438,11 @@ static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
}
val = (ucontrol->value.integer.value[0] & mask);
val <<= shift;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oval = snd_ymfpci_readl(chip, reg);
val = (oval & ~(mask << shift)) | val;
change = val != oval;
snd_ymfpci_writel(chip, reg, val);
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1536,9 +1478,8 @@ static int snd_ymfpci_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (reg < 0x80 || reg >= 0xc0)
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
val = snd_ymfpci_readl(chip, reg);
- spin_unlock_irq(&chip->reg_lock);
ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
return 0;
@@ -1558,12 +1499,11 @@ static int snd_ymfpci_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val2 = ucontrol->value.integer.value[1] & mask;
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oval = snd_ymfpci_readl(chip, reg);
val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
change = val1 != oval;
snd_ymfpci_writel(chip, reg, val1);
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1578,12 +1518,11 @@ static int snd_ymfpci_put_nativedacvol(struct snd_kcontrol *kcontrol,
value = ucontrol->value.integer.value[0] & 0x3fff;
value |= (ucontrol->value.integer.value[1] & 0x3fff) << 16;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
oval = snd_ymfpci_readl(chip, reg);
change = value != oval;
snd_ymfpci_writel(chip, reg, value);
snd_ymfpci_writel(chip, reg2, value);
- spin_unlock_irq(&chip->reg_lock);
return change;
}
@@ -1618,7 +1557,7 @@ static const struct snd_kcontrol_new snd_ymfpci_dup4ch = {
.put = snd_ymfpci_put_dup4ch,
};
-static struct snd_kcontrol_new snd_ymfpci_controls[] = {
+static const struct snd_kcontrol_new snd_ymfpci_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Wave Playback Volume",
@@ -1655,9 +1594,8 @@ YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4)
static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin)
{
u16 reg, mode;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
reg &= ~(1 << (pin + 8));
reg |= (1 << pin);
@@ -1668,23 +1606,20 @@ static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin)
snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode);
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return (mode >> pin) & 1;
}
static int snd_ymfpci_set_gpio_out(struct snd_ymfpci *chip, int pin, int enable)
{
u16 reg;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
reg &= ~(1 << pin);
reg &= ~(1 << (pin + 8));
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin);
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1752,7 +1687,6 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
unsigned int subs = kcontrol->id.subdevice;
struct snd_pcm_substream *substream;
- unsigned long flags;
if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left ||
ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
@@ -1764,13 +1698,12 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
chip->pcm_mixer[subs].right = 0x8000;
substream = (struct snd_pcm_substream *)kcontrol->private_value;
- spin_lock_irqsave(&chip->voice_lock, flags);
+ guard(spinlock_irqsave)(&chip->voice_lock);
if (substream->runtime && substream->runtime->private_data) {
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
if (!ypcm->use_441_slot)
ypcm->update_pcm_vol = 2;
}
- spin_unlock_irqrestore(&chip->voice_lock, flags);
return 1;
}
return 0;
@@ -1810,12 +1743,13 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
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 */
@@ -1823,7 +1757,8 @@ int 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 */
@@ -1831,7 +1766,8 @@ int 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) {
@@ -1844,27 +1780,37 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
/* 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)
- return err;
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip);
kctl->id.device = chip->pcm_spdif->device;
- if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip);
kctl->id.device = chip->pcm_spdif->device;
- if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip);
kctl->id.device = chip->pcm_spdif->device;
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
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;
}
@@ -1877,7 +1823,8 @@ int 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;
@@ -1896,11 +1843,10 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
static int snd_ymfpci_timer_start(struct snd_timer *timer)
{
struct snd_ymfpci *chip;
- unsigned long flags;
unsigned int count;
chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (timer->sticks > 1) {
chip->timer_ticks = timer->sticks;
count = timer->sticks - 1;
@@ -1914,19 +1860,16 @@ static int snd_ymfpci_timer_start(struct snd_timer *timer)
}
snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_ymfpci_timer_stop(struct snd_timer *timer)
{
struct snd_ymfpci *chip;
- unsigned long flags;
chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x00);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1938,7 +1881,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,
@@ -1958,8 +1901,9 @@ int 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) {
- strcpy(timer->name, "YMFPCI timer");
+ err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "YMFPCI timer");
timer->private_data = chip;
timer->hw = snd_ymfpci_timer_hw;
}
@@ -1985,11 +1929,7 @@ static void snd_ymfpci_proc_read(struct snd_info_entry *entry,
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);
}
/*
@@ -2126,16 +2066,17 @@ static int 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);
@@ -2174,8 +2115,8 @@ static int 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);
@@ -2206,116 +2147,55 @@ static int 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, PCI_D3hot);
-#endif
-
-#ifdef CONFIG_PM_SLEEP
- kfree(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);
- 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_SLEEP
-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)
-
static int snd_ymfpci_suspend(struct device *dev)
{
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);
- pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY,
- &chip->saved_dsxg_legacy);
- pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY,
- &chip->saved_dsxg_elegacy);
+
+ 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);
@@ -2327,7 +2207,10 @@ static int snd_ymfpci_resume(struct device *dev)
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;
+ 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);
@@ -2339,47 +2222,34 @@ static int snd_ymfpci_resume(struct device *dev)
snd_ac97_resume(chip->ac97);
- pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY,
- chip->saved_dsxg_legacy);
- pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY,
- chip->saved_dsxg_elegacy);
+ 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);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode);
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT);
- spin_unlock_irq(&chip->reg_lock);
}
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
-#endif /* CONFIG_PM_SLEEP */
+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,
- unsigned short old_legacy_ctrl,
- struct snd_ymfpci **rchip)
+ 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);
@@ -2390,69 +2260,52 @@ int snd_ymfpci_create(struct snd_card *card,
chip->irq = -1;
chip->device_id = pci->device;
chip->rev = pci->revision;
- chip->reg_area_phys = pci_resource_start(pci, 0);
- chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
- pci_set_master(pci);
- chip->src441_used = -1;
- if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
+ err = pcim_request_all_regions(pci, "YMFPCI");
+ if (err < 0)
+ return err;
+
+ chip->reg_area_phys = pci_resource_start(pci, 0);
+ 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);
- err = -EBUSY;
- goto free_chip;
+ return -EBUSY;
}
- if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
+ pci_set_master(pci);
+ chip->src441_used = -1;
+
+ 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);
- err = -EBUSY;
- goto free_chip;
+ 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) {
- err = -EIO;
- goto free_chip;
- }
+ if (snd_ymfpci_codec_ready(chip, 0) < 0)
+ return -EIO;
err = snd_ymfpci_request_firmware(chip);
if (err < 0) {
dev_err(chip->card->dev, "firmware request failed: %d\n", err);
- goto free_chip;
+ return err;
}
snd_ymfpci_download_image(chip);
udelay(100); /* seems we need a delay after downloading image.. */
- if (snd_ymfpci_memalloc(chip) < 0) {
- err = -EIO;
- goto free_chip;
- }
+ if (snd_ymfpci_memalloc(chip) < 0)
+ return -EIO;
err = snd_ymfpci_ac3_init(chip);
if (err < 0)
- goto free_chip;
-
-#ifdef CONFIG_PM_SLEEP
- chip->saved_regs = kmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32),
- GFP_KERNEL);
- if (chip->saved_regs == NULL) {
- err = -ENOMEM;
- goto free_chip;
- }
-#endif
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto free_chip;
+ return err;
snd_ymfpci_proc_init(card, chip);
- *rchip = chip;
return 0;
-
-free_chip:
- snd_ymfpci_free(chip);
- return err;
}
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index 7fbb190adf6d..2e3dfc1ff540 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -1,8 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PCMCIA drivers
menuconfig SND_PCMCIA
bool "PCMCIA sound devices"
depends on PCMCIA
+ depends on HAS_IOPORT
default y
help
Support for sound devices connected via the PCMCIA bus.
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..34a288c1eebd 100644
--- a/sound/pcmcia/pdaudiocf/Makefile
+++ b/sound/pcmcia/pdaudiocf/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2004 by Jaroslav Kysela <perex@perex.cz>
#
-snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o
+snd-pdaudiocf-y := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o
obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index 07f4b33db3af..13419837dfb7 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.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>
@@ -35,7 +22,6 @@
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 */
@@ -95,18 +81,17 @@ 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,
};
- snd_printdd(KERN_DEBUG "pdacf_attach called\n");
/* find an empty slot from the card list */
for (i = 0; i < SNDRV_CARDS; i++) {
if (! card_list[i])
break;
}
if (i >= SNDRV_CARDS) {
- snd_printk(KERN_ERR "pdacf: too many cards found\n");
+ dev_err(&link->dev, "pdacf: too many cards found\n");
return -EINVAL;
}
if (! enable[i])
@@ -116,7 +101,7 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
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");
+ dev_err(&link->dev, "pdacf: cannot create a card instance\n");
return err;
}
@@ -152,6 +137,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
*
@@ -165,7 +151,7 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq
int err;
struct snd_card *card = pdacf->card;
- snd_printdd(KERN_DEBUG "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq);
+ dev_dbg(card->dev, "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq);
pdacf->port = port;
pdacf->irq = irq;
pdacf->chip_status |= PDAUDIOCF_STAT_IS_CONFIGURED;
@@ -174,7 +160,7 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq
if (err < 0)
return err;
- strcpy(card->driver, "PDAudio-CF");
+ strscpy(card->driver, "PDAudio-CF");
sprintf(card->shortname, "Core Sound %s", card->driver);
sprintf(card->longname, "%s at 0x%x, irq %i",
card->shortname, port, irq);
@@ -183,7 +169,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;
@@ -197,8 +184,6 @@ static void snd_pdacf_detach(struct pcmcia_device *link)
{
struct snd_pdacf *chip = link->priv;
- snd_printdd(KERN_DEBUG "pdacf_detach called\n");
-
if (chip->chip_status & PDAUDIOCF_STAT_IS_CONFIGURED)
snd_pdacf_powerdown(chip);
chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */
@@ -215,7 +200,6 @@ static int pdacf_config(struct pcmcia_device *link)
struct snd_pdacf *pdacf = link->priv;
int ret;
- snd_printdd(KERN_DEBUG "pdacf_config called\n");
link->config_index = 0x5;
link->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ;
@@ -237,6 +221,7 @@ static int pdacf_config(struct pcmcia_device *link)
link->irq) < 0)
goto failed;
+ pdacf->card->sync_irq = link->irq;
return 0;
failed:
@@ -252,11 +237,8 @@ static int pdacf_suspend(struct pcmcia_device *link)
{
struct snd_pdacf *chip = link->priv;
- snd_printdd(KERN_DEBUG "SUSPEND\n");
- if (chip) {
- snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n");
+ if (chip)
snd_pdacf_suspend(chip);
- }
return 0;
}
@@ -265,14 +247,10 @@ static int pdacf_resume(struct pcmcia_device *link)
{
struct snd_pdacf *chip = link->priv;
- snd_printdd(KERN_DEBUG "RESUME\n");
if (pcmcia_dev_present(link)) {
- if (chip) {
- snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n");
+ if (chip)
snd_pdacf_resume(chip);
- }
}
- snd_printdd(KERN_DEBUG "resume done!\n");
return 0;
}
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h
index e9a7d3a784f7..12a29a2cb823 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.h
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h
@@ -1,21 +1,8 @@
+/* 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
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
index d724ab0653cf..a104baac3a94 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>
@@ -41,7 +28,7 @@ static unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg)
udelay(5);
if (--timeout == 0) {
spin_unlock_irqrestore(&chip->ak4117_lock, flags);
- snd_printk(KERN_ERR "AK4117 ready timeout (read)\n");
+ dev_err(chip->card->dev, "AK4117 ready timeout (read)\n");
return 0;
}
}
@@ -51,7 +38,7 @@ static unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg)
udelay(5);
if (--timeout == 0) {
spin_unlock_irqrestore(&chip->ak4117_lock, flags);
- snd_printk(KERN_ERR "AK4117 read timeout (read2)\n");
+ dev_err(chip->card->dev, "AK4117 read timeout (read2)\n");
return 0;
}
}
@@ -72,7 +59,7 @@ static void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned c
udelay(5);
if (--timeout == 0) {
spin_unlock_irqrestore(&chip->ak4117_lock, flags);
- snd_printk(KERN_ERR "AK4117 ready timeout (write)\n");
+ dev_err(chip->card->dev, "AK4117 ready timeout (write)\n");
return;
}
}
@@ -83,21 +70,21 @@ static void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned c
#if 0
void pdacf_dump(struct snd_pdacf *chip)
{
- printk(KERN_DEBUG "PDAUDIOCF DUMP (0x%lx):\n", chip->port);
- printk(KERN_DEBUG "WPD : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_WDP));
- printk(KERN_DEBUG "RDP : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_RDP));
- printk(KERN_DEBUG "TCR : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_TCR));
- printk(KERN_DEBUG "SCR : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_SCR));
- printk(KERN_DEBUG "ISR : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_ISR));
- printk(KERN_DEBUG "IER : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_IER));
- printk(KERN_DEBUG "AK_IFR : 0x%x\n",
- inw(chip->port + PDAUDIOCF_REG_AK_IFR));
+ dev_dbg(chip->card->dev, "PDAUDIOCF DUMP (0x%lx):\n", chip->port);
+ dev_dbg(chip->card->dev, "WPD : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_WDP));
+ dev_dbg(chip->card->dev, "RDP : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_RDP));
+ dev_dbg(chip->card->dev, "TCR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_TCR));
+ dev_dbg(chip->card->dev, "SCR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_SCR));
+ dev_dbg(chip->card->dev, "ISR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_ISR));
+ dev_dbg(chip->card->dev, "IER : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_IER));
+ dev_dbg(chip->card->dev, "AK_IFR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_AK_IFR));
}
#endif
@@ -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)
@@ -177,14 +161,13 @@ static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, uns
if (!(c0 & AK4117_UNLCK))
return;
- mutex_lock(&chip->reg_lock);
+ guard(mutex)(&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);
- mutex_unlock(&chip->reg_lock);
}
int snd_pdacf_ak4117_create(struct snd_pdacf *chip)
@@ -195,7 +178,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 */
@@ -265,7 +248,6 @@ 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 ecf0fbd91794..af40a2c8789a 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>
@@ -40,7 +27,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
stat = inw(chip->port + PDAUDIOCF_REG_ISR);
if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) {
if (stat & PDAUDIOCF_IRQOVR) /* should never happen */
- snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
+ dev_err(chip->card->dev, "PDAUDIOCF SRAM buffer overrun detected!\n");
if (chip->pcm_substream)
wake_thread = true;
if (!(stat & PDAUDIOCF_IRQAKM))
@@ -270,7 +257,6 @@ irqreturn_t pdacf_threaded_irq(int irq, void *dev)
rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
- /* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */
size = wdp - rdp;
if (size < 0)
size += 0x10000;
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
index 4a2354b5ae00..228822996ef7 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,44 +64,25 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
default:
return -EINVAL;
}
- mutex_lock(&chip->reg_lock);
- chip->pcm_running += inc;
- tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
- if (chip->pcm_running) {
- if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) {
- chip->pcm_running -= inc;
- ret = -EIO;
- goto __end;
+ scoped_guard(mutex, &chip->reg_lock) {
+ chip->pcm_running += inc;
+ tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+ if (chip->pcm_running) {
+ if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) {
+ chip->pcm_running -= inc;
+ ret = -EIO;
+ break;
+ }
}
+ tmp &= ~mask;
+ tmp |= val;
+ pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
}
- tmp &= ~mask;
- tmp |= val;
- pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
- __end:
- 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 +131,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;
@@ -269,14 +237,9 @@ static snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *sub
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,11 +256,13 @@ 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);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile
index 2bb42ea12f3a..abd187544946 100644
--- a/sound/pcmcia/vx/Makefile
+++ b/sound/pcmcia/vx/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vxpocket-objs := vxpocket.o vxp_ops.o vxp_mixer.o
+snd-vxpocket-y := vxpocket.o vxp_ops.o vxp_mixer.o
obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
index 304b153005a5..998cea2d0318 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>
@@ -56,14 +43,12 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if (val > MIC_LEVEL_MAX)
return -EINVAL;
- mutex_lock(&_chip->mixer_mutex);
+ guard(mutex)(&_chip->mixer_mutex);
if (chip->mic_level != ucontrol->value.integer.value[0]) {
vx_set_mic_level(_chip, ucontrol->value.integer.value[0]);
chip->mic_level = ucontrol->value.integer.value[0];
- mutex_unlock(&_chip->mixer_mutex);
return 1;
}
- mutex_unlock(&_chip->mixer_mutex);
return 0;
}
@@ -98,14 +83,13 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
struct snd_vxpocket *chip = to_vxpocket(_chip);
int val = !!ucontrol->value.integer.value[0];
- mutex_lock(&_chip->mixer_mutex);
+
+ guard(mutex)(&_chip->mixer_mutex);
if (chip->mic_level != val) {
vx_set_mic_boost(_chip, val);
chip->mic_level = val;
- mutex_unlock(&_chip->mixer_mutex);
return 1;
}
- mutex_unlock(&_chip->mixer_mutex);
return 0;
}
@@ -137,11 +121,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 8cde40226355..4211e7239138 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -1,23 +1,10 @@
+// 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>
@@ -28,7 +15,7 @@
#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
@@ -97,7 +84,7 @@ static int vx_check_magic(struct vx_core *chip)
return 0;
msleep(10);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c);
+ dev_err(chip->card->dev, "cannot find xilinx magic word (%x)\n", c);
return -EIO;
}
@@ -166,7 +153,6 @@ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *
vx_outb(chip, ICR, 0);
/* Wait for answer HF2 equal to 1 */
- snd_printdd(KERN_DEBUG "check ISR_HF2\n");
if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0)
goto _error;
@@ -183,7 +169,9 @@ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *
goto _error;
c = vx_inb(chip, RXL);
if (c != (int)data)
- snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data);
+ dev_err(_chip->card->dev,
+ "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n",
+ i, c, (int)data);
}
/* reset HF1 */
@@ -201,7 +189,8 @@ 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);
+ dev_dbg(_chip->card->dev,
+ "xilinx: dsp size received 0x%x, orig 0x%zx\n", c, fw->size);
vx_outb(chip, ICR, ICR_HF0);
@@ -250,9 +239,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:
@@ -375,7 +366,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
length >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
for (; length > 0; length--) {
- outw(cpu_to_le16(*addr), port);
+ outw(*addr, port);
addr++;
}
addr = (unsigned short *)runtime->dma_area;
@@ -385,7 +376,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
count >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
for (; count > 0; count--) {
- outw(cpu_to_le16(*addr), port);
+ outw(*addr, port);
addr++;
}
vx_release_pseudo_dma(chip);
@@ -417,7 +408,7 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
length >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
for (; length > 0; length--)
- *addr++ = le16_to_cpu(inw(port));
+ *addr++ = inw(port);
addr = (unsigned short *)runtime->dma_area;
pipe->hw_ptr = 0;
}
@@ -425,12 +416,12 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
count >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
for (; count > 1; count--)
- *addr++ = le16_to_cpu(inw(port));
+ *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);
@@ -472,7 +463,7 @@ void vx_set_mic_boost(struct vx_core *chip, int boost)
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) {
if (boost) {
/* boost: 38 dB */
@@ -485,7 +476,6 @@ void vx_set_mic_boost(struct vx_core *chip, int boost)
}
vx_outb(chip, CDSP, pchip->regCDSP);
}
- mutex_unlock(&chip->lock);
}
/*
@@ -514,12 +504,11 @@ void vx_set_mic_level(struct vx_core *chip, int level)
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) {
level = vx_compute_mic_level(level);
vx_outb(chip, MICRO, level);
}
- mutex_unlock(&chip->lock);
}
@@ -594,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 ca0d19e723fd..2e09f2a513a6 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -1,21 +1,9 @@
+// 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
*/
@@ -29,13 +17,9 @@
#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 */
@@ -67,19 +51,6 @@ static void vxpocket_release(struct pcmcia_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
*/
@@ -94,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,
@@ -116,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,
@@ -138,21 +109,12 @@ 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 = to_vxpocket(chip);
@@ -189,7 +151,8 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq
struct snd_card *card = chip->card;
struct snd_vxpocket *vxp = to_vxpocket(chip);
- snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
+ dev_dbg(chip->card->dev,
+ "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
vxp->port = port;
sprintf(card->shortname, "Digigram %s", card->driver);
@@ -197,8 +160,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;
@@ -214,17 +179,15 @@ static int vxpocket_config(struct pcmcia_device *link)
struct vx_core *chip = link->priv;
int ret;
- snd_printdd(KERN_DEBUG "vxpocket_config called\n");
-
/* redefine hardware record according to the VERSION1 string */
if (!strcmp(link->prod_id[1], "VX-POCKET")) {
- snd_printdd("VX-pocket is detected\n");
+ dev_dbg(chip->card->dev, "VX-pocket is detected\n");
} else {
- snd_printdd("VX-pocket 440 is detected\n");
+ dev_dbg(chip->card->dev, "VX-pocket 440 is detected\n");
/* overwrite the hardware information */
chip->hw = &vxp440_hw;
chip->type = vxp440_hw.type;
- strcpy(chip->card->driver, vxp440_hw.name);
+ strscpy(chip->card->driver, vxp440_hw.name);
}
ret = pcmcia_request_io(link);
@@ -241,8 +204,6 @@ static int vxpocket_config(struct pcmcia_device *link)
if (ret)
goto failed;
- chip->dev = &link->dev;
-
if (snd_vxpocket_assign_resources(chip, link->resource[0]->start,
link->irq) < 0)
goto failed;
@@ -262,11 +223,8 @@ static int vxp_suspend(struct pcmcia_device *link)
{
struct vx_core *chip = link->priv;
- snd_printdd(KERN_DEBUG "SUSPEND\n");
- if (chip) {
- snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
+ if (chip)
snd_vx_suspend(chip);
- }
return 0;
}
@@ -275,15 +233,10 @@ static int vxp_resume(struct pcmcia_device *link)
{
struct vx_core *chip = link->priv;
- snd_printdd(KERN_DEBUG "RESUME\n");
if (pcmcia_dev_present(link)) {
- //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
- if (chip) {
- snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
+ if (chip)
snd_vx_resume(chip);
- }
}
- snd_printdd(KERN_DEBUG "resume done!\n");
return 0;
}
@@ -305,7 +258,7 @@ static int vxpocket_probe(struct pcmcia_device *p_dev)
break;
}
if (i >= SNDRV_CARDS) {
- snd_printk(KERN_ERR "vxpocket: too many cards found\n");
+ dev_err(&p_dev->dev, "vxpocket: too many cards found\n");
return -EINVAL;
}
if (! enable[i])
@@ -315,7 +268,7 @@ static int vxpocket_probe(struct pcmcia_device *p_dev)
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");
+ dev_err(&p_dev->dev, "vxpocket: cannot create a card instance\n");
return err;
}
diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h
index 26f4255e132e..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
@@ -45,7 +32,7 @@ struct snd_vxpocket {
#define to_vxpocket(x) container_of(x, struct snd_vxpocket, core)
-extern struct snd_vx_ops snd_vxpocket_ops;
+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..655bcffba843 100644
--- a/sound/ppc/Makefile
+++ b/sound/ppc/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
+snd-powermac-y := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index d1e4ef1c5c30..c231a9d6d1de 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -1,22 +1,9 @@
+// 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
*/
@@ -24,6 +11,7 @@
#include <asm/nvram.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "pmac.h"
@@ -51,7 +39,7 @@ static void snd_pmac_screamer_wait(struct snd_pmac *chip)
while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
mdelay(1);
if (! --timeout) {
- snd_printd("snd_pmac_screamer_wait timeout\n");
+ dev_dbg(chip->card->dev, "%s timeout\n", __func__);
break;
}
}
@@ -70,7 +58,7 @@ snd_pmac_awacs_write(struct snd_pmac *chip, int val)
out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
if (! --timeout) {
- snd_printd("snd_pmac_awacs_write timeout\n");
+ dev_dbg(chip->card->dev, "%s timeout\n", __func__);
break;
}
}
@@ -149,13 +137,11 @@ static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
int reg = kcontrol->private_value & 0xff;
int lshift = (kcontrol->private_value >> 8) & 0xff;
int inverted = (kcontrol->private_value >> 16) & 1;
- unsigned long flags;
int vol[2];
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
vol[1] = chip->awacs_reg[reg] & 0xf;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (inverted) {
vol[0] = 0x0f - vol[0];
vol[1] = 0x0f - vol[1];
@@ -173,7 +159,6 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
int lshift = (kcontrol->private_value >> 8) & 0xff;
int inverted = (kcontrol->private_value >> 16) & 1;
int val, oldval;
- unsigned long flags;
unsigned int vol[2];
vol[0] = ucontrol->value.integer.value[0];
@@ -186,14 +171,13 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
}
vol[0] &= 0x0f;
vol[1] &= 0x0f;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oldval = chip->awacs_reg[reg];
val = oldval & ~(0xf | (0xf << lshift));
val |= vol[0] << lshift;
val |= vol[1];
if (oldval != val)
snd_pmac_awacs_write_reg(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return oldval != reg;
}
@@ -216,11 +200,9 @@ static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol,
int shift = (kcontrol->private_value >> 8) & 0xff;
int invert = (kcontrol->private_value >> 16) & 1;
int val;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->awacs_reg[reg] >> shift) & 1;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
val = 1 - val;
ucontrol->value.integer.value[0] = val;
@@ -236,16 +218,14 @@ static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol,
int invert = (kcontrol->private_value >> 16) & 1;
int mask = 1 << shift;
int val, changed;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = chip->awacs_reg[reg] & ~mask;
if (ucontrol->value.integer.value[0] != invert)
val |= mask;
changed = chip->awacs_reg[reg] != val;
if (changed)
snd_pmac_awacs_write_reg(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return changed;
}
@@ -477,7 +457,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[] = {
+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,
@@ -553,14 +533,12 @@ static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
int val = 0;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->awacs_reg[6] & MASK_MIC_BOOST)
val |= 2;
if (chip->awacs_reg[0] & MASK_GAINLINE)
val |= 1;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -571,9 +549,8 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int val0, val6;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
if (ucontrol->value.integer.value[0] & 1)
@@ -588,53 +565,52 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
snd_pmac_awacs_write_reg(chip, 6, val6);
changed = 1;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return changed;
}
/*
* lists of mixer elements
*/
-static struct snd_kcontrol_new snd_pmac_awacs_mixers[] = {
+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[] = {
+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[] = {
+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[] = {
+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[] = {
+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[] = {
+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[] = {
+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[] = {
+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 +618,34 @@ static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = {
/* FIXME: is this correct order?
* screamer (powerbook G3 pismo) seems to have different bits...
*/
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = {
+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[] = {
+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[] = {
+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 =
+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 =
+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 =
+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[] = {
+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[] = {
+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 +654,34 @@ static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = {
},
};
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] =
+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[] =
+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[] =
+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[] = {
+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 =
+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 =
+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 =
+static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 =
AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
@@ -713,7 +689,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;
@@ -968,7 +944,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
/*
* build mixers
*/
- strcpy(chip->card->mixername, "PowerMac AWACS");
+ strscpy(chip->card->mixername, "PowerMac AWACS");
err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
snd_pmac_awacs_mixers);
@@ -1076,12 +1052,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);
@@ -1089,10 +1065,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 f19eb3e39937..ab2468790b0c 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -1,21 +1,8 @@
+// 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 <linux/io.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,
@@ -101,7 +88,6 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
{
struct snd_pmac *chip;
struct pmac_beep *beep;
- unsigned long flags;
int beep_speed = 0;
int srate;
int period, ncycles, nsamples;
@@ -112,20 +98,22 @@ 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) {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (beep->running)
snd_pmac_beep_stop(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -135,13 +123,11 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
if (hz <= srate / BEEP_BUFLEN || hz > srate / 2)
hz = 1000;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->playback.running || chip->capture.running || beep->running) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (chip->playback.running || chip->capture.running || beep->running)
+ return 0;
+ beep->running = 1;
}
- beep->running = 1;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (hz == beep->hz && beep->volume == beep->volume_play) {
nsamples = beep->nsamples;
@@ -161,9 +147,8 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
beep->nsamples = nsamples;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index b86159e04493..5d6accce3a72 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -1,27 +1,15 @@
+// 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 <linux/io.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <sound/core.h>
#include "pmac.h"
#include "burgundy.h"
@@ -71,9 +59,8 @@ static unsigned
snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr)
{
unsigned val = 0;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
snd_pmac_burgundy_busy_wait(chip);
@@ -95,8 +82,6 @@ snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr)
snd_pmac_burgundy_extend_wait(chip);
val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
return val;
}
@@ -112,17 +97,14 @@ static unsigned
snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr)
{
unsigned val = 0;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
snd_pmac_burgundy_busy_wait(chip);
snd_pmac_burgundy_extend_wait(chip);
val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
return val;
}
@@ -467,7 +449,7 @@ static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol,
/*
* Burgundy mixers
*/
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] = {
+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 +477,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] = {
*/ BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
};
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] = {
+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 +503,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] = {
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[] = {
+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 +519,33 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] = {
/* 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 =
+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 =
+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 =
+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 =
+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 =
+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 =
+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 =
+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);
@@ -677,7 +659,7 @@ int snd_pmac_burgundy_init(struct snd_pmac *chip)
/*
* build burgundy mixers
*/
- strcpy(chip->card->mixername, "PowerMac Burgundy");
+ strscpy(chip->card->mixername, "PowerMac Burgundy");
for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) {
err = snd_ctl_add(chip->card,
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 b86526223e4e..a74114225b67 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
*/
@@ -82,7 +69,7 @@ static int daca_set_volume(struct pmac_daca *mix)
data[1] |= mix->deemphasis ? 0x40 : 0;
if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL,
2, data) < 0) {
- snd_printk(KERN_ERR "failed to set volume \n");
+ dev_err(&mix->i2c.client->dev, "failed to set volume\n");
return -EINVAL;
}
return 0;
@@ -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,
@@ -261,16 +254,18 @@ int 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;
/*
* build mixers
*/
- strcpy(chip->card->mixername, "PowerMac DACA");
+ strscpy(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 4373615f13e2..4ce81ac7f700 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -1,21 +1,8 @@
+// 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
*/
@@ -26,16 +13,10 @@
#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 */
@@ -53,6 +34,7 @@ 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;
@@ -61,11 +43,13 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
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
@@ -77,29 +61,21 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
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(&keywest_ctx->client->detected,
- &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)
{
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[] = {
- { "MAC,tas3004", 0 }, /* instantiated by i2c-powermac */
- { "keywest", 0 }, /* instantiated by us if needed */
+ { "MAC,tas3004" }, /* instantiated by i2c-powermac */
+ { "keywest" }, /* instantiated by us if needed */
{ }
};
MODULE_DEVICE_TABLE(i2c, keywest_i2c_id);
@@ -117,6 +93,7 @@ static struct i2c_driver keywest_driver = {
void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c)
{
if (keywest_ctx && keywest_ctx == i2c) {
+ i2c_unregister_device(keywest_ctx->client);
i2c_del_driver(&keywest_driver);
keywest_ctx = NULL;
}
@@ -129,8 +106,10 @@ int snd_pmac_tumbler_post_init(void)
if (!keywest_ctx || !keywest_ctx->client)
return -ENXIO;
- if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) {
- snd_printk(KERN_ERR "tumbler: %i :cannot initialize the MCS\n", err);
+ err = keywest_ctx->init_client(keywest_ctx);
+ if (err < 0) {
+ dev_err(&keywest_ctx->client->dev,
+ "tumbler: %i :cannot initialize the MCS\n", err);
return err;
}
return 0;
@@ -151,8 +130,9 @@ int snd_pmac_keywest_init(struct pmac_keywest *i2c)
keywest_ctx = i2c;
- if ((err = i2c_add_driver(&keywest_driver))) {
- snd_printk(KERN_ERR "cannot register keywest i2c driver\n");
+ err = i2c_add_driver(&keywest_driver);
+ if (err) {
+ dev_err(&i2c->client->dev, "cannot register keywest i2c driver\n");
i2c_put_adapter(adap);
return err;
}
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 48dd44f8e914..6d7dab26ddf2 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -1,22 +1,9 @@
+// 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
*/
@@ -37,11 +24,11 @@
/* 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
};
@@ -121,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)
@@ -237,32 +206,32 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec,
* common to many PowerBook G3 systems and random noise otherwise
* captured on iBook2's about every third time. -ReneR
*/
- spin_lock_irq(&chip->reg_lock);
- snd_pmac_dma_stop(rec);
- 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);
- mdelay(5);
- spin_lock_irq(&chip->reg_lock);
- /* continuous DMA memory type doesn't provide the physical address,
- * so we need to resolve the address here...
- */
- offset = runtime->dma_addr;
- for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {
- 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;
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_pmac_dma_stop(rec);
+ 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);
}
- /* make loop */
- cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
- cp->cmd_dep = cpu_to_le32(rec->cmd.addr);
+ mdelay(5);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ /* continuous DMA memory type doesn't provide the physical address,
+ * so we need to resolve the address here...
+ */
+ offset = runtime->dma_addr;
+ for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {
+ 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 */
+ 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);
- spin_unlock_irq(&chip->reg_lock);
+ snd_pmac_dma_stop(rec);
+ snd_pmac_dma_set_command(rec, &rec->cmd);
+ }
return 0;
}
@@ -284,27 +253,26 @@ static int snd_pmac_pcm_trigger(struct snd_pmac *chip, struct pmac_stream *rec,
return -EBUSY;
command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ?
OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS;
- spin_lock(&chip->reg_lock);
- snd_pmac_beep_stop(chip);
- snd_pmac_pcm_set_format(chip);
- for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
- out_le16(&cp->command, command);
- snd_pmac_dma_set_command(rec, &rec->cmd);
- (void)in_le32(&rec->dma->status);
- snd_pmac_dma_run(rec, RUN|WAKE);
- rec->running = 1;
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ snd_pmac_beep_stop(chip);
+ snd_pmac_pcm_set_format(chip);
+ for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
+ out_le16(&cp->command, command);
+ snd_pmac_dma_set_command(rec, &rec->cmd);
+ (void)in_le32(&rec->dma->status);
+ snd_pmac_dma_run(rec, RUN|WAKE);
+ rec->running = 1;
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- spin_lock(&chip->reg_lock);
- rec->running = 0;
- /*printk(KERN_DEBUG "stopped!!\n");*/
- snd_pmac_dma_stop(rec);
- for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
- out_le16(&cp->command, DBDMA_STOP);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ rec->running = 0;
+ snd_pmac_dma_stop(rec);
+ for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
+ out_le16(&cp->command, DBDMA_STOP);
+ }
break;
default:
@@ -335,7 +303,6 @@ static snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip,
}
#endif
count += rec->cur_period * rec->period_size;
- /*printk(KERN_DEBUG "pointer=%d\n", count);*/
return bytes_to_frames(subs->runtime, count);
}
@@ -415,8 +382,6 @@ static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec,
unsigned short req, res ;
unsigned int phy ;
- /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */
-
/* to clear DEAD status we must first clear RUN
set it to quiescent to be on the safe side */
(void)in_le32(&rec->dma->status);
@@ -487,7 +452,6 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
if (! (stat & ACTIVE))
break;
- /*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/
cp->xfer_status = cpu_to_le16(0);
cp->req_count = cpu_to_le16(rec->period_size);
/*cp->res_count = cpu_to_le16(0);*/
@@ -684,9 +648,6 @@ static int snd_pmac_capture_close(struct snd_pcm_substream *subs)
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,
@@ -695,9 +656,6 @@ static const struct snd_pcm_ops snd_pmac_playback_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,
@@ -721,7 +679,7 @@ int snd_pmac_pcm_new(struct snd_pmac *chip)
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE;
@@ -734,9 +692,9 @@ int 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;
}
@@ -763,7 +721,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add
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);
+ 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));
@@ -807,7 +765,6 @@ snd_pmac_ctrl_intr(int irq, void *devid)
struct snd_pmac *chip = devid;
int ctrl = in_le32(&chip->awacs->control);
- /*printk(KERN_DEBUG "pmac: control interrupt.. 0x%x\n", ctrl);*/
if (ctrl & MASK_PORTCHG) {
/* do something when headphone is plugged/unplugged? */
if (chip->update_automute)
@@ -816,7 +773,7 @@ snd_pmac_ctrl_intr(int irq, void *devid)
if (ctrl & MASK_CNTLERR) {
int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16;
if (err && chip->model <= PMAC_SCREAMER)
- snd_printk(KERN_DEBUG "error %x\n", err);
+ dev_dbg(chip->card->dev, "%s: error %x\n", __func__, err);
}
/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
out_le32(&chip->awacs->control, ctrl);
@@ -908,7 +865,7 @@ static void detect_byte_swap(struct snd_pmac *chip)
/* 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;
@@ -1001,9 +958,8 @@ static int snd_pmac_detect(struct snd_pmac *chip)
if (prop) {
/* partly deprecate snd-powermac, for those machines
* that have a layout-id property for now */
- printk(KERN_INFO "snd-powermac no longer handles any "
- "machines with a layout-id property "
- "in the device-tree, use snd-aoa.\n");
+ dev_info(chip->card->dev,
+ "snd-powermac no longer handles any machines with a layout-id property in the device-tree, use snd-aoa.\n");
of_node_put(sound);
of_node_put(chip->node);
chip->node = NULL;
@@ -1058,7 +1014,7 @@ static int snd_pmac_detect(struct snd_pmac *chip)
*/
macio = macio_find(chip->node, macio_unknown);
if (macio == NULL)
- printk(KERN_WARNING "snd-powermac: can't locate macio !\n");
+ dev_warn(chip->card->dev, "snd-powermac: can't locate macio !\n");
else {
struct pci_dev *pdev = NULL;
@@ -1071,8 +1027,8 @@ static int snd_pmac_detect(struct snd_pmac *chip)
}
}
if (chip->pdev == NULL)
- printk(KERN_WARNING "snd-powermac: can't locate macio PCI"
- " device !\n");
+ dev_warn(chip->card->dev,
+ "snd-powermac: can't locate macio PCI device !\n");
detect_byte_swap(chip);
@@ -1141,7 +1097,7 @@ static int pmac_hp_detect_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new auto_mute_controls[] = {
+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,
@@ -1162,7 +1118,8 @@ int snd_pmac_add_automute(struct snd_pmac *chip)
chip->auto_mute = 1;
err = snd_ctl_add(chip->card, snd_ctl_new1(&auto_mute_controls[0], chip));
if (err < 0) {
- printk(KERN_ERR "snd-powermac: Failed to add automute control\n");
+ dev_err(chip->card->dev,
+ "snd-powermac: Failed to add automute control\n");
return err;
}
chip->hp_detect_ctl = snd_ctl_new1(&auto_mute_controls[1], chip);
@@ -1180,7 +1137,7 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
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,
};
@@ -1197,7 +1154,8 @@ int 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 ||
@@ -1211,22 +1169,23 @@ int 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,
&chip->rsrc[i])) {
- printk(KERN_ERR "snd: can't translate rsrc "
- " %d (%s)\n", i, rnames[i]);
+ dev_err(chip->card->dev,
+ "snd: can't translate rsrc %d (%s)\n",
+ i, rnames[i]);
err = -ENODEV;
goto __error;
}
if (request_mem_region(chip->rsrc[i].start,
resource_size(&chip->rsrc[i]),
rnames[i]) == NULL) {
- printk(KERN_ERR "snd: can't request rsrc "
- " %d (%s: %pR)\n",
- i, rnames[i], &chip->rsrc[i]);
+ dev_err(chip->card->dev,
+ "snd: can't request rsrc %d (%s: %pR)\n",
+ i, rnames[i], &chip->rsrc[i]);
err = -ENODEV;
goto __error;
}
@@ -1236,22 +1195,23 @@ int 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,
&chip->rsrc[i])) {
- printk(KERN_ERR "snd: can't translate rsrc "
- " %d (%s)\n", i, rnames[i]);
+ dev_err(chip->card->dev,
+ "snd: can't translate rsrc %d (%s)\n",
+ i, rnames[i]);
err = -ENODEV;
goto __error;
}
if (request_mem_region(chip->rsrc[i].start,
resource_size(&chip->rsrc[i]),
rnames[i]) == NULL) {
- printk(KERN_ERR "snd: can't request rsrc "
- " %d (%s: %pR)\n",
- i, rnames[i], &chip->rsrc[i]);
+ dev_err(chip->card->dev,
+ "snd: can't request rsrc %d (%s: %pR)\n",
+ i, rnames[i], &chip->rsrc[i]);
err = -ENODEV;
goto __error;
}
@@ -1269,8 +1229,8 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
irq = irq_of_parse_and_map(np, 0);
if (request_irq(irq, snd_pmac_ctrl_intr, 0,
"PMac", (void*)chip)) {
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n",
- irq);
+ dev_err(chip->card->dev,
+ "pmac: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto __error;
}
@@ -1278,14 +1238,14 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
}
irq = irq_of_parse_and_map(np, 1);
if (request_irq(irq, snd_pmac_tx_intr, 0, "PMac Output", (void*)chip)){
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq);
+ dev_err(chip->card->dev, "pmac: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto __error;
}
chip->tx_irq = irq;
irq = irq_of_parse_and_map(np, 2);
if (request_irq(irq, snd_pmac_rx_intr, 0, "PMac Input", (void*)chip)) {
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq);
+ dev_err(chip->card->dev, "pmac: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto __error;
}
@@ -1313,7 +1273,7 @@ int 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 =
@@ -1336,7 +1296,8 @@ int 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;
@@ -1360,15 +1321,12 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
void snd_pmac_suspend(struct snd_pmac *chip)
{
- unsigned long flags;
-
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);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_pmac_beep_stop(chip);
+ }
if (chip->irq >= 0)
disable_irq(chip->irq);
if (chip->tx_irq >= 0)
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 33c6be9fb388..e685d245883e 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -1,21 +1,8 @@
+// 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>
@@ -31,7 +18,6 @@
#define CHIP_NAME "PMac"
MODULE_DESCRIPTION("PowerMac");
-MODULE_SUPPORTED_DEVICE("{{Apple,PowerMac}}");
MODULE_LICENSE("GPL");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
@@ -62,25 +48,28 @@ static int snd_pmac_probe(struct platform_device *devptr)
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;
switch (chip->model) {
case PMAC_BURGUNDY:
- strcpy(card->driver, "PMac Burgundy");
- strcpy(card->shortname, "PowerMac Burgundy");
+ strscpy(card->driver, "PMac Burgundy");
+ strscpy(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:
- strcpy(card->driver, "PMac DACA");
- strcpy(card->shortname, "PowerMac DACA");
+ strscpy(card->driver, "PMac DACA");
+ strscpy(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 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,23 +99,26 @@ static int 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:
- snd_printk(KERN_ERR "unsupported hardware %d\n", chip->model);
+ dev_err(&devptr->dev, "unsupported hardware %d\n", chip->model);
err = -EINVAL;
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);
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
goto __error;
platform_set_drvdata(devptr, card);
@@ -134,10 +130,9 @@ __error:
}
-static int snd_pmac_remove(struct platform_device *devptr)
+static void snd_pmac_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -176,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 36f34f434ecb..225b20f0b71a 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>
@@ -233,21 +221,19 @@ 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;
@@ -255,9 +241,8 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
snd_ps3_verify_dma_stop(card, 700, 0);
fill_stages = 4;
- spin_lock_irqsave(&card->dma_lock, irqsave);
+ guard(spinlock_irqsave)(&card->dma_lock);
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)
@@ -303,7 +288,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
}
/* ensure the hardware sees the change */
wmb();
- spin_unlock_irqrestore(&card->dma_lock, irqsave);
return 0;
}
@@ -526,9 +510,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;
@@ -551,22 +533,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)
-{
- return snd_pcm_lib_free_pages(substream);
-};
-
static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
unsigned int delay_ms)
{
@@ -593,7 +559,6 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
- unsigned long irqsave;
if (!snd_ps3_set_avsetting(substream)) {
/* some parameter changed */
@@ -610,8 +575,7 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
}
/* restart ring buffer pointer */
- spin_lock_irqsave(&card->dma_lock, irqsave);
- {
+ scoped_guard(spinlock_irqsave, &card->dma_lock) {
card->dma_buffer_size = runtime->dma_bytes;
card->dma_last_transfer_vaddr[SND_PS3_CH_L] =
@@ -632,7 +596,6 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
card->dma_start_bus_addr[SND_PS3_CH_L]);
}
- spin_unlock_irqrestore(&card->dma_lock, irqsave);
/* ensure the hardware sees the change */
mb();
@@ -644,18 +607,15 @@ 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:
/* clear outstanding interrupts */
update_reg(PS3_AUDIO_AX_IS, 0);
- spin_lock(&card->dma_lock);
- {
+ scoped_guard(spinlock, &card->dma_lock) {
card->running = 1;
}
- spin_unlock(&card->dma_lock);
snd_ps3_program_dma(card,
SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
@@ -669,11 +629,9 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_STOP:
- spin_lock(&card->dma_lock);
- {
+ scoped_guard(spinlock, &card->dma_lock) {
card->running = 0;
}
- spin_unlock(&card->dma_lock);
snd_ps3_wait_for_dma_stop(card);
break;
default:
@@ -681,7 +639,7 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
}
- return ret;
+ return 0;
};
/*
@@ -694,12 +652,10 @@ static snd_pcm_uframes_t snd_ps3_pcm_pointer(
size_t bytes;
snd_pcm_uframes_t ret;
- spin_lock(&card->dma_lock);
- {
+ scoped_guard(spinlock, &card->dma_lock) {
bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
card->dma_start_vaddr[SND_PS3_CH_L]);
}
- spin_unlock(&card->dma_lock);
ret = bytes_to_frames(substream->runtime, bytes * 2);
@@ -748,7 +704,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,
@@ -775,9 +731,6 @@ static struct snd_kcontrol_new spdif_ctls[] = {
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,
@@ -930,11 +883,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
{
int i, ret;
u64 lpar_addr, lpar_size;
-
- if (WARN_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)))
- return -ENODEV;
- if (WARN_ON(dev->match_id != PS3_MATCH_ID_SOUND))
- return -ENODEV;
+ static u64 dummy_mask;
the_card.ps3_dev = dev;
@@ -961,7 +910,7 @@ static int 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);
@@ -970,6 +919,10 @@ static int 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 */
@@ -987,9 +940,9 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
if (ret < 0)
goto clean_irq;
- strcpy(the_card.card->driver, "PS3");
- strcpy(the_card.card->shortname, "PS3");
- strcpy(the_card.card->longname, "PS3 sound");
+ strscpy(the_card.card->driver, "PS3");
+ strscpy(the_card.card->shortname, "PS3");
+ strscpy(the_card.card->longname, "PS3 sound");
/* create control elements */
for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) {
@@ -1011,7 +964,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
goto clean_card;
the_card.pcm->private_data = &the_card;
- strcpy(the_card.pcm->name, "SPDIF");
+ strscpy(the_card.pcm->name, "SPDIF");
/* set pcm ops */
snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -1019,15 +972,11 @@ static int 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
@@ -1084,20 +1033,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 +1056,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 2e6302079566..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
*/
/*
@@ -320,7 +308,7 @@ 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:
+Field values are the following:
0 - Interrupt hasn't occurred.
1 - Interrupt has occurred.
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 0779a2912237..3c09660e1522 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
- *
*/
@@ -43,7 +29,7 @@
#undef DEBUG
#ifdef DEBUG
-#define DBG(fmt...) printk(KERN_DEBUG fmt)
+#define DBG(fmt...) pr_debug(fmt)
#else
#define DBG(fmt...)
#endif
@@ -120,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;
@@ -142,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 */
@@ -153,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 */
@@ -244,7 +230,7 @@ static int tumbler_set_master_volume(struct pmac_tumbler *mix)
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_VOL, 6,
block) < 0) {
- snd_printk(KERN_ERR "failed to set volume \n");
+ dev_err(&mix->i2c.client->dev, "failed to set volume\n");
return -EINVAL;
}
DBG("(I) succeeded to set volume (%u, %u)\n", left_vol, right_vol);
@@ -355,7 +341,7 @@ static int tumbler_set_drc(struct pmac_tumbler *mix)
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
2, val) < 0) {
- snd_printk(KERN_ERR "failed to set DRC\n");
+ dev_err(&mix->i2c.client->dev, "failed to set DRC\n");
return -EINVAL;
}
DBG("(I) succeeded to set DRC (%u, %u)\n", val[0], val[1]);
@@ -392,7 +378,7 @@ static int snapper_set_drc(struct pmac_tumbler *mix)
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
6, val) < 0) {
- snd_printk(KERN_ERR "failed to set DRC\n");
+ dev_err(&mix->i2c.client->dev, "failed to set DRC\n");
return -EINVAL;
}
DBG("(I) succeeded to set DRC (%u, %u)\n", val[0], val[1]);
@@ -416,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;
@@ -430,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) {
@@ -456,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;
@@ -469,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) {
@@ -492,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;
@@ -513,8 +503,8 @@ static int tumbler_set_mono_volume(struct pmac_tumbler *mix,
block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff;
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, info->reg,
info->bytes, block) < 0) {
- snd_printk(KERN_ERR "failed to set mono volume %d\n",
- info->index);
+ dev_err(&mix->i2c.client->dev, "failed to set mono volume %d\n",
+ info->index);
return -EINVAL;
}
return 0;
@@ -538,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;
@@ -553,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)
@@ -567,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,
@@ -575,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,
@@ -583,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,
@@ -592,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,
@@ -600,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,
@@ -651,7 +643,8 @@ static int snapper_set_mix_vol1(struct pmac_tumbler *mix, int idx, int ch, int r
}
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, reg,
9, block) < 0) {
- snd_printk(KERN_ERR "failed to set mono volume %d\n", reg);
+ dev_err(&mix->i2c.client->dev,
+ "failed to set mono volume %d\n", reg);
return -EINVAL;
}
return 0;
@@ -683,7 +676,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];
@@ -699,7 +693,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];
@@ -730,7 +725,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:
@@ -759,7 +755,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:
@@ -840,7 +837,7 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
/*
*/
-static struct snd_kcontrol_new tumbler_mixers[] = {
+static const struct snd_kcontrol_new tumbler_mixers[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = tumbler_info_master_volume,
@@ -864,7 +861,7 @@ static struct snd_kcontrol_new tumbler_mixers[] = {
},
};
-static struct snd_kcontrol_new snapper_mixers[] = {
+static const struct snd_kcontrol_new snapper_mixers[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = tumbler_info_master_volume,
@@ -1064,8 +1061,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;
@@ -1084,8 +1080,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;
}
@@ -1108,7 +1103,6 @@ static long tumbler_find_device(const char *device, const char *platform,
node = find_audio_device(device);
if (! node) {
DBG("(W) cannot find audio device %s !\n", device);
- snd_printdd("cannot find device %s\n", device);
return -ENODEV;
}
@@ -1117,7 +1111,6 @@ static long tumbler_find_device(const char *device, const char *platform,
base = of_get_property(node, "reg", NULL);
if (!base) {
DBG("(E) cannot find address for device %s !\n", device);
- snd_printd("cannot find address for device %s\n", device);
of_node_put(node);
return -ENODEV;
}
@@ -1238,9 +1231,9 @@ static void tumbler_resume(struct snd_pmac *chip)
tumbler_reset_audio(chip);
if (mix->i2c.client && mix->i2c.init_client) {
if (mix->i2c.init_client(&mix->i2c) < 0)
- printk(KERN_ERR "tumbler_init_client error\n");
+ dev_err(chip->card->dev, "tumbler_init_client error\n");
} else
- printk(KERN_ERR "tumbler: i2c is not initialized\n");
+ dev_err(chip->card->dev, "tumbler: i2c is not initialized\n");
if (chip->model == PMAC_TUMBLER) {
tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
@@ -1365,16 +1358,18 @@ int 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 */
@@ -1405,7 +1400,8 @@ int 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;
/*
@@ -1415,28 +1411,34 @@ int 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% */
@@ -1459,9 +1461,11 @@ int 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 */
@@ -1469,8 +1473,9 @@ int 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);
@@ -1478,8 +1483,9 @@ int 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 549ec6a31a98..16d548019ee9 100644
--- a/sound/ppc/tumbler_volume.h
+++ b/sound/ppc/tumbler_volume.h
@@ -2,7 +2,7 @@
/* 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,
@@ -66,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,
@@ -96,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,
@@ -126,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,
@@ -191,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,
@@ -221,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..f5fa09d740b4 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
@@ -13,7 +14,7 @@ if SND_SUPERH
config SND_AICA
tristate "Dreamcast Yamaha AICA sound"
- depends on SH_DREAMCAST
+ depends on SH_DREAMCAST && SH_DMA_API
select SND_PCM
select G2_DMA
help
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 7d09b5188cf7..6871dece28a5 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-snd-aica-objs := aica.o
-snd-sh_dac_audio-objs := sh_dac_audio.o
+snd-aica-y := aica.o
+snd-sh_dac_audio-y := sh_dac_audio.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AICA) += snd-aica.o
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 2b26311405a4..fa81bfba59c1 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>
@@ -48,7 +32,6 @@
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 */
@@ -92,8 +75,7 @@ static void spu_write_wait(void)
/* To ensure hardware failure doesn't wedge kernel */
time_count++;
if (time_count > 0x10000) {
- snd_printk
- ("WARNING: G2 FIFO appears to be blocked.\n");
+ pr_warn("WARNING: G2 FIFO appears to be blocked.\n");
break;
}
}
@@ -117,10 +99,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;
@@ -295,18 +277,21 @@ static void run_spu_dma(struct work_struct *work)
dreamcastcard->clicks++;
if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
- mod_timer(&dreamcastcard->timer, jiffies + 1);
+ if (snd_pcm_running(dreamcastcard->substream))
+ mod_timer(&dreamcastcard->timer, jiffies + 1);
}
}
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->timer_substream;
+ struct snd_card_aica *dreamcastcard = timer_container_of(dreamcastcard,
+ t, timer);
+ struct snd_pcm_substream *substream = dreamcastcard->substream;
/*timer function - so cannot sleep */
int play_period;
struct snd_pcm_runtime *runtime;
+ if (!snd_pcm_running(substream))
+ return;
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/* Have we played out an additional period? */
@@ -330,18 +315,9 @@ static void aica_period_elapsed(struct timer_list *t)
static void spu_begin_dma(struct snd_pcm_substream *substream)
{
struct snd_card_aica *dreamcastcard;
- struct snd_pcm_runtime *runtime;
- runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
schedule_work(&(dreamcastcard->spu_dma_work));
- /* Timer may already be running */
- if (unlikely(dreamcastcard->timer_substream)) {
- mod_timer(&dreamcastcard->timer, jiffies + 4);
- return;
- }
- timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
- dreamcastcard->timer_substream = substream;
mod_timer(&dreamcastcard->timer, jiffies + 4);
}
@@ -374,35 +350,25 @@ static int snd_aicapcm_pcm_open(struct snd_pcm_substream
return 0;
}
+static int snd_aicapcm_pcm_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
+
+ timer_delete_sync(&dreamcastcard->timer);
+ cancel_work_sync(&dreamcastcard->spu_dma_work);
+ return 0;
+}
+
static int snd_aicapcm_pcm_close(struct snd_pcm_substream
*substream)
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
- flush_work(&(dreamcastcard->spu_dma_work));
- if (dreamcastcard->timer_substream)
- 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)
{
@@ -439,12 +405,10 @@ static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
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,
+ .sync_stop = snd_aicapcm_pcm_sync_stop,
};
/* TO DO: set up to handle more than one pcm instance */
@@ -460,18 +424,16 @@ static int __init snd_aicapcmchip(struct snd_card_aica
if (unlikely(err < 0))
return err;
pcm->private_data = dreamcastcard;
- strcpy(pcm->name, "AICA PCM");
+ strscpy(pcm->name, "AICA PCM");
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 */
@@ -507,8 +469,8 @@ static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_aica *dreamcastcard;
- dreamcastcard = kcontrol->private_data;
+ struct snd_card_aica *dreamcastcard = snd_kcontrol_chip(kcontrol);
+
if (unlikely(!dreamcastcard->channel))
return -ETXTBSY; /* we've not yet been set up */
ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
@@ -518,9 +480,9 @@ static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_aica *dreamcastcard;
+ struct snd_card_aica *dreamcastcard = snd_kcontrol_chip(kcontrol);
unsigned int vol;
- dreamcastcard = kcontrol->private_data;
+
if (unlikely(!dreamcastcard->channel))
return -ETXTBSY;
vol = ucontrol->value.integer.value[0];
@@ -585,15 +547,12 @@ static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
return 0;
}
-static int 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);
- return 0;
}
static int snd_aica_probe(struct platform_device *devptr)
@@ -609,12 +568,13 @@ static int snd_aica_probe(struct platform_device *devptr)
kfree(dreamcastcard);
return err;
}
- strcpy(dreamcastcard->card->driver, "snd_aica");
- strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
- strcpy(dreamcastcard->card->longname,
+ strscpy(dreamcastcard->card->driver, "snd_aica");
+ strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
+ strscpy(dreamcastcard->card->longname,
"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))
@@ -628,8 +588,8 @@ static int snd_aica_probe(struct platform_device *devptr)
if (unlikely(err < 0))
goto freedreamcast;
platform_set_drvdata(devptr, dreamcastcard);
- snd_printk
- ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
+ dev_info(&devptr->dev,
+ "ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
return 0;
freedreamcast:
snd_card_free(dreamcastcard->card);
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 834b2574786f..164f91240d02 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>
@@ -40,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;
@@ -140,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);
@@ -186,13 +158,12 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *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;
- if (copy_from_user_toio(chip->data_buffer + pos, src, count))
+ if (copy_from_iter(chip->data_buffer + pos, count, src) != count)
return -EFAULT;
chip->buffer_end = chip->data_buffer + pos + count;
@@ -204,34 +175,14 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
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);
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- memcpy_toio(chip->data_buffer + pos, src, count);
- chip->buffer_end = chip->data_buffer + pos + count;
-
- if (chip->empty) {
- chip->empty = 0;
- dac_audio_start_timer(chip);
- }
-
- return 0;
-}
-
static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
int channel, 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;
- memset_io(chip->data_buffer + pos, 0, count);
+ memset(chip->data_buffer + pos, 0, count);
chip->buffer_end = chip->data_buffer + pos + count;
if (chip->empty) {
@@ -255,16 +206,11 @@ snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
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_user = snd_sh_dac_pcm_copy,
- .copy_kernel = snd_sh_dac_pcm_copy_kernel,
+ .copy = snd_sh_dac_pcm_copy,
.fill_silence = snd_sh_dac_pcm_silence,
- .mmap = snd_pcm_lib_mmap_iomem,
};
static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
@@ -278,14 +224,12 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "SH_DAC PCM");
+ strscpy(pcm->name, "SH_DAC PCM");
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
/* buffer size=48K */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 48 * 1024,
- 48 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 48 * 1024, 48 * 1024);
return 0;
}
@@ -293,10 +237,9 @@ static int 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));
- return 0;
}
/* free -- it has been defined by create */
@@ -357,7 +300,7 @@ static int snd_sh_dac_create(struct snd_card *card,
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,
};
@@ -369,8 +312,7 @@ static int snd_sh_dac_create(struct snd_card *card,
chip->card = card;
- hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- chip->hrtimer.function = sh_dac_audio_timer;
+ hrtimer_setup(&chip->hrtimer, sh_dac_audio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
dac_audio_reset(chip);
chip->rate = 8000;
@@ -404,8 +346,8 @@ static int snd_sh_dac_probe(struct platform_device *devptr)
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;
+ dev_err(&devptr->dev, "cannot allocate the card\n");
+ return err;
}
err = snd_sh_dac_create(card, devptr, &chip);
@@ -416,15 +358,15 @@ static int snd_sh_dac_probe(struct platform_device *devptr)
if (err < 0)
goto probe_error;
- strcpy(card->driver, "snd_sh_dac");
- strcpy(card->shortname, "SuperH DAC audio driver");
- printk(KERN_INFO "%s %s", card->longname, card->shortname);
+ strscpy(card->driver, "snd_sh_dac");
+ strscpy(card->shortname, "SuperH DAC audio driver");
+ dev_info(&devptr->dev, "%s %s\n", card->longname, card->shortname);
err = snd_card_register(card);
if (err < 0)
goto probe_error;
- snd_printk(KERN_INFO "ALSA driver for SuperH DAC audio");
+ dev_info(&devptr->dev, "ALSA driver for SuperH DAC audio\n");
platform_set_drvdata(devptr, card);
return 0;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 84c3582f3982..ce74818bd715 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SoC audio configuration
#
@@ -9,11 +10,11 @@ menuconfig SND_SOC
select SND_JACK
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- ---help---
+ 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.
@@ -35,50 +36,118 @@ config SND_SOC_COMPRESS
config SND_SOC_TOPOLOGY
bool
+ select SND_DYNAMIC_MINORS
+
+config SND_SOC_TOPOLOGY_BUILD
+ bool "Build topology core"
+ select SND_SOC_TOPOLOGY
+ depends on KUNIT
+ help
+ This option exists to facilitate running the KUnit tests for
+ the topology core, KUnit is frequently tested in virtual
+ environments with minimal drivers enabled but the topology
+ core is usually selected by drivers. There is little reason
+ to enable it if not doing a KUnit build.
+
+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_CARD_KUNIT_TEST
+ tristate "KUnit tests for SoC card"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC card functions say Y here.
+ If unsure, say N.
+
+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_OPS_KUNIT_TEST
+ tristate "KUnit tests for SoC ops"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC ops library say Y here.
config SND_SOC_ACPI
tristate
+config SND_SOC_USB
+ tristate "SoC based USB audio offloading"
+ depends on SND_USB_AUDIO
+ help
+ Enable this option if an ASoC platform card has support to handle
+ USB audio offloading. This enables the SoC USB layer, which will
+ notify the ASoC USB DPCM backend DAI link about available USB audio
+ devices. Based on the notifications, sequences to enable the audio
+ stream can be taken based on the design.
+
# 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/bcm/Kconfig"
-source "sound/soc/blackfin/Kconfig"
source "sound/soc/cirrus/Kconfig"
-source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/google/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/loongson/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/qcom/Kconfig"
+source "sound/soc/renesas/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
-source "sound/soc/sh/Kconfig"
-source "sound/soc/sirf/Kconfig"
+source "sound/soc/sdca/Kconfig"
source "sound/soc/spear/Kconfig"
+source "sound/soc/sprd/Kconfig"
+source "sound/soc/starfive/Kconfig"
source "sound/soc/sti/Kconfig"
source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
-source "sound/soc/txx9/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"
-source "sound/soc/zte/Kconfig"
+
+# SOF
+source "sound/soc/sof/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
+source "sound/soc/sdw_utils/Kconfig"
+
# generic frame-work
source "sound/soc/generic/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 74cd1858d38b..462322c38aa4 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,61 +1,85 @@
# SPDX-License-Identifier: GPL-2.0
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-y := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
+snd-soc-core-y += 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
+snd-soc-core-y += soc-topology.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),)
+# snd-soc-test-y := soc-topology-test.o
+obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) += soc-topology-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_CARD_KUNIT_TEST),)
+obj-$(CONFIG_SND_SOC_CARD_KUNIT_TEST) += soc-card-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),)
+# snd-soc-test-y := soc-utils-test.o
+obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) += soc-utils-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_OPS_KUNIT_TEST),)
+obj-$(CONFIG_SND_SOC_OPS_KUNIT_TEST) += soc-ops-test.o
endif
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
-snd-soc-core-objs += soc-generic-dmaengine-pcm.o
+snd-soc-core-y += soc-generic-dmaengine-pcm.o
endif
ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
-snd-soc-core-objs += soc-ac97.o
+snd-soc-core-y += soc-ac97.o
endif
ifneq ($(CONFIG_SND_SOC_ACPI),)
-snd-soc-acpi-objs := soc-acpi.o
+snd-soc-acpi-y := soc-acpi.o
endif
obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
+obj-$(CONFIG_SND_SOC_USB) += soc-usb.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) += bcm/
-obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += cirrus/
-obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
+obj-$(CONFIG_SND_SOC) += google/
obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += jz4740/
+obj-$(CONFIG_SND_SOC) += loongson/
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) += nuc900/
-obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
+obj-$(CONFIG_SND_SOC) += renesas/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
-obj-$(CONFIG_SND_SOC) += sh/
-obj-$(CONFIG_SND_SOC) += sirf/
+obj-$(CONFIG_SND_SOC) += sdca/
+obj-$(CONFIG_SND_SOC) += sof/
obj-$(CONFIG_SND_SOC) += spear/
+obj-$(CONFIG_SND_SOC) += sprd/
+obj-$(CONFIG_SND_SOC) += starfive/
obj-$(CONFIG_SND_SOC) += sti/
obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
-obj-$(CONFIG_SND_SOC) += txx9/
+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/
-obj-$(CONFIG_SND_SOC) += zte/
+obj-$(CONFIG_SND_SOC) += sdw_utils/
diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig
index dd763f55edac..d47dffbf40d0 100644
--- a/sound/soc/adi/Kconfig
+++ b/sound/soc/adi/Kconfig
@@ -1,12 +1,8 @@
-config SND_SOC_ADI
- tristate "Audio support for Analog Devices reference designs"
- depends on MICROBLAZE || ARCH_ZYNQ || COMPILE_TEST
- help
- Audio support for various reference designs by Analog Devices.
+# SPDX-License-Identifier: GPL-2.0-only
+menu "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
@@ -14,8 +10,9 @@ config SND_SOC_ADI_AXI_I2S
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.
+
+endmenu
diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile
index 64456c1e5347..0d2db8d05806 100644
--- a/sound/soc/adi/Makefile
+++ b/sound/soc/adi/Makefile
@@ -1,5 +1,6 @@
-snd-soc-adi-axi-i2s-objs := axi-i2s.o
-snd-soc-adi-axi-spdif-objs := axi-spdif.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-adi-axi-i2s-y := axi-i2s.o
+snd-soc-adi-axi-spdif-y := 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
index 4c23381727a1..41f89384f8fd 100644
--- a/sound/soc/adi/axi-i2s.c
+++ b/sound/soc/adi/axi-i2s.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2013, Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/clk.h>
@@ -43,6 +42,9 @@ struct axi_i2s {
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;
@@ -136,13 +138,16 @@ 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->playback_dma_data,
- &i2s->capture_dma_data);
+ 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 = {
+ .probe = axi_i2s_dai_probe,
.startup = axi_i2s_startup,
.shutdown = axi_i2s_shutdown,
.trigger = axi_i2s_trigger,
@@ -150,25 +155,13 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
};
static struct snd_soc_dai_driver axi_i2s_dai = {
- .probe = axi_i2s_dai_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
.ops = &axi_i2s_dai_ops,
- .symmetric_rates = 1,
+ .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 = {
@@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
.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;
@@ -191,8 +197,9 @@ static int axi_i2s_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2s);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ 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);
@@ -213,13 +220,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
return ret;
- 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_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->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;
@@ -240,6 +263,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
goto err_clk_disable;
+ dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
+ str_enabled_disabled(i2s->has_capture),
+ str_enabled_disabled(i2s->has_playback));
+
return 0;
err_clk_disable:
@@ -247,13 +274,11 @@ err_clk_disable:
return ret;
}
-static int axi_i2s_dev_remove(struct platform_device *pdev)
+static void axi_i2s_dev_remove(struct platform_device *pdev)
{
struct axi_i2s *i2s = platform_get_drvdata(pdev);
clk_disable_unprepare(i2s->clk);
-
- return 0;
}
static const struct of_device_id axi_i2s_of_match[] = {
diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c
index d7259d412892..5581134201a3 100644
--- a/sound/soc/adi/axi-spdif.c
+++ b/sound/soc/adi/axi-spdif.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2013, Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/init.h>
@@ -149,6 +148,7 @@ static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
+ .probe = axi_spdif_dai_probe,
.startup = axi_spdif_startup,
.shutdown = axi_spdif_shutdown,
.trigger = axi_spdif_trigger,
@@ -156,7 +156,6 @@ static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
};
static struct snd_soc_dai_driver axi_spdif_dai = {
- .probe = axi_spdif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -168,6 +167,7 @@ static struct snd_soc_dai_driver axi_spdif_dai = {
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 = {
@@ -190,8 +190,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, spdif);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -240,13 +239,11 @@ err_clk_disable:
return ret;
}
-static int axi_spdif_dev_remove(struct platform_device *pdev)
+static void axi_spdif_dev_remove(struct platform_device *pdev)
{
struct axi_spdif *spdif = platform_get_drvdata(pdev);
clk_disable_unprepare(spdif->clk);
-
- return 0;
}
static const struct of_device_id axi_spdif_of_match[] = {
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index d5838402f667..fd35a03aadcb 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -1,11 +1,190 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "AMD"
+
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
+ select SND_AMD_ACP_CONFIG
+ 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/MAX98388"
+ select SND_SOC_NAU8821
+ select SND_SOC_CS35L41_SPI
+ select SND_SOC_MAX98388
+ select SND_AMD_ACP_CONFIG
+ depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
+ help
+ This option enables machine driver for Vangogh platform
+ using NAU8821 and either CS35L41 or MAX98388 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 && ACPI
+ 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_ACP63_TOPLEVEL
+ tristate "support for AMD platforms with ACP version >= 6.3"
+ default SND_AMD_ACP_CONFIG
+ depends on SND_AMD_ACP_CONFIG
+ depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
+ depends on X86 || COMPILE_TEST
+ help
+ This adds support for AMD platforms with ACP version >= 6.3.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_AMD_ACP63_TOPLEVEL
+
+config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ tristate
+ select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_AMD_SOUNDWIRE
+ tristate "Support for SoundWire based AMD platforms"
+ default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on ACPI
+ depends on SOUNDWIRE_AMD
+ help
+ This adds support for SoundWire for AMD platforms.
+ Say Y if you want to enable SoundWire links with SOF.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS
+ tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support"
+ select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_ACPI_AMD_MATCH
+ depends on X86 && PCI && ACPI
+ help
+ This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1
+ variants support. By enabling this flag build will be triggered
+ for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire 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".
+
+endif
+endmenu
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index f07fd2e2870a..4f89d962cce2 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -1,5 +1,21 @@
-acp_audio_dma-objs := acp-pcm-dma.o
-snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
+# SPDX-License-Identifier: GPL-2.0-only
+acp_audio_dma-y := acp-pcm-dma.o
+snd-soc-acp-da7219mx98357-mach-y := acp-da7219-max98357a.o
+snd-soc-acp-rt5645-mach-y := acp-rt5645.o
+snd-soc-acp-es8336-mach-y := acp-es8336.o
+snd-soc-acp-rt5682-mach-y := acp3x-rt5682-max9836.o
+snd-acp-config-y := 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_AMD_ACP_CONFIG) += 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..365209ea53f3
--- /dev/null
+++ b/sound/soc/amd/acp-config.c
@@ -0,0 +1,337 @@
+// 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, 2023 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"
+
+#define ACP_7_0_REV 0x70
+
+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"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ },
+ {}
+ },
+ },
+};
+
+static int snd_amd_acp_acpi_find_config(struct pci_dev *pci)
+{
+ const union acpi_object *obj;
+ int acp_flag = FLAG_AMD_LEGACY_ONLY_DMIC;
+
+ if (!acpi_dev_get_property(ACPI_COMPANION(&pci->dev), "acp-audio-config-flag",
+ ACPI_TYPE_INTEGER, &obj))
+ acp_flag = obj->integer.value;
+
+ return acp_flag;
+}
+
+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
+ * For platforms which has ACP 7.0 or higher, read the acp
+ * config flag from BIOS ACPI table and for older platforms
+ * read it from DMI tables.
+ */
+ if (!pci->revision)
+ return 0;
+ else if (pci->revision >= ACP_7_0_REV)
+ return snd_amd_acp_acpi_find_config(pci);
+
+ 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"}
+};
+
+static struct snd_soc_acpi_codecs amp_max98388 = {
+ .num_codecs = 1,
+ .codecs = {"ADS8388"}
+};
+
+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_vangogh_sof_machines[] = {
+ {
+ .id = "NVTN2020",
+ .drv_name = "nau8821-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max98388,
+ .fw_filename = "sof-vangogh.ri",
+ .sof_tplg_filename = "sof-vangogh-nau8821-max.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_vangogh_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);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "acp63-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_6_3.ri",
+ .sof_tplg_filename = "sof-acp_6_3.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = {
+ {
+ .id = "AMDI1010",
+ .drv_name = "acp70-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_7_0.ri",
+ .sof_tplg_filename = "sof-acp_7_0.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines);
+
+MODULE_DESCRIPTION("AMD ACP Machine Configuration Module");
+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..42aa009c4e13
--- /dev/null
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -0,0 +1,811 @@
+// 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 snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+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 = snd_soc_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_pins(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,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ 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 = snd_soc_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 = snd_soc_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_pins(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,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ 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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
+SND_SOC_DAILINK_DEF(designware3,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3")));
+
+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")));
+
+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,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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_LINE("Line Out", 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("Line Out"),
+ 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 const 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..b16dde0e2987
--- /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 = snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ card = rtd->card;
+ machine = snd_soc_card_get_drvdata(card);
+ codec_dai = snd_soc_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.1")));
+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.0")));
+
+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,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .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");
+ return -ENODEV;
+ }
+
+ 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
index c33a512283a4..897dde630022 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -1,21 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD ALSA SoC PCM Driver for ACP 2.x
*
* Copyright 2014-2015 Advanced Micro Devices, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#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>
@@ -23,6 +16,8 @@
#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
@@ -35,12 +30,14 @@
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER
-#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192
+#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 |
@@ -128,7 +125,8 @@ 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,
+/*
+ * 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,
@@ -147,17 +145,18 @@ static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
& dscr_strt_idx),
acp_mmio, mmACP_DMA_DSCR_STRT_IDX_0 + ch_num);
- /* program a DMA channel with the number of descriptors to be
+ /*
+ * 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);
+ 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 descritor information passed */
+/* 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)
@@ -178,139 +177,128 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
}
-/* Initialize the DMA descriptor information for transfer between
+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, u32 asic_type)
+ u32 size, int direction,
+ u32 pte_offset, u16 ch,
+ u32 sram_bank, u16 dma_dscr_idx,
+ u32 asic_type)
{
u16 i;
- u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
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 = PLAYBACK_START_DMA_DESCR_CH12 + i;
- dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS
- + (i * (size/2));
+ 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));
+ + (pte_offset * SZ_4K) + (i * (size / 2));
switch (asic_type) {
case CHIP_STONEY:
dmadscr[i].xfer_val |=
- (ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) |
+ (ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM << 16) |
(size / 2);
break;
default:
dmadscr[i].xfer_val |=
- (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
+ (ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM << 16) |
(size / 2);
}
} else {
- dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
+ 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].src = ACP_SHARED_RAM_BANK_3_ADDRESS +
- (i * (size/2));
- dmadscr[i].dest =
- ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
- (pte_offset * SZ_4K) + (i * (size/2));
dmadscr[i].xfer_val |=
- BIT(22) |
- (ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) |
+ (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) |
(size / 2);
break;
default:
- dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
- (i * (size/2));
- dmadscr[i].dest =
- ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
- (pte_offset * SZ_4K) + (i * (size/2));
dmadscr[i].xfer_val |=
- BIT(22) |
- (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
+ (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) |
(size / 2);
}
}
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
- &dmadscr[i]);
+ &dmadscr[i]);
}
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
- else
- config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH14,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
+ 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
+/*
+ * 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 asic_type)
+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;
- u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13;
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 = PLAYBACK_START_DMA_DESCR_CH13 + i;
- dmadscr[i].src = ACP_SHARED_RAM_BANK_1_ADDRESS +
- (i * (size/2));
+ 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) | (TO_ACP_I2S_1 << 16) |
+ dmadscr[i].xfer_val |= BIT(22) | (destination << 16) |
(size / 2);
} else {
- dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i;
+ dma_dscr_idx = dma_dscr_idx + i;
/* dmadscr[i].src is unused by hardware. */
dmadscr[i].src = 0;
- switch (asic_type) {
- case CHIP_STONEY:
- dmadscr[i].dest =
- ACP_SHARED_RAM_BANK_3_ADDRESS +
- (i * (size / 2));
- break;
- default:
- dmadscr[i].dest =
- ACP_SHARED_RAM_BANK_5_ADDRESS +
- (i * (size / 2));
- }
+ dmadscr[i].dest =
+ sram_bank + (i * (size / 2));
dmadscr[i].xfer_val |= BIT(22) |
- (FROM_ACP_I2S_1 << 16) | (size / 2);
+ (destination << 16) | (size / 2);
}
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
- &dmadscr[i]);
+ &dmadscr[i]);
}
- /* Configure the DMA channel with the above descriptore */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- config_acp_dma_channel(acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH13,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
- else
- config_acp_dma_channel(acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH15,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
+ 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, struct page *pg,
+static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr,
u16 num_of_pages, u32 pte_offset)
{
u16 page_idx;
- u64 addr;
u32 low;
u32 high;
u32 offset;
@@ -319,8 +307,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
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);
- addr = page_to_phys(pg);
+ acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
low = lower_32_bits(addr);
high = upper_32_bits(addr);
@@ -329,43 +316,102 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
/* 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);
+ 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 contiguos page */
- pg++;
+ /* Move to next physically contiguous page */
+ addr += PAGE_SIZE;
}
}
static void config_acp_dma(void __iomem *acp_mmio,
- struct audio_substream_data *audio_config,
- u32 asic_type)
+ struct audio_substream_data *rtd,
+ u32 asic_type)
{
- u32 pte_offset;
-
- if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK)
- pte_offset = ACP_PLAYBACK_PTE_OFFSET;
- else
- pte_offset = ACP_CAPTURE_PTE_OFFSET;
+ u16 ch_acp_sysmem, ch_acp_i2s;
- acp_pte_config(acp_mmio, audio_config->pg, audio_config->num_of_pages,
- pte_offset);
+ 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, audio_config->size,
- audio_config->direction, pte_offset, asic_type);
-
+ 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, audio_config->size,
- audio_config->direction, asic_type);
+ 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)
+static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular)
{
u32 dma_ctrl;
@@ -375,7 +421,8 @@ static void acp_dma_start(void __iomem *acp_mmio,
/* Invalidating the DAGB cache */
acp_reg_write(1, acp_mmio, mmACP_DAGB_ATU_CTRL);
- /* configure the DMA channel and start the DMA transfer
+ /*
+ * 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
*/
@@ -383,8 +430,10 @@ static void acp_dma_start(void __iomem *acp_mmio,
switch (ch_num) {
case ACP_TO_I2S_DMA_CH_NUM:
- case ACP_TO_SYSRAM_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:
@@ -392,7 +441,7 @@ static void acp_dma_start(void __iomem *acp_mmio,
break;
}
- /* enable for ACP SRAM to/from I2S DMA channel */
+ /* enable for ACP to SRAM DMA channel */
if (is_circular == true)
dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
else
@@ -410,9 +459,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
- /* clear the dma control register fields before writing zero
+ /*
+ * 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;
@@ -420,9 +470,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 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
- */
+ /*
+ * 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);
}
@@ -431,13 +482,14 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
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
- */
+ /*
+ * 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);
+ + ch_num);
break;
}
if (--count == 0) {
@@ -450,7 +502,7 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
}
static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
- bool power_on)
+ bool power_on)
{
u32 val, req_reg, sts_reg, sts_reg_mask;
u32 loops = 1000;
@@ -530,7 +582,7 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
while (true) {
val = acp_reg_read(acp_mmio, mmACP_STATUS);
- if (val & (u32) 0x1)
+ if (val & (u32)0x1)
break;
if (--count == 0) {
pr_err("Failed to reset ACP\n");
@@ -544,13 +596,20 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
- /* initiailize Onion control DAGB register */
+ /* 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);
+ mmACP_AXI2DAGB_ONION_CNTL);
- /* initiailize Garlic control DAGB registers */
+ /* initialize Garlic control DAGB registers */
acp_reg_write(ACP_GARLIC_CNTL_DEFAULT, acp_mmio,
- mmACP_AXI2DAGB_GARLIC_CNTL);
+ mmACP_AXI2DAGB_GARLIC_CNTL);
sram_pte_offset = ACP_DAGB_GRP_SRAM_BASE_ADDRESS |
ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK |
@@ -558,17 +617,18 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
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);
+ mmACP_DAGB_PAGE_SIZE_GRP_1);
acp_reg_write(ACP_SRAM_BASE_ADDRESS, acp_mmio,
- mmACP_DMA_DESC_BASE_ADDR);
+ mmACP_DMA_DESC_BASE_ADDR);
- /* Num of descriptiors in SRAM 0x4, means 256 descriptors;(64 * 4) */
+ /* 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);
+ acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
- /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
+ /*
+ * 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
@@ -579,13 +639,6 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
for (bank = 1; bank < 48; bank++)
acp_set_sram_bank_state(acp_mmio, bank, false);
}
-
- /* Stoney supports 16bit resolution */
- if (asic_type == CHIP_STONEY) {
- val = acp_reg_read(acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
- val |= 0x03;
- acp_reg_write(val, acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
- }
return 0;
}
@@ -613,7 +666,7 @@ static int acp_deinit(void __iomem *acp_mmio)
}
udelay(100);
}
- /** Disable ACP clock */
+ /* Disable ACP clock */
val = acp_reg_read(acp_mmio, mmACP_CONTROL);
val &= ~ACP_CONTROL__ClkEn_MASK;
acp_reg_write(val, acp_mmio, mmACP_CONTROL);
@@ -622,7 +675,7 @@ static int acp_deinit(void __iomem *acp_mmio)
while (true) {
val = acp_reg_read(acp_mmio, mmACP_STATUS);
- if (!(val & (u32) 0x1))
+ if (!(val & (u32)0x1))
break;
if (--count == 0) {
pr_err("Failed to reset ACP\n");
@@ -653,24 +706,29 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
if ((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) != 0) {
valid_irq = true;
- if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
- PLAYBACK_START_DMA_DESCR_CH13)
- dscr_idx = PLAYBACK_END_DMA_DESCR_CH12;
- else
- dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
- config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx,
- 1, 0);
- acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
+ 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);
+ }
- snd_pcm_period_elapsed(irq_data->play_stream);
+ 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);
+ }
- 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_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_15) ==
+ if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_14) ==
CAPTURE_START_DMA_DESCR_CH15)
dscr_idx = CAPTURE_END_DMA_DESCR_CH14;
else
@@ -679,15 +737,28 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
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);
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
}
- if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) {
+ if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) {
valid_irq = true;
- snd_pcm_period_elapsed(irq_data->capture_stream);
- acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16,
- acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ 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)
@@ -696,17 +767,16 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
return IRQ_NONE;
}
-static int acp_dma_open(struct snd_pcm_substream *substream)
+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 snd_soc_pcm_runtime *prtd = substream->private_data;
- struct audio_drv_data *intr_data = dev_get_drvdata(prtd->platform->dev);
-
+ 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 == NULL)
+ if (!adata)
return -ENOMEM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -730,7 +800,7 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
- dev_err(prtd->platform->dev, "set integer constraint failed\n");
+ dev_err(component->dev, "set integer constraint failed\n");
kfree(adata);
return ret;
}
@@ -738,17 +808,20 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
adata->acp_mmio = intr_data->acp_mmio;
runtime->private_data = adata;
- /* Enable ACP irq, when neither playback or capture streams are
+ /*
+ * 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_stream && !intr_data->capture_stream)
+ */
+ 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) {
- intr_data->play_stream = substream;
- /* For Stoney, Memory gating is disabled,i.e SRAM Banks
+ /*
+ * 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.
*/
@@ -758,7 +831,6 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
bank, true);
}
} else {
- intr_data->capture_stream = substream;
if (intr_data->asic_type != CHIP_STONEY) {
for (bank = 5; bank <= 8; bank++)
acp_set_sram_bank_state(intr_data->acp_mmio,
@@ -769,16 +841,18 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int acp_dma_hw_params(struct snd_pcm_substream *substream,
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- int status;
uint64_t size;
- struct page *pg;
+ u32 val = 0;
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
+ struct snd_soc_pcm_runtime *prtd = snd_soc_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;
@@ -786,126 +860,262 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
if (WARN_ON(!rtd))
return -EINVAL;
- size = params_buffer_bytes(params);
- status = snd_pcm_lib_malloc_pages(substream, size);
- if (status < 0)
- return status;
+ 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);
+ }
- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
- pg = virt_to_page(substream->dma_buffer.area);
+ 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;
+ }
+ }
- if (pg != NULL) {
- acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
- /* Save for runtime private data */
- rtd->pg = pg;
- rtd->order = get_order(size);
+ size = params_buffer_bytes(params);
- /* Fill the page table entries in ACP SRAM */
- rtd->pg = pg;
- rtd->size = size;
- rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
- rtd->direction = substream->stream;
+ 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);
- config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
- status = 0;
- } else {
- status = -ENOMEM;
- }
- return status;
-}
+ /* Fill the page table entries in ACP SRAM */
+ rtd->size = size;
+ rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ rtd->direction = substream->stream;
-static int acp_dma_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
+ config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
+ return 0;
}
-static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream)
+static u64 acp_get_byte_count(struct audio_substream_data *rtd)
{
- union acp_dma_count playback_dma_count;
- union acp_dma_count capture_dma_count;
- u64 bytescount = 0;
+ union acp_dma_count byte_count;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- playback_dma_count.bcount.high = acp_reg_read(acp_mmio,
- mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH);
- playback_dma_count.bcount.low = acp_reg_read(acp_mmio,
- mmACP_I2S_TRANSMIT_BYTE_CNT_LOW);
- bytescount = playback_dma_count.bytescount;
- } else {
- capture_dma_count.bcount.high = acp_reg_read(acp_mmio,
- mmACP_I2S_RECEIVED_BYTE_CNT_HIGH);
- capture_dma_count.bcount.low = acp_reg_read(acp_mmio,
- mmACP_I2S_RECEIVED_BYTE_CNT_LOW);
- bytescount = capture_dma_count.bytescount;
- }
- return bytescount;
+ 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_pcm_substream *substream)
+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;
- buffersize = frames_to_bytes(runtime, runtime->buffer_size);
- bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (bytescount > rtd->renderbytescount)
- bytescount = bytescount - rtd->renderbytescount;
+ 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 {
- if (bytescount > rtd->capturebytescount)
- bytescount = bytescount - rtd->capturebytescount;
+ 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);
}
- pos = do_div(bytescount, buffersize);
return bytes_to_frames(runtime, pos);
}
-static int acp_dma_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return snd_pcm_lib_default_mmap(substream, vma);
+ 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_pcm_substream *substream)
+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 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- NUM_DSCRS_PER_CHANNEL, 0);
- config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH13,
- NUM_DSCRS_PER_CHANNEL, 0);
+
+ if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_acp_sysmem = rtd->ch1;
+ ch_acp_i2s = rtd->ch2;
} else {
- config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH14,
- NUM_DSCRS_PER_CHANNEL, 0);
- config_acp_dma_channel(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH15,
- NUM_DSCRS_PER_CHANNEL, 0);
+ 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_pcm_substream *substream, int cmd)
+static int acp_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret;
- u32 loops = 4000;
- u64 bytescount = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct audio_substream_data *rtd = runtime->private_data;
if (!rtd)
@@ -914,146 +1124,141 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- bytescount = acp_get_byte_count(rtd->acp_mmio,
- substream->stream);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (rtd->renderbytescount == 0)
- rtd->renderbytescount = bytescount;
- acp_dma_start(rtd->acp_mmio,
- SYSRAM_TO_ACP_CH_NUM, false);
- while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) &
- BIT(SYSRAM_TO_ACP_CH_NUM)) {
- if (!loops--) {
- dev_err(prtd->platform->dev,
- "acp dma start timeout\n");
- return -ETIMEDOUT;
- }
- cpu_relax();
+ 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);
}
-
- acp_dma_start(rtd->acp_mmio,
- ACP_TO_I2S_DMA_CH_NUM, true);
-
+ 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 {
- if (rtd->capturebytescount == 0)
- rtd->capturebytescount = bytescount;
- acp_dma_start(rtd->acp_mmio,
- I2S_TO_ACP_DMA_CH_NUM, true);
+ 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:
- /* Need to stop only circular DMA channels :
- * ACP_TO_I2S_DMA_CH_NUM / I2S_TO_ACP_DMA_CH_NUM. Non-circular
- * channels will stopped automatically after its transfer
- * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = acp_dma_stop(rtd->acp_mmio,
- ACP_TO_I2S_DMA_CH_NUM);
- rtd->renderbytescount = 0;
- } else {
- ret = acp_dma_stop(rtd->acp_mmio,
- I2S_TO_ACP_DMA_CH_NUM);
- rtd->capturebytescount = 0;
- }
+ 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_pcm_runtime *rtd)
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- int ret;
- struct audio_drv_data *adata = dev_get_drvdata(rtd->platform->dev);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ struct device *parent = component->dev->parent;
switch (adata->asic_type) {
case CHIP_STONEY:
- ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- NULL, ST_MIN_BUFFER,
- ST_MAX_BUFFER);
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ ST_MIN_BUFFER,
+ ST_MAX_BUFFER);
break;
default:
- ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- NULL, MIN_BUFFER,
- MAX_BUFFER);
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ MIN_BUFFER,
+ MAX_BUFFER);
break;
}
- if (ret < 0)
- dev_err(rtd->platform->dev,
- "buffer preallocation failer error:%d\n", ret);
- return ret;
+ return 0;
}
-static int acp_dma_close(struct snd_pcm_substream *substream)
+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 snd_soc_pcm_runtime *prtd = substream->private_data;
- struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
-
- kfree(rtd);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- adata->play_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);
+ 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 {
- adata->capture_stream = NULL;
- if (adata->asic_type != CHIP_STONEY) {
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- false);
+ 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
+ /*
+ * Disable ACP irq, when the current stream is being closed and
* another stream is also not active.
- */
- if (!adata->play_stream && !adata->capture_stream)
+ */
+ 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_pcm_ops acp_dma_ops = {
- .open = acp_dma_open,
- .close = acp_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = acp_dma_hw_params,
- .hw_free = acp_dma_hw_free,
- .trigger = acp_dma_trigger,
- .pointer = acp_dma_pointer,
- .mmap = acp_dma_mmap,
- .prepare = acp_dma_prepare,
-};
-
-static struct snd_soc_platform_driver acp_asoc_platform = {
- .ops = &acp_dma_ops,
- .pcm_new = acp_dma_new,
+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;
+ int status, irq;
struct audio_drv_data *audio_drv_data;
- struct resource *res;
const u32 *pdata = pdev->dev.platform_data;
if (!pdata) {
@@ -1062,32 +1267,34 @@ static int acp_audio_probe(struct platform_device *pdev)
}
audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
- GFP_KERNEL);
- if (audio_drv_data == NULL)
+ GFP_KERNEL);
+ if (!audio_drv_data)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
+ 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'
+ /*
+ * 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_stream = NULL;
- audio_drv_data->capture_stream = NULL;
+ 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;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
return -ENODEV;
- }
- status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler,
- 0, "ACP_IRQ", &pdev->dev);
+ 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;
@@ -1102,7 +1309,8 @@ static int acp_audio_probe(struct platform_device *pdev)
return status;
}
- status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform);
+ 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;
@@ -1115,7 +1323,7 @@ static int acp_audio_probe(struct platform_device *pdev)
return status;
}
-static int acp_audio_remove(struct platform_device *pdev)
+static void acp_audio_remove(struct platform_device *pdev)
{
int status;
struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
@@ -1123,16 +1331,14 @@ static int acp_audio_remove(struct platform_device *pdev)
status = acp_deinit(adata->acp_mmio);
if (status)
dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status);
- snd_soc_unregister_platform(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
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);
@@ -1141,29 +1347,46 @@ static int acp_pcm_resume(struct device *dev)
return status;
}
- if (adata->play_stream && adata->play_stream->runtime) {
- /* For Stoney, Memory gating is disabled,i.e SRAM Banks
+ 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);
+ true);
}
- config_acp_dma(adata->acp_mmio,
- adata->play_stream->runtime->private_data,
- adata->asic_type);
+ rtd = adata->play_i2ssp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
}
- if (adata->capture_stream && adata->capture_stream->runtime) {
+ 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);
+ 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);
}
- config_acp_dma(adata->acp_mmio,
- adata->capture_stream->runtime->private_data,
- adata->asic_type);
}
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index 941aed6bb364..11d373169380 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -42,13 +42,23 @@
#define CZ_PLAT_CLK 24000000
static struct snd_soc_jack cz_jack;
+static struct snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
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 = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_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);
@@ -71,16 +81,18 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
struct snd_soc_card *card;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *codec;
- codec = rtd->codec;
+ codec = snd_soc_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, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(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,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -91,33 +103,38 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static struct snd_soc_ops cz_aif1_ops = {
+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")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
+
+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")));
+
static struct snd_soc_dai_link cz_dai_rt5650[] = {
{
.name = "amd-rt5645-play",
.stream_name = "RT5645_AIF1",
- .platform_name = "acp_audio_dma.0.auto",
- .cpu_dai_name = "designware-i2s.1.auto",
- .codec_dai_name = "rt5645-aif1",
- .codec_name = "i2c-10EC5650:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | 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",
- .platform_name = "acp_audio_dma.0.auto",
- .cpu_dai_name = "designware-i2s.2.auto",
- .codec_dai_name = "rt5645-aif1",
- .codec_name = "i2c-10EC5650:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &cz_aif1_ops,
+ SND_SOC_DAILINK_REG(designware2, codec, platform),
},
};
@@ -177,11 +194,13 @@ static int cz_probe(struct platform_device *pdev)
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 = {
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index ecb458935d1e..b29bef90f886 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -10,17 +10,30 @@
#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
-/* Playback SRAM address (as a destination in dma descriptor) */
-#define ACP_SHARED_RAM_BANK_1_ADDRESS 0x4002000
-
-/* Capture SRAM address (as a source in dma descriptor) */
-#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
-#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000
+/*
+ * 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
@@ -35,8 +48,16 @@
#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
@@ -54,8 +75,20 @@
#define ACP_TO_I2S_DMA_CH_NUM 13
/* Capture DMA channels */
-#define ACP_TO_SYSRAM_CH_NUM 14
-#define I2S_TO_ACP_DMA_CH_NUM 15
+#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
@@ -69,7 +102,30 @@
#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,
@@ -79,21 +135,46 @@ enum acp_dma_priority_level {
};
struct audio_substream_data {
- struct page *pg;
+ 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 renderbytescount;
- u64 capturebytescount;
+ u64 bytescount;
void __iomem *acp_mmio;
};
struct audio_drv_data {
- struct snd_pcm_substream *play_stream;
- struct snd_pcm_substream *capture_stream;
+ 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 {
@@ -113,26 +194,30 @@ enum {
};
enum {
- ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION = 0x0,
- ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
- ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM = 0x8,
- ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
- ACP_DMA_ATTRIBUTES_FORCE_SIZE = 0xF
+ 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
+ /*
+ * 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
- */
+ /*
+ * 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..c2a60bc80ee6
--- /dev/null
+++ b/sound/soc/amd/acp/Kconfig
@@ -0,0 +1,189 @@
+# 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.
+
+config SND_SOC_ACPI_AMD_MATCH
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+if SND_SOC_AMD_ACP_COMMON
+
+config SND_SOC_AMD_ACP_PDM
+ tristate
+
+config SND_SOC_AMD_ACP_LEGACY_COMMON
+ tristate
+
+config SND_SOC_AMD_ACP_I2S
+ tristate
+
+config SND_SOC_AMD_ACPI_MACH
+ 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
+ depends on ACPI
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This options enables generic PCI driver for ACP device.
+
+config SND_AMD_ASOC_RENOIR
+ tristate "AMD ACP ASOC Renoir Support"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ 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"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ depends on AMD_NODE
+ 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_AMD_ASOC_ACP63
+ tristate "AMD ACP ASOC ACP6.3 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp6.3 I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP6.3
+ If unsure select "N".
+
+config SND_AMD_ASOC_ACP70
+ tristate "AMD ACP ASOC Acp7.0 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp7.0 PDM support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP7.0
+ 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
+ select SND_SOC_NAU8821
+ select SND_SOC_MAX98388
+ 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.
+
+config SND_SOC_AMD_SDW_MACH_COMMON
+ tristate
+ help
+ This option enables common SoundWire Machine driver module for
+ AMD platforms.
+
+config SND_SOC_AMD_SOF_SDW_MACH
+ tristate "AMD SOF Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ help
+ This option enables SOF sound card support for SoundWire enabled
+ AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+config SND_SOC_AMD_LEGACY_SDW_MACH
+ tristate "AMD Legacy(No DSP) Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT712_SDCA_SDW
+ select SND_SOC_RT712_SDCA_DMIC_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT1320_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ help
+ This option enables Legacy(No DSP) sound card support for SoundWire
+ enabled AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+endif # SND_SOC_AMD_ACP_COMMON
+
+config SND_AMD_SOUNDWIRE_ACPI
+ tristate
+ depends on ACPI
+ help
+ This options enables ACPI helper functions for SoundWire
+ interface for AMD platforms.
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
new file mode 100644
index 000000000000..08220b9a3802
--- /dev/null
+++ b/sound/soc/amd/acp/Makefile
@@ -0,0 +1,50 @@
+# 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-y := acp-platform.o
+snd-acp-i2s-y := acp-i2s.o
+snd-acp-pdm-y := acp-pdm.o
+snd-acp-legacy-common-y := acp-legacy-common.o
+snd-acp-pci-y := acp-pci.o
+snd-amd-sdw-acpi-y := amd-sdw-acpi.o
+snd-amd-acpi-mach-y := amd-acpi-mach.o
+
+#platform specific driver
+snd-acp-renoir-y := acp-renoir.o
+snd-acp-rembrandt-y := acp-rembrandt.o
+snd-acp63-y := acp63.o
+snd-acp70-y := acp70.o
+
+#machine specific driver
+snd-acp-mach-y := acp-mach-common.o
+snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
+snd-acp-sof-mach-y := acp-sof-mach.o
+snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
+snd-acp-sdw-mach-y := acp-sdw-mach-common.o
+snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
+snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-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_LEGACY_COMMON) += snd-acp-legacy-common.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
+obj-$(CONFIG_SND_SOC_AMD_ACPI_MACH) += snd-amd-acpi-mach.o
+
+obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
+
+obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.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
+obj-$(CONFIG_SND_SOC_ACPI_AMD_MATCH) += snd-soc-acpi-amd-match.o
+obj-$(CONFIG_SND_SOC_AMD_SDW_MACH_COMMON) += snd-acp-sdw-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_SDW_MACH) += snd-acp-sdw-sof-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_SDW_MACH) += snd-acp-sdw-legacy-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..4ba0a66981ea
--- /dev/null
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -0,0 +1,699 @@
+// 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 <linux/bitfield.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_playcap"
+#define I2S_MASTER_MODE_ENABLE 1
+#define LRCLK_DIV_FIELD GENMASK(10, 2)
+#define BCLK_DIV_FIELD GENMASK(23, 11)
+#define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2)
+#define ACP63_BCLK_DIV_FIELD GENMASK(23, 13)
+
+static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id)
+{
+ u32 i2s_clk_reg, val;
+
+ switch (dai_id) {
+ case I2S_SP_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ case I2S_BT_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN;
+ break;
+ case I2S_HS_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ default:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ val = I2S_MASTER_MODE_ENABLE;
+ if (chip->tdm_mode)
+ val |= BIT(1);
+
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div);
+ break;
+ default:
+ val |= FIELD_PREP(LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(BCLK_DIV_FIELD, chip->bclk_div);
+ }
+ writel(val, chip->base + i2s_clk_reg);
+}
+
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct device *dev = cpu_dai->component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ int mode;
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ chip->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ chip->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_chip_info *chip;
+ struct acp_stream *stream;
+ int slot_len, no_of_slots;
+
+ chip = dev_get_drvdata(dev->parent);
+ 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 (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ 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;
+ }
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ switch (slots) {
+ case 1 ... 31:
+ no_of_slots = slots;
+ break;
+ case 32:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ return -EINVAL;
+ }
+
+ slots = no_of_slots;
+
+ spin_lock_irq(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ spin_unlock_irq(&chip->acp_lock);
+ return -EINVAL;
+ }
+ }
+ spin_unlock_irq(&chip->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_chip_info *chip;
+ struct acp_resource *rsrc;
+ u32 val;
+ u32 xfer_resolution;
+ u32 reg_val, fmt_reg, tdm_fmt;
+ u32 lrclk_div_val, bclk_div_val;
+
+ chip = dev_get_drvdata(dev->parent);
+ rsrc = chip->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;
+ }
+ chip->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution;
+ } 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;
+ }
+ chip->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution;
+ }
+
+ val = readl(chip->base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (xfer_resolution << 3);
+ writel(val, chip->base + reg_val);
+
+ if (chip->tdm_mode) {
+ val = readl(chip->base + reg_val);
+ writel(val | BIT(1), chip->base + reg_val);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm_fmt = chip->tdm_tx_fmt[dai->driver->id - 1];
+ else
+ tdm_fmt = chip->tdm_rx_fmt[dai->driver->id - 1];
+ writel(tdm_fmt, chip->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;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 48000:
+ case 96000:
+ case 192000:
+ switch (params_channels(params)) {
+ case 2:
+ break;
+ case 4:
+ bclk_div_val = bclk_div_val >> 1;
+ lrclk_div_val = lrclk_div_val << 1;
+ break;
+ case 8:
+ bclk_div_val = bclk_div_val >> 2;
+ lrclk_div_val = lrclk_div_val << 2;
+ break;
+ case 16:
+ bclk_div_val = bclk_div_val >> 3;
+ lrclk_div_val = lrclk_div_val << 3;
+ break;
+ case 32:
+ bclk_div_val = bclk_div_val >> 4;
+ lrclk_div_val = lrclk_div_val << 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported channels %#x\n",
+ params_channels(params));
+ }
+ break;
+ default:
+ break;
+ }
+ chip->lrclk_div = lrclk_div_val;
+ chip->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_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->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(chip, 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(chip);
+ reg_val = ACP_BTTDM_ITER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_TX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE(chip);
+ 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(chip);
+ reg_val = ACP_BTTDM_IRER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_RX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE(chip);
+ 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, chip->base + water_val);
+ writel(buf_size, chip->base + buf_reg);
+ if (rsrc->soc_mclk)
+ acp_set_i2s_clk(chip, dai->driver->id);
+ val = readl(chip->base + reg_val);
+ val = val | BIT(0);
+ writel(val, chip->base + reg_val);
+ writel(1, chip->base + ier_val);
+ 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(chip->base + reg_val);
+ val = val & ~BIT(0);
+ writel(val, chip->base + reg_val);
+
+ if (!(readl(chip->base + ACP_BTTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_BTTDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_BTTDM_IER);
+ if (!(readl(chip->base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_I2STDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_I2STDM_IER);
+ if (!(readl(chip->base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_HSTDM_IRER) & BIT(0)))
+ writel(0, chip->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_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->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(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ 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;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->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;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 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(chip, 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_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->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");
+
+MODULE_DESCRIPTION("AMD ACP Audio I2S controller");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
new file mode 100644
index 000000000000..3078f459e005
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -0,0 +1,647 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com>
+//
+
+/*
+ * Common file to be used by amd platforms
+ */
+
+#include "amd.h"
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/export.h>
+
+#include "../mach-config.h"
+
+#define ACP_RENOIR_PDM_ADDR 0x02
+#define ACP_REMBRANDT_PDM_ADDR 0x03
+#define ACP63_PDM_ADDR 0x02
+#define ACP70_PDM_ADDR 0x02
+
+struct acp_resource rn_rsrc = {
+ .offset = 20,
+ .no_of_ctrls = 1,
+ .irqp_used = 0,
+ .irq_reg_offset = 0x1800,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x02052800,
+};
+EXPORT_SYMBOL_NS_GPL(rn_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource rmb_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(rmb_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp63_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(acp63_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp70_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x10000,
+ .sram_pte_offset = 0x03800000,
+};
+EXPORT_SYMBOL_NS_GPL(acp70_rsrc, "SND_SOC_ACP_COMMON");
+
+static const struct snd_acp_hw_ops acp_common_hw_ops = {
+ /* ACP hardware initilizations */
+ .acp_init = acp_init,
+ .acp_deinit = acp_deinit,
+
+ /* ACP Interrupts*/
+ .irq = acp_irq_handler,
+ .en_interrupts = acp_enable_interrupts,
+ .dis_interrupts = acp_disable_interrupts,
+};
+
+irqreturn_t acp_irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream;
+ u16 i2s_flag = 0;
+ u32 ext_intr_stat, ext_intr_stat1;
+
+ if (rsrc->no_of_ctrls == 2)
+ ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(chip, (rsrc->irqp_used - 1)));
+
+ ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ if (ext_intr_stat & stream->irq_bit) {
+ writel(stream->irq_bit,
+ ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ if (chip->rsrc->no_of_ctrls == 2) {
+ if (ext_intr_stat1 & stream->irq_bit) {
+ writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(chip,
+ (rsrc->irqp_used - 1)));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ if (i2s_flag)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+int acp_enable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+ u32 ext_intr_ctrl;
+
+ rsrc = chip->rsrc;
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(chip));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, "SND_SOC_ACP_COMMON");
+
+int acp_disable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+
+ rsrc = chip->rsrc;
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(chip));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, "SND_SOC_ACP_COMMON");
+
+static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct acp_stream *stream = runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+
+ u32 physical_addr, pdm_size, period_bytes;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ pdm_size = frames_to_bytes(runtime, runtime->buffer_size);
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init ACP PDM Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(pdm_size, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void set_acp_pdm_clk(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int pdm_ctrl;
+
+ /* Enable default ACP PDM clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(pdm_ctrl, chip->base + ACP_WOV_MISC_CTRL);
+ set_acp_pdm_ring_buffer(substream, dai);
+}
+
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 ext_int_ctrl;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+
+ /* Programming channel mask and sampling rate */
+ writel(chip->ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ /* Enabling ACP Pdm interuppts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ set_acp_pdm_clk(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, "SND_SOC_ACP_COMMON");
+
+static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size, reg_fifo_size, reg_fifo_addr;
+ u32 phy_addr, acp_fifo_addr, 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(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ 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, chip->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, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 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(chip, rsrc->irqp_used));
+ return 0;
+}
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip,
+ struct acp_stream *stream)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 tdm_fmt, reg_val, fmt_reg, val;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tdm_fmt = chip->tdm_tx_fmt[stream->dai_id - 1];
+ switch (stream->dai_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:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_tx_resolution[stream->dai_id - 1] << 3;
+ } else {
+ tdm_fmt = chip->tdm_rx_fmt[stream->dai_id - 1];
+ switch (stream->dai_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:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_rx_resolution[stream->dai_id - 1] << 3;
+ }
+ writel(val, chip->base + reg_val);
+ if (chip->tdm_mode == TDM_ENABLE) {
+ writel(tdm_fmt, chip->base + fmt_reg);
+ val = readl(chip->base + reg_val);
+ writel(val | 0x2, chip->base + reg_val);
+ }
+ return set_acp_i2s_dma_fifo(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, "SND_SOC_ACP_COMMON");
+
+static int acp_power_on(struct acp_chip_info *chip)
+{
+ u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg;
+ void __iomem *base;
+
+ base = chip->base;
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;
+ break;
+ case ACP_RMB_PCI_ID:
+ acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;
+ break;
+ case ACP63_PCI_ID:
+ acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL;
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(base + acp_pgfsm_stat_reg);
+ 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 + acp_pgfsm_ctrl_reg);
+
+ return readl_poll_timeout(base + acp_pgfsm_stat_reg, val,
+ !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp_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);
+}
+
+int acp_init(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* power on */
+ ret = acp_power_on(chip);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, chip->base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ writel(0, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_init, "SND_SOC_ACP_COMMON");
+
+int acp_deinit(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret)
+ return ret;
+
+ if (chip->acp_rev < ACP70_PCI_ID)
+ writel(0, chip->base + ACP_CONTROL);
+ else
+ writel(0x01, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_deinit, "SND_SOC_ACP_COMMON");
+int acp_machine_select(struct acp_chip_info *chip)
+{
+ struct snd_soc_acpi_mach *mach;
+ int size, platform;
+
+ if (chip->flag == FLAG_AMD_LEGACY_ONLY_DMIC && chip->is_pdm_dev) {
+ platform = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, "acp-pdm-mach",
+ PLATFORM_DEVID_NONE, &platform,
+ sizeof(platform));
+ } else {
+ size = sizeof(*chip->machines);
+ mach = snd_soc_acpi_find_machine(chip->machines);
+ if (!mach) {
+ dev_err(chip->dev, "warning: No matching ASoC machine driver found\n");
+ return -EINVAL;
+ }
+ mach->mach_params.subsystem_rev = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ }
+ if (IS_ERR(chip->mach_dev))
+ dev_warn(chip->dev, "Unable to register Machine device\n");
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_machine_select, "SND_SOC_ACP_COMMON");
+
+static void check_acp3x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP3X_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ chip->is_pdm_config = true;
+ break;
+ }
+}
+
+static void check_acp6x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void check_acp70_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_19:
+ case ACP_CONFIG_20:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ u32 pdm_addr, ret;
+
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ pdm_addr = ACP_RENOIR_PDM_ADDR;
+ check_acp3x_config(chip);
+ break;
+ case ACP_RMB_PCI_ID:
+ pdm_addr = ACP_REMBRANDT_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP63_PCI_ID:
+ pdm_addr = ACP63_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ pdm_addr = ACP70_PDM_ADDR;
+ check_acp70_config(chip);
+ break;
+ default:
+ break;
+ }
+
+ if (chip->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);
+ if (pdm_dev) {
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == pdm_addr)
+ chip->is_pdm_dev = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ chip->is_pdm_dev = dmic_status;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(check_acp_config, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp31_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp31_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp31_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp31_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp31_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp31_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp6x_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp6x_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp6x_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp6x_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp6x_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp6x_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp63_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp63_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp63_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp63_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp63_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp63_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp70_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp70_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp70_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp70_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp70_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp70_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP legacy common features");
+MODULE_LICENSE("Dual BSD/GPL");
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..a7a551366a40
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -0,0 +1,245 @@
+// 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"
+#include "acp3x-es83xx/acp3x-es83xx.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 es83xx_rn_data = {
+ .hs_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = ES83XX,
+ .dmic_codec_id = DMIC,
+};
+
+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,
+ .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,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata acp_dmic_data = {
+ .dmic_cpu_id = DMIC,
+ .dmic_codec_id = DMIC,
+};
+
+static bool acp_asoc_init_ops(struct acp_card_drvdata *priv)
+{
+ bool has_ops = false;
+
+ if (priv->hs_codec_id == ES83XX) {
+ has_ops = true;
+ acp3x_es83xx_init_ops(&priv->ops);
+ }
+ return has_ops;
+}
+
+static int acp_asoc_suspend_pre(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_suspend_pre(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_resume_post(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_resume_post(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ acp_card_drvdata = card->drvdata;
+ acp_card_drvdata->acpi_mach = (struct snd_soc_acpi_mach *)pdev->dev.platform_data;
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+
+ acp_asoc_init_ops(card->drvdata);
+
+ /* If widgets and controls are not set in specific callback,
+ * they will be added per-codec in acp-mach-common.c
+ */
+ ret = acp_ops_configure_widgets(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot configure widgets for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ card->suspend_pre = acp_asoc_suspend_pre;
+ card->resume_post = acp_asoc_resume_post;
+
+ ret = acp_ops_probe(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot probe card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ if (!strcmp(pdev->name, "acp-pdm-mach"))
+ acp_card_drvdata->acp_rev = *((int *)dev->platform_data);
+ else
+ acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ ret = acp_legacy_dai_links_create(card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Cannot create dai links for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+
+ 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);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+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 = "acp3x-es83xx",
+ .driver_data = (kernel_ulong_t)&es83xx_rn_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,
+ },
+ {
+ .name = "acp-pdm-mach",
+ .driver_data = (kernel_ulong_t)&acp_dmic_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+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_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..c4bc8e849284
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -0,0 +1,1797 @@
+// 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, 2023 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 "../../codecs/nau8821.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 NAU8821_CODEC_DAI "nau8821-hifi"
+#define NAU8821_BCLK 1536000
+#define NAU8821_FREQ_OUT 12288000
+#define MAX98388_CODEC_DAI "max98388-aif1"
+
+#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 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 struct snd_soc_jack rt5682_jack;
+static struct snd_soc_jack_pin rt5682_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+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 = snd_soc_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_dapm_new_controls(&card->dapm, rt5682_widgets,
+ ARRAY_SIZE(rt5682_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682_controls,
+ ARRAY_SIZE(rt5682_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(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,
+ &rt5682_jack,
+ rt5682_jack_pins,
+ ARRAY_SIZE(rt5682_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682_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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_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 = snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_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;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ 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_PLL1,
+ 49152000, 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 struct snd_soc_jack rt5682s_jack;
+static struct snd_soc_jack_pin rt5682s_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682s_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682s_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+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 = snd_soc_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_dapm_new_controls(&card->dapm, rt5682s_widgets,
+ ARRAY_SIZE(rt5682s_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682s_controls,
+ ARRAY_SIZE(rt5682s_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(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,
+ &rt5682s_jack,
+ rt5682s_jack_pins,
+ ARRAY_SIZE(rt5682s_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682s_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 = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_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;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ 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_PLL1,
+ 49152000, 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);
+ 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_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_kcontrol_new rt1019_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget rt1019_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+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;
+ int ret;
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt1019_widgets,
+ ARRAY_SIZE(rt1019_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt1019_controls,
+ ARRAY_SIZE(rt1019_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ 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 = snd_soc_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_kcontrol_new max98360a_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98360a_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+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;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98360A)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max98360a_widgets,
+ ARRAY_SIZE(max98360a_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98360a_controls,
+ ARRAY_SIZE(max98360a_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ 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 = snd_soc_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,
+};
+
+SND_SOC_DAILINK_DEF(max98388,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI),
+ COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI)));
+
+static const struct snd_kcontrol_new max98388_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98388_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max98388_map[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static const unsigned int max98388_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits_max = {
+ .list = max98388_format,
+ .count = ARRAY_SIZE(max98388_format),
+};
+
+static int acp_card_max98388_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);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits_max);
+
+ return 0;
+}
+
+static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98388)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max98388_widgets,
+ ARRAY_SIZE(max98388_widgets));
+
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98388_controls,
+ ARRAY_SIZE(max98388_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98388_map,
+ ARRAY_SIZE(max98388_map));
+}
+
+static int acp_max98388_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 snd_soc_dai *codec_dai =
+ snd_soc_card_get_codec_dai(card,
+ MAX98388_CODEC_DAI);
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_max98388_ops = {
+ .startup = acp_card_max98388_startup,
+ .hw_params = acp_max98388_hw_params,
+};
+
+/* Declare nau8825 codec components */
+SND_SOC_DAILINK_DEF(nau8825,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi")));
+
+static struct snd_soc_jack nau8825_jack;
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+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 = snd_soc_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_dapm_new_controls(&card->dapm, nau8825_widgets,
+ ARRAY_SIZE(nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8825_controls,
+ ARRAY_SIZE(nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(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,
+ &nau8825_jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &nau8825_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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_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,
+};
+
+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 *codec_dai;
+ int ret = 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, NAU8821_CODEC_DAI);
+ if (!codec_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(codec_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(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, NAU8821_BCLK,
+ NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ }
+ return ret;
+}
+
+static struct snd_soc_jack nau8821_jack;
+static struct snd_soc_jack_pin nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8821_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", 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 nau8821_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static const unsigned int nau8821_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = nau8821_format,
+ .count = ARRAY_SIZE(nau8821_format),
+};
+
+static int acp_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_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);
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, nau8821_widgets,
+ ARRAY_SIZE(nau8821_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ // Don't need to add routes if widget addition failed
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8821_controls,
+ ARRAY_SIZE(nau8821_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(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,
+ &nau8821_jack,
+ nau8821_jack_pins,
+ ARRAY_SIZE(nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ nau8821_enable_jack_detect(component, &nau8821_jack);
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8821_audio_route,
+ ARRAY_SIZE(nau8821_audio_route));
+}
+
+static int acp_8821_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);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+ return 0;
+}
+
+static int acp_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->soc_mclk)
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt = SND_SOC_DAIFMT_I2S | 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;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_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);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
+ 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 acp_8821_ops = {
+ .startup = acp_8821_startup,
+ .hw_params = acp_nau8821_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(nau8821,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
+ NAU8821_CODEC_DAI)));
+
+/* 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 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 platform_acp63_component[] = {
+ {
+ .name = "acp_asoc_acp63.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_acp70_component[] = {
+ {
+ .name = "acp_asoc_acp70.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_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt")));
+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->bt_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].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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;
+ }
+ if (drv_data->hs_codec_id == NAU8821) {
+ links[i].codecs = nau8821;
+ links[i].num_codecs = ARRAY_SIZE(nau8821);
+ links[i].init = acp_8821_init;
+ links[i].ops = &acp_8821_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].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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;
+ if (drv_data->acp_rev == ACP_RN_PCI_ID) {
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ } else {
+ 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].playback_only = 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 = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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].playback_only = 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 = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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 == MAX98388) {
+ links[i].playback_only = 0;
+ links[i].codecs = max98388;
+ links[i].num_codecs = ARRAY_SIZE(max98388);
+ links[i].ops = &acp_max98388_ops;
+ links[i].init = acp_card_max98388_init;
+ card->codec_conf = max98388_conf;
+ card->num_configs = ARRAY_SIZE(max98388_conf);
+ }
+ 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->bt_cpu_id == I2S_BT) {
+ links[i].name = "acp-bt-codec";
+ links[i].id = BT_BE_ID;
+ links[i].cpus = sof_bt;
+ links[i].num_cpus = ARRAY_SIZE(sof_bt);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->bt_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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].capture_only = 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;
+ int rc;
+
+ 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);
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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;
+ }
+ if (drv_data->hs_codec_id == ES83XX) {
+ rc = acp_ops_configure_link(card, &links[i]);
+ if (rc != 0) {
+ dev_err(dev, "Failed to configure link for ES83XX: %d\n", rc);
+ return rc;
+ }
+ }
+ 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);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ links[i].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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].stream_name = "DMIC capture";
+ 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 = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ links[i].cpus = pdm_dmic;
+ links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ links[i].platforms = platform_acp70_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp70_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+ links[i].ops = &acp_card_dmic_ops;
+ links[i].capture_only = 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_DESCRIPTION("AMD ACP Common Machine driver");
+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..f94c30c20f20
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -0,0 +1,139 @@
+/* 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>
+
+#include "acp_common.h"
+
+#define TDM_CHANNELS 8
+
+#define ACP_OPS(priv, cb) ((priv)->ops.cb)
+
+#define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata)
+
+enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
+ DMIC_BE_ID,
+ BT_BE_ID,
+};
+
+enum cpu_endpoints {
+ NONE = 0,
+ I2S_HS,
+ I2S_SP,
+ I2S_BT,
+ DMIC,
+};
+
+enum codec_endpoints {
+ DUMMY = 0,
+ RT5682,
+ RT1019,
+ MAX98360A,
+ RT5682S,
+ NAU8825,
+ NAU8821,
+ MAX98388,
+ ES83XX,
+};
+
+struct acp_mach_ops {
+ int (*probe)(struct snd_soc_card *card);
+ int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+ int (*configure_widgets)(struct snd_soc_card *card);
+ int (*suspend_pre)(struct snd_soc_card *card);
+ int (*resume_post)(struct snd_soc_card *card);
+};
+
+struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
+ unsigned int bt_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
+ unsigned int bt_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ unsigned int acp_rev;
+ struct clk *wclk;
+ struct clk *bclk;
+ struct acp_mach_ops ops;
+ struct snd_soc_acpi_mach *acpi_mach;
+ void *mach_priv;
+ 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[];
+
+static inline int acp_ops_probe(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, probe))
+ ret = ACP_OPS(priv, probe)(card);
+ return ret;
+}
+
+static inline int acp_ops_configure_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_link))
+ ret = ACP_OPS(priv, configure_link)(card, dai_link);
+ return ret;
+}
+
+static inline int acp_ops_configure_widgets(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_widgets))
+ ret = ACP_OPS(priv, configure_widgets)(card);
+ return ret;
+}
+
+static inline int acp_ops_suspend_pre(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, suspend_pre))
+ ret = ACP_OPS(priv, suspend_pre)(card);
+ return ret;
+}
+
+static inline int acp_ops_resume_post(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, resume_post))
+ ret = ACP_OPS(priv, resume_post)(card);
+ return ret;
+}
+
+#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..f83708755ed1
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -0,0 +1,301 @@
+// 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/module.h>
+#include <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_pci"
+
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x125C000
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->irq)
+ return chip->acp_hw_ops->irq(irq, chip);
+
+ return IRQ_NONE;
+}
+static void acp_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_acp_platform_devs(struct pci_dev *pci, struct acp_chip_info *chip, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (chip->is_i2s_config || chip->is_pdm_dev) {
+ chip->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!chip->res) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ chip->res->flags = IORESOURCE_MEM;
+ chip->res->start = addr;
+ chip->res->end = addr + (ACP3x_REG_END - ACP3x_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp_fill_platform_dev_info(&pdevinfo, parent, NULL, chip->name,
+ 0, chip->res, 1, chip, sizeof(*chip));
+
+ chip->acp_plat_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(chip->acp_plat_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(chip->acp_plat_dev);
+ goto err;
+ }
+ if (chip->is_pdm_dev && chip->is_pdm_config) {
+ chip->dmic_codec_dev = platform_device_register_data(&pci->dev,
+ "dmic-codec",
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(chip->dmic_codec_dev)) {
+ dev_err(&pci->dev, "failed to create DMIC device\n");
+ ret = PTR_ERR(chip->dmic_codec_dev);
+ goto unregister_acp_plat_dev;
+ }
+ }
+ return 0;
+unregister_acp_plat_dev:
+ platform_device_unregister(chip->acp_plat_dev);
+err:
+ return ret;
+}
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct device *dev = &pci->dev;
+ struct acp_chip_info *chip;
+ unsigned int flag, addr;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC)
+ 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);
+
+ chip->acp_rev = pci->revision;
+ switch (pci->revision) {
+ case 0x01:
+ chip->name = "acp_asoc_renoir";
+ chip->rsrc = &rn_rsrc;
+ chip->acp_hw_ops_init = acp31_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp_machines;
+ break;
+ case 0x6f:
+ chip->name = "acp_asoc_rembrandt";
+ chip->rsrc = &rmb_rsrc;
+ chip->acp_hw_ops_init = acp6x_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_rmb_acp_machines;
+ break;
+ case 0x63:
+ chip->name = "acp_asoc_acp63";
+ chip->rsrc = &acp63_rsrc;
+ chip->acp_hw_ops_init = acp63_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp63_acp_machines;
+ break;
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ chip->name = "acp_asoc_acp70";
+ chip->rsrc = &acp70_rsrc;
+ chip->acp_hw_ops_init = acp70_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp70_acp_machines;
+ break;
+ default:
+ dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+ ret = -EINVAL;
+ goto release_regions;
+ }
+ chip->flag = flag;
+
+ addr = pci_resource_start(pci, 0);
+ chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+ if (!chip->base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ chip->addr = addr;
+
+ chip->acp_hw_ops_init(chip);
+ ret = acp_hw_init(chip);
+ if (ret)
+ goto release_regions;
+
+ ret = devm_request_irq(dev, pci->irq, irq_handler,
+ IRQF_SHARED, "ACP_I2S_IRQ", chip);
+ if (ret) {
+ dev_err(&pci->dev, "ACP I2S IRQ request failed %d\n", ret);
+ goto de_init;
+ }
+
+ check_acp_config(pci, chip);
+ if (!chip->is_pdm_dev && !chip->is_i2s_config)
+ goto skip_pdev_creation;
+
+ ret = create_acp_platform_devs(pci, chip, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+
+ chip->chip_pdev = chip->acp_plat_dev;
+ chip->dev = &chip->acp_plat_dev->dev;
+
+ acp_machine_select(chip);
+
+ INIT_LIST_HEAD(&chip->stream_list);
+ spin_lock_init(&chip->acp_lock);
+skip_pdev_creation:
+ dev_set_drvdata(&pci->dev, chip);
+ 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 ret;
+
+de_init:
+ acp_hw_deinit(chip);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+};
+
+static int snd_acp_suspend(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_init(chip);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP en-interrupts failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops acp_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = pci_get_drvdata(pci);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ if (chip->dmic_codec_dev)
+ platform_device_unregister(chip->dmic_codec_dev);
+ if (chip->acp_plat_dev)
+ platform_device_unregister(chip->acp_plat_dev);
+ if (chip->mach_dev)
+ platform_device_unregister(chip->mach_dev);
+
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+}
+
+/* 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,
+ .driver = {
+ .pm = pm_ptr(&acp_pm_ops),
+ },
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_DESCRIPTION("AMD ACP common PCI support");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+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..1bfc34c2aa53
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -0,0 +1,187 @@
+// 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"
+
+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_chip_info *chip;
+ u32 physical_addr, size_dmic, period_bytes;
+ unsigned int dmic_ctrl;
+
+ chip = dev_get_platdata(dev);
+ /* Enable default DMIC clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ dmic_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ dmic_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(dmic_ctrl, chip->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);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ physical_addr = ACP7x_DMIC_MEM_WINDOW_START;
+ else
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init DMIC Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(size_dmic, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->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_chip_info *chip = dev_get_platdata(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(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if (!(dma_enable & DMA_EN_MASK)) {
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ }
+
+ ret = readl_poll_timeout_atomic(chip->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(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((dma_enable & DMA_EN_MASK)) {
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+
+ }
+
+ ret = readl_poll_timeout_atomic(chip->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_chip_info *chip = dev_get_platdata(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;
+ }
+
+ chip->ch_mask = ch_mask;
+ 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, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->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_chip_info *chip = dev_get_platdata(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(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 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_chip_info *chip = dev_get_platdata(dev);
+ u32 ext_int_ctrl;
+
+ /* Disable DMIC interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 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_DESCRIPTION("AMD ACP Audio PDM controller");
+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..b25ac5612808
--- /dev/null
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -0,0 +1,360 @@
+// 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/dma-mapping.h>
+
+#include "amd.h"
+#include "acp-mach.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,
+};
+
+static const struct snd_pcm_hardware acp6x_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_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .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 acp6x_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_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .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,
+};
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream)
+{
+ struct acp_resource *rsrc = chip->rsrc;
+ u32 reg_val;
+
+ reg_val = rsrc->sram_pte_offset;
+ stream->reg_offset = 0x02000000;
+
+ writel((reg_val + GRP1_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ writel((reg_val + GRP2_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+
+ writel(reg_val | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_5);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5);
+
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, "SND_SOC_ACP_COMMON");
+
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct acp_resource *rsrc = chip->rsrc;
+ dma_addr_t addr = substream->dma_buffer.addr;
+ int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ u32 low, high, val;
+ u16 page_idx;
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ switch (stream->dai_id) {
+ case I2S_SP_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x0;
+ else
+ val = 0x1000;
+ break;
+ case I2S_BT_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x2000;
+ else
+ val = 0x3000;
+ break;
+ case I2S_HS_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x4000;
+ else
+ val = 0x5000;
+ break;
+ case DMIC_INSTANCE:
+ val = 0x6000;
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", stream->dai_id);
+ return;
+ }
+ break;
+ default:
+ val = stream->pte_offset;
+ break;
+ }
+
+ 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, chip->base + rsrc->scratch_reg_offset + val);
+ high |= BIT(31);
+ writel(high, chip->base + rsrc->scratch_reg_offset + val + 4);
+
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(config_acp_dma, "SND_SOC_ACP_COMMON");
+
+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_chip_info *chip;
+ struct acp_stream *stream;
+ int ret;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->substream = substream;
+ chip = dev_get_drvdata(dev->parent);
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp6x_pcm_hardware_playback;
+ else
+ runtime->hw = acp6x_pcm_hardware_capture;
+ break;
+ default:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp_pcm_hardware_playback;
+ else
+ runtime->hw = acp_pcm_hardware_capture;
+ break;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ 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(chip));
+
+ spin_lock_irq(&chip->acp_lock);
+ list_add_tail(&stream->list, &chip->stream_list);
+ spin_unlock_irq(&chip->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 device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u64 size = params_buffer_bytes(params);
+
+ /* Configure ACP DMA block with params */
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, 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_chip_info *chip = dev_get_drvdata(dev->parent);
+ 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(chip, 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_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+
+ /* Remove entry from list */
+ spin_lock_irq(&chip->acp_lock);
+ list_del(&stream->list);
+ spin_unlock_irq(&chip->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_chip_info *chip;
+ struct snd_soc_dai_driver;
+ unsigned int status;
+
+ chip = dev_get_platdata(dev);
+ if (!chip || !chip->base) {
+ dev_err(dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ status = devm_snd_soc_register_component(dev, &acp_pcm_component,
+ chip->dai_driver,
+ chip->num_dai);
+ if (status) {
+ dev_err(dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_register, "SND_SOC_ACP_COMMON");
+
+int acp_platform_unregister(struct device *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..7e9c07488dcc
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,247 @@
+// 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 <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define MP1_C2PMSG_69 0x3B10A14
+#define MP1_C2PMSG_85 0x3B10A54
+#define MP1_C2PMSG_93 0x3B10A74
+
+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,
+},
+{
+ .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,
+},
+{
+ .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,
+},
+{
+ .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_master_clock_generate(struct device *dev)
+{
+ int data, rc;
+
+ rc = amd_smn_write(0, MP1_C2PMSG_93, 0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_85, 0xC4);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_69, 0x4);
+ if (rc)
+ return rc;
+
+ return read_poll_timeout(smn_read_register, data, data > 0, DELAY_US,
+ ACP_TIMEOUT, false, MP1_C2PMSG_93);
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ 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 != ACP_RMB_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_rmb_dai;
+ chip->num_dai = ARRAY_SIZE(acp_rmb_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp6x_master_clock_generate(dev);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void rembrandt_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int rmb_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp6x_master_clock_generate(dev);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rmb_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
+};
+
+static struct platform_driver rembrandt_driver = {
+ .probe = rembrandt_audio_probe,
+ .remove = rembrandt_audio_remove,
+ .driver = {
+ .name = "acp_asoc_rembrandt",
+ .pm = pm_ptr(&rmb_dma_pm_ops),
+ },
+};
+
+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..04f6d70b6a92
--- /dev/null
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -0,0 +1,191 @@
+// 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 <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_renoir"
+
+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,
+},
+{
+ .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,
+},
+{
+ .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 renoir_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ 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 != ACP_RN_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_renoir_dai;
+ chip->num_dai = ARRAY_SIZE(acp_renoir_dai);
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+
+ acp_platform_register(dev);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void renoir_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+}
+
+static int rn_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rn_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume)
+};
+
+static struct platform_driver renoir_driver = {
+ .probe = renoir_audio_probe,
+ .remove = renoir_audio_remove,
+ .driver = {
+ .name = "acp_asoc_renoir",
+ .pm = pm_ptr(&rn_dma_pm_ops),
+ },
+};
+
+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-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
new file mode 100644
index 000000000000..5a3cfedacbaf
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long soc_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(soc_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(soc_sdw_quirk));
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+ if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR)
+ dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n");
+}
+
+static int soc_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ soc_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id soc_sdw_quirk_table[] = {
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD3"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD4"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {}
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *soc_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf,
+ struct snd_soc_dai_link_component *sdw_platform_component)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *soc_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->name_prefix) {
+ (*codec_conf)->dlc.name = soc_end->codec_name;
+ (*codec_conf)->name_prefix = soc_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (soc_end->include_sidecar) {
+ ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(soc_dai->link_mask[stream]);
+ int num_codecs = soc_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!soc_dai->num_devs[stream])
+ continue;
+
+ soc_end = list_first_entry(&soc_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = soc_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[soc_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (!soc_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(soc_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = soc_end->codec_name;
+ codecs[j].dai_name = soc_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, sdw_platform_component,
+ 1, codecs, num_codecs,
+ 0, asoc_sdw_rtd_init, &sdw_ops);
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->dai_info->init)
+ soc_end->dai_info->init(card, *dai_links,
+ soc_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *soc_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *sdw_platform_component;
+ int ret;
+
+ sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!sdw_platform_component)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ sdw_platform_component->name = "amd_ps_sdw_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* generate DAI links by each sdw link */
+ while (soc_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, soc_dais, dai_links,
+ &current_be_id, codec_conf, sdw_platform_component);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ soc_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *pdm_cpu;
+ struct snd_soc_dai_link_component *pdm_platform;
+ int ret;
+
+ pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_cpu)
+ return -ENOMEM;
+
+ pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_platform)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ pdm_cpu->name = "acp_ps_pdm_dma.0";
+ pdm_platform->name = "acp_ps_pdm_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *be_id = ACP_DMIC_BE_ID;
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ pdm_cpu->name, pdm_platform->name,
+ "dmic-codec.0", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int soc_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
+ if (!soc_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
+ if (!soc_ends)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, soc_dais, soc_ends, &num_devs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_devs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ soc_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ dmi_check_system(soc_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ soc_sdw_quirk, quirk_override);
+ soc_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = soc_sdw_quirk;
+ dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = soc_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+ if (mach->mach_params.dmic_num) {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:dmic cfg-mics:%d",
+ card->components,
+ mach->mach_params.dmic_num);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver soc_sdw_driver = {
+ .driver = {
+ .name = "amd_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(soc_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
diff --git a/sound/soc/amd/acp/acp-sdw-mach-common.c b/sound/soc/amd/acp/acp-sdw-mach-common.c
new file mode 100644
index 000000000000..e5f394dc2f4c
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-mach-common.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-mach-common - Common machine driver helper functions for
+ * legacy(No DSP) stack and SOF stack.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include "soc_amd_sdw_common.h"
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ case SOC_SDW_AMP_IN_DAI_ID:
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_RX;
+ break;
+ default:
+ dev_err(dev, "invalid be_id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(dev, "sdw_link_id:%d, be_id:%d, cpu_pin_id:%d\n", sdw_link_id, be_id, *cpu_pin_id);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp70_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+MODULE_DESCRIPTION("AMD SoundWire Common Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
new file mode 100644
index 000000000000..91d72d4bb9a2
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long sof_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(sof_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(sof_sdw_quirk));
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+}
+
+static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_sdw_quirk_table[] = {
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {}
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:04:00.5",
+ }
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *sof_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *sof_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->name_prefix) {
+ (*codec_conf)->dlc.name = sof_end->codec_name;
+ (*codec_conf)->name_prefix = sof_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (sof_end->include_sidecar) {
+ ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(sof_dai->link_mask[stream]);
+ int num_codecs = sof_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!sof_dai->num_devs[stream])
+ continue;
+
+ sof_end = list_first_entry(&sof_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = sof_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[sof_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (!sof_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(sof_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = sof_end->codec_name;
+ codecs[j].dai_name = sof_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, platform_component,
+ ARRAY_SIZE(platform_component), codecs, num_codecs,
+ 1, asoc_sdw_rtd_init, &sdw_ops);
+
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->dai_info->init)
+ sof_end->dai_info->init(card, *dai_links,
+ sof_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *sof_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ int ret;
+
+ /* generate DAI links by each sdw link */
+ while (sof_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, sof_dais, dai_links,
+ &current_be_id, codec_conf);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ sof_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ int ret;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ "acp-sof-dmic", platform_component->name,
+ "dmic-codec", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int sof_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
+ if (!sof_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ if (!sof_ends)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_devs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ sof_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ dmi_check_system(sof_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ sof_sdw_quirk, quirk_override);
+ sof_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = sof_sdw_quirk;
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = sof_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sof_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver sof_sdw_driver = {
+ .driver = {
+ .name = "amd_sof_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(sof_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
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..6215e31ecedd
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -0,0 +1,174 @@
+// 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, 2023 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,
+};
+
+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,
+};
+
+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,
+};
+
+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,
+};
+
+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,
+};
+
+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,
+};
+
+static struct acp_card_drvdata sof_nau8821_max98388_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_HS,
+ .bt_cpu_id = I2S_BT,
+ .hs_codec_id = NAU8821,
+ .amp_codec_id = MAX98388,
+ .soc_mclk = true,
+};
+
+static int acp_sof_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&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->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ /* Widgets and controls added per-codec in acp-mach-common.c */
+
+ 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_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+ ret = acp_sofdsp_dai_links_create(card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n");
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register card(%s)\n", card->name);
+ 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
+ },
+ {
+ .name = "nau8821-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+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 SOF Machine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
new file mode 100644
index 000000000000..eb5d4a5baef2
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using ES8336 codec.
+//
+// Copyright 2023 Marian Postevca <posteuca@mutex.one>
+#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 <sound/soc-acpi.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 <linux/dmi.h>
+#include <linux/string_choices.h>
+#include "../acp-mach.h"
+#include "acp3x-es83xx.h"
+
+#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv))
+
+#define DUAL_CHANNEL 2
+
+#define ES83XX_ENABLE_DMIC BIT(4)
+#define ES83XX_48_MHZ_MCLK BIT(5)
+
+struct acp3x_es83xx_private {
+ bool speaker_on;
+ bool headphone_on;
+ unsigned long quirk;
+ struct snd_soc_component *codec;
+ struct device *codec_dev;
+ struct gpio_desc *gpio_speakers, *gpio_headphone;
+ struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio;
+ struct acpi_gpio_mapping gpio_mapping[3];
+ struct snd_soc_dapm_route mic_map[2];
+};
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256)
+#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000)
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct acp3x_es83xx_private *priv;
+ unsigned int freq;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ priv = get_mach_priv(rtd->card);
+
+ if (priv->quirk & ES83XX_48_MHZ_MCLK) {
+ dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n");
+ freq = ES83xx_48_MHZ_MCLK_FREQ;
+ } else {
+ dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n");
+ freq = ES83xx_12288_KHZ_MCLK_FREQ;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT);
+ 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,
+ &constraints_channels);
+
+ return 0;
+}
+
+static struct snd_soc_jack es83xx_jack;
+
+static struct snd_soc_jack_pin es83xx_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget acp3x_es83xx_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("Headphone Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_headphone_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headphone", NULL, "Headphone Power"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled Speaker and/or headphone switch.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+
+static const struct snd_kcontrol_new acp3x_es83xx_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card)
+{
+ card->dapm_widgets = acp3x_es83xx_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets);
+ card->controls = acp3x_es83xx_controls;
+ card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls);
+ card->dapm_routes = acp3x_es83xx_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map);
+
+ return 0;
+}
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card);
+
+ dev_dbg(priv->codec_dev, "headphone power event = %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->headphone_on = true;
+ else
+ priv->headphone_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card);
+
+ dev_dbg(priv->codec_dev, "speaker power event: %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->speaker_on = true;
+ else
+ priv->speaker_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We need to disable the jack in the machine driver suspend
+ * callback so that the CODEC suspend callback actually gets
+ * called. Without doing it, the CODEC suspend/resume
+ * callbacks do not get called if headphones are plugged in.
+ * This is because plugging in headphones keeps some supplies
+ * active, this in turn means that the lowest bias level
+ * that the CODEC can go to is SND_SOC_BIAS_STANDBY.
+ * If components do not set idle_bias_on to true then
+ * their suspend/resume callbacks do not get called.
+ */
+ dev_dbg(priv->codec_dev, "card suspend\n");
+ snd_soc_component_set_jack(priv->codec, NULL, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_resume_post(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We disabled jack detection in suspend callback,
+ * enable it back.
+ */
+ dev_dbg(priv->codec_dev, "card resume\n");
+ snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
+{
+
+ priv->enable_spk_gpio.crs_entry_index = 0;
+ priv->enable_hp_gpio.crs_entry_index = 1;
+
+ priv->enable_spk_gpio.active_low = false;
+ priv->enable_hp_gpio.active_low = false;
+
+ priv->gpio_mapping[0].name = "speakers-enable-gpios";
+ priv->gpio_mapping[0].data = &priv->enable_spk_gpio;
+ priv->gpio_mapping[0].size = 1;
+ priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ priv->gpio_mapping[1].name = "headphone-enable-gpios";
+ priv->gpio_mapping[1].data = &priv->enable_hp_gpio;
+ priv->gpio_mapping[1].size = 1;
+ priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",
+ priv->enable_spk_gpio.crs_entry_index,
+ str_low_high(priv->enable_spk_gpio.active_low),
+ priv->enable_hp_gpio.crs_entry_index,
+ str_low_high(priv->enable_hp_gpio.active_low));
+ return 0;
+}
+
+static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)
+{
+ int num_routes = 0;
+ int i;
+
+ if (!(priv->quirk & ES83XX_ENABLE_DMIC)) {
+ priv->mic_map[num_routes].sink = "MIC1";
+ priv->mic_map[num_routes].source = "Internal Mic";
+ num_routes++;
+ }
+
+ priv->mic_map[num_routes].sink = "MIC2";
+ priv->mic_map[num_routes].source = "Headset Mic";
+ num_routes++;
+
+ for (i = 0; i < num_routes; i++)
+ dev_info(priv->codec_dev, "%s is %s\n",
+ priv->mic_map[i].source, priv->mic_map[i].sink);
+
+ return num_routes;
+}
+
+static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+ int ret = 0;
+ int num_routes;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &es83xx_jack, es83xx_jack_pins,
+ ARRAY_SIZE(es83xx_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+ snd_soc_component_set_jack(codec, &es83xx_jack, NULL);
+
+ priv->codec = codec;
+ acp3x_es83xx_configure_gpios(priv);
+
+ ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping);
+ if (ret)
+ dev_warn(priv->codec_dev, "failed to add speaker gpio\n");
+
+ priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable",
+ priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_speakers)) {
+ dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n");
+ return PTR_ERR(priv->gpio_speakers);
+ }
+
+ priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable",
+ priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_headphone)) {
+ dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n");
+ return PTR_ERR(priv->gpio_headphone);
+ }
+
+ num_routes = acp3x_es83xx_configure_mics(priv);
+ if (num_routes > 0) {
+ ret = snd_soc_dapm_add_routes(&card->dapm, priv->mic_map, num_routes);
+ if (ret != 0)
+ device_remove_software_node(priv->codec_dev);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp3x_es83xx_ops = {
+ .startup = acp3x_es83xx_codec_startup,
+};
+
+
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+
+static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {}
+};
+
+static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
+{
+ link->codecs = codec;
+ link->num_codecs = ARRAY_SIZE(codec);
+ link->init = acp3x_es83xx_init;
+ link->ops = &acp3x_es83xx_ops;
+ link->dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ return 0;
+}
+
+static int acp3x_es83xx_probe(struct snd_soc_card *card)
+{
+ int ret = 0;
+ struct device *dev = card->dev;
+ const struct dmi_system_id *dmi_id;
+
+ dmi_id = dmi_first_match(acp3x_es83xx_dmi_table);
+ if (dmi_id && dmi_id->driver_data) {
+ struct acp3x_es83xx_private *priv;
+ struct acp_card_drvdata *acp_drvdata;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+
+ acp_drvdata = (struct acp_card_drvdata *)card->drvdata;
+
+ dev_info(dev, "matched DMI table with this system, trying to register sound card\n");
+
+ adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1);
+ if (!adev) {
+ dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id);
+ return -ENXIO;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev) {
+ dev_warn(dev, "Error cannot find codec device, will defer probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ put_device(codec_dev);
+ return -ENOMEM;
+ }
+
+ priv->codec_dev = codec_dev;
+ priv->quirk = (unsigned long)dmi_id->driver_data;
+ acp_drvdata->mach_priv = priv;
+ dev_info(dev, "successfully probed the sound card\n");
+ } else {
+ ret = -ENODEV;
+ dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n");
+ }
+ return ret;
+}
+
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops)
+{
+ ops->probe = acp3x_es83xx_probe;
+ ops->configure_widgets = acp3x_es83xx_configure_widgets;
+ ops->configure_link = acp3x_es83xx_configure_link;
+ ops->suspend_pre = acp3x_es83xx_suspend_pre;
+ ops->resume_post = acp3x_es83xx_resume_post;
+}
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
new file mode 100644
index 000000000000..03551ffdd9da
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Marian Postevca <posteuca@mutex.one>
+ */
+
+#ifndef __ACP3X_ES83XX_H
+#define __ACP3X_ES83XX_H
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops);
+
+#endif
+
diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c
new file mode 100644
index 000000000000..10fb416b959d
--- /dev/null
+++ b/sound/soc/amd/acp/acp63.c
@@ -0,0 +1,298 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP6.3 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 <linux/pm_runtime.h>
+#include <linux/pci.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_asoc_acp63"
+
+#define CLK_PLL_PWR_REQ_N0 0X0006C2C0
+#define CLK_SPLL_FIELD_2_N0 0X0006C114
+#define CLK_PLL_REQ_N0 0X0006C0DC
+#define CLK_DFSBYPASS_CONTR 0X0006C2C8
+#define CLK_DFS_CNTL_N0 0X0006C1A4
+
+#define PLL_AUTO_STOP_REQ BIT(4)
+#define PLL_AUTO_START_REQ BIT(0)
+#define PLL_FRANCE_EN BIT(4)
+#define EXIT_DPF_BYPASS_0 BIT(16)
+#define EXIT_DPF_BYPASS_1 BIT(17)
+#define CLK0_DIVIDER 0X30
+
+union clk_pll_req_no {
+ struct {
+ u32 fb_mult_int : 9;
+ u32 reserved : 3;
+ u32 pll_spine_div : 4;
+ u32 gb_mult_frac : 16;
+ } bitfields, bits;
+ u32 clk_pll_req_no_reg;
+};
+
+static struct snd_soc_dai_driver acp63_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,
+},
+{
+ .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,
+},
+{
+ .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,
+},
+{
+ .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 acp63_i2s_master_clock_generate(struct acp_chip_info *chip)
+{
+ int rc;
+ u32 data;
+ union clk_pll_req_no clk_pll;
+
+ /* Clk5 pll register values to get mclk as 196.6MHz*/
+ clk_pll.bits.fb_mult_int = 0x31;
+ clk_pll.bits.pll_spine_div = 0;
+ clk_pll.bits.gb_mult_frac = 0x26E9;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_SPLL_FIELD_2_N0, &data);
+ if (rc)
+ return rc;
+ if (data & PLL_FRANCE_EN) {
+ rc = amd_smn_write(0, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN);
+ if (rc)
+ return rc;
+ }
+
+ rc = amd_smn_write(0, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_DFSBYPASS_CONTR, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1);
+ if (rc)
+ return rc;
+
+ return amd_smn_write(0, CLK_DFS_CNTL_N0, CLK0_DIVIDER);
+}
+
+static int acp63_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ 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 != ACP63_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp63_dai;
+ chip->num_dai = ARRAY_SIZE(acp63_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp63_i2s_master_clock_generate(chip);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp63_i2s_master_clock_generate(chip);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume)
+};
+
+static struct platform_driver acp63_driver = {
+ .probe = acp63_audio_probe,
+ .remove = acp63_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp63",
+ .pm = pm_ptr(&acp63_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_driver);
+
+MODULE_DESCRIPTION("AMD ACP acp63 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/acp70.c b/sound/soc/amd/acp/acp70.c
new file mode 100644
index 000000000000..bca311c88139
--- /dev/null
+++ b/sound/soc/amd/acp/acp70.c
@@ -0,0 +1,228 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP7.0 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 <linux/pm_runtime.h>
+#include <linux/pci.h>
+#include "amd.h"
+#include "acp-mach.h"
+
+#include <asm/amd/node.h>
+
+#define DRV_NAME "acp_asoc_acp70"
+
+#define CLK7_CLK0_DFS_CNTL_N1 0X0006C1A4
+#define CLK0_DIVIDER 0X19
+
+static struct snd_soc_dai_driver acp70_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .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 acp_acp70_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp70_dai;
+ chip->num_dai = ARRAY_SIZE(acp70_dai);
+
+ /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/
+ ret = amd_smn_write(0, CLK7_CLK0_DFS_CNTL_N1, CLK0_DIVIDER);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set I2S master clock as 196.608MHz\n");
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp_acp70_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp70_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp70_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume)
+};
+
+static struct platform_driver acp70_driver = {
+ .probe = acp_acp70_audio_probe,
+ .remove = acp_acp70_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp70",
+ .pm = pm_ptr(&acp70_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp70_driver);
+
+MODULE_DESCRIPTION("AMD ACP ACP70 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_common.h b/sound/soc/amd/acp/acp_common.h
new file mode 100644
index 000000000000..984685602e3d
--- /dev/null
+++ b/sound/soc/amd/acp/acp_common.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * acp_common.h - acp common header file
+ */
+
+#ifndef __ACP_COMMON_H
+#define __ACP_COMMON_H
+
+#define ACP_RN_PCI_ID 0x01
+#define ACP_VANGOGH_PCI_ID 0x50
+#define ACP_RMB_PCI_ID 0x6F
+#define ACP63_PCI_ID 0x63
+#define ACP70_PCI_ID 0x70
+#define ACP71_PCI_ID 0x71
+#define ACP72_PCI_ID 0x72
+
+#endif
diff --git a/sound/soc/amd/acp/amd-acp63-acpi-match.c b/sound/soc/amd/acp/amd-acp63-acpi-match.c
new file mode 100644
index 000000000000..9b6a49c051cd
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp63-acpi-match.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp63-acpi-match.c - tables and support for ACP 6.3 platform
+ * ACPI enumeration.
+ *
+ * Copyright 2024 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_6_3-rt711-l0-rt1316-l0-rt714-l1.tplg",
+ .fw_filename = "sof-acp_6_3.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_sdw_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp63_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP6.3 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
new file mode 100644
index 000000000000..dcecac792e6d
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_single_adr),
+ .adr_d = rt1320_1_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_rt722_l0_rt1320_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg",
+ .fw_filename = "sof-acp_7_0.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c
new file mode 100644
index 000000000000..27da2a862f1c
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acpi-mach.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acpi-match.c - tables and support for ACP platforms
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+
+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_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",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "acp3x-es83xx",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp_machines, "SND_SOC_ACP_COMMON");
+
+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,
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_rmb_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = {
+ {
+ .id = "AMDI0052",
+ .drv_name = "acp63-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp63_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = {
+ {
+ .id = "AMDI0029",
+ .drv_name = "acp70-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp70_acp_machines, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Venkataprasad.potturu@amd.com");
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
new file mode 100644
index 000000000000..0160b0df26a0
--- /dev/null
+++ b/sound/soc/amd/acp/amd-sdw-acpi.c
@@ -0,0 +1,62 @@
+// 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) 2023 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+
+/*
+ * SDW AMD ACPI scan helper function
+ */
+
+#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/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw_amd.h>
+#include <linux/string.h>
+
+int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+ u32 sdw_bitmap = 0;
+ u8 count = 0;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ /* Found controller, find links supported */
+ ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev),
+ "mipi-sdw-manager-list", &sdw_bitmap, 1);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-manager-list: %d\n", ret);
+ return -EINVAL;
+ }
+ count = hweight32(sdw_bitmap);
+ /* Check count is within bounds */
+ if (count > info->count) {
+ dev_err(&adev->dev, "Manager count %d exceeds max %d\n",
+ count, info->count);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_dbg(&adev->dev, "No SoundWire Managers detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count);
+ info->link_mask = sdw_bitmap;
+ return 0;
+}
+EXPORT_SYMBOL_NS(amd_sdw_scan_controller, "SND_AMD_SOUNDWIRE_ACPI");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("AMD SoundWire ACPI helpers");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
new file mode 100644
index 000000000000..73a028e67246
--- /dev/null
+++ b/sound/soc/amd/acp/amd.h
@@ -0,0 +1,365 @@
+/* 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 "acp_common.h"
+#include "chip_offset_byte.h"
+
+#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 ACP7x_I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define ACP7x_I2S_SP_RX_MEM_WINDOW_START 0x4200000
+#define ACP7x_I2S_BT_TX_MEM_WINDOW_START 0x4400000
+#define ACP7x_I2S_BT_RX_MEM_WINDOW_START 0x4600000
+#define ACP7x_I2S_HS_TX_MEM_WINDOW_START 0x4800000
+#define ACP7x_I2S_HS_RX_MEM_WINDOW_START 0x4A00000
+#define ACP7x_DMIC_MEM_WINDOW_START 0x4C00000
+
+#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
+
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP63_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP63_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP70_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP70_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP_ZSC_DSP_CTRL 0x0001014
+#define ACP_ZSC_STS 0x0001018
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0xffffffff
+#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 ACP_TIMEOUT 500
+#define DELAY_US 5
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#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 0x18
+#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
+
+struct acp_chip_info {
+ char *name; /* Platform name */
+ struct resource *res;
+ struct device *dev;
+ struct snd_soc_dai_driver *dai_driver;
+
+ unsigned int acp_rev; /* ACP Revision id */
+ void __iomem *base; /* ACP memory PCI base */
+ struct snd_acp_hw_ops *acp_hw_ops;
+ int (*acp_hw_ops_init)(struct acp_chip_info *chip);
+ struct platform_device *chip_pdev;
+ struct acp_resource *rsrc; /* Platform specific resources*/
+ struct list_head stream_list;
+ spinlock_t acp_lock; /* Used to protect stream_list */
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *acp_plat_dev;
+ struct platform_device *mach_dev;
+ struct snd_soc_acpi_mach *machines;
+ int num_dai;
+ u32 addr;
+ u32 bclk_div;
+ u32 lrclk_div;
+ u32 ch_mask;
+ u32 tdm_tx_fmt[3];
+ u32 tdm_rx_fmt[3];
+ u32 xfer_tx_resolution[3];
+ u32 xfer_rx_resolution[3];
+ unsigned int flag; /* Distinguish b/w Legacy or Only PDM */
+ bool is_pdm_dev; /* flag set to true when ACP PDM controller exists */
+ bool is_pdm_config; /* flag set to true when PDM configuration is selected from BIOS */
+ bool is_i2s_config; /* flag set to true when I2S configuration is selected from BIOS */
+ bool tdm_mode;
+};
+
+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;
+ u64 scratch_reg_offset;
+ u64 sram_pte_offset;
+};
+
+/**
+ * struct snd_acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @irq: ACP irq handler
+ * @en_interrupts: ACP enable interrupts
+ * @dis_interrupts: ACP disable interrupts
+ */
+struct snd_acp_hw_ops {
+ /* ACP hardware initilizations */
+ int (*acp_init)(struct acp_chip_info *chip);
+ int (*acp_deinit)(struct acp_chip_info *chip);
+
+ /* ACP Interrupts*/
+ irqreturn_t (*irq)(int irq, void *data);
+ int (*en_interrupts)(struct acp_chip_info *chip);
+ int (*dis_interrupts)(struct acp_chip_info *chip);
+};
+
+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,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+extern struct acp_resource rn_rsrc;
+extern struct acp_resource rmb_rsrc;
+extern struct acp_resource acp63_rsrc;
+extern struct acp_resource acp70_rsrc;
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[];
+
+extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
+
+int acp_platform_register(struct device *dev);
+int acp_platform_unregister(struct device *dev);
+
+int acp_machine_select(struct acp_chip_info *chip);
+
+int acp_init(struct acp_chip_info *chip);
+int acp_deinit(struct acp_chip_info *chip);
+int acp_enable_interrupts(struct acp_chip_info *chip);
+int acp_disable_interrupts(struct acp_chip_info *chip);
+irqreturn_t acp_irq_handler(int irq, void *data);
+
+extern struct snd_acp_hw_ops acp31_common_hw_ops;
+extern struct snd_acp_hw_ops acp6x_common_hw_ops;
+extern struct snd_acp_hw_ops acp63_common_hw_ops;
+extern struct snd_acp_hw_ops acp70_common_hw_ops;
+extern int acp31_hw_ops_init(struct acp_chip_info *chip);
+extern int acp6x_hw_ops_init(struct acp_chip_info *chip);
+extern int acp63_hw_ops_init(struct acp_chip_info *chip);
+extern int acp70_hw_ops_init(struct acp_chip_info *chip);
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream);
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size);
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip);
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip, struct acp_stream *stream);
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip);
+
+static inline int acp_hw_init(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_init)
+ return chip->acp_hw_ops->acp_init(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_deinit)
+ return chip->acp_hw_ops->acp_deinit(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_en_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->en_interrupts)
+ return chip->acp_hw_ops->en_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_dis_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->dis_interrupts)
+ chip->acp_hw_ops->dis_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline u64 acp_get_byte_count(struct acp_chip_info *chip, int dai_id, int direction)
+{
+ u64 byte_count = 0, low = 0, high = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ } else {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case DMIC_INSTANCE:
+ high = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ }
+ /* Get 64 bit value from two 32 bit registers */
+ byte_count = (high << 32) | low;
+
+POINTER_RETURN_BYTES:
+ return byte_count;
+}
+#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..82275c9de53a
--- /dev/null
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -0,0 +1,146 @@
+/* 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_1 0xC00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+
+#define GRP1_OFFSET 0x0
+#define GRP2_OFFSET 0x4000
+
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+#define ACP_SOFT_RESET 0x1000
+#define ACP_CONTROL 0x1004
+#define ACP_PIN_CONFIG 0x1440
+#define ACP3X_PIN_CONFIG 0x1400
+
+#define ACP_EXTERNAL_INTR_REG_ADDR(chip, offset, ctrl) \
+ (chip->base + chip->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
+
+#define ACP_EXTERNAL_INTR_ENB(chip) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x0, 0x0)
+#define ACP_EXTERNAL_INTR_CNTL(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x4, ctrl)
+#define ACP_EXTERNAL_INTR_STAT(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, \
+ (0x4 + (chip->rsrc->no_of_ctrls * 0x04)), ctrl)
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+
+#define ACP_I2S_REG_ADDR(acp_adata, addr) \
+ ((addr) + (acp_adata->rsrc->irqp_used * \
+ acp_adata->rsrc->irq_reg_offset))
+
+#define ACP_I2S_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2000)
+#define ACP_I2S_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2004)
+#define ACP_I2S_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2008)
+#define ACP_I2S_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x200C)
+#define ACP_I2S_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2010)
+#define ACP_I2S_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2014)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2018)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x201C)
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2020)
+#define ACP_I2S_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2024)
+#define ACP_I2S_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2028)
+#define ACP_I2S_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x202C)
+#define ACP_I2S_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2030)
+#define ACP_I2S_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2034)
+#define ACP_I2S_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2038)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x203C)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2040)
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2044)
+#define ACP_BT_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2048)
+#define ACP_BT_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x204C)
+#define ACP_BT_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2050)
+#define ACP_BT_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2054)
+#define ACP_BT_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2058)
+#define ACP_BT_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x205C)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2060)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2064)
+#define ACP_BT_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2068)
+#define ACP_BT_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x206C)
+#define ACP_BT_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2070)
+#define ACP_BT_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2074)
+#define ACP_BT_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2078)
+#define ACP_BT_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x207C)
+#define ACP_BT_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2080)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2084)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2088)
+#define ACP_BT_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 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/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h
new file mode 100644
index 000000000000..3930cc46fa58
--- /dev/null
+++ b/sound/soc/amd/acp/soc_amd_sdw_common.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * soc_amd_sdw_common.h - prototypes for common helpers
+ */
+
+#ifndef SOC_AMD_SDW_COMMON_H
+#define SOC_AMD_SDW_COMMON_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+#include <sound/soc_sdw_utils.h>
+
+#define ACP63_SDW_MAX_CPU_DAIS 8
+#define ACP63_SDW_MAX_LINKS 2
+
+#define AMD_SDW_MAX_GROUPS 9
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+#define ACP72_PCI_REV 0x72
+
+#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
+#define ASOC_SDW_FOUR_SPK BIT(4)
+#define ASOC_SDW_ACP_DMIC BIT(5)
+#define ASOC_SDW_CODEC_SPKR BIT(15)
+
+#define AMD_SDW0 0
+#define AMD_SDW1 1
+#define ACP63_SW0_AUDIO0_TX 0
+#define ACP63_SW0_AUDIO1_TX 1
+#define ACP63_SW0_AUDIO2_TX 2
+
+#define ACP63_SW0_AUDIO0_RX 3
+#define ACP63_SW0_AUDIO1_RX 4
+#define ACP63_SW0_AUDIO2_RX 5
+
+#define ACP63_SW1_AUDIO0_TX 0
+#define ACP63_SW1_AUDIO0_RX 1
+
+#define ACP_DMIC_BE_ID 4
+
+#define ACP70_SW_AUDIO0_TX 0
+#define ACP70_SW_AUDIO1_TX 1
+#define ACP70_SW_AUDIO2_TX 2
+
+#define ACP70_SW_AUDIO0_RX 3
+#define ACP70_SW_AUDIO1_RX 4
+#define ACP70_SW_AUDIO2_RX 5
+
+struct amd_mc_ctx {
+ unsigned int acp_rev;
+ unsigned int max_sdw_links;
+};
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+
+#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..4ca1978020a9
--- /dev/null
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -0,0 +1,556 @@
+// 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 snd_soc_jack_pin pco_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+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 = snd_soc_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_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack,
+ pco_jack_pins,
+ ARRAY_SIZE(pco_jack_pins));
+ 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 = snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ 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 = snd_soc_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 = snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_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,
+ .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,
+ .playback_only = 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,
+ .capture_only = 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/mach-config.h b/sound/soc/amd/mach-config.h
new file mode 100644
index 000000000000..5b6362103ca0
--- /dev/null
+++ b/sound/soc/amd/mach-config.h
@@ -0,0 +1,38 @@
+/* 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 FLAG_AMD_LEGACY_ONLY_DMIC BIT(4)
+
+#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[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_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..778ee4726389
--- /dev/null
+++ b/sound/soc/amd/ps/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Pink Sardine platform Support
+snd-pci-ps-y := pci-ps.o ps-common.o
+snd-ps-pdm-dma-y := ps-pdm-dma.o
+snd-soc-ps-mach-y := ps-mach.o
+snd-ps-sdw-dma-y := ps-sdw-dma.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) += snd-ps-sdw-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..90fc016dac0b
--- /dev/null
+++ b/sound/soc/amd/ps/acp63.h
@@ -0,0 +1,446 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms
+ *
+ * Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/soundwire/sdw_amd.h>
+#include <sound/acp63_chip_offset_byte.h>
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP63_REG_START 0x1240000
+#define ACP63_REG_END 0x125C000
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+#define ACP72_PCI_REV 0x72
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP63_PGFSM_STATUS_MASK 3
+#define ACP63_POWERED_ON 0
+#define ACP63_POWER_ON_IN_PROGRESS 1
+#define ACP63_POWERED_OFF 2
+#define ACP63_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 ACP_DMIC_DEV 2
+
+#define ACP63_DMIC_ADDR 2
+#define ACP63_SDW_ADDR 5
+#define AMD_SDW_MAX_MANAGERS 2
+
+/* time in ms for acp timeout */
+#define ACP63_TIMEOUT 500
+
+#define ACP_SDW0_STAT BIT(21)
+#define ACP_SDW1_STAT BIT(2)
+#define ACP_ERROR_IRQ BIT(29)
+
+#define ACP_AUDIO0_TX_THRESHOLD 0x1c
+#define ACP_AUDIO1_TX_THRESHOLD 0x1a
+#define ACP_AUDIO2_TX_THRESHOLD 0x18
+#define ACP_AUDIO0_RX_THRESHOLD 0x1b
+#define ACP_AUDIO1_RX_THRESHOLD 0x19
+#define ACP_AUDIO2_RX_THRESHOLD 0x17
+#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6)
+#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5)
+#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60
+#define ACP63_SDW0_DMA_MAX_STREAMS 6
+#define ACP63_SDW1_DMA_MAX_STREAMS 2
+#define ACP63_P1_AUDIO_TX_THRESHOLD 6
+
+/*
+ * Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL register.
+ * Stream id IRQ Bit
+ * 0 (SDW0_AUDIO0_TX) 28
+ * 1 (SDW0_AUDIO1_TX) 26
+ * 2 (SDW0_AUDIO2_TX) 24
+ * 3 (SDW0_AUDIO0_RX) 27
+ * 4 (SDW0_AUDIO1_RX) 25
+ * 5 (SDW0_AUDIO2_RX) 23
+ */
+#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register.
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO1_TX) 6
+ * 1 (SDW1_AUDIO1_RX) 5
+ */
+#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i))
+
+#define ACP_DELAY_US 5
+#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
+#define SDW0_MEM_WINDOW_START 0x4800000
+#define ACP_SDW_SRAM_PTE_OFFSET 0x03800400
+#define SDW0_PTE_OFFSET 0x400
+#define SDW_FIFO_SIZE 0x100
+#define SDW_DMA_SIZE 0x40
+#define ACP_SDW0_FIFO_OFFSET 0x100
+#define ACP_SDW_PTE_OFFSET 0x100
+#define SDW_FIFO_OFFSET 0x100
+#define SDW_PTE_OFFSET(i) (SDW0_PTE_OFFSET + ((i) * 0x600))
+#define ACP_SDW_FIFO_OFFSET(i) (ACP_SDW0_FIFO_OFFSET + ((i) * 0x500))
+#define SDW_MEM_WINDOW_START(i) (SDW0_MEM_WINDOW_START + ((i) * 0xC0000))
+
+#define SDW_PLAYBACK_MIN_NUM_PERIODS 2
+#define SDW_PLAYBACK_MAX_NUM_PERIODS 8
+#define SDW_PLAYBACK_MAX_PERIOD_SIZE 8192
+#define SDW_PLAYBACK_MIN_PERIOD_SIZE 1024
+#define SDW_CAPTURE_MIN_NUM_PERIODS 2
+#define SDW_CAPTURE_MAX_NUM_PERIODS 8
+#define SDW_CAPTURE_MAX_PERIOD_SIZE 8192
+#define SDW_CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
+#define SDW_MIN_BUFFER SDW_MAX_BUFFER
+
+#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb)
+
+#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
+#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP70_PGFSM_STATUS_MASK 0xFF
+#define ACP70_TIMEOUT 2000
+#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
+#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
+#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
+#define ACP70_SDW0_PME_STAT BIT(26)
+#define ACP70_SDW1_PME_STAT BIT(27)
+
+#define ACP70_SDW0_DMA_MAX_STREAMS 6
+#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS
+#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8
+
+#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8
+#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6
+#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4
+#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7
+#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5
+#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3
+
+#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO0_TX) 8
+ * 1 (SDW1_AUDIO1_TX) 6
+ * 2 (SDW1_AUDIO2_TX) 4
+ * 3 (SDW1_AUDIO0_RX) 7
+ * 4 (SDW1_AUDIO1_RX) 5
+ * 5 (SDW1_AUDIO2_RX) 3
+ */
+#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN
+#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN
+#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN
+#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN
+#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN
+#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN
+
+#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10
+#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50
+#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C
+#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88
+#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28
+#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44
+
+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,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+enum amd_acp63_sdw0_channel {
+ ACP63_SDW0_AUDIO0_TX = 0,
+ ACP63_SDW0_AUDIO1_TX,
+ ACP63_SDW0_AUDIO2_TX,
+ ACP63_SDW0_AUDIO0_RX,
+ ACP63_SDW0_AUDIO1_RX,
+ ACP63_SDW0_AUDIO2_RX,
+};
+
+enum amd_acp63_sdw1_channel {
+ ACP63_SDW1_AUDIO1_TX,
+ ACP63_SDW1_AUDIO1_RX,
+};
+
+enum amd_acp70_sdw_channel {
+ ACP70_SDW_AUDIO0_TX = 0,
+ ACP70_SDW_AUDIO1_TX,
+ ACP70_SDW_AUDIO2_TX,
+ ACP70_SDW_AUDIO0_RX,
+ ACP70_SDW_AUDIO1_RX,
+ ACP70_SDW_AUDIO2_RX,
+};
+
+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;
+};
+
+struct sdw_dma_dev_data {
+ void __iomem *acp_base;
+ struct mutex *acp_lock; /* used to protect acp common register access */
+ u32 acp_rev;
+ struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+struct acp_sdw_dma_stream {
+ u16 num_pages;
+ u16 channels;
+ u32 stream_id;
+ u32 instance;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+};
+
+union acp_sdw_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct sdw_dma_ring_buf_reg {
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 water_mark_size_reg;
+ u32 pos_low_reg;
+ u32 pos_high_reg;
+};
+
+struct acp63_dev_data;
+
+/**
+ * struct acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @acp_get_config: function to read the acp pin configuration
+ * @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread
+ * acp_suspend: ACP system level suspend callback
+ * acp_resume: ACP system level resume callback
+ * acp_suspend_runtime: ACP runtime suspend callback
+ * acp_resume_runtime: ACP runtime resume callback
+ */
+struct acp_hw_ops {
+ int (*acp_init)(void __iomem *acp_base, struct device *dev);
+ int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
+ void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data);
+ void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data);
+ int (*acp_suspend)(struct device *dev);
+ int (*acp_resume)(struct device *dev);
+ int (*acp_suspend_runtime)(struct device *dev);
+ int (*acp_resume_runtime)(struct device *dev);
+};
+
+/**
+ * struct acp63_dev_data - acp pci driver context
+ * @acp63_base: acp mmio base
+ * @res: resource
+ * @hw_ops: ACP pci driver platform-specific ops
+ * @pdm_dev: ACP PDM controller platform device
+ * @dmic_codec: platform device for DMIC Codec
+ * sdw_dma_dev: platform device for SoundWire DMA controller
+ * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration
+ * @acp_lock: used to protect acp common registers
+ * @info: SoundWire AMD information found in ACPI tables
+ * @sdw: SoundWire context for all SoundWire manager instances
+ * @machine: ACPI machines for SoundWire interface
+ * @is_sdw_dev: flag set to true when any SoundWire manager instances are available
+ * @is_pdm_dev: flag set to true when ACP PDM controller exists
+ * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
+ * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
+ * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
+ * @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance
+ * @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance
+ * @addr: pci ioremap address
+ * @reg_range: ACP reigister range
+ * @acp_rev: ACP PCI revision id
+ * @acp_sw_pad_keeper_en: store acp SoundWire pad keeper enable register value
+ * @acp_pad_pulldown_ctrl: store acp pad pulldown control register value
+ * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW0 instance
+ * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW1 instance
+ * @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW0 instance
+ * @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW1 instance
+ */
+
+struct acp63_dev_data {
+ void __iomem *acp63_base;
+ struct resource *res;
+ struct acp_hw_ops *hw_ops;
+ struct platform_device *pdm_dev;
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *sdw_dma_dev;
+ struct platform_device *mach_dev;
+ struct mutex acp_lock; /* protect shared registers */
+ struct sdw_amd_acpi_info info;
+ /* sdw context allocated by SoundWire driver */
+ struct sdw_amd_ctx *sdw;
+ struct snd_soc_acpi_mach *machines;
+ bool is_sdw_dev;
+ bool is_pdm_dev;
+ bool is_pdm_config;
+ bool is_sdw_config;
+ bool sdw_en_stat;
+ bool acp70_sdw0_wake_event;
+ bool acp70_sdw1_wake_event;
+ u32 addr;
+ u32 reg_range;
+ u32 acp_rev;
+ u32 acp_sw_pad_keeper_en;
+ u32 acp_pad_pulldown_ctrl;
+ u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
+ u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops);
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops);
+
+static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_init)
+ return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
+ return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_get_config)
+ ACP_HW_OPS(adata, acp_get_config)(pci, adata);
+}
+
+static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread)
+ ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata);
+}
+
+static inline int acp_hw_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
+ return ACP_HW_OPS(adata, acp_suspend)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
+ return ACP_HW_OPS(adata, acp_resume)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_suspend_runtime(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
+ return ACP_HW_OPS(adata, acp_suspend_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
+ return ACP_HW_OPS(adata, acp_resume_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+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..c62299b29204
--- /dev/null
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -0,0 +1,746 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/bitops.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 <linux/iopoll.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "../mach-config.h"
+
+#include "acp63.h"
+
+static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata)
+{
+ struct amd_sdw_manager *amd_manager;
+
+ if (adata->acp70_sdw0_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw0_wake_event = 0;
+ }
+
+ if (adata->acp70_sdw1_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw1_wake_event = 0;
+ }
+}
+
+static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata)
+{
+ u32 ext_intr_stat1;
+ int irq_flag = 0;
+ bool sdw_wake_irq = false;
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
+ writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
+ writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW0_WAKE_EN);
+ writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW1_WAKE_EN);
+ writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (sdw_wake_irq) {
+ handle_acp70_sdw_wake_event(adata);
+ irq_flag = 1;
+ }
+ return irq_flag;
+}
+
+static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat,
+ u32 ext_intr_stat1)
+{
+ u32 stream_id = 0;
+ u16 sdw_dma_irq_flag = 0;
+ u16 index;
+
+ if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) {
+ for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat & BIT(index)) {
+ writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ switch (index) {
+ case ACP_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_RX;
+ break;
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ }
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) {
+ for (index = ACP70_P1_AUDIO2_RX_THRESHOLD;
+ index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat1 & BIT(index)) {
+ writel(BIT(index),
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ switch (index) {
+ case ACP70_P1_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_TX;
+ break;
+ case ACP70_P1_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_TX;
+ break;
+ case ACP70_P1_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_TX;
+ break;
+ case ACP70_P1_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_RX;
+ break;
+ case ACP70_P1_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_RX;
+ break;
+ case ACP70_P1_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_RX;
+ break;
+ }
+
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ break;
+ }
+ return sdw_dma_irq_flag;
+}
+
+static irqreturn_t acp63_irq_thread(int irq, void *context)
+{
+ struct acp63_dev_data *adata = context;
+
+ acp_hw_sdw_dma_irq_thread(adata);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
+{
+ struct acp63_dev_data *adata;
+ struct pdm_dev_data *ps_pdm_data;
+ struct amd_sdw_manager *amd_manager;
+ u32 ext_intr_stat, ext_intr_stat1;
+ u16 irq_flag = 0;
+ u16 wake_irq_flag = 0;
+ u16 sdw_dma_irq_flag = 0;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+ /* ACP interrupts will be cleared by reading particular bit and writing
+ * same value to the status register. writing zero's doesn't have any
+ * effect.
+ * Bit by bit checking of IRQ field is implemented.
+ */
+ ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ext_intr_stat & ACP_SDW0_STAT) {
+ writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP_SDW1_STAT) {
+ writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ if (ext_intr_stat & ACP_ERROR_IRQ) {
+ writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ /* TODO: Report SoundWire Manager instance errors */
+ writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_ERROR_STATUS);
+ irq_flag = 1;
+ }
+
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ wake_irq_flag = check_and_handle_acp70_sdw_wake_irq(adata);
+
+ if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
+ ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
+ 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);
+ irq_flag = 1;
+ }
+
+ sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1);
+ if (sdw_dma_irq_flag)
+ return IRQ_WAKE_THREAD;
+
+ if (irq_flag | wake_irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE)
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ struct acpi_device *sdw_dev;
+ struct acp63_dev_data *acp_data;
+
+ acp_data = dev_get_drvdata(dev);
+ if (!addr)
+ return -ENODEV;
+
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0);
+ if (!sdw_dev)
+ return -ENODEV;
+
+ acp_data->info.handle = sdw_dev->handle;
+ acp_data->info.count = AMD_SDW_MAX_MANAGERS;
+ return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ struct acp63_dev_data *acp_data;
+ struct sdw_amd_res sdw_res;
+ int ret;
+
+ acp_data = dev_get_drvdata(dev);
+ memset(&sdw_res, 0, sizeof(sdw_res));
+ sdw_res.addr = acp_data->addr;
+ sdw_res.reg_range = acp_data->reg_range;
+ sdw_res.handle = acp_data->info.handle;
+ sdw_res.parent = dev;
+ sdw_res.dev = dev;
+ sdw_res.acp_lock = &acp_data->acp_lock;
+ sdw_res.count = acp_data->info.count;
+ sdw_res.mmio_base = acp_data->acp63_base;
+ sdw_res.acp_rev = acp_data->acp_rev;
+ sdw_res.link_mask = acp_data->info.link_mask;
+ ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+ if (ret)
+ dev_err(dev, "error: SoundWire probe failed\n");
+ return ret;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ if (acp_data->sdw)
+ sdw_amd_exit(acp_data->sdw);
+ acp_data->sdw = NULL;
+
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ const struct snd_soc_acpi_link_adr *link;
+ struct acp63_dev_data *acp_data = dev_get_drvdata(dev);
+ int ret, i;
+
+ if (acp_data->info.count) {
+ ret = sdw_amd_get_slave_info(acp_data->sdw);
+ if (ret) {
+ dev_dbg(dev, "failed to read slave information\n");
+ return NULL;
+ }
+ for (mach = acp_data->machines; mach; mach++) {
+ if (!mach->links)
+ break;
+ link = mach->links;
+ for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+ if (!snd_soc_acpi_sdw_link_slaves_found(dev, link,
+ acp_data->sdw->peripherals))
+ break;
+ }
+ if (i == acp_data->info.count || !link->num_adr)
+ break;
+ }
+ if (mach && mach->link_mask) {
+ mach->mach_params.links = mach->links;
+ mach->mach_params.link_mask = mach->link_mask;
+ mach->mach_params.subsystem_rev = acp_data->acp_rev;
+ return mach;
+ }
+ }
+ dev_dbg(dev, "No SoundWire machine driver found\n");
+ return NULL;
+}
+#else
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ return 0;
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int acp63_machine_register(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+ int size;
+
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ size = sizeof(*adata->machines);
+ mach = acp63_sdw_machine_select(dev);
+ if (mach) {
+ adata->mach_dev = platform_device_register_data(dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach,
+ size);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev,
+ "cannot register Machine device for SoundWire Interface\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+
+ } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) {
+ adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach",
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev, "cannot register amd_ps_mach device\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+ return 0;
+}
+
+static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ bool is_dmic_dev = false;
+ bool is_sdw_dev = false;
+ bool wov_en, dmic_en;
+ int ret;
+
+ /* IF WOV entry not found, enable dmic based on acp-audio-device-type entry*/
+ wov_en = true;
+ dmic_en = false;
+
+ acp_hw_get_config(pci, acp_data);
+
+ if (acp_data->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (pdm_dev) {
+ /* is_dmic_dev flag will be set when ACP PDM controller device exists */
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ dmic_en = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ wov_en = dmic_status;
+ }
+
+ if (dmic_en && wov_en)
+ is_dmic_dev = true;
+
+ if (acp_data->is_sdw_config) {
+ ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR);
+ if (!ret && acp_data->info.link_mask)
+ is_sdw_dev = true;
+ }
+
+ acp_data->is_pdm_dev = is_dmic_dev;
+ acp_data->is_sdw_dev = is_sdw_dev;
+ if (!is_dmic_dev && !is_sdw_dev) {
+ dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+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;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (adata->is_sdw_dev || adata->is_pdm_dev) {
+ 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));
+ }
+
+ if (adata->is_pdm_dev && adata->is_pdm_config) {
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->pdm_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->pdm_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->pdm_dev);
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ adata->dmic_codec_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->dmic_codec_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->dmic_codec_dev);
+ goto unregister_pdm_dev;
+ }
+ }
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ ret = amd_sdw_probe(&pci->dev);
+ if (ret) {
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->sdw_dma_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->sdw_dma_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->sdw_dma_dev);
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ }
+
+ return 0;
+unregister_dmic_codec_dev:
+ platform_device_unregister(adata->dmic_codec_dev);
+unregister_pdm_dev:
+ platform_device_unregister(adata->pdm_dev);
+de_init:
+ if (acp_hw_deinit(adata, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci)
+{
+ adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
+ GFP_KERNEL);
+ if (!adata->hw_ops)
+ return -ENOMEM;
+
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ acp63_hw_init_ops(adata->hw_ops);
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ acp70_hw_init_ops(adata->hw_ops);
+ break;
+ default:
+ dev_err(&pci->dev, "ACP device not found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+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 ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */
+ switch (pci->revision) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp63/acp70/acp71 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;
+ }
+ adata->addr = addr;
+ adata->reg_range = ACP63_REG_END - ACP63_REG_START;
+ adata->acp_rev = pci->revision;
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ mutex_init(&adata->acp_lock);
+ ret = acp_hw_init_ops(adata, pci);
+ if (ret) {
+ dev_err(&pci->dev, "ACP hw ops init failed\n");
+ goto release_regions;
+ }
+ ret = acp_hw_init(adata, &pci->dev);
+ if (ret)
+ goto release_regions;
+ ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto de_init;
+ }
+ ret = get_acp63_device_config(pci, adata);
+ /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
+ if (ret) {
+ dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
+ goto skip_pdev_creation;
+ }
+ ret = create_acp63_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ adata->machines = snd_soc_acpi_amd_acp70_sdw_machines;
+ else
+ adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
+
+ ret = acp63_machine_register(&pci->dev);
+ if (ret) {
+ dev_err(&pci->dev, "ACP machine register failed\n");
+ goto de_init;
+ }
+skip_pdev_creation:
+ device_set_wakeup_enable(&pci->dev, true);
+ 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 (acp_hw_deinit(adata, &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 snd_acp_suspend(struct device *dev)
+{
+ return acp_hw_suspend(dev);
+}
+
+static int snd_acp_runtime_resume(struct device *dev)
+{
+ return acp_hw_runtime_resume(dev);
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ return acp_hw_resume(dev);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void snd_acp63_remove(struct pci_dev *pci)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->sdw) {
+ amd_sdw_exit(adata);
+ platform_device_unregister(adata->sdw_dma_dev);
+ }
+ if (adata->is_pdm_dev) {
+ platform_device_unregister(adata->pdm_dev);
+ platform_device_unregister(adata->dmic_codec_dev);
+ }
+ if (adata->mach_dev)
+ platform_device_unregister(adata->mach_dev);
+ ret = acp_hw_deinit(adata, &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 = pm_ptr(&acp63_pm_ops),
+ }
+};
+
+module_pci_driver(ps_acp63_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
+MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c
new file mode 100644
index 000000000000..7b4966b75dc6
--- /dev/null
+++ b/sound/soc/amd/ps/ps-common.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1
+ * platforms.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ * Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+
+#include "acp63.h"
+
+static int acp63_power_on(void __iomem *acp_base)
+{
+ u32 val;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS)
+ writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static int acp63_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP63_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static void acp63_enable_interrupts(void __iomem *acp_base)
+{
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp63_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ 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;
+ }
+ 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);
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ 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;
+ }
+ writel(0, acp_base + ACP_CONTROL);
+ writel(1, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+{
+ u32 sdw0_en, sdw1_en;
+
+ sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
+ sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
+ return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
+{
+ u32 val;
+
+ val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
+ if (val && adata->sdw->pdev[0])
+ pm_request_resume(&adata->sdw->pdev[0]->dev);
+
+ val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
+ if (val && adata->sdw->pdev[1])
+ pm_request_resume(&adata->sdw->pdev[1]->dev);
+}
+
+static int __maybe_unused snd_acp63_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+
+ if (!adata->sdw_en_stat)
+ handle_acp63_sdw_pme_event(adata);
+ return 0;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
+ return ret;
+}
+
+static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]);
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]);
+ adata->acp63_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp63_init;
+ hw_ops->acp_deinit = acp63_deinit;
+ hw_ops->acp_get_config = acp63_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp63_suspend;
+ hw_ops->acp_resume = snd_acp63_resume;
+ hw_ops->acp_suspend_runtime = snd_acp63_suspend;
+ hw_ops->acp_resume_runtime = snd_acp63_runtime_resume;
+}
+
+static int acp70_power_on(void __iomem *acp_base)
+{
+ u32 val = 0;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return 0;
+ if (val & ACP70_PGFSM_STATUS_MASK)
+ writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static int acp70_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP70_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_cntl1;
+
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+}
+
+static void acp70_enable_interrupts(void __iomem *acp_base)
+{
+ u32 sdw0_wake_en, sdw1_wake_en;
+
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN);
+ sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN);
+ if (sdw0_wake_en || sdw1_wake_en)
+ acp70_enable_sdw_host_wake_interrupts(acp_base);
+}
+
+static void acp70_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp70_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp70_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ acp70_enable_interrupts(acp_base);
+ writel(0x1, acp_base + ACP_PME_EN);
+ return 0;
+}
+
+static int acp70_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp70_disable_interrupts(acp_base);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_20:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_16:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ case ACP_CONFIG_19:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]);
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]);
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+static int __maybe_unused snd_acp70_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int __maybe_unused snd_acp70_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
+ return ret;
+}
+
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp70_init;
+ hw_ops->acp_deinit = acp70_deinit;
+ hw_ops->acp_get_config = acp70_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp70_suspend;
+ hw_ops->acp_resume = snd_acp70_resume;
+ hw_ops->acp_suspend_runtime = snd_acp70_suspend;
+ hw_ops->acp_resume_runtime = snd_acp70_runtime_resume;
+}
diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c
new file mode 100644
index 000000000000..ff8ad036b077
--- /dev/null
+++ b/sound/soc/amd/ps/ps-mach.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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_DESCRIPTION("AMD Pink Sardine support for DMIC");
+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..9cfbe05ad996
--- /dev/null
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 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)
+{
+ writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ 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;
+
+ writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = 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));
+ 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 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ 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 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ 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 = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = 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);
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = 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 = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = 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;
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ 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 */
+ writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ 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);
+
+ writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ 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 = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count = high;
+ low = 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:
+ writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ 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;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ 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 = &acp_data->acp_lock;
+ 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_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int 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 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 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 = {
+ RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume)
+};
+
+static struct platform_driver acp63_pdm_dma_driver = {
+ .probe = acp63_pdm_audio_probe,
+ .remove = acp63_pdm_audio_remove,
+ .driver = {
+ .name = "acp_ps_pdm_dma",
+ .pm = pm_ptr(&acp63_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_pdm_dma_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
new file mode 100644
index 000000000000..5449323e2728
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1
+ * platforms.
+ *
+ * Copyright 2023, 2025 Advanced Micro Devices, Inc.
+ */
+
+#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 <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "acp63.h"
+
+#define DRV_NAME "amd_ps_sdw_dma"
+
+static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
+ * set as per hardware register documentation
+ */
+static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+};
+
+static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ ACP_SW0_AUDIO0_TX_EN,
+ ACP_SW0_AUDIO1_TX_EN,
+ ACP_SW0_AUDIO2_TX_EN,
+ ACP_SW0_AUDIO0_RX_EN,
+ ACP_SW0_AUDIO1_RX_EN,
+ ACP_SW0_AUDIO2_RX_EN,
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA enable register programming for SDW1 instance,
+ * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
+ * as per hardware register documentation.
+ */
+static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ ACP_SW1_AUDIO1_TX_EN,
+ ACP_SW1_AUDIO1_RX_EN,
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE,
+ ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR,
+ ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE,
+ ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR,
+ ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE,
+ ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR,
+ ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE,
+ ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR,
+ ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ ACP70_SW0_AUDIO0_TX_EN,
+ ACP70_SW0_AUDIO1_TX_EN,
+ ACP70_SW0_AUDIO2_TX_EN,
+ ACP70_SW0_AUDIO0_RX_EN,
+ ACP70_SW0_AUDIO1_RX_EN,
+ ACP70_SW0_AUDIO2_RX_EN,
+};
+
+static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ ACP70_SW1_AUDIO0_TX_EN,
+ ACP70_SW1_AUDIO1_TX_EN,
+ ACP70_SW1_AUDIO2_TX_EN,
+ ACP70_SW1_AUDIO0_RX_EN,
+ ACP70_SW1_AUDIO1_RX_EN,
+ ACP70_SW1_AUDIO2_RX_EN,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_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_S24_LE | 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 = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_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_S24_LE | 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 = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask,
+ u32 irq_mask1, bool enable)
+{
+ u32 ext_intr_cntl, ext_intr_cntl1;
+
+ if (enable) {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl |= irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ } else {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl &= ~irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 &= ~irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ }
+}
+
+static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 stream_id)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ u32 sdw_dma_pte_offset;
+ dma_addr_t addr;
+
+ addr = stream->dma_addr;
+ sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance);
+ val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET);
+
+ /* Group Enable */
+ writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+ for (page_idx = 0; page_idx < stream->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, acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+ writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size,
+ u32 manager_instance, u32 acp_rev)
+{
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 sdw_fifo_addr;
+ u32 sdw_fifo_offset;
+ u32 sdw_ring_buf_addr;
+ u32 sdw_ring_buf_size;
+ u32 sdw_mem_window_offset;
+
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance);
+ sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance);
+ sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET);
+ sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET);
+ sdw_ring_buf_size = size;
+ writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size);
+ writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr);
+ writel(sdw_fifo_addr, acp_base + reg_fifo_addr);
+ writel(SDW_DMA_SIZE, acp_base + reg_dma_size);
+ writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size);
+ return 0;
+}
+
+static int acp63_sdw_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct acp_sdw_dma_stream *stream;
+ struct snd_soc_dai *cpu_dai;
+ struct amd_sdw_manager *amd_manager;
+ struct snd_soc_pcm_runtime *prtd = snd_soc_substream_to_rtd(substream);
+ int ret;
+
+ runtime = substream->runtime;
+ cpu_dai = snd_soc_rtd_to_cpu(prtd, 0);
+ amd_manager = snd_soc_dai_get_drvdata(cpu_dai);
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp63_sdw_hardware_playback;
+ else
+ runtime->hw = acp63_sdw_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;
+ }
+
+ stream->stream_id = cpu_dai->id;
+ stream->instance = amd_manager->instance;
+ runtime->private_data = stream;
+ return ret;
+}
+
+static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct sdw_dma_dev_data *sdw_data;
+ u32 period_bytes;
+ u32 water_mark_size_reg;
+ u32 irq_mask, ext_intr_ctrl;
+ u64 size;
+ u32 stream_id;
+ u32 acp_ext_intr_cntl_reg;
+ int ret;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ stream_id = stream->stream_id;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id));
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ stream->dma_addr = substream->runtime->dma_addr;
+ stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(stream, sdw_data->acp_base, stream_id);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size,
+ stream->instance, sdw_data->acp_rev);
+ if (ret) {
+ dev_err(component->dev, "Invalid DMA channel\n");
+ return -EINVAL;
+ }
+ ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ ext_intr_ctrl |= irq_mask;
+ writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ return 0;
+}
+
+static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 acp_rev)
+{
+ union acp_sdw_dma_count byte_count;
+ u32 pos_low_reg, pos_high_reg;
+
+ byte_count.bytescount = 0;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ if (pos_low_reg) {
+ byte_count.bcount.high = readl(acp_base + pos_high_reg);
+ byte_count.bcount.low = readl(acp_base + pos_low_reg);
+ }
+POINTER_RETURN_BYTES:
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ stream = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev);
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp63_sdw_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, SDW_MIN_BUFFER, SDW_MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_sdw_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ kfree(stream);
+ return 0;
+}
+
+static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
+ void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable)
+{
+ struct acp_sdw_dma_stream *stream;
+ u32 stream_id;
+ u32 sdw_dma_en_reg;
+ u32 sdw_dma_en_stat_reg;
+ u32 sdw_dma_stat;
+ u32 dma_enable;
+
+ stream = substream->runtime->private_data;
+ stream_id = stream->stream_id;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_dma_en_stat_reg = sdw_dma_en_reg + 4;
+ dma_enable = sdw_dma_enable;
+ writel(dma_enable, acp_base + sdw_dma_en_reg);
+ return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat,
+ (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER);
+}
+
+static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ int ret;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ dev_err(comp->dev, "trigger %d failed: %d", cmd, ret);
+ return ret;
+}
+
+static const struct snd_soc_component_driver acp63_sdw_component = {
+ .name = DRV_NAME,
+ .open = acp63_sdw_dma_open,
+ .close = acp63_sdw_dma_close,
+ .hw_params = acp63_sdw_dma_hw_params,
+ .trigger = acp63_sdw_dma_trigger,
+ .pointer = acp63_sdw_dma_pointer,
+ .pcm_construct = acp63_sdw_dma_new,
+ .use_dai_pcm_id = true,
+
+};
+
+static int acp63_sdw_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
+ if (!sdw_data)
+ return -ENOMEM;
+
+ sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!sdw_data->acp_base)
+ return -ENOMEM;
+
+ sdw_data->acp_lock = &acp_data->acp_lock;
+ sdw_data->acp_rev = acp_data->acp_rev;
+ dev_set_drvdata(&pdev->dev, sdw_data);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_sdw_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register sdw dma component\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP63_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ if (instance == ACP_SDW0)
+ stream_count = ACP63_SDW0_DMA_MAX_STREAMS;
+ else
+ stream_count = ACP63_SDW1_DMA_MAX_STREAMS;
+
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp63_sdw0_dma_stream[index];
+ water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp63_sdw1_dma_stream[index];
+ water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ ACP63_PCI_REV);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP70_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK;
+ stream_count = ACP70_SDW0_DMA_MAX_STREAMS;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp70_sdw0_dma_stream[index];
+ water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp70_sdw1_dma_stream[index];
+ water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ sdw_data->acp_rev);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp63_sdw_pcm_resume(struct device *dev)
+{
+ struct sdw_dma_dev_data *sdw_data;
+
+ sdw_data = dev_get_drvdata(dev);
+ if (sdw_data->acp_rev == ACP63_PCI_REV)
+ return acp63_restore_sdw_dma_config(sdw_data);
+ else
+ return acp70_restore_sdw_dma_config(sdw_data);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume)
+};
+
+static struct platform_driver acp63_sdw_dma_driver = {
+ .probe = acp63_sdw_platform_probe,
+ .remove = acp63_sdw_platform_remove,
+ .driver = {
+ .name = "amd_ps_sdw_dma",
+ .pm = pm_ptr(&acp63_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL");
+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..b2ea030cbf25
--- /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-y := pci-acp3x.o
+snd-acp3x-pcm-dma-y := acp3x-pcm-dma.o
+snd-acp3x-i2s-y := 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..352485dd98b1
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -0,0 +1,331 @@
+// 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 = snd_soc_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 val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+ int ret;
+
+ 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..bb9ed52d744d
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -0,0 +1,526 @@
+// 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 = snd_soc_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 = snd_soc_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 = snd_soc_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_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&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 = 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..76b4a9c3e24f
--- /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-y := rn-pci-acp3x.o
+snd-acp3x-pdm-dma-y := acp3x-pdm-dma.o
+snd-acp3x-rn-y := 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..95ac8c680037
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -0,0 +1,504 @@
+// 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_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&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 = 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..3249f74a0197
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-rn.c
@@ -0,0 +1,77 @@
+// 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_DESCRIPTION("AMD Renoir support for DMIC");
+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..a3825c5be4e7
--- /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-y := 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..e3afe9172bdf
--- /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 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 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 = {
+ RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
+ 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 = pm_ptr(&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..7eae82faa392
--- /dev/null
+++ b/sound/soc/amd/vangogh/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Vangogh platform Support
+snd-pci-acp5x-y := pci-acp5x.o
+snd-acp5x-i2s-y := acp5x-i2s.o
+snd-acp5x-pcm-dma-y := acp5x-pcm-dma.o
+snd-soc-acp5x-mach-y := 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..bf719f628617
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -0,0 +1,417 @@
+// 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 = snd_soc_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 val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+ int ret;
+
+ 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..2ca904db82ab
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Vangogh platform using either
+ * NAU8821 & CS35L41 or NAU8821 & MAX98388 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 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"
+#define ACP5X_MAX98388_COMP_LNAME "i2c-ADS8388:00"
+#define ACP5X_MAX98388_COMP_RNAME "i2c-ADS8388:01"
+#define ACP5X_MAX98388_DAI_NAME "max98388-aif1"
+
+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 = snd_soc_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 = snd_soc_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 = snd_soc_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;
+
+ if (!dai)
+ return -EINVAL;
+
+ 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 = snd_soc_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 = snd_soc_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,
+ .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,
+ .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_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_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 const struct snd_soc_ops acp5x_max98388_play_ops = {
+ .startup = acp5x_max98388_startup,
+};
+
+static struct snd_soc_codec_conf acp5x_max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME,
+ ACP5X_MAX98388_DAI_NAME),
+ COMP_CODEC(ACP5X_MAX98388_COMP_RNAME,
+ ACP5X_MAX98388_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_98388_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,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-max98388-play",
+ .stream_name = "MAX98388 Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp5x_max98388_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_98388_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),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = {
+ { "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" },
+
+ { "SPK", NULL, "Left BE_OUT" },
+ { "SPK", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_card acp5x_8821_98388_card = {
+ .name = "acp5x-max98388",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_98388_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_98388_dai),
+ .dapm_widgets = acp5x_8821_98388_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets),
+ .dapm_routes = acp5x_8821_98388_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route),
+ .codec_conf = acp5x_max98388_conf,
+ .num_configs = ARRAY_SIZE(acp5x_max98388_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static const struct dmi_system_id acp5x_vg_quirk_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ .driver_data = (void *)&acp5x_8821_35l41_card,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ .driver_data = (void *)&acp5x_8821_98388_card,
+ },
+ {}
+};
+
+static int acp5x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp5x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ dmi_id = dmi_first_match(acp5x_vg_quirk_table);
+ if (!dmi_id || !dmi_id->driver_data)
+ return -ENODEV;
+
+ machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card = dmi_id->driver_data;
+ 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 = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp5x_probe,
+};
+
+module_platform_driver(acp5x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 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..aa4726899434
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
@@ -0,0 +1,513 @@
+// 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 = snd_soc_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 = snd_soc_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 = snd_soc_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_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp5x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int 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 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 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 = {
+ RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
+};
+
+static struct platform_driver acp5x_dma_driver = {
+ .probe = acp5x_audio_probe,
+ .remove = 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..ac1936a8c43f
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -0,0 +1,224 @@
+/* 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);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+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..af56ff09f02a
--- /dev/null
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Vangogh ACP PCI Driver
+//
+// Copyright (C) 2021, 2023 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"
+#include "../mach-config.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, flag;
+ int ret, i;
+ u32 addr, val;
+
+ /*
+ * Return if ACP config flag is defined, except when board
+ * supports SOF while it is not being enabled in kernel config.
+ */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY &&
+ (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))
+ return -ENODEV;
+
+ 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 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 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 = {
+ RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)
+ 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 = pm_ptr(&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..7a0a3a410b2d
--- /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-y := pci-acp6x.o
+snd-acp6x-pdm-dma-y := acp6x-pdm-dma.o
+snd-soc-acp6x-mach-y := 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..f210a253da9f
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -0,0 +1,740 @@
+// 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, "21EF"),
+ }
+ },
+ {
+ .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, "21J0"),
+ }
+ },
+ {
+ .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, "21M1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21ME"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82TL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82V2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82YM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83AS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83BS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83L3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83N6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83J2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83J3"),
+ }
+ },
+ {
+ .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, "M5602RA"),
+ }
+ },
+ {
+ .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, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6501RM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1404FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1504FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M7600RE"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3502RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VEK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"),
+ }
+ },
+ {
+ .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, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi G 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_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb1xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb2xxx"),
+ }
+ },
+ {
+ .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, "8A44"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A22"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A3E"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A7F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A81"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B27"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B2F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8BD6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MECHREVO"),
+ DMI_MATCH(DMI_BOARD_NAME, "MRID6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MDC"),
+ DMI_MATCH(DMI_BOARD_NAME, "Herbag_MDU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang13"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7UCX"),
+ }
+ },
+ {}
+};
+
+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;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ int ret;
+ bool is_dmic_enable, wov_en;
+
+ /* IF WOV entry not found, enable dmic based on AcpDmicConnected entry*/
+ is_dmic_enable = false;
+ wov_en = true;
+ /* 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)
+ is_dmic_enable = true;
+ }
+
+ handle = ACPI_HANDLE(pdev->dev.parent);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret)) {
+ wov_en = dmic_status;
+ if (!wov_en)
+ return -ENODEV;
+ } else {
+ /* Incase of ACPI method read failure then jump to check_dmi_entry */
+ goto check_dmi_entry;
+ }
+
+ if (is_dmic_enable)
+ platform_set_drvdata(pdev, &acp6x_card);
+
+check_dmi_entry:
+ /* 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_DESCRIPTION("AMD Yellow Carp support for DMIC");
+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..ac758b90f441
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -0,0 +1,455 @@
+// 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_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp6x_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int 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 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 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 = {
+ RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
+};
+
+static struct platform_driver acp6x_pdm_dma_driver = {
+ .probe = acp6x_pdm_audio_probe,
+ .remove = acp6x_pdm_audio_remove,
+ .driver = {
+ .name = "acp_yc_pdm_dma",
+ .pm = pm_ptr(&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..1140ed1cbb3d
--- /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 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 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 = {
+ RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
+ 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 = pm_ptr(&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..d8dc2f1ccc83
--- /dev/null
+++ b/sound/soc/apple/Kconfig
@@ -0,0 +1,11 @@
+menu "Apple"
+
+config SND_SOC_APPLE_MCA
+ tristate "Apple Silicon MCA driver"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select SND_DMAENGINE_PCM
+ help
+ This option enables an ASoC platform driver for MCA peripherals found
+ on Apple Silicon SoCs.
+
+endmenu
diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile
new file mode 100644
index 000000000000..1eb8fbef60c6
--- /dev/null
+++ b/sound/soc/apple/Makefile
@@ -0,0 +1,3 @@
+snd-soc-apple-mca-y := 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..c4dcb2b54591
--- /dev/null
+++ b/sound/soc/apple/mca.c
@@ -0,0 +1,1212 @@
+// 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[] __counted_by(nclusters);
+};
+
+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_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ unsigned int mask, nchannels;
+
+ if (cl->tdm_slots) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mask = cl->tdm_tx_mask;
+ else
+ mask = cl->tdm_rx_mask;
+
+ nchannels = hweight32(mask);
+ } else {
+ nchannels = 2;
+ }
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, nchannels);
+}
+
+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 = snd_soc_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(snd_soc_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=%u 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 = {
+ .startup = mca_fe_startup,
+ .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 = snd_soc_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(snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ struct mca_cluster *cl = mca_dai_to_cluster(snd_soc_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 = snd_soc_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 = snd_soc_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 = snd_soc_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, snd_soc_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 = snd_soc_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(snd_soc_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(snd_soc_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,t8103-mca", },
+ { .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 = 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 dcee145dd179..4f51612f3dd2 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -1,42 +1,43 @@
-config SND_ATMEL_SOC
- tristate "SoC Audio for the Atmel System-on-Chip"
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Atmel"
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
- tristate
- depends on HAS_DMA
- default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_PDC
- tristate
+ bool
config SND_ATMEL_SOC_DMA
- tristate
+ bool
select SND_SOC_GENERIC_DMAENGINE_PCM
- default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_DMA
- tristate
config SND_ATMEL_SOC_SSC
tristate
- default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
- default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
+ 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 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 ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ depends on ATMEL_SSC && I2C
select SND_ATMEL_SOC_SSC_PDC
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
@@ -88,4 +89,84 @@ config SND_ATMEL_SOC_TSE850_PCM5142
help
Say Y if you want to add support for the ASoC driver for the
Axentia TSE-850 with a PCM5142 codec.
-endif
+
+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 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.
+
+endmenu
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 4440646416e8..03d9c419c93f 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -1,20 +1,37 @@
# SPDX-License-Identifier: GPL-2.0
# AT91 Platform Support
-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-pcm-pdc-y := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-dma-y := atmel-pcm-dma.o
+snd-soc-atmel_ssc_dai-y := atmel_ssc_dai.o
+snd-soc-atmel-i2s-y := atmel-i2s.o
+snd-soc-mchp-i2s-mcc-y := mchp-i2s-mcc.o
+snd-soc-mchp-spdiftx-y := mchp-spdiftx.o
+snd-soc-mchp-spdifrx-y := mchp-spdifrx.o
+snd-soc-mchp-pdmc-y := mchp-pdmc.o
-obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
-obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.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
-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-sam9g20-wm8731-y := sam9g20_wm8731.o
+snd-atmel-soc-wm8904-y := atmel_wm8904.o
+snd-soc-sam9x5-wm8731-y := sam9x5_wm8731.o
+snd-atmel-soc-classd-y := atmel-classd.o
+snd-atmel-soc-pdmic-y := atmel-pdmic.o
+snd-atmel-soc-tse850-pcm5142-y := tse850-pcm5142.o
+snd-soc-mikroe-proto-y := mikroe-proto.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
@@ -22,3 +39,4 @@ 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
index ebabed69f0e6..1f8c60d2de82 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 or later
- * as published by the Free Software Foundation.
*/
#include <linux/of.h>
@@ -14,6 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -32,6 +30,7 @@ struct atmel_classd {
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
+ struct device *dev;
int irq;
const struct atmel_classd_pdata *pdata;
};
@@ -50,7 +49,7 @@ 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;
+ const char *pwm_type_s;
int ret;
if (!np) {
@@ -62,8 +61,8 @@ static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
if (!pdata)
return ERR_PTR(-ENOMEM);
- ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
- if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+ 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;
@@ -120,52 +119,34 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int err;
regmap_write(dd->regmap, CLASSD_THR, 0x0);
- return clk_prepare_enable(dd->pclk);
-}
-
-static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
-
- clk_disable_unprepare(dd->pclk);
+ 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;
}
-static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
- .startup = atmel_classd_cpu_dai_startup,
- .shutdown = atmel_classd_cpu_dai_shutdown,
-};
-
-static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
- .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",
-};
-
/* 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 = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
if (params_physical_width(params) != 16) {
- dev_err(rtd->platform->dev,
+ dev_err(dd->dev,
"only supports 16-bit audio data\n");
return -EINVAL;
}
@@ -247,9 +228,9 @@ static const char * const pwm_type[] = {
"Single ended", "Differential"
};
-static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+static int atmel_classd_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -283,54 +264,35 @@ static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
default:
val |= (CLASSD_MR_NOVR_VAL_10NS
<< CLASSD_MR_NOVR_VAL_SHIFT);
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"non-overlapping value %d is invalid, the default value 10 is specified\n",
pdata->non_overlap_time);
break;
}
}
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
- dev_info(codec->dev,
+ 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");
+ str_enabled_disabled(pdata->non_overlap_enable));
return 0;
}
-static int atmel_classd_codec_resume(struct snd_soc_codec *codec)
+static int atmel_classd_component_resume(struct snd_soc_component *component)
{
- struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ 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 struct snd_soc_codec_driver soc_codec_dev_classd = {
- .probe = atmel_classd_codec_probe,
- .resume = atmel_classd_codec_resume,
- .component_driver = {
- .controls = atmel_classd_snd_controls,
- .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
- },
-};
-
-/* codec dai component */
-static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
-
- return clk_prepare_enable(dd->gclk);
-}
-
-static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
- int mute)
+static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
+ int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 mask, val;
mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
@@ -340,7 +302,7 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
else
val = 0;
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
return 0;
}
@@ -373,13 +335,13 @@ static struct {
};
static int
-atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *codec_dai)
+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 = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
int fs;
int i, best, best_val, cur_val, ret;
u32 mask, val;
@@ -397,7 +359,7 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
}
}
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
sample_rates[best].rate, sample_rates[best].gclk_rate);
@@ -411,27 +373,27 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
| (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
- snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
return clk_prepare_enable(dd->gclk);
}
static void
-atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
clk_disable_unprepare(dd->gclk);
}
-static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
- snd_soc_update_bits(codec, CLASSD_MR,
+ 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));
@@ -439,10 +401,10 @@ static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *codec_dai)
+static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 mask, val;
mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
@@ -463,24 +425,22 @@ static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
return 0;
}
-static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
- .digital_mute = atmel_classd_codec_dai_digital_mute,
- .startup = atmel_classd_codec_dai_startup,
- .shutdown = atmel_classd_codec_dai_shutdown,
- .hw_params = atmel_classd_codec_dai_hw_params,
- .prepare = atmel_classd_codec_dai_prepare,
- .trigger = atmel_classd_codec_dai_trigger,
+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,
};
-#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
-
-static struct snd_soc_dai_driver atmel_classd_codec_dai = {
- .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -488,7 +448,18 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &atmel_classd_codec_dai_ops,
+ .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 */
@@ -497,17 +468,28 @@ static int atmel_classd_asoc_card_init(struct device *dev,
{
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 = &snd_soc_dummy_dlc;
+ dai_link->platforms = &comp[1];
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+ dai_link->num_platforms = 1;
+
dai_link->name = "CLASSD";
dai_link->stream_name = "CLASSD PCM";
- dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME;
- dai_link->cpu_dai_name = dev_name(dev);
- dai_link->codec_name = dev_name(dev);
- dai_link->platform_name = dev_name(dev);
+ dai_link->cpus->dai_name = dev_name(dev);
+ dai_link->platforms->name = dev_name(dev);
card->dai_link = dai_link;
card->num_links = 1;
@@ -558,11 +540,8 @@ static int atmel_classd_probe(struct platform_device *pdev)
dd->pdata = pdata;
dd->irq = platform_get_irq(pdev, 0);
- if (dd->irq < 0) {
- ret = dd->irq;
- dev_err(dev, "failed to could not get irq: %d\n", ret);
- return ret;
- }
+ if (dd->irq < 0)
+ return dd->irq;
dd->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dd->pclk)) {
@@ -578,15 +557,12 @@ static int atmel_classd_probe(struct platform_device *pdev)
return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- io_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(io_base)) {
- ret = PTR_ERR(io_base);
- dev_err(dev, "failed to remap register memory: %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);
@@ -612,13 +588,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
return ret;
}
- ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
- &atmel_classd_codec_dai, 1);
- if (ret) {
- dev_err(dev, "could not register codec: %d\n", ret);
- return ret;
- }
-
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
@@ -643,16 +612,9 @@ static int atmel_classd_probe(struct platform_device *pdev)
return 0;
unregister_codec:
- snd_soc_unregister_codec(dev);
return ret;
}
-static int atmel_classd_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver atmel_classd_driver = {
.driver = {
.name = "atmel-classd",
@@ -660,7 +622,6 @@ static struct platform_driver atmel_classd_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = atmel_classd_probe,
- .remove = atmel_classd_remove,
};
module_platform_driver(atmel_classd_driver);
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
new file mode 100644
index 000000000000..762199faf872
--- /dev/null
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -0,0 +1,742 @@
+// 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
+#define I2S_MCK_6M144 6144000UL
+
+/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
+static const struct atmel_i2s_gck_param gck_params[] = {
+ /* mck = 6.144Mhz */
+ { 8000, I2S_MCK_6M144, 1, 47}, /* mck = 768 fs */
+
+ /* mck = 12.288MHz */
+ { 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 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 const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+ .probe = atmel_i2s_dai_probe,
+ .prepare = atmel_i2s_prepare,
+ .trigger = atmel_i2s_trigger,
+ .hw_params = atmel_i2s_hw_params,
+ .set_fmt = atmel_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver atmel_i2s_dai = {
+ .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 = atmel_i2s_dt_ids,
+ },
+ .probe = atmel_i2s_probe,
+ .remove = 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
index dd57a9eac171..7306e04da513 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
*
@@ -8,20 +9,6 @@
* Based on atmel-pcm by:
* Sedji Gaouaou <sedji.gaouaou@atmel.com>
* Copyright 2008 Atmel
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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>
@@ -31,7 +18,6 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/atmel-ssc.h>
-#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -57,7 +43,7 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
.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
@@ -66,10 +52,10 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
static void atmel_pcm_dma_irq(u32 ssc_sr,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pcm_dma_params *prtd;
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (ssc_sr & prtd->mask->ssc_error) {
if (snd_pcm_running(substream))
@@ -91,12 +77,12 @@ static void atmel_pcm_dma_irq(u32 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 = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pcm_dma_params *prtd;
struct ssc_device *ssc;
int ret;
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
ssc = prtd->ssc;
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
@@ -124,16 +110,11 @@ static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
int atmel_pcm_dma_platform_register(struct device *dev)
{
- return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0);
+ return devm_snd_dmaengine_pcm_register(dev,
+ &atmel_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
-void atmel_pcm_dma_platform_unregister(struct device *dev)
-{
- snd_dmaengine_pcm_unregister(dev);
-}
-EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
-
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-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
index 91b7069c3499..7db8df85c54f 100644
--- a/sound/soc/atmel/atmel-pcm-pdc.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,83 +34,21 @@
#include "atmel-pcm.h"
-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_SSC_DMABUF_SIZE;
-
- 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: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
- (void *)buf->area, (void *)(long)buf->addr, size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- 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 int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int atmel_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
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;
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
- ret = atmel_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- pr_debug("atmel-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;
-}
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, ATMEL_SSC_DMABUF_SIZE,
+ ATMEL_SSC_DMABUF_SIZE);
-static void atmel_pcm_free(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;
- }
+ return 0;
}
/*--------------------------------------------------------------------------*\
@@ -209,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 = snd_soc_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(snd_soc_rtd_to_cpu(rtd, 0), substream);
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
prtd->dma_buffer = runtime->dma_addr;
@@ -238,7 +161,8 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
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;
@@ -252,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;
@@ -264,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;
@@ -330,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;
@@ -348,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;
@@ -373,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;
@@ -381,36 +308,24 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static const 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,
-};
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free,
+ .pcm_construct = atmel_pcm_new,
};
int atmel_pcm_pdc_platform_register(struct device *dev)
{
- return snd_soc_register_platform(dev, &atmel_soc_platform);
+ return devm_snd_soc_register_component(dev, &atmel_soc_platform,
+ NULL, 0);
}
EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
-void atmel_pcm_pdc_platform_unregister(struct device *dev)
-{
- snd_soc_unregister_platform(dev);
-}
-EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
-
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("Atmel PCM module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 4b27aed40a51..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
@@ -85,28 +72,20 @@ struct atmel_pcm_dma_params {
#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_PDC)
int atmel_pcm_pdc_platform_register(struct device *dev);
-void atmel_pcm_pdc_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_pdc_platform_register(struct device *dev)
{
return 0;
}
-static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
-{
-}
#endif
#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_DMA)
int atmel_pcm_dma_platform_register(struct device *dev);
-void atmel_pcm_dma_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_dma_platform_register(struct device *dev)
{
return 0;
}
-static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
-{
-}
#endif
#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index 8e3d34be9e69..fa29dd8ef208 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Atmel PDMIC driver
*
* Copyright (C) 2015 Atmel
*
* Author: Songjun Wu <songjun.wu@atmel.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 or later
- * as published by the Free Software Foundation.
*/
#include <linux/of.h>
@@ -32,6 +29,7 @@ struct atmel_pdmic {
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;
@@ -106,7 +104,7 @@ static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
int ret;
@@ -134,7 +132,7 @@ static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
/* Disable the overrun error interrupt */
@@ -147,34 +145,28 @@ static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_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 */
- return regmap_read(dd->regmap, PDMIC_CDR, &val);
-}
-
-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,
-};
+ ret = regmap_read(dd->regmap, PDMIC_CDR, &val);
+ if (ret < 0)
+ return 0;
-#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+ 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;
-static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
- .capture = {
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = ATMEL_PDMIC_FORMATS,},
- .ops = &atmel_pdmic_cpu_dai_ops,
-};
+ return 0;
+}
-static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
- .name = "atmel-pdmic",
-};
+#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* platform */
#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
@@ -199,14 +191,14 @@ 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 = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_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(rtd->platform->dev,
+ dev_err(dd->dev,
"hw params to dma slave configure failed\n");
return ret;
}
@@ -288,14 +280,14 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
static int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int dgain_val, scale_val;
int i;
- dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
+ dgain_val = (snd_soc_component_read(component, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
>> PDMIC_DSPR1_DGAIN_SHIFT;
- scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
+ 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++) {
@@ -312,7 +304,7 @@ static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int max = mc->max;
unsigned int val;
int ret;
@@ -322,12 +314,12 @@ static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
if (val > max)
return -EINVAL;
- ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
+ 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_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
+ 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;
@@ -346,36 +338,27 @@ SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
};
-static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
+static int atmel_pdmic_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
- snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
+ snd_soc_component_update_bits(component, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
(u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
- .probe = atmel_pdmic_codec_probe,
- .component_driver = {
- .controls = atmel_pdmic_snd_controls,
- .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
- },
-};
-
-/* codec dai component */
#define PDMIC_MR_PRESCAL_MAX_VAL 127
static int
-atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *codec_dai)
+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 = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_codec *codec = codec_dai->codec;
+ 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);
@@ -385,13 +368,13 @@ atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
if (params_channels(params) != 1) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"only supports one channel\n");
return -EINVAL;
}
if ((fs < rate_min) || (fs > rate_max)) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
fs, rate_min, rate_max);
@@ -436,30 +419,19 @@ atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
}
- snd_soc_update_bits(codec, PDMIC_MR,
+ snd_soc_component_update_bits(component, PDMIC_MR,
PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
- snd_soc_update_bits(codec, PDMIC_DSPR0,
+ snd_soc_component_update_bits(component, PDMIC_DSPR0,
PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
return 0;
}
-static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+static int atmel_pdmic_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
-
- snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
- PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
-
- return 0;
-}
-
-static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *codec_dai)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 val;
switch (cmd) {
@@ -477,21 +449,21 @@ static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
+ snd_soc_component_update_bits(component, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
return 0;
}
-static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
- .hw_params = atmel_pdmic_codec_dai_hw_params,
- .prepare = atmel_pdmic_codec_dai_prepare,
- .trigger = atmel_pdmic_codec_dai_trigger,
+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,
};
-#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
-static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
- .name = ATMEL_PDMIC_CODEC_DAI_NAME,
+static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -499,7 +471,17 @@ static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ATMEL_PDMIC_FORMATS,
},
- .ops = &atmel_pdmic_codec_dai_ops,
+ .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 */
@@ -508,17 +490,25 @@ static int atmel_pdmic_asoc_card_init(struct device *dev,
{
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, sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai_link->cpus = comp;
+ dai_link->codecs = &snd_soc_dummy_dlc;
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+
dai_link->name = "PDMIC";
dai_link->stream_name = "PDMIC PCM";
- dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME;
- dai_link->cpu_dai_name = dev_name(dev);
- dai_link->codec_name = dev_name(dev);
- dai_link->platform_name = dev_name(dev);
+ dai_link->cpus->dai_name = dev_name(dev);
card->dai_link = dai_link;
card->num_links = 1;
@@ -596,13 +586,11 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return -ENOMEM;
dd->pdata = pdata;
+ dd->dev = dev;
dd->irq = platform_get_irq(pdev, 0);
- if (dd->irq < 0) {
- ret = dd->irq;
- dev_err(dev, "failed to get irq: %d\n", ret);
- return ret;
- }
+ if (dd->irq < 0)
+ return dd->irq;
dd->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dd->pclk)) {
@@ -627,13 +615,9 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- io_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(io_base)) {
- ret = PTR_ERR(io_base);
- dev_err(dev, "failed to remap register memory: %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;
@@ -676,16 +660,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- /* register codec and codec dai */
- atmel_pdmic_codec_dai.capture.rate_min = rate_min;
- atmel_pdmic_codec_dai.capture.rate_max = rate_max;
- ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
- &atmel_pdmic_codec_dai, 1);
- if (ret) {
- dev_err(dev, "could not register codec: %d\n", ret);
- return ret;
- }
-
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
@@ -710,24 +684,16 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return 0;
unregister_codec:
- snd_soc_unregister_codec(dev);
return ret;
}
-static int atmel_pdmic_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver atmel_pdmic_driver = {
.driver = {
.name = "atmel-pdmic",
- .of_match_table = of_match_ptr(atmel_pdmic_of_match),
+ .of_match_table = atmel_pdmic_of_match,
.pm = &snd_soc_pm_ops,
},
.probe = atmel_pdmic_probe,
- .remove = atmel_pdmic_remove,
};
module_platform_driver(atmel_pdmic_driver);
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index a1e2c5682dcd..89098f41679c 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>
@@ -129,19 +116,16 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
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,
},
{
.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,
},
@@ -225,8 +209,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
if (frame_size < 0)
return frame_size;
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
+ 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)
@@ -236,7 +220,7 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
mck_div = 3;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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)
@@ -248,8 +232,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
break;
}
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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);
@@ -259,8 +243,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
}
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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;
@@ -296,7 +280,10 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
/* Enable PMC peripheral clock for this SSC */
pr_debug("atmel_ssc_dai: Starting clock\n");
- clk_enable(ssc_p->ssc->clk);
+ 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 */
@@ -330,13 +317,10 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, dma_params);
- spin_lock_irq(&ssc_p->lock);
- if (ssc_p->dir_mask & dir_mask) {
- spin_unlock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask)
return -EBUSY;
- }
+
ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
return 0;
}
@@ -368,7 +352,6 @@ 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) {
@@ -382,7 +365,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
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");
@@ -450,9 +432,9 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
/* 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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;
@@ -461,9 +443,9 @@ static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
/* 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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;
@@ -484,7 +466,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
int dir, channels, bits;
u32 tfmr, rfmr, tcmr, rcmr;
int ret;
- int fslen, fslen_ext;
+ int fslen, fslen_ext, fs_osync, fs_edge;
u32 cmr_div;
u32 tcmr_period;
u32 rcmr_period;
@@ -571,226 +553,45 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
/*
* 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.
- */
+ fslen_ext = (bits - 1) / 16;
+ fslen = (bits - 1) % 16;
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, 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_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | 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));
-
- tcmr = SSC_BF(TCMR_PERIOD, 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_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | 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));
- break;
+ 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);
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
- /* I2S format, CODEC supplies BCLK and LRC clocks. */
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | 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->clk_from_rk_pin ?
- SSC_CKS_PIN : 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, (channels - 1))
- | 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, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : 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, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
break;
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
- /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ case SND_SOC_DAIFMT_I2S:
+ fs_osync = SSC_FSOS_NEGATIVE;
+ fs_edge = SSC_START_FALLING_RF;
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, 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->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | 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));
-
- tcmr = SSC_BF(TCMR_PERIOD, 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_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | 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));
- break;
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output
- * on the SSC TK line.
- */
- rcmr = SSC_BF(RCMR_PERIOD, 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, tcmr_period)
- | SSC_BF(TCMR_STTDLY, 1)
- | SSC_BF(TCMR_START, SSC_START_RISING_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_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;
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_A:
/*
- * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
+ * DSP/PCM Mode A format
*
* Data is transferred on first BCLK after LRC pulse rising
* edge.If stereo, the right channel data is contiguous with
* the left channel data.
*/
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | 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->clk_from_rk_pin ?
- SSC_CKS_PIN : 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, (channels - 1))
- | 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, SSC_START_RISING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : 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, (channels - 1))
- | 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;
default:
@@ -798,6 +599,70 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
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)) {
+ /*
+ * 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_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);
+ }
+
+ 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);
@@ -820,7 +685,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
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;
}
@@ -897,13 +762,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
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(cpu_dai->dev);
+ 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[pdev->id];
@@ -925,15 +789,13 @@ 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(cpu_dai->dev);
+ 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[pdev->id];
@@ -958,13 +820,10 @@ 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 */
+/* S24_LE is not supported if more than 2 channels (of TDM slots) are used. */
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.startup = atmel_ssc_startup,
@@ -977,9 +836,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
};
static struct snd_soc_dai_driver atmel_ssc_dai = {
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
.playback = {
+ .stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -987,6 +845,7 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
.rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
+ .stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -997,20 +856,22 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
};
static const struct snd_soc_component_driver atmel_ssc_component = {
- .name = "atmel-ssc",
+ .name = "atmel-ssc",
+ .suspend = pm_ptr(atmel_ssc_suspend),
+ .resume = pm_ptr(atmel_ssc_resume),
+ .legacy_dai_naming = 1,
};
static int asoc_ssc_init(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssc_device *ssc = platform_get_drvdata(pdev);
+ struct ssc_device *ssc = dev_get_drvdata(dev);
int ret;
- ret = snd_soc_register_component(dev, &atmel_ssc_component,
+ 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);
- goto err;
+ return ret;
}
if (ssc->pdata->use_dma)
@@ -1020,37 +881,19 @@ static int asoc_ssc_init(struct device *dev)
if (ret) {
dev_err(dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ return ret;
}
return 0;
-
-err_unregister_dai:
- snd_soc_unregister_component(dev);
-err:
- return ret;
-}
-
-static void asoc_ssc_exit(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssc_device *ssc = platform_get_drvdata(pdev);
-
- if (ssc->pdata->use_dma)
- atmel_pcm_dma_platform_unregister(dev);
- else
- atmel_pcm_pdc_platform_unregister(dev);
-
- snd_soc_unregister_component(dev);
}
/**
* 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;
- int ret;
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
@@ -1062,9 +905,7 @@ int atmel_ssc_set_audio(int ssc_id)
ssc_info[ssc_id].ssc = ssc;
}
- ret = asoc_ssc_init(&ssc->pdev->dev);
-
- return ret;
+ return asoc_ssc_init(&ssc->pdev->dev);
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
@@ -1072,7 +913,6 @@ void atmel_ssc_put_audio(int ssc_id)
{
struct ssc_device *ssc = ssc_info[ssc_id].ssc;
- asoc_ssc_exit(&ssc->pdev->dev);
ssc_free(ssc);
}
EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index 75194f582131..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,7 +93,6 @@ 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;
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index fbc10f61eb55..0f4021c6c588 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -1,17 +1,15 @@
+// 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>
- *
- * GPLv2 or later
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <sound/soc.h>
@@ -27,8 +25,8 @@ static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
static int atmel_asoc_wm8904_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
@@ -57,14 +55,19 @@ 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",
- .codec_dai_name = "wm8904-hifi",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &atmel_asoc_wm8904_ops,
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card atmel_asoc_wm8904_card = {
@@ -108,8 +111,8 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
ret = -EINVAL;
return ret;
}
- dailink->cpu_of_node = cpu_np;
- dailink->platform_of_node = cpu_np;
+ 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);
@@ -118,7 +121,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
ret = -EINVAL;
return ret;
}
- dailink->codec_of_node = codec_np;
+ dailink->codecs->of_node = codec_np;
of_node_put(codec_np);
return 0;
@@ -137,7 +140,7 @@ static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
return ret;
}
- id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+ 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);
@@ -157,18 +160,16 @@ err_set_audio:
return ret;
}
-static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
+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->cpu_of_node, "ssc");
+ id = of_alias_get_id((struct device_node *)dailink->cpus->of_node, "ssc");
snd_soc_unregister_card(card);
atmel_ssc_put_audio(id);
-
- return 0;
}
#ifdef CONFIG_OF
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
new file mode 100644
index 000000000000..17d138bb9064
--- /dev/null
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -0,0 +1,1138 @@
+// 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.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
+
+/*
+ * ---- DMA chunk size allowed ----
+ */
+#define MCHP_I2SMCC_DMA_8_WORD_CHUNK 8
+#define MCHP_I2SMCC_DMA_4_WORD_CHUNK 4
+#define MCHP_I2SMCC_DMA_2_WORD_CHUNK 2
+#define MCHP_I2SMCC_DMA_1_WORD_CHUNK 1
+#define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w))
+
+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 inline int mchp_i2s_mcc_period_to_maxburst(int period_size, int sample_size)
+{
+ int p_size = period_size;
+ int s_size = sample_size;
+
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_8_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_8_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_4_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_4_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_2_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_2_WORD_CHUNK;
+ return MCHP_I2SMCC_DMA_1_WORD_CHUNK;
+}
+
+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);
+ int sample_bytes = params_physical_width(params) / 8;
+ int period_bytes = params_period_size(params) *
+ params_channels(params) * sample_bytes;
+ int maxburst;
+ 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 period_bytes=%d\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params), period_bytes);
+
+ 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
+ */
+ maxburst = mchp_i2s_mcc_period_to_maxburst(period_bytes, sample_bytes);
+ mrb |= MCHP_I2SMCC_MRB_DMACHUNK(maxburst);
+ if (is_playback)
+ dev->playback.maxburst = maxburst;
+ else
+ dev->capture.maxburst = maxburst;
+
+ 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 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;
+}
+
+static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
+ .probe = mchp_i2s_mcc_dai_probe,
+ .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,
+};
+
+#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 = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "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 = mchp_i2s_mcc_dt_ids,
+ },
+ .probe = mchp_i2s_mcc_probe,
+ .remove = 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..06dc3c48e7e8
--- /dev/null
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -0,0 +1,1156 @@
+// 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
+
+/*
+ * ---- DMA chunk size allowed ----
+ */
+#define MCHP_PDMC_DMA_8_WORD_CHUNK 8
+#define MCHP_PDMC_DMA_4_WORD_CHUNK 4
+#define MCHP_PDMC_DMA_2_WORD_CHUNK 2
+#define MCHP_PDMC_DMA_1_WORD_CHUNK 1
+#define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w))
+
+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;
+ atomic_t busy_stream;
+};
+
+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 (atomic_read(&dd->busy_stream))
+ return -EBUSY;
+
+ 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 (atomic_read(&dd->busy_stream))
+ return -EBUSY;
+
+ 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;
+
+ if (!substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+
+ 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 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),
+};
+
+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, int sample_size)
+{
+ int p_size = period_size;
+ int s_size = sample_size;
+
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_8_WORD_CHUNK))
+ return MCHP_PDMC_DMA_8_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_4_WORD_CHUNK))
+ return MCHP_PDMC_DMA_4_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_2_WORD_CHUNK))
+ return MCHP_PDMC_DMA_2_WORD_CHUNK;
+ return MCHP_PDMC_DMA_1_WORD_CHUNK;
+}
+
+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);
+ int sample_bytes = params_physical_width(params) / 8;
+ int period_bytes = params_period_size(params) *
+ params_channels(params) * sample_bytes;
+ int maxburst;
+ 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 period_bytes=%d\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params), period_bytes);
+
+ 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);
+ }
+
+ /*
+ * from these point forward, we consider the controller busy, so the
+ * audio filter and SINC order can't be changed
+ */
+ atomic_set(&dd->busy_stream, 1);
+ 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);
+
+ maxburst = mchp_pdmc_period_to_maxburst(period_bytes, sample_bytes);
+ dd->addr.maxburst = maxburst;
+ 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 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 const struct snd_soc_dai_ops mchp_pdmc_dai_ops = {
+ .probe = mchp_pdmc_dai_probe,
+ .set_fmt = mchp_pdmc_set_fmt,
+ .startup = mchp_pdmc_startup,
+ .hw_params = mchp_pdmc_hw_params,
+ .trigger = mchp_pdmc_trigger,
+ .pcm_new = &mchp_pdmc_pcm_new,
+};
+
+static struct snd_soc_dai_driver mchp_pdmc_dai = {
+ .name = "mchp-pdmc",
+ .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,
+};
+
+/* 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,
+ 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);
+
+ atomic_set(&dd->busy_stream, 0);
+
+ 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 = 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..521bee4998f8
--- /dev/null
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -0,0 +1,1203 @@
+// 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;
+}
+
+#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_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_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_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_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_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_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 const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = {
+ .probe = mchp_spdifrx_dai_probe,
+ .remove = mchp_spdifrx_dai_remove,
+ .trigger = mchp_spdifrx_trigger,
+ .hw_params = mchp_spdifrx_hw_params,
+};
+
+static struct snd_soc_dai_driver mchp_spdifrx_dai = {
+ .name = "mchp-spdifrx",
+ .capture = {
+ .stream_name = "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 = mchp_spdifrx_remove,
+ .driver = {
+ .name = "mchp_spdifrx",
+ .of_match_table = 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..245c0352c141
--- /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);
+}
+
+#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 const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = {
+ .probe = mchp_spdiftx_dai_probe,
+ .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,
+};
+
+static struct snd_soc_dai_driver mchp_spdiftx_dai = {
+ .name = "mchp-spdiftx",
+ .playback = {
+ .stream_name = "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 = mchp_spdiftx_remove,
+ .driver = {
+ .name = "mchp_spdiftx",
+ .of_match_table = 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..8341a6e06493
--- /dev/null
+++ b/sound/soc/atmel/mikroe-proto.c
@@ -0,0 +1,176 @@
+// 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 = snd_soc_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 = devm_snd_soc_register_card(&pdev->dev, &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 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,
+};
+
+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/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 98f93e79c654..335e216ea7b4 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>
@@ -36,7 +23,6 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/atmel-ssc.h>
@@ -59,35 +45,6 @@
*/
#undef ENABLE_MIC_INPUT
-static struct clk *mclk;
-
-static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm,
- 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),
@@ -109,7 +66,7 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct device *dev = rtd->dev;
int ret;
@@ -129,16 +86,21 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
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 = "at91rm9200_ssc.0",
- .codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
- .platform_name = "at91rm9200_ssc.0",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ 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 = {
@@ -146,7 +108,6 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.owner = THIS_MODULE,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
- .set_bias_level = at91sam9g20ek_set_bias_level,
.dapm_widgets = at91sam9g20ek_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
@@ -159,7 +120,6 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *codec_np, *cpu_np;
- struct clk *pllb;
struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
int ret;
@@ -169,35 +129,10 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = atmel_ssc_set_audio(0);
if (ret) {
- dev_err(&pdev->dev, "ssc channel is not valid\n");
- return -EINVAL;
- }
-
- /*
- * Codec MCLK is supplied by PCK0 - set it up.
- */
- mclk = clk_get(NULL, "pck0");
- if (IS_ERR(mclk)) {
- dev_err(&pdev->dev, "Failed to get MCLK\n");
- ret = PTR_ERR(mclk);
- goto err;
- }
-
- pllb = clk_get(NULL, "pllb");
- if (IS_ERR(pllb)) {
- dev_err(&pdev->dev, "Failed to get PLLB\n");
- ret = PTR_ERR(pllb);
- goto err_mclk;
- }
- ret = clk_set_parent(mclk, pllb);
- clk_put(pllb);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set MCLK parent\n");
- goto err_mclk;
+ dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
+ return ret;
}
- clk_set_rate(mclk, MCLK_RATE);
-
card->dev = &pdev->dev;
/* Parse device node info */
@@ -211,53 +146,51 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
goto err;
/* Parse codec info */
- at91sam9g20ek_dai.codec_name = NULL;
+ 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");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
- at91sam9g20ek_dai.codec_of_node = codec_np;
+ at91sam9g20ek_dai.codecs->of_node = codec_np;
/* Parse dai and platform info */
- at91sam9g20ek_dai.cpu_dai_name = NULL;
- at91sam9g20ek_dai.platform_name = NULL;
+ 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");
- return -EINVAL;
+ of_node_put(codec_np);
+ ret = -EINVAL;
+ goto err;
}
- at91sam9g20ek_dai.cpu_of_node = cpu_np;
- at91sam9g20ek_dai.platform_of_node = cpu_np;
+ at91sam9g20ek_dai.cpus->of_node = cpu_np;
+ at91sam9g20ek_dai.platforms->of_node = cpu_np;
of_node_put(codec_np);
of_node_put(cpu_np);
ret = snd_soc_register_card(card);
if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed\n");
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+ goto err;
}
- return ret;
+ return 0;
-err_mclk:
- clk_put(mclk);
- mclk = NULL;
err:
atmel_ssc_put_audio(0);
return ret;
}
-static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
+static void at91sam9g20ek_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- clk_disable(mclk);
- mclk = NULL;
snd_soc_unregister_card(card);
atmel_ssc_put_audio(0);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -274,7 +207,7 @@ static struct platform_driver at91sam9g20ek_audio_driver = {
.of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
},
.probe = at91sam9g20ek_audio_probe,
- .remove = at91sam9g20ek_audio_remove,
+ .remove = at91sam9g20ek_audio_remove,
};
module_platform_driver(at91sam9g20ek_audio_driver);
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
index e6c303ab869d..1b5ef4e9d2b8 100644
--- a/sound/soc/atmel/sam9x5_wm8731.c
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
* that are using WM8731 as codec.
@@ -10,12 +11,6 @@
*
* Based on sam9g20_wm8731.c by:
* Sedji Gaouaou <sedji.gaouaou@atmel.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/of.h>
#include <linux/export.h>
@@ -45,7 +40,7 @@ struct sam9x5_drvdata {
*/
static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct device *dev = rtd->dev;
int ret;
@@ -82,6 +77,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
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) {
@@ -92,7 +88,8 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
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);
- if (!dai || !card || !priv) {
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!dai || !card || !priv || !comp) {
ret = -ENOMEM;
goto out;
}
@@ -105,12 +102,20 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
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->codec_dai_name = "wm8731-hifi";
+ 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_CBM_CFM;
+ | SND_SOC_DAIFMT_CBP_CFP;
ret = snd_soc_of_parse_card_name(card, "atmel,model");
if (ret) {
@@ -131,16 +136,16 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
goto out;
}
- dai->codec_of_node = codec_np;
+ 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;
+ goto out_put_codec_np;
}
- dai->cpu_of_node = cpu_np;
- dai->platform_of_node = cpu_np;
+ dai->cpus->of_node = cpu_np;
+ dai->platforms->of_node = cpu_np;
priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
@@ -148,13 +153,10 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
ret, priv->ssc_id);
- goto out;
+ goto out_put_cpu_np;
}
- of_node_put(codec_np);
- of_node_put(cpu_np);
-
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "Platform device allocation failed\n");
goto out_put_audio;
@@ -162,23 +164,24 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "%s ok\n", __func__);
- return ret;
+ 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 int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+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;
- snd_soc_unregister_card(card);
atmel_ssc_put_audio(priv->ssc_id);
-
- return 0;
}
static const struct of_device_id sam9x5_wm8731_of_match[] = {
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
index 3a1393283156..2a1d0408a4cf 100644
--- a/sound/soc/atmel/tse850-pcm5142.c
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -1,51 +1,43 @@
-/*
- * 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>
- *
- * 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.
- */
-
-/*
- * loop1 relays
- * IN1 +---o +------------+ o---+ OUT1
- * \ /
- * + +
- * | / |
- * +--o +--. |
- * | add | |
- * | V |
- * | .---. |
- * DAC +----------->|Sum|---+
- * | '---' |
- * | |
- * + +
- *
- * IN2 +---o--+------------+--o---+ OUT2
- * loop2 relays
- *
- * The 'loop1' gpio pin controlls 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.
- */
+// 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/gpio/consumer.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>
@@ -123,8 +115,8 @@ static int tse850_put_mux2(struct snd_kcontrol *kctrl,
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
}
-int tse850_get_mix(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *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;
@@ -135,8 +127,8 @@ int tse850_get_mix(struct snd_kcontrol *kctrl,
return 0;
}
-int tse850_put_mix(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *ucontrol)
+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;
@@ -157,8 +149,8 @@ int tse850_put_mix(struct snd_kcontrol *kctrl,
return 1;
}
-int tse850_get_ana(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *ucontrol)
+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;
@@ -190,8 +182,8 @@ int tse850_get_ana(struct snd_kcontrol *kctrl,
return 0;
}
-int tse850_put_ana(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *ucontrol)
+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;
@@ -235,16 +227,9 @@ static const struct snd_kcontrol_new 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),
+ SOC_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
+ tse850_get_mix, tse850_put_mix),
};
static const char * const ana_text[] = {
@@ -300,13 +285,18 @@ static const struct snd_soc_dapm_route tse850_intercon[] = {
{ "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",
- .codec_dai_name = "pcm512x-hifi",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFS,
+ | SND_SOC_DAIFMT_CBP_CFC,
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card tse850_card = {
@@ -337,8 +327,8 @@ static int tse850_dt_init(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get cpu dai\n");
return -EINVAL;
}
- dailink->cpu_of_node = cpu_np;
- dailink->platform_of_node = cpu_np;
+ 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);
@@ -346,7 +336,7 @@ static int tse850_dt_init(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get codec info\n");
return -EINVAL;
}
- dailink->codec_of_node = codec_np;
+ dailink->codecs->of_node = codec_np;
of_node_put(codec_np);
return 0;
@@ -372,35 +362,27 @@ static int tse850_probe(struct platform_device *pdev)
}
tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
- if (IS_ERR(tse850->add)) {
- if (PTR_ERR(tse850->add) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'add' gpio\n");
- return PTR_ERR(tse850->add);
- }
+ 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)) {
- if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'loop1' gpio\n");
- return PTR_ERR(tse850->loop1);
- }
+ 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)) {
- if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'loop2' gpio\n");
- return PTR_ERR(tse850->loop2);
- }
+ 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)) {
- if (PTR_ERR(tse850->ana) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'ana' regulator\n");
- return PTR_ERR(tse850->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) {
@@ -421,15 +403,13 @@ err_disable_ana:
return ret;
}
-static int tse850_remove(struct platform_device *pdev)
+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);
-
- return 0;
}
static const struct of_device_id tse850_dt_ids[] = {
@@ -441,7 +421,7 @@ 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),
+ .of_match_table = tse850_dt_ids,
},
.probe = tse850_probe,
.remove = tse850_remove,
@@ -452,4 +432,4 @@ 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");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index a56104040e83..a7630897bc0b 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -1,6 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
##
## Au1200/Au1550/Au1300 PSC + DBDMA
##
+menu "Au1x"
+
config SND_SOC_AU1XPSC
tristate "SoC Audio for Au12xx/Au13xx/Au1550"
depends on MIPS_ALCHEMY
@@ -57,8 +60,10 @@ config SND_SOC_DB1200
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 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.
+
+endmenu
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
index 33183d7fe057..9c6f5c38f92d 100644
--- a/sound/soc/au1x/Makefile
+++ b/sound/soc/au1x/Makefile
@@ -1,13 +1,13 @@
# 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
+snd-soc-au1xpsc-dbdma-y := dbdma2.o
+snd-soc-au1xpsc-i2s-y := psc-i2s.o
+snd-soc-au1xpsc-ac97-y := 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
+snd-soc-au1x-dma-y := dma.o
+snd-soc-au1x-ac97c-y := ac97c.o
+snd-soc-au1x-i2sc-y := i2sc.o
obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
@@ -17,8 +17,8 @@ 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
+snd-soc-db1000-y := db1000.o
+snd-soc-db1200-y := 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
index 66d6c52e7761..f8ab936250dc 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 AC97C controller driver for ASoC
*
@@ -194,19 +195,18 @@ static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
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 const struct snd_soc_dai_ops alchemy_ac97c_ops = {
+ .probe = au1xac97c_dai_probe,
+ .startup = alchemy_ac97c_startup,
+};
+
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
.name = "alchemy-ac97c",
- .bus_control = true,
- .probe = au1xac97c_dai_probe,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -223,7 +223,8 @@ static struct snd_soc_dai_driver au1xac97c_dai_driver = {
};
static const struct snd_soc_component_driver au1xac97c_component = {
- .name = "au1xac97c",
+ .name = "au1xac97c",
+ .legacy_dai_naming = 1,
};
static int au1xac97c_drvprobe(struct platform_device *pdev)
@@ -247,7 +248,7 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
- ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start,
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
resource_size(iores));
if (!ctx->mmio)
return -EBUSY;
@@ -284,7 +285,7 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
return 0;
}
-static int au1xac97c_drvremove(struct platform_device *pdev)
+static void au1xac97c_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
@@ -293,8 +294,6 @@ static int au1xac97c_drvremove(struct platform_device *pdev)
WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
ac97c_workdata = NULL; /* MDEV */
-
- return 0;
}
#ifdef CONFIG_PM
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
index e97c32798e98..c0e105a56cc5 100644
--- a/sound/soc/au1x/db1000.c
+++ b/sound/soc/au1x/db1000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DB1000/DB1500/DB1100 ASoC audio fabric support code.
*
@@ -18,13 +19,15 @@
#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",
- .codec_dai_name = "ac97-hifi",
- .cpu_dai_name = "alchemy-ac97c",
- .platform_name = "alchemy-pcm-dma.0",
- .codec_name = "ac97-codec",
+ SND_SOC_DAILINK_REG(hifi),
};
static struct snd_soc_card db1000_ac97 = {
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index 301e1fc9a377..81abe2e18402 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DB1200/DB1300/DB1550 ASoC audio fabric support code.
*
@@ -43,16 +44,19 @@ static const struct platform_device_id db1200_pids[] = {
},
{},
};
+MODULE_DEVICE_TABLE(platform, db1200_pids);
/*------------------------- 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 = {
@@ -62,13 +66,15 @@ static struct snd_soc_card db1200_ac97_machine = {
.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",
- .codec_dai_name = "wm9712-hifi",
- .cpu_dai_name = "au1xpsc_ac97.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm9712-codec.1",
+ SND_SOC_DAILINK_REG(db1300_ac97),
};
static struct snd_soc_card db1300_ac97_machine = {
@@ -89,8 +95,8 @@ static struct snd_soc_card db1550_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
/* WM8731 has its own 12MHz crystal */
snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
@@ -103,16 +109,18 @@ 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.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1200_i2s),
};
static struct snd_soc_card db1200_i2s_machine = {
@@ -122,16 +130,18 @@ static struct snd_soc_card db1200_i2s_machine = {
.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",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.2",
- .platform_name = "au1xpsc-pcm.2",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1300_i2s),
};
static struct snd_soc_card db1300_i2s_machine = {
@@ -141,16 +151,18 @@ static struct snd_soc_card db1300_i2s_machine = {
.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",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.3",
- .platform_name = "au1xpsc-pcm.3",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1550_i2s),
};
static struct snd_soc_card db1550_i2s_machine = {
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 6a035ca0f521..3392693faeb9 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
@@ -184,27 +182,23 @@ 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);
+ 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->stream;
- pcd = to_dmadata(substream);
+ pcd = to_dmadata(substream, component);
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
"runtime->min_align %lu\n",
@@ -234,15 +228,10 @@ out:
return ret;
}
-static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
+ 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);
@@ -257,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:
@@ -277,18 +267,21 @@ 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);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int stype = substream->stream, *dmaids;
- dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dmaids = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dmaids)
return -ENODEV; /* whoa, has ordering changed? */
@@ -298,54 +291,51 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
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 const 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 int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int au1xpsc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->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 */
-static struct snd_soc_platform_driver au1xpsc_soc_platform = {
- .ops = &au1xpsc_pcm_ops,
- .pcm_new = au1xpsc_pcm_new,
+static const 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 au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
struct au1xpsc_audio_dmadata *dmadata;
- dmadata = devm_kzalloc(&pdev->dev,
- 2 * sizeof(struct au1xpsc_audio_dmadata),
+ dmadata = devm_kcalloc(&pdev->dev,
+ 2, sizeof(struct au1xpsc_audio_dmadata),
GFP_KERNEL);
if (!dmadata)
return -ENOMEM;
platform_set_drvdata(pdev, dmadata);
- return devm_snd_soc_register_platform(&pdev->dev,
- &au1xpsc_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_soc_component, NULL, 0);
}
static struct platform_driver au1xpsc_pcm_driver = {
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
index 19457e2b351e..c9c2b1e71d55 100644
--- a/sound/soc/au1x/dma.c
+++ b/sound/soc/au1x/dma.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 Audio DMA support.
*
@@ -21,6 +22,8 @@
#include "psc.h"
+#define DRV_NAME "au1x_dma"
+
struct pcm_period {
u32 start;
u32 relative_end; /* relative to start of buffer */
@@ -171,26 +174,28 @@ static const struct snd_pcm_hardware alchemy_pcm_hardware = {
.fifo_size = 16,
};
-static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
+static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- return snd_soc_platform_get_drvdata(rtd->platform);
+ return snd_soc_component_get_drvdata(component);
}
-static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
+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);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component);
return &(ctx->stream[ss->stream]);
}
-static int alchemy_pcm_open(struct snd_pcm_substream *substream)
+static int alchemy_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int *dmaids, s = substream->stream;
char *name;
- dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dmaids = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dmaids)
return -ENODEV; /* whoa, has ordering changed? */
@@ -209,9 +214,10 @@ static int alchemy_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_close(struct snd_pcm_substream *substream)
+static int alchemy_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
int stype = substream->stream;
ctx->stream[stype].substream = NULL;
@@ -220,35 +226,29 @@ static int alchemy_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
+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);
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- err = au1000_setup_dma_link(stream,
- params_period_bytes(hw_params),
- params_periods(hw_params));
- if (err)
- snd_pcm_lib_free_pages(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
- return err;
+ return au1000_setup_dma_link(stream,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
}
-static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
+static int alchemy_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
au1000_release_dma_link(stream);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
-static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+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);
+ struct audio_stream *stream = ss_to_as(substream, component);
int err = 0;
switch (cmd) {
@@ -265,9 +265,10 @@ static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
}
-static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
+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);
+ struct audio_stream *stream = ss_to_as(ss, component);
long location;
location = get_dma_residue(stream->dma);
@@ -277,29 +278,26 @@ static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, location);
}
-static const struct snd_pcm_ops alchemy_pcm_ops = {
- .open = alchemy_pcm_open,
- .close = alchemy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = alchemy_pcm_hw_params,
- .hw_free = alchemy_pcm_hw_free,
- .trigger = alchemy_pcm_trigger,
- .pointer = alchemy_pcm_pointer,
-};
-
-static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int alchemy_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 65536, (4096 * 1024) - 1);
return 0;
}
-static struct snd_soc_platform_driver alchemy_pcm_soc_platform = {
- .ops = &alchemy_pcm_ops,
- .pcm_new = alchemy_pcm_new,
+static const 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)
@@ -312,8 +310,8 @@ static int alchemy_pcm_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- return devm_snd_soc_register_platform(&pdev->dev,
- &alchemy_pcm_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &alchemy_pcm_soc_component, NULL, 0);
}
static struct platform_driver alchemy_pcmdma_driver = {
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
index 450c842c776c..57735004f416 100644
--- a/sound/soc/au1x/i2sc.c
+++ b/sound/soc/au1x/i2sc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 I2S controller driver for ASoC
*
@@ -118,9 +119,9 @@ static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
goto out;
}
- /* I2S controller only supports master */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
+ /* 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;
@@ -209,7 +210,7 @@ static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
};
static struct snd_soc_dai_driver au1xi2s_dai_driver = {
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.playback = {
.rates = AU1XI2SC_RATES,
.formats = AU1XI2SC_FMTS,
@@ -226,7 +227,8 @@ static struct snd_soc_dai_driver au1xi2s_dai_driver = {
};
static const struct snd_soc_component_driver au1xi2s_component = {
- .name = "au1xi2s",
+ .name = "au1xi2s",
+ .legacy_dai_naming = 1,
};
static int au1xi2s_drvprobe(struct platform_device *pdev)
@@ -247,7 +249,7 @@ static int au1xi2s_drvprobe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
- ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start,
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
resource_size(iores));
if (!ctx->mmio)
return -EBUSY;
@@ -268,18 +270,15 @@ static int au1xi2s_drvprobe(struct platform_device *pdev)
&au1xi2s_dai_driver, 1);
}
-static int au1xi2s_drvremove(struct platform_device *pdev)
+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 */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xi2s_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
@@ -294,23 +293,13 @@ 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 DEFINE_SIMPLE_DEV_PM_OPS(au1xi2sc_pmops, au1xi2s_drvsuspend,
+ au1xi2s_drvresume);
static struct platform_driver au1xi2s_driver = {
.driver = {
.name = "alchemy-i2sc",
- .pm = AU1XI2SC_PMOPS,
+ .pm = pm_ptr(&au1xi2sc_pmops),
},
.probe = au1xi2s_drvprobe,
.remove = au1xi2s_drvremove,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a2050ae5a3fe..94698e08a513 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>
@@ -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->snd_soc_rtd_to_cpu(rtd, 0));
}
#else
@@ -337,14 +333,13 @@ static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
}
static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+ .probe = au1xpsc_ac97_probe,
.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 = {
- .bus_control = true,
- .probe = au1xpsc_ac97_probe,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -361,13 +356,14 @@ static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
};
static const struct snd_soc_component_driver au1xpsc_ac97_component = {
- .name = "au1xpsc-ac97",
+ .name = "au1xpsc-ac97",
+ .legacy_dai_naming = 1,
};
static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
int ret;
- struct resource *iores, *dmares;
+ struct resource *dmares;
unsigned long sel;
struct au1xpsc_audio_data *wd;
@@ -378,8 +374,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
mutex_init(&wd->lock);
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wd->mmio))
return PTR_ERR(wd->mmio);
@@ -426,7 +421,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
return 0;
}
-static int 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);
@@ -439,11 +434,8 @@ static int au1xpsc_ac97_drvremove(struct platform_device *pdev)
wmb(); /* drain writebuffer */
au1xpsc_ac97_workdata = NULL; /* MDEV */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xpsc_ac97_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
@@ -474,23 +466,13 @@ static int au1xpsc_ac97_drvresume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops au1xpscac97_pmops = {
- .suspend = au1xpsc_ac97_drvsuspend,
- .resume = au1xpsc_ac97_drvresume,
-};
-
-#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
-
-#else
-
-#define AU1XPSCAC97_PMOPS NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xpscac97_pmops, au1xpsc_ac97_drvsuspend,
+ au1xpsc_ac97_drvresume);
static struct platform_driver au1xpsc_ac97_driver = {
.driver = {
.name = "au1xpsc_ac97",
- .pm = AU1XPSCAC97_PMOPS,
+ .pm = pm_ptr(&au1xpscac97_pmops),
},
.probe = au1xpsc_ac97_drvprobe,
.remove = au1xpsc_ac97_drvremove,
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index e6eec081eaae..bf59105fcb7a 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.
@@ -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;
@@ -289,12 +286,13 @@ static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
};
static const struct snd_soc_component_driver au1xpsc_i2s_component = {
- .name = "au1xpsc-i2s",
+ .name = "au1xpsc-i2s",
+ .legacy_dai_naming = 1,
};
static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
- struct resource *iores, *dmares;
+ struct resource *dmares;
unsigned long sel;
struct au1xpsc_audio_data *wd;
@@ -303,8 +301,7 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
if (!wd)
return -ENOMEM;
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wd->mmio))
return PTR_ERR(wd->mmio);
@@ -343,25 +340,20 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, wd);
- return snd_soc_register_component(&pdev->dev, &au1xpsc_i2s_component,
- &wd->dai_drv, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_i2s_component, &wd->dai_drv, 1);
}
-static int 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);
- snd_soc_unregister_component(&pdev->dev);
-
__raw_writel(0, I2S_CFG(wd));
wmb(); /* drain writebuffer */
__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
wmb(); /* drain writebuffer */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xpsc_i2s_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
@@ -392,23 +384,13 @@ static int au1xpsc_i2s_drvresume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops au1xpsci2s_pmops = {
- .suspend = au1xpsc_i2s_drvsuspend,
- .resume = au1xpsc_i2s_drvresume,
-};
-
-#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
-
-#else
-
-#define AU1XPSCI2S_PMOPS NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xpsci2s_pmops, au1xpsc_i2s_drvsuspend,
+ au1xpsc_i2s_drvresume);
static struct platform_driver au1xpsc_i2s_driver = {
.driver = {
.name = "au1xpsc_i2s",
- .pm = AU1XPSCI2S_PMOPS,
+ .pm = pm_ptr(&au1xpsci2s_pmops),
},
.probe = au1xpsc_i2s_drvprobe,
.remove = au1xpsc_i2s_drvremove,
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 74dffeb641fa..216596e4348a 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Alchemy ALSA ASoC audio support.
*
* (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
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index edf367100ebd..de4e8a0daf1c 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Broadcom"
+
config SND_BCM2835_SOC_I2S
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
depends on ARCH_BCM2835 || COMPILE_TEST
@@ -11,9 +14,19 @@ config SND_BCM2835_SOC_I2S
config SND_SOC_CYGNUS
tristate "SoC platform audio for Broadcom Cygnus chips"
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
- depends on HAS_DMA
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. \ No newline at end of file
+ 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
+
+endmenu
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
index fc739d007884..0c1325a97b70 100644
--- a/sound/soc/bcm/Makefile
+++ b/sound/soc/bcm/Makefile
@@ -1,10 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
# BCM2835 Platform Support
-snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
+snd-soc-bcm2835-i2s-y := 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
+snd-soc-cygnus-y := cygnus-pcm.o cygnus-ssp.o
obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+# BCM63XX Platform Support
+snd-soc-63xx-y := 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
index d5f73a8ab893..87d2f06c2f53 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC I2S Audio Layer for Broadcom BCM2835 SoC
*
@@ -20,15 +21,6 @@
* Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
* Author: Timur Tabi <timur@freescale.com>
* Copyright 2007-2010 Freescale Semiconductor, 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/bitops.h>
@@ -135,14 +127,14 @@ struct bcm2835_i2s_dev {
static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
{
- unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
if (dev->clk_prepared)
return;
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ switch (provider) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BP_FC:
clk_prepare_enable(dev->clk);
dev->clk_prepared = true;
break;
@@ -345,8 +337,8 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
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_master = false;
- bool frame_sync_master = false;
+ bool bit_clock_provider = false;
+ bool frame_sync_provider = false;
bool frame_start_falling_edge = false;
uint32_t csreg;
int ret = 0;
@@ -391,36 +383,36 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
if (data_length > slot_width)
return -EINVAL;
- /* Check if CPU is bit clock master */
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- bit_clock_master = true;
+ /* 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_CBM_CFS:
- case SND_SOC_DAIFMT_CBM_CFM:
- bit_clock_master = false;
+ 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 master */
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- frame_sync_master = true;
+ /* 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_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFM:
- frame_sync_master = false;
+ 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_master &&
+ if (bit_clock_provider &&
(!dev->clk_prepared || dev->clk_rate != bclk_rate)) {
if (dev->clk_prepared)
bcm2835_i2s_stop_clock(dev);
@@ -509,11 +501,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/*
* Transmitting data immediately after frame start, eg
* in left-justified or DSP mode A, only works stable
- * if bcm2835 is the frame clock master.
+ * if bcm2835 is the frame clock provider.
*/
- if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master)
+ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider)
dev_warn(dev->dev,
- "Unstable slave config detected, L/R may be swapped");
+ "Unstable consumer config detected, L/R may be swapped");
/*
* Set format for both streams.
@@ -546,11 +538,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
mode |= BCM2835_I2S_FSLEN(framesync_length);
/* CLKM selects bcm2835 clock slave mode */
- if (!bit_clock_master)
+ if (!bit_clock_provider)
mode |= BCM2835_I2S_CLKM;
/* FSM selects bcm2835 frame sync slave mode */
- if (!frame_sync_master)
+ if (!frame_sync_provider)
mode |= BCM2835_I2S_FSM;
/* CLKI selects normal clocking mode, sampling on rising edge */
@@ -661,7 +653,7 @@ static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev,
BCM2835_I2S_CS_A_REG, mask, 0);
/* Stop also the clock when not SND_SOC_DAIFMT_CONT */
- if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
+ if (!snd_soc_dai_active(dai) && !(dev->fmt & SND_SOC_DAIFMT_CONT))
bcm2835_i2s_stop_clock(dev);
}
@@ -703,7 +695,7 @@ static int bcm2835_i2s_startup(struct snd_pcm_substream *substream,
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- if (dai->active)
+ if (snd_soc_dai_active(dai))
return 0;
/* Should this still be running stop it */
@@ -731,7 +723,7 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
bcm2835_i2s_stop(dev, substream, dai);
/* If both streams are stopped, disable module and clock */
- if (dai->active)
+ if (snd_soc_dai_active(dai))
return;
/* Disable the module */
@@ -745,7 +737,19 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
bcm2835_i2s_stop_clock(dev);
}
+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 const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
+ .probe = bcm2835_i2s_dai_probe,
.startup = bcm2835_i2s_startup,
.shutdown = bcm2835_i2s_shutdown,
.prepare = bcm2835_i2s_prepare,
@@ -756,20 +760,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
.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,
@@ -791,8 +783,8 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
| SNDRV_PCM_FMTBIT_S32_LE
},
.ops = &bcm2835_i2s_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
@@ -805,7 +797,7 @@ static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
return true;
default:
return false;
- };
+ }
}
static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
@@ -815,7 +807,7 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
return true;
default:
return false;
- };
+ }
}
static const struct regmap_config bcm2835_regmap_config = {
@@ -825,18 +817,18 @@ static const struct regmap_config bcm2835_regmap_config = {
.max_register = BCM2835_I2S_GRAY_REG,
.precious_reg = bcm2835_i2s_precious_reg,
.volatile_reg = bcm2835_i2s_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct snd_soc_component_driver bcm2835_i2s_component = {
- .name = "bcm2835-i2s-comp",
+ .name = "bcm2835-i2s-comp",
+ .legacy_dai_naming = 1,
};
static int bcm2835_i2s_probe(struct platform_device *pdev)
{
struct bcm2835_i2s_dev *dev;
int ret;
- struct resource *mem;
void __iomem *base;
const __be32 *addr;
dma_addr_t dma_base;
@@ -849,15 +841,12 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
/* get the clock */
dev->clk_prepared = false;
dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- dev_err(&pdev->dev, "could not get clk: %ld\n",
- PTR_ERR(dev->clk));
- return PTR_ERR(dev->clk);
- }
+ if (IS_ERR(dev->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk),
+ "could not get clk\n");
/* Request ioarea */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c
new file mode 100644
index 000000000000..c47ed1e6ea2b
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c
@@ -0,0 +1,303 @@
+// 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 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);
+ }
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ 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 = 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..e3a4fcc63a56
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c
@@ -0,0 +1,418 @@
+// 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 = snd_soc_substream_to_rtd(substream);
+
+ dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
+ if (!dma_desc)
+ return -ENOMEM;
+
+ snd_soc_dai_set_dma_data(snd_soc_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 = snd_soc_substream_to_rtd(substream);
+
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ i2s_priv = dev_get_drvdata(snd_soc_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 = snd_soc_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(snd_soc_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(snd_soc_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 = snd_soc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+
+ offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
+ I2S_RX_DESC_OFF_LEVEL_SHIFT;
+ bool val_read = false;
+ while (offlevel) {
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
+ val_read = true;
+ offlevel--;
+ }
+ if (val_read)
+ 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 = snd_soc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_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(snd_soc_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
index d616e096462e..4cb2fe10bcdc 100644
--- a/sound/soc/bcm/cygnus-pcm.c
+++ b/sound/soc/bcm/cygnus-pcm.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2014-2015 Broadcom 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// 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>
@@ -207,9 +197,9 @@ 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 = substream->private_data;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
- return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
+ return snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(soc_runtime, 0), substream);
}
static void ringbuf_set_initial(void __iomem *audio_io,
@@ -353,13 +343,13 @@ static void enable_intr(struct snd_pcm_substream *substream)
static void disable_intr(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct cygnus_aio_port *aio;
u32 set_mask;
aio = cygnus_dai_get_dma_data(substream);
- dev_dbg(rtd->cpu_dai->dev, "%s on port %d\n", __func__, aio->portnum);
+ dev_dbg(snd_soc_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);
@@ -376,7 +366,8 @@ static void disable_intr(struct snd_pcm_substream *substream)
}
-static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int cygnus_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
@@ -577,9 +568,10 @@ static irqreturn_t cygnus_dma_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int cygnus_pcm_open(struct snd_pcm_substream *substream)
+static int cygnus_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct cygnus_aio_port *aio;
int ret;
@@ -588,7 +580,7 @@ static int cygnus_pcm_open(struct snd_pcm_substream *substream)
if (!aio)
return -ENODEV;
- dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
@@ -613,14 +605,15 @@ static int cygnus_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int cygnus_pcm_close(struct snd_pcm_substream *substream)
+static int cygnus_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct cygnus_aio_port *aio;
aio = cygnus_dai_get_dma_data(substream);
- dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->play_stream = NULL;
@@ -628,58 +621,29 @@ static int cygnus_pcm_close(struct snd_pcm_substream *substream)
aio->capture_stream = NULL;
if (!aio->play_stream && !aio->capture_stream)
- dev_dbg(rtd->cpu_dai->dev, "freed port %d\n", aio->portnum);
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum);
return 0;
}
-static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int cygnus_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct cygnus_aio_port *aio;
- int ret = 0;
-
- aio = cygnus_dai_get_dma_data(substream);
- dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- return ret;
-}
-
-static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct cygnus_aio_port *aio;
-
- aio = cygnus_dai_get_dma_data(substream);
- dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
-
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
-static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct cygnus_aio_port *aio;
unsigned long bufsize, periodsize;
- int ret = 0;
bool is_play;
u32 start;
struct ringbuf_regs *p_rbuf = NULL;
aio = cygnus_dai_get_dma_data(substream);
- dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+ dev_dbg(snd_soc_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(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n",
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
__func__, bufsize, periodsize);
configure_ringbuf_regs(substream);
@@ -693,10 +657,11 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
periodsize, bufsize);
- return ret;
+ return 0;
}
-static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
+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;
@@ -725,111 +690,36 @@ static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, res);
}
-static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size;
-
- size = cygnus_pcm_hw.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);
-
- dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
- __func__, size, buf->area);
-
- if (!buf->area) {
- dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
- return -ENOMEM;
- }
- buf->bytes = size;
-
- return 0;
-}
-
-
-static const struct snd_pcm_ops cygnus_pcm_ops = {
- .open = cygnus_pcm_open,
- .close = cygnus_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = cygnus_pcm_hw_params,
- .hw_free = cygnus_pcm_hw_free,
- .prepare = cygnus_pcm_prepare,
- .trigger = cygnus_pcm_trigger,
- .pointer = cygnus_pcm_pointer,
-};
-
-static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
-
- substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- if (substream) {
- buf = &substream->dma_buffer;
- if (buf->area) {
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
- }
-
- substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
- if (substream) {
- buf = &substream->dma_buffer;
- if (buf->area) {
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
- }
-}
-
-static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
+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;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
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);
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = cygnus_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = cygnus_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret) {
- cygnus_dma_free_dma_buffers(pcm);
- return ret;
- }
- }
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
return 0;
}
-static struct snd_soc_platform_driver cygnus_soc_platform = {
- .ops = &cygnus_pcm_ops,
- .pcm_new = cygnus_dma_new,
- .pcm_free = cygnus_dma_free_dma_buffers,
+static const 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 = 0;
+ int rc;
dev_dbg(dev, "%s Enter\n", __func__);
@@ -840,7 +730,8 @@ int cygnus_soc_platform_register(struct device *dev,
return rc;
}
- rc = snd_soc_register_platform(dev, &cygnus_soc_platform);
+ rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
+ NULL, 0);
if (rc) {
dev_err(dev, "%s failed\n", __func__);
return rc;
@@ -851,8 +742,6 @@ int cygnus_soc_platform_register(struct device *dev,
int cygnus_soc_platform_unregister(struct device *dev)
{
- snd_soc_unregister_platform(dev);
-
return 0;
}
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
index abafadc0b534..e0ce0232eb1e 100644
--- a/sound/soc/bcm/cygnus-ssp.c
+++ b/sound/soc/bcm/cygnus-ssp.c
@@ -1,21 +1,11 @@
-/*
- * Copyright (C) 2014-2015 Broadcom 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// 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/of.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -848,12 +838,12 @@ static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
ssp_newcfg = 0;
- 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_BC_FC:
ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_BP_FP:
ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 0;
break;
@@ -1052,10 +1042,13 @@ static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
}
#ifdef CONFIG_PM_SLEEP
-static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
+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;
@@ -1078,11 +1071,25 @@ static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
+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->
@@ -1109,6 +1116,18 @@ static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
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
@@ -1149,8 +1168,6 @@ static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &cygnus_ssp_dai_ops, \
- .suspend = cygnus_ssp_suspend, \
- .resume = cygnus_ssp_resume, \
}
static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
@@ -1169,14 +1186,15 @@ static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &cygnus_spdif_dai_ops,
- .suspend = cygnus_ssp_suspend,
- .resume = cygnus_ssp_resume,
};
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",
+ .name = "cygnus-audio",
+ .suspend = cygnus_ssp_suspend,
+ .resume = cygnus_ssp_resume,
+ .legacy_dai_naming = 1,
};
/*
@@ -1281,9 +1299,8 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child_node;
- struct resource *res = pdev->resource;
struct cygnus_audio *cygaud;
- int err = -EINVAL;
+ int err;
int node_count;
int active_port_count;
@@ -1293,13 +1310,11 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
dev_set_drvdata(dev, cygaud);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
- cygaud->audio = devm_ioremap_resource(dev, res);
+ cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud");
if (IS_ERR(cygaud->audio))
return PTR_ERR(cygaud->audio);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in");
- cygaud->i2s_in = devm_ioremap_resource(dev, res);
+ cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in");
if (IS_ERR(cygaud->i2s_in))
return PTR_ERR(cygaud->i2s_in);
@@ -1321,8 +1336,10 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
&cygnus_ssp_dai[active_port_count]);
/* negative is err, 0 is active and good, 1 is disabled */
- if (err < 0)
+ 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);
@@ -1334,7 +1351,7 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
cygaud->active_ports = 0;
dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
- err = snd_soc_register_component(dev, &cygnus_ssp_component,
+ 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");
@@ -1342,37 +1359,27 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
}
cygaud->irq_num = platform_get_irq(pdev, 0);
- if (cygaud->irq_num <= 0) {
- dev_err(dev, "platform_get_irq failed\n");
- err = cygaud->irq_num;
- goto err_irq;
- }
+ 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");
- goto err_irq;
+ return err;
}
err = cygnus_soc_platform_register(dev, cygaud);
if (err) {
dev_err(dev, "platform reg error %d\n", err);
- goto err_irq;
+ return err;
}
return 0;
-
-err_irq:
- snd_soc_unregister_component(dev);
- return err;
}
-static int cygnus_ssp_remove(struct platform_device *pdev)
+static void cygnus_ssp_remove(struct platform_device *pdev)
{
cygnus_soc_platform_unregister(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
-
- return 0;
}
static const struct of_device_id cygnus_ssp_of_match[] = {
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
index 33dd34305928..4925e03c3c30 100644
--- a/sound/soc/bcm/cygnus-ssp.h
+++ b/sound/soc/bcm/cygnus-ssp.h
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2014-2015 Broadcom 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2014-2015 Broadcom Corporation */
#ifndef __CYGNUS_SSP_H__
#define __CYGNUS_SSP_H__
@@ -127,8 +117,6 @@ struct cygnus_audio {
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,
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
deleted file mode 100644
index 6410aa2cc2cf..000000000000
--- a/sound/soc/blackfin/Kconfig
+++ /dev/null
@@ -1,205 +0,0 @@
-config SND_BF5XX_I2S
- tristate "SoC I2S Audio for the ADI Blackfin chip"
- depends on BLACKFIN
- select SND_BF5XX_SOC_SPORT if !BF60x
- select SND_BF6XX_SOC_SPORT if BF60x
- 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 Codec Add-On Card support"
- depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S if !BF60x
- select SND_BF6XX_SOC_I2S if BF60x
- select SND_SOC_SSM2602_SPI if SPI_MASTER
- select SND_SOC_SSM2602_I2C if I2C
- help
- Say Y if you want to add support for the Analog Devices
- SSM2602 Audio Codec Add-On Card.
-
-config SND_SOC_BFIN_EVAL_ADAU1701
- tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1701
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
-config SND_SOC_BFIN_EVAL_ADAU1373
- tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1373
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that first ADAU1373 DAI is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAU1X61
- tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1761_I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAU1X61 is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAU1X81
- tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1781_I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAU1X81 is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAV80X
- tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
- depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAV801 if SPI_MASTER
- select SND_SOC_ADAV803 if I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or
- EVAL-ADAV803 board connected to one of the Blackfin evaluation boards
- like the BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAV80X digital record and playback
- interfaces are connected to the first SPORT port on the BF5XX board.
-
-config SND_BF5XX_SOC_AD1836
- tristate "SoC AD1836 Audio support for BF5xx"
- depends on SND_BF5XX_I2S && SPI_MASTER
- select SND_BF5XX_SOC_I2S
- 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_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S
- select SND_SOC_AD193X_I2C if I2C
- select SND_SOC_AD193X_SPI if SPI_MASTER
- 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_SOC_AD73311
- tristate "SoC AD73311 Audio support for Blackfin"
- depends on SND_BF5XX_I2S
- select SND_BF5XX_SOC_I2S
- select SND_SOC_AD73311
- help
- Say Y if you want to add support for AD73311 codec on Blackfin.
-
-config SND_BFIN_AD73311_SE
- int "PF pin for AD73311L Chip Select"
- depends on SND_BF5XX_SOC_AD73311
- default 4
- help
- Enter the GPIO used to control AD73311's SE pin. Acceptable
- values are 0 to 7
-
-config SND_BF5XX_AC97
- tristate "SoC AC97 Audio for the ADI BF5xx chip"
- depends on BLACKFIN
- select AC97_BUS
- select SND_SOC_AC97_BUS
- select SND_BF5XX_SOC_SPORT
- select SND_BF5XX_SOC_AC97
- 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_BF6XX_SOC_SPORT
- tristate
-
-config SND_BF5XX_SOC_I2S
- tristate
-
-config SND_BF6XX_SOC_I2S
- tristate
-
-config SND_BF5XX_SOC_AC97
- tristate
-
-config SND_BF5XX_SPORT_NUM
- int "Set a SPORT for Sound chip"
- depends on (SND_BF5XX_SOC_SPORT || SND_BF6XX_SOC_SPORT)
- 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 ebeb6a9cedd2..000000000000
--- a/sound/soc/blackfin/Makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Blackfin Platform Support
-snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
-snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
-snd-soc-bf5xx-sport-objs := bf5xx-sport.o
-snd-soc-bf6xx-sport-objs := bf6xx-sport.o
-snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
-snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
-snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o
-
-obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
-obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
-obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
-obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-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_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.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
-snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
-snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
-snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
-snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
-snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.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
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
deleted file mode 100644
index 8c1d1983b8f9..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ /dev/null
@@ -1,480 +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.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,
-
- .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_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- 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, unsigned long pos,
- void *buf, unsigned long count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
- struct ac97_frame *dst;
-
- pr_debug("%s copy pos:0x%lx count:0x%lx\n",
- substream->stream ? "Capture" : "Playback", pos, count);
- dst = (struct ac97_frame *)runtime->dma_area +
- bytes_to_frames(runtime, pos);
- count = bytes_to_frames(runtime, count);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- bf5xx_pcm_to_ac97(dst, buf, count, chan_mask);
- else
- bf5xx_ac97_to_pcm(dst, buf, count);
- return 0;
-}
-
-static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void __user *buf, unsigned long count)
-{
- return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count);
-}
-#endif
-
-static const 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_user = bf5xx_pcm_copy_user,
- .copy_kernel = bf5xx_pcm_copy,
-#endif
-};
-
-static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- 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)
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- 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
- }
-}
-
-static int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- 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 bf5xx_soc_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_platform(&pdev->dev,
- &bf5xx_ac97_soc_platform);
-}
-
-static struct platform_driver bf5xx_pcm_driver = {
- .driver = {
- .name = "bfin-ac97-pcm-audio",
- },
-
- .probe = bf5xx_soc_platform_probe,
-};
-
-module_platform_driver(bf5xx_pcm_driver);
-
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
deleted file mode 100644
index a040cfe29fc0..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ /dev/null
@@ -1,388 +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 struct sport_device *ac97_sport_handle;
-
-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 = ac97_sport_handle;
- int *cmd_count = sport->private_data;
- 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 sport_device *sport_handle = ac97_sport_handle;
- 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)
-{
- struct sport_device *sport_handle = ac97_sport_handle;
-
- 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)
-{
- struct sport_device *sport_handle = ac97_sport_handle;
- u16 gpio = P_IDENT(sport_handle->pin_req[3]);
-
- pr_debug("%s enter\n", __func__);
-
- peripheral_free_list(sport_handle->pin_req);
- gpio_request(gpio, "bf5xx-ac97");
- gpio_direction_output(gpio, 1);
- udelay(2);
- gpio_set_value(gpio, 0);
- udelay(1);
- gpio_free(gpio);
- peripheral_request_list(sport_handle->pin_req, "soc-audio");
-}
-
-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
-}
-
-static struct snd_ac97_bus_ops bf5xx_ac97_ops = {
- .read = bf5xx_ac97_read,
- .write = bf5xx_ac97_write,
- .warm_reset = bf5xx_ac97_warm_reset,
- .reset = bf5xx_ac97_cold_reset,
-};
-
-#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, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport, 16, 0x1F, 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 struct snd_soc_dai_driver bfin_ac97_dai = {
- .bus_control = true,
- .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, },
-};
-
-static const struct snd_soc_component_driver bfin_ac97_component = {
- .name = "bfin-ac97",
-};
-
-static int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- struct sport_device *sport_handle;
- int ret;
-
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- /* Request PB3 as reset pin */
- ret = devm_gpio_request_one(&pdev->dev,
- CONFIG_SND_BF5XX_RESET_GPIO_NUM,
- GPIOF_OUT_INIT_HIGH, "SND_AD198x RESET");
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to request GPIO_%d for reset: %d\n",
- CONFIG_SND_BF5XX_RESET_GPIO_NUM, ret);
- return ret;
- }
-#endif
-
- sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
- PAGE_SIZE);
- 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, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport_handle, 16, 0x1F, 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;
- }
-
- ret = snd_soc_set_ac97_ops(&bf5xx_ac97_ops);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
- goto sport_config_err;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component,
- &bfin_ac97_dai, 1);
- if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
- goto sport_config_err;
- }
-
- ac97_sport_handle = sport_handle;
-
- return 0;
-
-sport_config_err:
- sport_done(sport_handle);
-sport_err:
- snd_soc_set_ac97_ops(NULL);
-
- return ret;
-}
-
-static int asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- struct sport_device *sport_handle = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_done(sport_handle);
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver asoc_bfin_ac97_driver = {
- .driver = {
- .name = "bfin-ac97",
- },
-
- .probe = asoc_bfin_ac97_probe,
- .remove = asoc_bfin_ac97_remove,
-};
-
-module_platform_driver(asoc_bfin_ac97_driver);
-
-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 a680fdc9bb42..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.h
+++ /dev/null
@@ -1,57 +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
-
-/* 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 864df2616e10..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ /dev/null
@@ -1,109 +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/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"
-
-static struct snd_soc_card bf5xx_ad1836;
-
-static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
- int ret = 0;
-
- /* 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;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .dai_fmt = BF5XX_AD1836_DAIFMT,
- .init = bf5xx_ad1836_init,
-};
-
-static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bfin-ad1836",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ad1836_dai,
- .num_links = 1,
-};
-
-static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bf5xx_ad1836;
- const char **link_name;
- int ret;
-
- link_name = pdev->dev.platform_data;
- if (!link_name) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
- bf5xx_ad1836_dai.cpu_dai_name = link_name[0];
- bf5xx_ad1836_dai.codec_name = link_name[1];
-
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "Failed to register card\n");
- return ret;
-}
-
-static struct platform_driver bf5xx_ad1836_driver = {
- .driver = {
- .name = "bfin-snd-ad1836",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bf5xx_ad1836_driver_probe,
-};
-module_platform_driver(bf5xx_ad1836_driver);
-
-/* 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 603ad1f2b9b9..000000000000
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ /dev/null
@@ -1,131 +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/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"
-
-static struct snd_soc_card bf5xx_ad193x;
-
-static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN);
- 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;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-#define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
- {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "spi0.5",
- .dai_fmt = BF5XX_AD193X_DAIFMT,
- .init = bf5xx_ad193x_link_init,
- },
- {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "spi0.5",
- .dai_fmt = BF5XX_AD193X_DAIFMT,
- .init = bf5xx_ad193x_link_init,
- },
-};
-
-static struct snd_soc_card bf5xx_ad193x = {
- .name = "bfin-ad193x",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .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 0fa81a523b8a..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ /dev/null
@@ -1,109 +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 "bf5xx-ac97.h"
-
-static struct snd_soc_card bf5xx_board;
-
-static struct snd_soc_dai_link bf5xx_board_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97.0",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-ac97-pcm-audio",
- .codec_name = "ad1980",
- },
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97.1",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-ac97-pcm-audio",
- .codec_name = "ad1980",
- },
-};
-
-static struct snd_soc_card bf5xx_board = {
- .name = "bfin-ad1980",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .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 786bbdd96e7c..000000000000
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ /dev/null
@@ -1,212 +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/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"
-
-#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 snd_soc_card *card)
-{
- 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;
-}
-
-#define BF5XX_AD7311_DAI_FMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
- {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ad73311",
- .dai_fmt = BF5XX_AD7311_DAI_FMT,
- },
- {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ad73311",
- .dai_fmt = BF5XX_AD7311_DAI_FMT,
- },
-};
-
-static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bfin-ad73311",
- .owner = THIS_MODULE,
- .probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .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 51cae76f14e6..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ /dev/null
@@ -1,373 +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-sport.h"
-#include "bf5xx-i2s-pcm.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_VALID |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .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)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- unsigned int buffer_size = params_buffer_bytes(params);
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode)
- buffer_size = buffer_size / params_channels(params) * 8;
-
- return snd_pcm_lib_malloc_pages(substream, buffer_size);
-}
-
-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_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int period_bytes = frames_to_bytes(runtime, runtime->period_size);
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode)
- period_bytes = period_bytes / runtime->channels * 8;
-
- 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_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- unsigned int diff;
- snd_pcm_uframes_t frames;
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- pr_debug("%s enter\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- diff = sport_curr_offset_tx(sport);
- } else {
- diff = sport_curr_offset_rx(sport);
- }
-
- /*
- * TX at least can report one frame beyond the end of the
- * buffer if we hit the wraparound case - clamp to within the
- * buffer as the ALSA APIs require.
- */
- if (diff == snd_pcm_lib_buffer_bytes(substream))
- diff = 0;
-
- frames = bytes_to_frames(substream->runtime, diff);
- if (dma_data->tdm_mode)
- frames = frames * runtime->channels / 8;
-
- return frames;
-}
-
-static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- struct bf5xx_i2s_pcm_data *dma_data;
- int ret;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- pr_debug("%s enter\n", __func__);
-
- snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
- if (dma_data->tdm_mode)
- runtime->hw.buffer_bytes_max /= 4;
- else
- runtime->hw.info |= SNDRV_PCM_INFO_MMAP;
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- if (sport_handle != NULL) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
- 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 int bf5xx_pcm_copy(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *buf, unsigned long count)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int sample_size = runtime->sample_bits / 8;
- struct bf5xx_i2s_pcm_data *dma_data;
- unsigned int i;
- void *src, *dst;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode) {
- pos = bytes_to_frames(runtime, pos);
- count = bytes_to_frames(runtime, count);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = runtime->dma_area;
- dst += pos * sample_size * 8;
-
- while (count--) {
- for (i = 0; i < runtime->channels; i++) {
- memcpy(dst + dma_data->map[i] *
- sample_size, src, sample_size);
- src += sample_size;
- }
- dst += 8 * sample_size;
- }
- } else {
- src = runtime->dma_area;
- src += pos * sample_size * 8;
- dst = buf;
-
- while (count--) {
- for (i = 0; i < runtime->channels; i++) {
- memcpy(dst, src + dma_data->map[i] *
- sample_size, sample_size);
- dst += sample_size;
- }
- src += 8 * sample_size;
- }
- }
- } else {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = runtime->dma_area;
- dst += pos;
- } else {
- src = runtime->dma_area;
- src += pos;
- dst = buf;
- }
-
- memcpy(dst, src, count);
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void __user *buf, unsigned long count)
-{
- return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count);
-}
-
-static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- unsigned long count)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int sample_size = runtime->sample_bits / 8;
- void *buf = runtime->dma_area;
- struct bf5xx_i2s_pcm_data *dma_data;
- unsigned int offset, samples;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode) {
- offset = bytes_to_frames(runtime, pos) * 8 * sample_size;
- samples = bytes_to_frames(runtime, count) * 8;
- } else {
- offset = pos;
- samples = bytes_to_samples(runtime, count);
- }
-
- snd_pcm_format_set_silence(runtime->format, buf + offset, samples);
-
- return 0;
-}
-
-static const 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,
- .copy_user = bf5xx_pcm_copy_user,
- .copy_kernel = bf5xx_pcm_copy,
- .fill_silence = bf5xx_pcm_silence,
-};
-
-static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
- SNDRV_DMA_TYPE_DEV, card->dev, size, size);
-}
-
-static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
- .ops = &bf5xx_pcm_i2s_ops,
- .pcm_new = bf5xx_pcm_i2s_new,
-};
-
-static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_platform(&pdev->dev,
- &bf5xx_i2s_soc_platform);
-}
-
-static struct platform_driver bfin_i2s_pcm_driver = {
- .driver = {
- .name = "bfin-i2s-pcm-audio",
- },
-
- .probe = bfin_i2s_soc_platform_probe,
-};
-
-module_platform_driver(bfin_i2s_pcm_driver);
-
-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 1f0435249f88..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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
-
-#define BFIN_TDM_DAI_MAX_SLOTS 8
-
-struct bf5xx_i2s_pcm_data {
- unsigned int map[BFIN_TDM_DAI_MAX_SLOTS];
- bool tdm_mode;
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
deleted file mode 100644
index b69aeef6418e..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ /dev/null
@@ -1,391 +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"
-#include "bf5xx-i2s-pcm.h"
-
-struct bf5xx_i2s_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- int configured;
-
- unsigned int slots;
- unsigned int tx_mask;
- unsigned int rx_mask;
-
- struct bf5xx_i2s_pcm_data tx_dma_data;
- struct bf5xx_i2s_pcm_data rx_dma_data;
-};
-
-static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- 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:
- dev_err(cpu_dai->dev, "%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:
- dev_err(cpu_dai->dev, "%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)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- int ret = 0;
-
- bf5xx_i2s->tcr2 &= ~0x1f;
- bf5xx_i2s->rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- bf5xx_i2s->tcr2 |= 7;
- bf5xx_i2s->rcr2 |= 7;
- sport_handle->wdsize = 1;
- break;
- 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) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
- bf5xx_i2s->tcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
- }
-
- return 0;
-}
-
-static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
-
- dev_dbg(dai->dev, "%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_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 sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- unsigned int tx_mapped = 0, rx_mapped = 0;
- unsigned int slot;
- int i;
-
- 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_i2s->tx_dma_data.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_i2s->rx_dma_data.map[i] = slot;
- rx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots, int width)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
-
- if (slots % 8 != 0 || slots > 8)
- return -EINVAL;
-
- if (width != 32)
- return -EINVAL;
-
- bf5xx_i2s->slots = slots;
- bf5xx_i2s->tx_mask = tx_mask;
- bf5xx_i2s->rx_mask = rx_mask;
-
- bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
- bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
-
- return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
-}
-
-#ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
-
- dev_dbg(dai->dev, "%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)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- int ret;
-
- dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
-
- ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
- bf5xx_i2s->rcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
- bf5xx_i2s->tcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
- bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
-}
-
-#else
-#define bf5xx_i2s_suspend NULL
-#define bf5xx_i2s_resume NULL
-#endif
-
-static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- unsigned int i;
-
- for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
- bf5xx_i2s->tx_dma_data.map[i] = i;
- bf5xx_i2s->rx_dma_data.map[i] = i;
- }
-
- dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
- dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
-
- return 0;
-}
-
-#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_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
-
-static const 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,
- .set_tdm_slot = bf5xx_i2s_set_tdm_slot,
- .set_channel_map = bf5xx_i2s_set_channel_map,
-};
-
-static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .probe = bf5xx_i2s_dai_probe,
- .suspend = bf5xx_i2s_suspend,
- .resume = bf5xx_i2s_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = BF5XX_I2S_RATES,
- .formats = BF5XX_I2S_FORMATS,},
- .capture = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = BF5XX_I2S_RATES,
- .formats = BF5XX_I2S_FORMATS,},
- .ops = &bf5xx_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver bf5xx_i2s_component = {
- .name = "bf5xx-i2s",
-};
-
-static int bf5xx_i2s_probe(struct platform_device *pdev)
-{
- struct sport_device *sport_handle;
- int ret;
-
- /* configure SPORT for I2S */
- sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
- sizeof(struct bf5xx_i2s_port));
- if (!sport_handle)
- return -ENODEV;
-
- /* register with the ASoC layers */
- ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
- &bf5xx_i2s_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
- sport_done(sport_handle);
- return ret;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_remove(struct platform_device *pdev)
-{
- struct sport_device *sport_handle = platform_get_drvdata(pdev);
-
- dev_dbg(&pdev->dev, "%s enter\n", __func__);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_done(sport_handle);
-
- return 0;
-}
-
-static struct platform_driver bfin_i2s_driver = {
- .probe = bf5xx_i2s_probe,
- .remove = bf5xx_i2s_remove,
- .driver = {
- .name = "bfin-i2s",
- },
-};
-
-module_platform_driver(bfin_i2s_driver);
-
-/* 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 9dfa1241ea66..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ /dev/null
@@ -1,1102 +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 <linux/module.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)
-
-/* 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 tx_mask, u32 rx_mask, int packed)
-{
- pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n",
- __func__, tdm_count, tx_mask, rx_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 = tx_mask;
- sport->regs->mrcs0 = rx_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;
-
- if (WARN_ON(!sport->dummy_rx_desc) ||
- WARN_ON(sport->curr_rx_desc == sport->dummy_rx_desc))
- return -EINVAL;
-
- /* 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 */
- if (WARN_ON(!sport->dma_rx_desc) ||
- WARN_ON(sport->curr_rx_desc != sport->dummy_rx_desc))
- return -EINVAL;
- 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;
-
- if (WARN_ON(!sport->dummy_tx_desc) ||
- WARN_ON(sport->curr_tx_desc == sport->dummy_tx_desc))
- return -EINVAL;
-
- 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) {
- if (WARN_ON(!sport->dma_tx_desc) ||
- WARN_ON(sport->curr_tx_desc != sport->dummy_tx_desc))
- return -EINVAL;
- /* 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)
-{
- if (WARN_ON(!rx_callback))
- return -EINVAL;
- 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)
-{
- if (WARN_ON(!tx_callback))
- return -EINVAL;
- 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)
-{
- if (WARN_ON(!err_callback))
- return -EINVAL;
- sport->err_callback = err_callback;
- sport->err_data = err_data;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_set_err_callback);
-
-static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
-{
- /* Extract settings from platform data */
- struct device *dev = &pdev->dev;
- struct bfin_snd_platform_data *pdata = dev->platform_data;
- struct resource *res;
-
- param->num = pdev->id;
-
- if (!pdata) {
- dev_err(dev, "no platform_data\n");
- return -ENODEV;
- }
- param->pin_req = pdata->pin_req;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no MEM resource\n");
- return -ENODEV;
- }
- param->regs = (struct sport_register *)res->start;
-
- /* first RX, then TX */
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(dev, "no rx DMA resource\n");
- return -ENODEV;
- }
- param->dma_rx_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(dev, "no tx DMA resource\n");
- return -ENODEV;
- }
- param->dma_tx_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "no irq resource\n");
- return -ENODEV;
- }
- param->err_irq = res->start;
-
- return 0;
-}
-
-struct sport_device *sport_init(struct platform_device *pdev,
- unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
-{
- struct device *dev = &pdev->dev;
- struct sport_param param;
- struct sport_device *sport;
- int ret;
-
- dev_dbg(dev, "%s enter\n", __func__);
-
- param.wdsize = wdsize;
- param.dummy_count = dummy_count;
- if (WARN_ON(param.wdsize == 0 || param.dummy_count == 0))
- return NULL;
-
- ret = sport_config_pdev(pdev, &param);
- if (ret)
- return NULL;
-
- if (peripheral_request_list(param.pin_req, "soc-audio")) {
- dev_err(dev, "requesting Peripherals failed\n");
- return NULL;
- }
-
- sport = kzalloc(sizeof(*sport), GFP_KERNEL);
- if (!sport) {
- dev_err(dev, "failed to allocate for sport device\n");
- goto __init_err0;
- }
-
- sport->num = param.num;
- 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->pin_req = param.pin_req;
-
- if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
- dev_err(dev, "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) {
- dev_err(dev, "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) {
- dev_err(dev, "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) {
- dev_err(dev, "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) {
- dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
- goto __init_err3;
- }
-
- dev_info(dev, "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 = param.wdsize;
- sport->dummy_count = param.dummy_count;
-
- sport->private_data = kzalloc(priv_size, GFP_KERNEL);
- if (!sport->private_data) {
- dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
- goto __init_err4;
- }
-
- if (L1_DATA_A_LENGTH)
- sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
- else
- sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
- if (sport->dummy_buf == NULL) {
- dev_err(dev, "failed to allocate dummy buffer\n");
- goto __error1;
- }
-
- ret = sport_config_rx_dummy(sport);
- if (ret) {
- dev_err(dev, "failed to config rx dummy ring\n");
- goto __error2;
- }
- ret = sport_config_tx_dummy(sport);
- if (ret) {
- dev_err(dev, "failed to config tx dummy ring\n");
- goto __error3;
- }
-
- platform_set_drvdata(pdev, sport);
-
- return sport;
-__error3:
- if (L1_DATA_A_LENGTH)
- l1_data_sram_free(sport->dummy_rx_desc);
- else
- dma_free_coherent(NULL, 2*sizeof(struct dmasg),
- sport->dummy_rx_desc, 0);
-__error2:
- if (L1_DATA_A_LENGTH)
- l1_data_sram_free(sport->dummy_buf);
- else
- kfree(sport->dummy_buf);
-__error1:
- kfree(sport->private_data);
-__init_err4:
- 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);
-__init_err0:
- peripheral_free_list(param.pin_req);
- 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->private_data);
- peripheral_free_list(sport->pin_req);
- kfree(sport);
-}
-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 9fc2192feb3b..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * File: bf5xx_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 <linux/platform_device.h>
-#include <asm/dma.h>
-#include <asm/bfin_sport.h>
-
-#define DESC_ELEMENT_COUNT 9
-
-struct sport_device {
- int num;
- int dma_rx_chan;
- int dma_tx_chan;
- int err_irq;
- const unsigned short *pin_req;
- 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;
-};
-
-struct sport_param {
- int num;
- int dma_rx_chan;
- int dma_tx_chan;
- int err_irq;
- const unsigned short *pin_req;
- struct sport_register *regs;
- unsigned int wdsize;
- unsigned int dummy_count;
- void *private_data;
-};
-
-struct sport_device *sport_init(struct platform_device *pdev,
- unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
-
-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 tx_mask, u32 rx_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 9c19ccc936e2..000000000000
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ /dev/null
@@ -1,126 +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/pcm_params.h>
-
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <linux/gpio.h>
-#include "../codecs/ssm2602.h"
-#include "bf5xx-sport.h"
-
-static struct snd_soc_card bf5xx_ssm2602;
-
-static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- /*
- * 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.
- */
- return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000,
- SND_SOC_CLOCK_IN);
-}
-
-/* CODEC is master for BCLK and LRC in this configuration. */
-#define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
- {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ssm2602.0-001b",
- .init = bf5xx_ssm2602_dai_init,
- .dai_fmt = BF5XX_SSM2602_DAIFMT,
- },
- {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ssm2602.0-001b",
- .init = bf5xx_ssm2602_dai_init,
- .dai_fmt = BF5XX_SSM2602_DAIFMT,
- },
-};
-
-static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bfin-ssm2602",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .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/bf6xx-i2s.c b/sound/soc/blackfin/bf6xx-i2s.c
deleted file mode 100644
index 819cff149a25..000000000000
--- a/sound/soc/blackfin/bf6xx-i2s.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * bf6xx-i2s.c - Analog Devices BF6XX i2s interface driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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/device.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dai.h>
-
-#include "bf6xx-sport.h"
-
-struct sport_params param;
-
-static int bfin_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai);
- struct device *dev = &sport->pdev->dev;
- int ret = 0;
-
- param.spctl &= ~(SPORT_CTL_OPMODE | SPORT_CTL_CKRE | SPORT_CTL_FSR
- | SPORT_CTL_LFS | SPORT_CTL_LAFS);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_CKRE
- | SPORT_CTL_LFS;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- param.spctl |= SPORT_CTL_FSR;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_LFS
- | SPORT_CTL_LAFS;
- break;
- default:
- dev_err(dev, "%s: Unknown DAI format type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- param.spctl &= ~(SPORT_CTL_ICLK | SPORT_CTL_IFS);
- 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:
- dev_err(dev, "%s: Unknown DAI master type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bfin_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- struct device *dev = &sport->pdev->dev;
- int ret = 0;
-
- param.spctl &= ~SPORT_CTL_SLEN;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- param.spctl |= 0x70;
- sport->wdsize = 1;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- param.spctl |= 0xf0;
- sport->wdsize = 2;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- param.spctl |= 0x170;
- sport->wdsize = 3;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- param.spctl |= 0x1f0;
- sport->wdsize = 4;
- break;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = sport_set_tx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT tx is busy!\n");
- return ret;
- }
- } else {
- ret = sport_set_rx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT rx is busy!\n");
- return ret;
- }
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- if (dai->capture_active)
- sport_rx_stop(sport);
- if (dai->playback_active)
- sport_tx_stop(sport);
- return 0;
-}
-
-static int bfin_i2s_resume(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- struct device *dev = &sport->pdev->dev;
- int ret;
-
- ret = sport_set_tx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT tx is busy!\n");
- return ret;
- }
- ret = sport_set_rx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT rx is busy!\n");
- return ret;
- }
-
- return 0;
-}
-
-#else
-#define bfin_i2s_suspend NULL
-#define bfin_i2s_resume NULL
-#endif
-
-#define BFIN_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 BFIN_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-static const struct snd_soc_dai_ops bfin_i2s_dai_ops = {
- .hw_params = bfin_i2s_hw_params,
- .set_fmt = bfin_i2s_set_dai_fmt,
-};
-
-static struct snd_soc_dai_driver bfin_i2s_dai = {
- .suspend = bfin_i2s_suspend,
- .resume = bfin_i2s_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = BFIN_I2S_RATES,
- .formats = BFIN_I2S_FORMATS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = BFIN_I2S_RATES,
- .formats = BFIN_I2S_FORMATS,
- },
- .ops = &bfin_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver bfin_i2s_component = {
- .name = "bfin-i2s",
-};
-
-static int bfin_i2s_probe(struct platform_device *pdev)
-{
- struct sport_device *sport;
- struct device *dev = &pdev->dev;
- int ret;
-
- sport = sport_create(pdev);
- if (!sport)
- return -ENODEV;
-
- /* register with the ASoC layers */
- ret = snd_soc_register_component(dev, &bfin_i2s_component,
- &bfin_i2s_dai, 1);
- if (ret) {
- dev_err(dev, "Failed to register DAI: %d\n", ret);
- sport_delete(sport);
- return ret;
- }
- platform_set_drvdata(pdev, sport);
-
- return 0;
-}
-
-static int bfin_i2s_remove(struct platform_device *pdev)
-{
- struct sport_device *sport = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_delete(sport);
-
- return 0;
-}
-
-static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_probe,
- .remove = bfin_i2s_remove,
- .driver = {
- .name = "bfin-i2s",
- },
-};
-
-module_platform_driver(bfin_i2s_driver);
-
-MODULE_DESCRIPTION("Analog Devices BF6XX i2s interface driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf6xx-sport.c b/sound/soc/blackfin/bf6xx-sport.c
deleted file mode 100644
index d2caadfe7b6d..000000000000
--- a/sound/soc/blackfin/bf6xx-sport.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * bf6xx_sport.c Analog Devices BF6XX SPORT driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <asm/blackfin.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "bf6xx-sport.h"
-
-int sport_set_tx_params(struct sport_device *sport,
- struct sport_params *params)
-{
- if (sport->tx_regs->spctl & SPORT_CTL_SPENPRI)
- return -EBUSY;
- sport->tx_regs->spctl = params->spctl | SPORT_CTL_SPTRAN;
- sport->tx_regs->div = params->div;
- SSYNC();
- return 0;
-}
-EXPORT_SYMBOL(sport_set_tx_params);
-
-int sport_set_rx_params(struct sport_device *sport,
- struct sport_params *params)
-{
- if (sport->rx_regs->spctl & SPORT_CTL_SPENPRI)
- return -EBUSY;
- sport->rx_regs->spctl = params->spctl & ~SPORT_CTL_SPTRAN;
- sport->rx_regs->div = params->div;
- SSYNC();
- return 0;
-}
-EXPORT_SYMBOL(sport_set_rx_params);
-
-static int compute_wdsize(size_t wdsize)
-{
- switch (wdsize) {
- case 1:
- return WDSIZE_8 | PSIZE_8;
- case 2:
- return WDSIZE_16 | PSIZE_16;
- default:
- return WDSIZE_32 | PSIZE_32;
- }
-}
-
-void sport_tx_start(struct sport_device *sport)
-{
- set_dma_next_desc_addr(sport->tx_dma_chan, sport->tx_desc);
- set_dma_config(sport->tx_dma_chan, DMAFLOW_LIST | DI_EN
- | compute_wdsize(sport->wdsize) | NDSIZE_6);
- enable_dma(sport->tx_dma_chan);
- sport->tx_regs->spctl |= SPORT_CTL_SPENPRI;
- SSYNC();
-}
-EXPORT_SYMBOL(sport_tx_start);
-
-void sport_rx_start(struct sport_device *sport)
-{
- set_dma_next_desc_addr(sport->rx_dma_chan, sport->rx_desc);
- set_dma_config(sport->rx_dma_chan, DMAFLOW_LIST | DI_EN | WNR
- | compute_wdsize(sport->wdsize) | NDSIZE_6);
- enable_dma(sport->rx_dma_chan);
- sport->rx_regs->spctl |= SPORT_CTL_SPENPRI;
- SSYNC();
-}
-EXPORT_SYMBOL(sport_rx_start);
-
-void sport_tx_stop(struct sport_device *sport)
-{
- sport->tx_regs->spctl &= ~SPORT_CTL_SPENPRI;
- SSYNC();
- disable_dma(sport->tx_dma_chan);
-}
-EXPORT_SYMBOL(sport_tx_stop);
-
-void sport_rx_stop(struct sport_device *sport)
-{
- sport->rx_regs->spctl &= ~SPORT_CTL_SPENPRI;
- SSYNC();
- disable_dma(sport->rx_dma_chan);
-}
-EXPORT_SYMBOL(sport_rx_stop);
-
-void sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data)
-{
- sport->tx_callback = tx_callback;
- sport->tx_data = tx_data;
-}
-EXPORT_SYMBOL(sport_set_tx_callback);
-
-void sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data)
-{
- sport->rx_callback = rx_callback;
- sport->rx_data = rx_data;
-}
-EXPORT_SYMBOL(sport_set_rx_callback);
-
-static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
- size_t fragsize, unsigned int cfg,
- unsigned int count, 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 = count;
- desc[i].x_modify = wdsize;
- desc[i].y_count = 0;
- desc[i].y_modify = 0;
- }
-
- /* make circular */
- desc[fragcount - 1].next_desc_addr = desc;
-}
-
-int sport_config_tx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize)
-{
- unsigned int count;
- unsigned int cfg;
- dma_addr_t addr;
-
- count = fragsize / sport->wdsize;
-
- if (sport->tx_desc)
- dma_free_coherent(NULL, sport->tx_desc_size,
- sport->tx_desc, 0);
-
- sport->tx_desc = dma_alloc_coherent(NULL,
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->tx_desc_size = fragcount * sizeof(struct dmasg);
- if (!sport->tx_desc)
- return -ENOMEM;
-
- sport->tx_buf = buf;
- sport->tx_fragsize = fragsize;
- sport->tx_frags = fragcount;
- cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) | NDSIZE_6;
-
- setup_desc(sport->tx_desc, buf, fragcount, fragsize,
- cfg | DMAEN, count, sport->wdsize);
- return 0;
-}
-EXPORT_SYMBOL(sport_config_tx_dma);
-
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize)
-{
- unsigned int count;
- unsigned int cfg;
- dma_addr_t addr;
-
- count = fragsize / sport->wdsize;
-
- if (sport->rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_size,
- sport->rx_desc, 0);
-
- sport->rx_desc = dma_alloc_coherent(NULL,
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->rx_desc_size = fragcount * sizeof(struct dmasg);
- if (!sport->rx_desc)
- return -ENOMEM;
-
- sport->rx_buf = buf;
- sport->rx_fragsize = fragsize;
- sport->rx_frags = fragcount;
- cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize)
- | WNR | NDSIZE_6;
-
- setup_desc(sport->rx_desc, buf, fragcount, fragsize,
- cfg | DMAEN, count, sport->wdsize);
- return 0;
-}
-EXPORT_SYMBOL(sport_config_rx_dma);
-
-unsigned long sport_curr_offset_tx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->tx_dma_chan);
-
- return (unsigned char *)curr - sport->tx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_tx);
-
-unsigned long sport_curr_offset_rx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->rx_dma_chan);
-
- return (unsigned char *)curr - sport->rx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_rx);
-
-static irqreturn_t sport_tx_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- static unsigned long status;
-
- status = get_dma_curr_irqstat(sport->tx_dma_chan);
- if (status & (DMA_DONE | DMA_ERR)) {
- clear_dma_irqstat(sport->tx_dma_chan);
- SSYNC();
- }
- if (sport->tx_callback)
- sport->tx_callback(sport->tx_data);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_rx_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- unsigned long status;
-
- status = get_dma_curr_irqstat(sport->rx_dma_chan);
- if (status & (DMA_DONE | DMA_ERR)) {
- clear_dma_irqstat(sport->rx_dma_chan);
- SSYNC();
- }
- if (sport->rx_callback)
- sport->rx_callback(sport->rx_data);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_err_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- struct device *dev = &sport->pdev->dev;
-
- if (sport->tx_regs->spctl & SPORT_CTL_DERRPRI)
- dev_err(dev, "sport error: TUVF\n");
- if (sport->rx_regs->spctl & SPORT_CTL_DERRPRI)
- dev_err(dev, "sport error: ROVF\n");
-
- return IRQ_HANDLED;
-}
-
-static int sport_get_resource(struct sport_device *sport)
-{
- struct platform_device *pdev = sport->pdev;
- struct device *dev = &pdev->dev;
- struct bfin_snd_platform_data *pdata = dev->platform_data;
- struct resource *res;
-
- if (!pdata) {
- dev_err(dev, "No platform data\n");
- return -ENODEV;
- }
- sport->pin_req = pdata->pin_req;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "No tx MEM resource\n");
- return -ENODEV;
- }
- sport->tx_regs = (struct sport_register *)res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(dev, "No rx MEM resource\n");
- return -ENODEV;
- }
- sport->rx_regs = (struct sport_register *)res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(dev, "No tx DMA resource\n");
- return -ENODEV;
- }
- sport->tx_dma_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(dev, "No rx DMA resource\n");
- return -ENODEV;
- }
- sport->rx_dma_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "No tx error irq resource\n");
- return -ENODEV;
- }
- sport->tx_err_irq = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!res) {
- dev_err(dev, "No rx error irq resource\n");
- return -ENODEV;
- }
- sport->rx_err_irq = res->start;
-
- return 0;
-}
-
-static int sport_request_resource(struct sport_device *sport)
-{
- struct device *dev = &sport->pdev->dev;
- int ret;
-
- ret = peripheral_request_list(sport->pin_req, "soc-audio");
- if (ret) {
- dev_err(dev, "Unable to request sport pin\n");
- return ret;
- }
-
- ret = request_dma(sport->tx_dma_chan, "SPORT TX Data");
- if (ret) {
- dev_err(dev, "Unable to allocate DMA channel for sport tx\n");
- goto err_tx_dma;
- }
- set_dma_callback(sport->tx_dma_chan, sport_tx_irq, sport);
-
- ret = request_dma(sport->rx_dma_chan, "SPORT RX Data");
- if (ret) {
- dev_err(dev, "Unable to allocate DMA channel for sport rx\n");
- goto err_rx_dma;
- }
- set_dma_callback(sport->rx_dma_chan, sport_rx_irq, sport);
-
- ret = request_irq(sport->tx_err_irq, sport_err_irq,
- 0, "SPORT TX ERROR", sport);
- if (ret) {
- dev_err(dev, "Unable to allocate tx error IRQ for sport\n");
- goto err_tx_irq;
- }
-
- ret = request_irq(sport->rx_err_irq, sport_err_irq,
- 0, "SPORT RX ERROR", sport);
- if (ret) {
- dev_err(dev, "Unable to allocate rx error IRQ for sport\n");
- goto err_rx_irq;
- }
-
- return 0;
-err_rx_irq:
- free_irq(sport->tx_err_irq, sport);
-err_tx_irq:
- free_dma(sport->rx_dma_chan);
-err_rx_dma:
- free_dma(sport->tx_dma_chan);
-err_tx_dma:
- peripheral_free_list(sport->pin_req);
- return ret;
-}
-
-static void sport_free_resource(struct sport_device *sport)
-{
- free_irq(sport->rx_err_irq, sport);
- free_irq(sport->tx_err_irq, sport);
- free_dma(sport->rx_dma_chan);
- free_dma(sport->tx_dma_chan);
- peripheral_free_list(sport->pin_req);
-}
-
-struct sport_device *sport_create(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct sport_device *sport;
- int ret;
-
- sport = kzalloc(sizeof(*sport), GFP_KERNEL);
- if (!sport)
- return NULL;
-
- sport->pdev = pdev;
-
- ret = sport_get_resource(sport);
- if (ret)
- goto free_data;
-
- ret = sport_request_resource(sport);
- if (ret)
- goto free_data;
-
- dev_dbg(dev, "SPORT create success\n");
- return sport;
-free_data:
- kfree(sport);
- return NULL;
-}
-EXPORT_SYMBOL(sport_create);
-
-void sport_delete(struct sport_device *sport)
-{
- if (sport->tx_desc)
- dma_free_coherent(NULL, sport->tx_desc_size,
- sport->tx_desc, 0);
- if (sport->rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_size,
- sport->rx_desc, 0);
- sport_free_resource(sport);
- kfree(sport);
-}
-EXPORT_SYMBOL(sport_delete);
-
-MODULE_DESCRIPTION("Analog Devices BF6XX SPORT driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf6xx-sport.h b/sound/soc/blackfin/bf6xx-sport.h
deleted file mode 100644
index 307d193cfcef..000000000000
--- a/sound/soc/blackfin/bf6xx-sport.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * bf6xx_sport - Analog Devices BF6XX SPORT driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 _BF6XX_SPORT_H_
-#define _BF6XX_SPORT_H_
-
-#include <linux/platform_device.h>
-#include <asm/bfin_sport3.h>
-
-struct sport_device {
- struct platform_device *pdev;
- const unsigned short *pin_req;
- struct sport_register *tx_regs;
- struct sport_register *rx_regs;
- int tx_dma_chan;
- int rx_dma_chan;
- int tx_err_irq;
- int rx_err_irq;
-
- void (*tx_callback)(void *data);
- void *tx_data;
- void (*rx_callback)(void *data);
- void *rx_data;
-
- struct dmasg *tx_desc;
- struct dmasg *rx_desc;
- unsigned int tx_desc_size;
- unsigned int rx_desc_size;
- unsigned char *tx_buf;
- unsigned char *rx_buf;
- unsigned int tx_fragsize;
- unsigned int rx_fragsize;
- unsigned int tx_frags;
- unsigned int rx_frags;
- unsigned int wdsize;
-};
-
-struct sport_params {
- u32 spctl;
- u32 div;
-};
-
-struct sport_device *sport_create(struct platform_device *pdev);
-void sport_delete(struct sport_device *sport);
-int sport_set_tx_params(struct sport_device *sport,
- struct sport_params *params);
-int sport_set_rx_params(struct sport_device *sport,
- struct sport_params *params);
-void sport_tx_start(struct sport_device *sport);
-void sport_rx_start(struct sport_device *sport);
-void sport_tx_stop(struct sport_device *sport);
-void sport_rx_stop(struct sport_device *sport);
-void sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data);
-void sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data);
-int sport_config_tx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize);
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize);
-unsigned long sport_curr_offset_tx(struct sport_device *sport);
-unsigned long sport_curr_offset_rx(struct sport_device *sport);
-
-
-
-#endif
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
deleted file mode 100644
index 64b88fdc1f6c..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1373 on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau1373.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Line In1", NULL),
- SND_SOC_DAPM_LINE("Line In2", NULL),
- SND_SOC_DAPM_LINE("Line In3", NULL),
- SND_SOC_DAPM_LINE("Line In4", NULL),
-
- SND_SOC_DAPM_LINE("Line Out1", NULL),
- SND_SOC_DAPM_LINE("Line Out2", NULL),
- SND_SOC_DAPM_LINE("Stereo Out", NULL),
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_HP("Earpiece", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
- { "AIN1L", NULL, "Line In1" },
- { "AIN1R", NULL, "Line In1" },
- { "AIN2L", NULL, "Line In2" },
- { "AIN2R", NULL, "Line In2" },
- { "AIN3L", NULL, "Line In3" },
- { "AIN3R", NULL, "Line In3" },
- { "AIN4L", NULL, "Line In4" },
- { "AIN4R", NULL, "Line In4" },
-
- /* MICBIAS can be connected via a jumper to the line-in jack, since w
- don't know which one is going to be used, just power both. */
- { "Line In1", NULL, "MICBIAS1" },
- { "Line In2", NULL, "MICBIAS1" },
- { "Line In3", NULL, "MICBIAS1" },
- { "Line In4", NULL, "MICBIAS1" },
- { "Line In1", NULL, "MICBIAS2" },
- { "Line In2", NULL, "MICBIAS2" },
- { "Line In3", NULL, "MICBIAS2" },
- { "Line In4", NULL, "MICBIAS2" },
-
- { "Line Out1", NULL, "LOUT1L" },
- { "Line Out1", NULL, "LOUT1R" },
- { "Line Out2", NULL, "LOUT2L" },
- { "Line Out2", NULL, "LOUT2R" },
- { "Headphone", NULL, "HPL" },
- { "Headphone", NULL, "HPR" },
- { "Earpiece", NULL, "EP" },
- { "Speaker", NULL, "SPKL" },
- { "Stereo Out", NULL, "SPKR" },
-};
-
-static int bfin_eval_adau1373_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;
- int ret;
- int pll_rate;
-
- switch (params_rate(params)) {
- case 48000:
- case 8000:
- case 12000:
- case 16000:
- case 24000:
- case 32000:
- pll_rate = 48000 * 1024;
- break;
- case 44100:
- case 7350:
- case 11025:
- case 14700:
- case 22050:
- case 29400:
- pll_rate = 44100 * 1024;
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
- ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int pll_rate = 48000 * 1024;
- int ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
- ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-static const struct snd_soc_ops bfin_eval_adau1373_ops = {
- .hw_params = bfin_eval_adau1373_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
- .name = "adau1373",
- .stream_name = "adau1373",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau1373-aif1",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1373.0-001a",
- .ops = &bfin_eval_adau1373_ops,
- .init = bfin_eval_adau1373_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1373 = {
- .name = "bfin-eval-adau1373",
- .owner = THIS_MODULE,
- .dai_link = &bfin_eval_adau1373_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1373_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
- .dapm_routes = bfin_eval_adau1373_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
-};
-
-static int bfin_eval_adau1373_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adau1373;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
-}
-
-static struct platform_driver bfin_eval_adau1373_driver = {
- .driver = {
- .name = "bfin-eval-adau1373",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1373_probe,
-};
-
-module_platform_driver(bfin_eval_adau1373_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1373");
diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c
deleted file mode 100644
index 5c67f72cf9a9..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1701.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau1701.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = {
- { "Speaker", NULL, "OUT0" },
- { "Speaker", NULL, "OUT1" },
- { "Line Out", NULL, "OUT2" },
- { "Line Out", NULL, "OUT3" },
-
- { "IN0", NULL, "Line In" },
- { "IN1", NULL, "Line In" },
-};
-
-static int bfin_eval_adau1701_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;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static struct snd_soc_ops bfin_eval_adau1701_ops = {
- .hw_params = bfin_eval_adau1701_hw_params,
-};
-
-#define BFIN_EVAL_ADAU1701_DAI_FMT (SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = {
- {
- .name = "adau1701",
- .stream_name = "adau1701",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau1701",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1701.0-0034",
- .ops = &bfin_eval_adau1701_ops,
- .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT,
- },
- {
- .name = "adau1701",
- .stream_name = "adau1701",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "adau1701",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1701.0-0034",
- .ops = &bfin_eval_adau1701_ops,
- .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT,
- },
-};
-
-static struct snd_soc_card bfin_eval_adau1701 = {
- .name = "bfin-eval-adau1701",
- .owner = THIS_MODULE,
- .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1701_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets),
- .dapm_routes = bfin_eval_adau1701_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes),
-};
-
-static int bfin_eval_adau1701_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adau1701;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
-}
-
-static struct platform_driver bfin_eval_adau1701_driver = {
- .driver = {
- .name = "bfin-eval-adau1701",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1701_probe,
-};
-
-module_platform_driver(bfin_eval_adau1701_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1701");
diff --git a/sound/soc/blackfin/bfin-eval-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c
deleted file mode 100644
index fddfe00c9d69..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1x61.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011-2014 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau17x1.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("In 1", NULL),
- SND_SOC_DAPM_LINE("In 2", NULL),
- SND_SOC_DAPM_LINE("In 3-4", NULL),
-
- SND_SOC_DAPM_LINE("Diff Out L", NULL),
- SND_SOC_DAPM_LINE("Diff Out R", NULL),
- SND_SOC_DAPM_LINE("Stereo Out", NULL),
- SND_SOC_DAPM_HP("Capless HP Out", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
- { "LAUX", NULL, "In 3-4" },
- { "RAUX", NULL, "In 3-4" },
- { "LINP", NULL, "In 1" },
- { "LINN", NULL, "In 1"},
- { "RINP", NULL, "In 2" },
- { "RINN", NULL, "In 2" },
-
- { "In 1", NULL, "MICBIAS" },
- { "In 2", NULL, "MICBIAS" },
-
- { "Capless HP Out", NULL, "LHP" },
- { "Capless HP Out", NULL, "RHP" },
- { "Diff Out L", NULL, "LOUT" },
- { "Diff Out R", NULL, "ROUT" },
- { "Stereo Out", NULL, "LOUT" },
- { "Stereo Out", NULL, "ROUT" },
-};
-
-static int bfin_eval_adau1x61_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;
- int pll_rate;
- int ret;
-
- 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;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
- ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
- .hw_params = bfin_eval_adau1x61_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
- .name = "adau1x61",
- .stream_name = "adau1x61",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1761.0-0038",
- .ops = &bfin_eval_adau1x61_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1x61 = {
- .name = "bfin-eval-adau1x61",
- .owner = THIS_MODULE,
- .driver_name = "eval-adau1x61",
- .dai_link = &bfin_eval_adau1x61_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1x61_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
- .dapm_routes = bfin_eval_adau1x61_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
- .fully_routed = true,
-};
-
-static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
-{
- bfin_eval_adau1x61.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61);
-}
-
-static struct platform_driver bfin_eval_adau1x61_driver = {
- .driver = {
- .name = "bfin-eval-adau1x61",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1x61_probe,
-};
-module_platform_driver(bfin_eval_adau1x61_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1x61");
diff --git a/sound/soc/blackfin/bfin-eval-adau1x81.c b/sound/soc/blackfin/bfin-eval-adau1x81.c
deleted file mode 100644
index 3e01cbe53fc7..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1x81.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1x81 on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011-2014 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau17x1.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Stereo In", NULL),
- SND_SOC_DAPM_LINE("Beep", NULL),
-
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_HP("Headphone", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = {
- { "BEEP", NULL, "Beep" },
- { "LMIC", NULL, "Stereo In" },
- { "LMIC", NULL, "Stereo In" },
-
- { "Headphone", NULL, "AOUTL" },
- { "Headphone", NULL, "AOUTR" },
- { "Speaker", NULL, "SP" },
-};
-
-static int bfin_eval_adau1x81_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;
- int pll_rate;
- int ret;
-
- 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;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
- ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static const struct snd_soc_ops bfin_eval_adau1x81_ops = {
- .hw_params = bfin_eval_adau1x81_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1x81_dai = {
- .name = "adau1x81",
- .stream_name = "adau1x81",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1781.0-0038",
- .ops = &bfin_eval_adau1x81_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1x81 = {
- .name = "bfin-eval-adau1x81",
- .driver_name = "eval-adau1x81",
- .dai_link = &bfin_eval_adau1x81_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1x81_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets),
- .dapm_routes = bfin_eval_adau1x81_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes),
- .fully_routed = true,
-};
-
-static int bfin_eval_adau1x81_probe(struct platform_device *pdev)
-{
- bfin_eval_adau1x81.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x81);
-}
-
-static struct platform_driver bfin_eval_adau1x81_driver = {
- .driver = {
- .name = "bfin-eval-adau1x81",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1x81_probe,
-};
-module_platform_driver(bfin_eval_adau1x81_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1x81");
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
deleted file mode 100644
index 99e5ecabdcda..000000000000
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include "../codecs/adav80x.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = {
- { "Line Out", NULL, "VOUTL" },
- { "Line Out", NULL, "VOUTR" },
-
- { "VINL", NULL, "Line In" },
- { "VINR", NULL, "Line In" },
-};
-
-static int bfin_eval_adav80x_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;
- int ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL,
- 27000000, params_rate(params) * 256);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1,
- params_rate(params) * 256, SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0,
- SND_SOC_CLOCK_OUT);
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0,
- SND_SOC_CLOCK_OUT);
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0,
- SND_SOC_CLOCK_OUT);
-
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0);
-
- return 0;
-}
-
-static const struct snd_soc_ops bfin_eval_adav80x_ops = {
- .hw_params = bfin_eval_adav80x_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = {
- {
- .name = "adav80x",
- .stream_name = "ADAV80x HiFi",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adav80x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .init = bfin_eval_adav80x_codec_init,
- .ops = &bfin_eval_adav80x_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- },
-};
-
-static struct snd_soc_card bfin_eval_adav80x = {
- .name = "bfin-eval-adav80x",
- .owner = THIS_MODULE,
- .dai_link = bfin_eval_adav80x_dais,
- .num_links = ARRAY_SIZE(bfin_eval_adav80x_dais),
-
- .dapm_widgets = bfin_eval_adav80x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets),
- .dapm_routes = bfin_eval_adav80x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes),
-};
-
-enum bfin_eval_adav80x_type {
- BFIN_EVAL_ADAV801,
- BFIN_EVAL_ADAV803,
-};
-
-static int bfin_eval_adav80x_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adav80x;
- const char *codec_name;
-
- switch (platform_get_device_id(pdev)->driver_data) {
- case BFIN_EVAL_ADAV801:
- codec_name = "spi0.1";
- break;
- case BFIN_EVAL_ADAV803:
- codec_name = "adav803.0-0034";
- break;
- default:
- return -EINVAL;
- }
-
- bfin_eval_adav80x_dais[0].codec_name = codec_name;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
-}
-
-static const struct platform_device_id bfin_eval_adav80x_ids[] = {
- { "bfin-eval-adav801", BFIN_EVAL_ADAV801 },
- { "bfin-eval-adav803", BFIN_EVAL_ADAV803 },
- { },
-};
-MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids);
-
-static struct platform_driver bfin_eval_adav80x_driver = {
- .driver = {
- .name = "bfin-eval-adav80x",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adav80x_probe,
- .id_table = bfin_eval_adav80x_ids,
-};
-
-module_platform_driver(bfin_eval_adav80x_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index c7cd60f009e9..31475e64e7dd 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Cirrus Logic"
+
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX || COMPILE_TEST
@@ -7,37 +10,27 @@ config SND_EP93XX_SOC
the EP93xx I2S or AC97 interfaces.
config SND_EP93XX_SOC_I2S
- tristate
-
-config SND_EP93XX_SOC_AC97
- tristate
- select AC97_BUS
- select SND_SOC_AC97_BUS
+ 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.
-config SND_EP93XX_SOC_SNAPPERCL15
- tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
- depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 && I2C
- select SND_EP93XX_SOC_I2S
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y or M here if you want to add support for I2S audio on the
- Bluewater Systems Snapper CL15 module.
+if SND_EP93XX_SOC_I2S
-config SND_EP93XX_SOC_SIMONE
- tristate "SoC Audio support for Simplemachines Sim.One board"
- depends on SND_EP93XX_SOC && MACH_SIM_ONE
- select SND_EP93XX_SOC_AC97
- select SND_SOC_AC97_CODEC
+config SND_EP93XX_SOC_I2S_WATCHDOG
+ bool "IRQ based underflow watchdog workaround"
+ default y
help
- Say Y or M here if you want to add support for AC97 audio on the
- Simplemachines Sim.One board.
+ 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.
-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.
+ If you are unsure how to answer this question, answer Y.
+
+endif # if SND_EP93XX_SOC_I2S
+
+endmenu
diff --git a/sound/soc/cirrus/Makefile b/sound/soc/cirrus/Makefile
index bfb8dc409f53..61d8cf64e859 100644
--- a/sound/soc/cirrus/Makefile
+++ b/sound/soc/cirrus/Makefile
@@ -1,18 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# EP93xx Platform Support
-snd-soc-ep93xx-objs := ep93xx-pcm.o
-snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
-snd-soc-ep93xx-ac97-objs := ep93xx-ac97.o
+snd-soc-ep93xx-y := ep93xx-pcm.o
+snd-soc-ep93xx-i2s-y := ep93xx-i2s.o
obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
-obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
-# EP93XX Machine Support
-snd-soc-snappercl15-objs := snappercl15.o
-snd-soc-simone-objs := simone.o
-snd-soc-edb93xx-objs := edb93xx.o
-
-obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
-obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.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
deleted file mode 100644
index c53bd6f2c2d7..000000000000
--- a/sound/soc/cirrus/edb93xx.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * SoC audio for EDB93xx
- *
- * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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 <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-static int edb93xx_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;
- 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,
-};
-
-static struct snd_soc_dai_link edb93xx_dai = {
- .name = "CS4271",
- .stream_name = "CS4271 HiFi",
- .platform_name = "ep93xx-i2s",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_name = "spi0.0",
- .codec_dai_name = "cs4271-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &edb93xx_ops,
-};
-
-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 int edb93xx_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver edb93xx_driver = {
- .driver = {
- .name = "edb93xx-audio",
- },
- .probe = edb93xx_probe,
- .remove = 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/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
deleted file mode 100644
index cd5a939ad608..000000000000
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * ASoC driver for Cirrus Logic EP93xx AC97 controller.
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
- *
- * 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/delay.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/dmaengine_pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/soc.h>
-
-#include <linux/platform_data/dma-ep93xx.h>
-
-#include "ep93xx-pcm.h"
-
-/*
- * Per channel (1-4) registers.
- */
-#define AC97CH(n) (((n) - 1) * 0x20)
-
-#define AC97DR(n) (AC97CH(n) + 0x0000)
-
-#define AC97RXCR(n) (AC97CH(n) + 0x0004)
-#define AC97RXCR_REN BIT(0)
-#define AC97RXCR_RX3 BIT(3)
-#define AC97RXCR_RX4 BIT(4)
-#define AC97RXCR_CM BIT(15)
-
-#define AC97TXCR(n) (AC97CH(n) + 0x0008)
-#define AC97TXCR_TEN BIT(0)
-#define AC97TXCR_TX3 BIT(3)
-#define AC97TXCR_TX4 BIT(4)
-#define AC97TXCR_CM BIT(15)
-
-#define AC97SR(n) (AC97CH(n) + 0x000c)
-#define AC97SR_TXFE BIT(1)
-#define AC97SR_TXUE BIT(6)
-
-#define AC97RISR(n) (AC97CH(n) + 0x0010)
-#define AC97ISR(n) (AC97CH(n) + 0x0014)
-#define AC97IE(n) (AC97CH(n) + 0x0018)
-
-/*
- * Global AC97 controller registers.
- */
-#define AC97S1DATA 0x0080
-#define AC97S2DATA 0x0084
-#define AC97S12DATA 0x0088
-
-#define AC97RGIS 0x008c
-#define AC97GIS 0x0090
-#define AC97IM 0x0094
-/*
- * Common bits for RGIS, GIS and IM registers.
- */
-#define AC97_SLOT2RXVALID BIT(1)
-#define AC97_CODECREADY BIT(5)
-#define AC97_SLOT2TXCOMPLETE BIT(6)
-
-#define AC97EOI 0x0098
-#define AC97EOI_WINT BIT(0)
-#define AC97EOI_CODECREADY BIT(1)
-
-#define AC97GCR 0x009c
-#define AC97GCR_AC97IFE BIT(0)
-
-#define AC97RESET 0x00a0
-#define AC97RESET_TIMEDRESET BIT(0)
-
-#define AC97SYNC 0x00a4
-#define AC97SYNC_TIMEDSYNC BIT(0)
-
-#define AC97_TIMEOUT msecs_to_jiffies(5)
-
-/**
- * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
- * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
- * @dev: pointer to the platform device dev structure
- * @regs: mapped AC97 controller registers
- * @done: bus ops wait here for an interrupt
- */
-struct ep93xx_ac97_info {
- struct mutex lock;
- struct device *dev;
- void __iomem *regs;
- struct completion done;
- struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct snd_dmaengine_dai_dma_data dma_params_tx;
-};
-
-/* currently ALSA only supports a single AC97 device */
-static struct ep93xx_ac97_info *ep93xx_ac97_info;
-
-static struct ep93xx_dma_data ep93xx_ac97_pcm_out = {
- .name = "ac97-pcm-out",
- .port = EP93XX_DMA_AAC1,
- .direction = DMA_MEM_TO_DEV,
-};
-
-static struct ep93xx_dma_data ep93xx_ac97_pcm_in = {
- .name = "ac97-pcm-in",
- .port = EP93XX_DMA_AAC1,
- .direction = DMA_DEV_TO_MEM,
-};
-
-static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
- unsigned reg)
-{
- return __raw_readl(info->regs + reg);
-}
-
-static inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
- unsigned reg, unsigned val)
-{
- __raw_writel(val, info->regs + reg);
-}
-
-static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
- unsigned short val;
-
- mutex_lock(&info->lock);
-
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
- dev_warn(info->dev, "timeout reading register %x\n", reg);
- mutex_unlock(&info->lock);
- return -ETIMEDOUT;
- }
- val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
-
- mutex_unlock(&info->lock);
- return val;
-}
-
-static void ep93xx_ac97_write(struct snd_ac97 *ac97,
- unsigned short reg,
- unsigned short val)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * Writes to the codec need to be done so that slot 2 is filled in
- * before slot 1.
- */
- ep93xx_ac97_write_reg(info, AC97S2DATA, val);
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
-
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "timeout writing register %x\n", reg);
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * We are assuming that before this functions gets called, the codec
- * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
- * control the SYNC signal directly via AC97SYNC register. Using
- * TIMEDSYNC the controller will keep the SYNC high > 1us.
- */
- ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec warm reset timeout\n");
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * For doing cold reset, we disable the AC97 controller interface, clear
- * WINT and CODECREADY bits, and finally enable the interface again.
- */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
- ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
- ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
-
- /*
- * Now, assert the reset and wait for the codec to become ready.
- */
- ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec cold reset timeout\n");
-
- /*
- * Give the codec some time to come fully out from the reset. This way
- * we ensure that the subsequent reads/writes will work.
- */
- usleep_range(15000, 20000);
-
- mutex_unlock(&info->lock);
-}
-
-static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
-{
- struct ep93xx_ac97_info *info = dev_id;
- unsigned status, mask;
-
- /*
- * Just mask out the interrupt and wake up the waiting thread.
- * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
- * the waiting thread.
- */
- status = ep93xx_ac97_read_reg(info, AC97GIS);
- mask = ep93xx_ac97_read_reg(info, AC97IM);
- mask &= ~status;
- ep93xx_ac97_write_reg(info, AC97IM, mask);
-
- complete(&info->done);
- return IRQ_HANDLED;
-}
-
-static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
- .read = ep93xx_ac97_read,
- .write = ep93xx_ac97_write,
- .reset = ep93xx_ac97_cold_reset,
- .warm_reset = ep93xx_ac97_warm_reset,
-};
-
-static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
- unsigned v = 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) {
- /*
- * Enable compact mode, TX slots 3 & 4, and the TX FIFO
- * itself.
- */
- v |= AC97TXCR_CM;
- v |= AC97TXCR_TX3 | AC97TXCR_TX4;
- v |= AC97TXCR_TEN;
- ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
- } else {
- /*
- * Enable compact mode, RX slots 3 & 4, and the RX FIFO
- * itself.
- */
- v |= AC97RXCR_CM;
- v |= AC97RXCR_RX3 | AC97RXCR_RX4;
- v |= AC97RXCR_REN;
- ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
- }
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /*
- * As per Cirrus EP93xx errata described below:
- *
- * http://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
- *
- * we will wait for the TX FIFO to be empty before
- * clearing the TEN bit.
- */
- unsigned long timeout = jiffies + AC97_TIMEOUT;
-
- do {
- v = ep93xx_ac97_read_reg(info, AC97SR(1));
- if (time_after(jiffies, timeout)) {
- dev_warn(info->dev, "TX timeout\n");
- break;
- }
- } while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
-
- /* disable the TX FIFO */
- ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
- } else {
- /* disable the RX FIFO */
- ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
- }
- break;
-
- default:
- dev_warn(info->dev, "unknown command %d\n", cmd);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
-{
- struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
-
- info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out;
- info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in;
-
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
- .trigger = ep93xx_ac97_trigger,
-};
-
-static struct snd_soc_dai_driver ep93xx_ac97_dai = {
- .name = "ep93xx-ac97",
- .id = 0,
- .bus_control = true,
- .probe = ep93xx_ac97_dai_probe,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &ep93xx_ac97_dai_ops,
-};
-
-static const struct snd_soc_component_driver ep93xx_ac97_component = {
- .name = "ep93xx-ac97",
-};
-
-static int ep93xx_ac97_probe(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info;
- struct resource *res;
- int irq;
- int ret;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(info->regs))
- return PTR_ERR(info->regs);
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return irq < 0 ? irq : -ENODEV;
-
- ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
- IRQF_TRIGGER_HIGH, pdev->name, info);
- if (ret)
- goto fail;
-
- dev_set_drvdata(&pdev->dev, info);
-
- mutex_init(&info->lock);
- init_completion(&info->done);
- info->dev = &pdev->dev;
-
- ep93xx_ac97_info = info;
- platform_set_drvdata(pdev, info);
-
- ret = snd_soc_set_ac97_ops(&ep93xx_ac97_ops);
- if (ret)
- goto fail;
-
- ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
- &ep93xx_ac97_dai, 1);
- if (ret)
- goto fail;
-
- ret = devm_ep93xx_pcm_platform_register(&pdev->dev);
- if (ret)
- goto fail_unregister;
-
- return 0;
-
-fail_unregister:
- snd_soc_unregister_component(&pdev->dev);
-fail:
- ep93xx_ac97_info = NULL;
- snd_soc_set_ac97_ops(NULL);
- return ret;
-}
-
-static int ep93xx_ac97_remove(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
-
- /* disable the AC97 controller */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
-
- ep93xx_ac97_info = NULL;
-
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver ep93xx_ac97_driver = {
- .probe = ep93xx_ac97_probe,
- .remove = ep93xx_ac97_remove,
- .driver = {
- .name = "ep93xx-ac97",
- },
-};
-
-module_platform_driver(ep93xx_ac97_driver);
-
-MODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-ac97");
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 934f8aefdd90..cca01c03f048 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/soc/ep93xx-i2s.c
* EP93xx I2S driver
@@ -7,11 +8,6 @@
* 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,6 +15,7 @@
#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>
@@ -27,16 +24,18 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.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
@@ -51,7 +50,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 */
@@ -59,6 +68,8 @@
#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;
@@ -68,19 +79,6 @@ struct ep93xx_i2s_info {
struct snd_dmaengine_dai_dma_data dma_params_tx;
};
-static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = {
- .name = "i2s-pcm-out",
- .port = EP93XX_DMA_I2S1,
- .direction = DMA_MEM_TO_DEV,
- },
- [SNDRV_PCM_STREAM_CAPTURE] = {
- .name = "i2s-pcm-in",
- .port = EP93XX_DMA_I2S1,
- .direction = DMA_DEV_TO_MEM,
- },
-};
-
static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
unsigned reg, unsigned val)
{
@@ -96,40 +94,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) {
@@ -137,23 +143,59 @@ 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);
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
+ return 0;
+}
+
+static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ ep93xx_i2s_enable(info, substream->stream);
return 0;
}
@@ -170,39 +212,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;
@@ -213,32 +255,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;
}
@@ -298,7 +340,6 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
if (err)
return err;
- ep93xx_i2s_enable(info, substream->stream);
return 0;
}
@@ -309,16 +350,18 @@ 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)
+ if (!snd_soc_component_active(component))
return 0;
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
@@ -327,11 +370,11 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
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)
+ if (!snd_soc_component_active(component))
return 0;
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
@@ -345,6 +388,8 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
#endif
static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+ .probe = ep93xx_i2s_dai_probe,
+ .startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
.set_sysclk = ep93xx_i2s_set_sysclk,
@@ -354,10 +399,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
- .symmetric_rates= 1,
- .probe = ep93xx_i2s_dai_probe,
- .suspend = ep93xx_i2s_suspend,
- .resume = ep93xx_i2s_resume,
+ .symmetric_rate = 1,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -374,24 +416,36 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
};
static const struct snd_soc_component_driver ep93xx_i2s_component = {
- .name = "ep93xx-i2s",
+ .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 = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, res);
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) {
+ int irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq < 0 ? irq : -ENODEV;
+
+ 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);
@@ -412,19 +466,17 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, info);
- err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
+ 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_unregister;
+ goto fail_put_lrclk;
return 0;
-fail_unregister:
- snd_soc_unregister_component(&pdev->dev);
fail_put_lrclk:
clk_put(info->lrclk);
fail_put_sclk:
@@ -435,22 +487,27 @@ fail:
return err;
}
-static int 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_component(&pdev->dev);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
- 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 = ep93xx_i2s_remove,
+ .remove = ep93xx_i2s_remove,
.driver = {
.name = "ep93xx-i2s",
+ .of_match_table = ep93xx_i2s_of_ids,
},
};
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 67a73330db5e..5ecb4671cbba 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
*
@@ -6,10 +7,6 @@
*
* Rewritten for the SoC audio subsystem (Based on PXA2xx code):
* Copyright (c) 2008 Ryan Mallon
- *
- * 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>
@@ -21,8 +18,6 @@
#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 = {
@@ -38,30 +33,15 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.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);
+ &ep93xx_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL_GPL(devm_ep93xx_pcm_platform_register);
diff --git a/sound/soc/cirrus/ep93xx-pcm.h b/sound/soc/cirrus/ep93xx-pcm.h
index b7a12a2fae9c..8e1c722bffe0 100644
--- a/sound/soc/cirrus/ep93xx-pcm.h
+++ b/sound/soc/cirrus/ep93xx-pcm.h
@@ -1,17 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/>.
*/
#ifndef __EP93XX_PCM_H__
diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c
deleted file mode 100644
index 1ec661834e5a..000000000000
--- a/sound/soc/cirrus/simone.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * simone.c -- ASoC audio for Simplemachines Sim.One board
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on snappercl15 machine driver by Ryan Mallon.
- *
- * 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 <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-static struct snd_soc_dai_link simone_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "ep93xx-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "ep93xx-ac97",
-};
-
-static struct snd_soc_card snd_soc_simone = {
- .name = "Sim.One",
- .owner = THIS_MODULE,
- .dai_link = &simone_dai,
- .num_links = 1,
-};
-
-static struct platform_device *simone_snd_ac97_device;
-
-static int simone_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_simone;
- int ret;
-
- simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
- -1, NULL, 0);
- if (IS_ERR(simone_snd_ac97_device))
- return PTR_ERR(simone_snd_ac97_device);
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- platform_device_unregister(simone_snd_ac97_device);
- }
-
- return ret;
-}
-
-static int simone_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- platform_device_unregister(simone_snd_ac97_device);
-
- return 0;
-}
-
-static struct platform_driver simone_driver = {
- .driver = {
- .name = "simone-audio",
- },
- .probe = simone_probe,
- .remove = simone_remove,
-};
-
-module_platform_driver(simone_driver);
-
-MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:simone-audio");
diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
deleted file mode 100644
index 2334ec19e7eb..000000000000
--- a/sound/soc/cirrus/snappercl15.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
- *
- * Copyright (C) 2008 Bluewater Systems Ltd
- * Author: Ryan Mallon
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-#include "../codecs/tlv320aic23.h"
-
-#define CODEC_CLOCK 5644800
-
-static int snappercl15_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;
-
- err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_IN);
- if (err)
- return err;
-
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_OUT);
- if (err)
- return err;
-
- return 0;
-}
-
-static const struct snd_soc_ops snappercl15_ops = {
- .hw_params = snappercl15_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 struct snd_soc_dai_link snappercl15_dai = {
- .name = "tlv320aic23",
- .stream_name = "AIC23",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_dai_name = "tlv320aic23-hifi",
- .codec_name = "tlv320aic23-codec.0-001a",
- .platform_name = "ep93xx-i2s",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &snappercl15_ops,
-};
-
-static struct snd_soc_card snd_soc_snappercl15 = {
- .name = "Snapper CL15",
- .owner = THIS_MODULE,
- .dai_link = &snappercl15_dai,
- .num_links = 1,
-
- .dapm_widgets = tlv320aic23_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int snappercl15_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_snappercl15;
- 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 int snappercl15_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver snappercl15_driver = {
- .driver = {
- .name = "snappercl15-audio",
- },
- .probe = snappercl15_probe,
- .remove = snappercl15_remove,
-};
-
-module_platform_driver(snappercl15_driver);
-
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:snappercl15-audio");
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index be8ea723dff9..be01f0928393 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>
@@ -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+1];
+ unsigned char name[4][MAX_NAME_LEN];
};
/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -272,15 +269,15 @@ static int snd_soc_get_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_soc_kcontrol_codec(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]))
@@ -296,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_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int err;
@@ -308,18 +305,18 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
return -EINVAL;
- err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
+ 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;
}
@@ -329,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_soc_kcontrol_codec(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;
@@ -349,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_soc_kcontrol_codec(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;
@@ -365,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;
}
@@ -382,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 = snd_soc_dapm_to_codec(w->dapm);
+ 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
@@ -390,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;
}
@@ -399,41 +396,41 @@ 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 = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int dac = 0;
int data;
- if (!strcmp(w->name, "Left DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "Left DAC"))
dac = DAC_LEFT;
- if (!strcmp(w->name, "Right DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "Right DAC"))
dac = DAC_RIGHT;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
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;
}
@@ -532,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);
@@ -552,17 +545,6 @@ 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 SOC_ENUM_SINGLE_DECL(aif1_enum,
- PM860X_PCM_IFACE_3, 6, 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",
@@ -920,15 +902,15 @@ static const struct snd_soc_dapm_route pm860x_dapm_routes[] = {
* 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;
}
@@ -937,7 +919,7 @@ 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 */
@@ -952,7 +934,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
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)) {
@@ -971,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;
}
@@ -979,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;
@@ -1012,15 +994,15 @@ 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;
@@ -1034,7 +1016,7 @@ 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 */
@@ -1048,7 +1030,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
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)) {
@@ -1076,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;
}
@@ -1084,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
@@ -1116,14 +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_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
switch (level) {
@@ -1134,7 +1116,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == 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);
@@ -1154,17 +1136,19 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
}
static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+ .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 const struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+ .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,7 +1200,7 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
},
};
-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;
@@ -1230,7 +1214,7 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
#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->codec->dev));
+ trace_snd_soc_jack_irq(dev_name(pm860x->component->dev));
#endif
if ((pm860x->det.hp_det & SND_JACK_HEADPHONE)
@@ -1256,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;
@@ -1290,15 +1274,15 @@ int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
}
/* 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;
@@ -1308,25 +1292,25 @@ int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
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;
- snd_soc_codec_init_regmap(codec, pm860x->regmap);
+ 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");
+ dev_err(component->dev, "Failed to request IRQ!\n");
goto out;
}
}
@@ -1339,29 +1323,28 @@ out:
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);
- return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_pm860x = {
- .probe = pm860x_probe,
- .remove = pm860x_remove,
- .set_bias_level = pm860x_set_bias_level,
-
- .component_driver = {
- .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),
- },
+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 pm860x_codec_probe(struct platform_device *pdev)
@@ -1390,30 +1373,24 @@ static int pm860x_codec_probe(struct platform_device *pdev)
return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
- strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
+ strscpy(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");
+ dev_err(&pdev->dev, "Failed to register component\n");
return -EINVAL;
}
return ret;
}
-static int pm860x_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver pm860x_codec_driver = {
.driver = {
.name = "88pm860x-codec",
},
.probe = pm860x_codec_probe,
- .remove = pm860x_codec_remove,
};
module_platform_driver(pm860x_codec_driver);
diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h
index f7282f4f4a79..f025146e506c 100644
--- a/sound/soc/codecs/88pm860x-codec.h
+++ b/sound/soc/codecs/88pm860x-codec.h
@@ -1,12 +1,9 @@
+/* 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
@@ -88,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 2b331f7266ab..160c07699a8b 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
@@ -13,227 +14,368 @@ menu "CODEC drivers"
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
depends on COMPILE_TEST
- select SND_SOC_88PM860X if MFD_88PM860X
- select SND_SOC_L3
- select SND_SOC_AB8500_CODEC if ABX500_CORE
- select SND_SOC_AC97_CODEC
- select SND_SOC_AD1836 if SPI_MASTER
- select SND_SOC_AD193X_SPI if SPI_MASTER
- select SND_SOC_AD193X_I2C if I2C
- select SND_SOC_AD1980 if SND_SOC_AC97_BUS
- select SND_SOC_AD73311
- select SND_SOC_ADAU1373 if I2C
- select SND_SOC_ADAU1761_I2C if I2C
- select SND_SOC_ADAU1761_SPI if SPI
- select SND_SOC_ADAU1781_I2C if I2C
- select SND_SOC_ADAU1781_SPI if SPI
- select SND_SOC_ADAV801 if SPI_MASTER
- select SND_SOC_ADAV803 if I2C
- select SND_SOC_ADAU1977_SPI if SPI_MASTER
- select SND_SOC_ADAU1977_I2C if I2C
- select SND_SOC_ADAU1701 if I2C
- select SND_SOC_ADAU7002
- select SND_SOC_ADS117X
- select SND_SOC_AK4104 if SPI_MASTER
- select SND_SOC_AK4535 if I2C
- select SND_SOC_AK4554
- select SND_SOC_AK4613 if I2C
- select SND_SOC_AK4641 if I2C
- select SND_SOC_AK4642 if I2C
- select SND_SOC_AK4671 if I2C
- select SND_SOC_AK5386
- select SND_SOC_ALC5623 if I2C
- select SND_SOC_ALC5632 if I2C
- select SND_SOC_BT_SCO
- select SND_SOC_CQ0093VC
- select SND_SOC_CS35L32 if I2C
- select SND_SOC_CS35L33 if I2C
- select SND_SOC_CS35L34 if I2C
- select SND_SOC_CS35L35 if I2C
- select SND_SOC_CS42L42 if I2C
- select SND_SOC_CS42L51_I2C if I2C
- select SND_SOC_CS42L52 if I2C && INPUT
- select SND_SOC_CS42L56 if I2C && INPUT
- select SND_SOC_CS42L73 if I2C
- select SND_SOC_CS4265 if I2C
- select SND_SOC_CS4270 if I2C
- select SND_SOC_CS4271_I2C if I2C
- select SND_SOC_CS4271_SPI if SPI_MASTER
- select SND_SOC_CS42XX8_I2C if I2C
- select SND_SOC_CS43130 if I2C
- select SND_SOC_CS4349 if I2C
- select SND_SOC_CS47L24 if MFD_CS47L24
- select SND_SOC_CS53L30 if I2C
- select SND_SOC_CX20442 if TTY
- select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
- select SND_SOC_DA7213 if I2C
- select SND_SOC_DA7218 if I2C
- select SND_SOC_DA7219 if I2C
- select SND_SOC_DA732X if I2C
- select SND_SOC_DA9055 if I2C
- select SND_SOC_DIO2125
- select SND_SOC_DMIC if GPIOLIB
- select SND_SOC_ES8316 if I2C
- select SND_SOC_ES8328_SPI if SPI_MASTER
- select SND_SOC_ES8328_I2C if I2C
- select SND_SOC_ES7134
- select SND_SOC_GTM601
- select SND_SOC_HDAC_HDMI
- select SND_SOC_ICS43432
- select SND_SOC_INNO_RK3036
- select SND_SOC_ISABELLE if I2C
- select SND_SOC_JZ4740_CODEC
- select SND_SOC_LM4857 if I2C
- select SND_SOC_LM49453 if I2C
- select SND_SOC_MAX98088 if I2C
- select SND_SOC_MAX98090 if I2C
- select SND_SOC_MAX98095 if I2C
- select SND_SOC_MAX98357A if GPIOLIB
- select SND_SOC_MAX98371 if I2C
- select SND_SOC_MAX98504 if I2C
- select SND_SOC_MAX9867 if I2C
- select SND_SOC_MAX98925 if I2C
- select SND_SOC_MAX98926 if I2C
- select SND_SOC_MAX98927 if I2C
- select SND_SOC_MAX98373 if I2C
- select SND_SOC_MAX9850 if I2C
- select SND_SOC_MAX9860 if I2C
- select SND_SOC_MAX9768 if I2C
- select SND_SOC_MAX9877 if I2C
- select SND_SOC_MC13783 if MFD_MC13XXX
- select SND_SOC_ML26124 if I2C
- select SND_SOC_NAU8540 if I2C
- select SND_SOC_NAU8810 if I2C
- select SND_SOC_NAU8824 if I2C
- select SND_SOC_NAU8825 if I2C
- select SND_SOC_HDMI_CODEC
- select SND_SOC_PCM1681 if I2C
- select SND_SOC_PCM179X_I2C if I2C
- select SND_SOC_PCM179X_SPI if SPI_MASTER
- select SND_SOC_PCM186X_I2C if I2C
- select SND_SOC_PCM186X_SPI if SPI_MASTER
- select SND_SOC_PCM3008
- select SND_SOC_PCM3168A_I2C if I2C
- select SND_SOC_PCM3168A_SPI if SPI_MASTER
- select SND_SOC_PCM5102A
- select SND_SOC_PCM512x_I2C if I2C
- select SND_SOC_PCM512x_SPI if SPI_MASTER
- select SND_SOC_RT274 if I2C
- select SND_SOC_RT286 if I2C
- select SND_SOC_RT298 if I2C
- select SND_SOC_RT5514 if I2C
- select SND_SOC_RT5616 if I2C
- select SND_SOC_RT5631 if I2C
- select SND_SOC_RT5640 if I2C
- select SND_SOC_RT5645 if I2C
- select SND_SOC_RT5651 if I2C
- select SND_SOC_RT5659 if I2C
- select SND_SOC_RT5660 if I2C
- select SND_SOC_RT5663 if I2C
- select SND_SOC_RT5665 if I2C
- select SND_SOC_RT5670 if I2C
- select SND_SOC_RT5677 if I2C && SPI_MASTER
- select SND_SOC_SGTL5000 if I2C
- select SND_SOC_SI476X if MFD_SI476X_CORE
- select SND_SOC_SIRF_AUDIO_CODEC
- select SND_SOC_SPDIF
- select SND_SOC_SSM2518 if I2C
- select SND_SOC_SSM2602_SPI if SPI_MASTER
- select SND_SOC_SSM2602_I2C if I2C
- select SND_SOC_SSM4567 if I2C
- select SND_SOC_STA32X if I2C
- select SND_SOC_STA350 if I2C
- select SND_SOC_STA529 if I2C
- select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
- select SND_SOC_STI_SAS
- select SND_SOC_TAS2552 if I2C
- select SND_SOC_TAS5086 if I2C
- select SND_SOC_TAS571X if I2C
- select SND_SOC_TAS5720 if I2C
- select SND_SOC_TAS6424 if I2C
- select SND_SOC_TFA9879 if I2C
- select SND_SOC_TLV320AIC23_I2C if I2C
- select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
- select SND_SOC_TLV320AIC26 if SPI_MASTER
- select SND_SOC_TLV320AIC31XX if I2C
- select SND_SOC_TLV320AIC32X4_I2C if I2C
- select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
- select SND_SOC_TLV320AIC3X if I2C
- select SND_SOC_TPA6130A2 if I2C
- select SND_SOC_TLV320DAC33 if I2C
- select SND_SOC_TSCS42XX if I2C
- select SND_SOC_TS3A227E if I2C
- select SND_SOC_TWL4030 if TWL4030_CORE
- select SND_SOC_TWL6040 if TWL6040_CORE
- select SND_SOC_UDA134X
- select SND_SOC_UDA1380 if I2C
- select SND_SOC_WL1273 if MFD_WL1273_CORE
- select SND_SOC_WM0010 if SPI_MASTER
- select SND_SOC_WM1250_EV1 if I2C
- select SND_SOC_WM2000 if I2C
- select SND_SOC_WM2200 if I2C
- select SND_SOC_WM5100 if I2C
- select SND_SOC_WM5102 if MFD_WM5102
- select SND_SOC_WM5110 if MFD_WM5110
- 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_WM8524 if GPIOLIB
- 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_WM8737 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_WM8770 if SPI_MASTER
- select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8782
- select SND_SOC_WM8804_I2C if I2C
- select SND_SOC_WM8804_SPI if SPI_MASTER
- 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 && INPUT
- select SND_SOC_WM8971 if I2C
- select SND_SOC_WM8974 if I2C
- select SND_SOC_WM8978 if I2C
- select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
- 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_WM8991 if I2C
- select SND_SOC_WM8993 if I2C
- select SND_SOC_WM8994 if MFD_WM8994
- select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8996 if I2C
- select SND_SOC_WM8997 if MFD_WM8997
- select SND_SOC_WM8998 if MFD_WM8998
- select SND_SOC_WM9081 if I2C
- select SND_SOC_WM9090 if I2C
- select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
- select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
- select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
- 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.
+ imply SND_SOC_88PM860X
+ 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_AK4619
+ 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_AUDIO_IIO_AUX
+ imply SND_SOC_AW8738
+ imply SND_SOC_AW87390
+ imply SND_SOC_AW88395
+ imply SND_SOC_AW88081
+ imply SND_SOC_AW88166
+ imply SND_SOC_AW88261
+ imply SND_SOC_AW88399
+ imply SND_SOC_BT_SCO
+ imply SND_SOC_BD28623
+ imply SND_SOC_CHV3_CODEC
+ 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_CS40L50
+ imply SND_SOC_CS42L42
+ imply SND_SOC_CS42L42_SDW
+ imply SND_SOC_CS42L43
+ imply SND_SOC_CS42L43_SDW
+ imply SND_SOC_CS42L51_I2C
+ imply SND_SOC_CS42L52
+ imply SND_SOC_CS42L56
+ imply SND_SOC_CS42L73
+ imply SND_SOC_CS42L84
+ 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_CS48L32
+ imply SND_SOC_CS53L30
+ imply SND_SOC_CS530X_I2C
+ 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_ES8323
+ imply SND_SOC_ES8326
+ imply SND_SOC_ES8328_SPI
+ imply SND_SOC_ES8328_I2C
+ imply SND_SOC_ES8375
+ imply SND_SOC_ES8389
+ imply SND_SOC_ES7134
+ imply SND_SOC_ES7241
+ imply SND_SOC_FRAMER
+ imply SND_SOC_FS210X
+ 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_MAX98388
+ 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_MT6357
+ 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_PCM1754
+ 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_PCM6240
+ imply SND_SOC_PEB2466
+ imply SND_SOC_PM4125_SDW
+ imply SND_SOC_RK3308
+ 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_RT1017_SDCA_SDW
+ 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_RT721_SDCA_SDW
+ imply SND_SOC_RT722_SDCA_SDW
+ imply SND_SOC_RT1308_SDW
+ imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT1318
+ imply SND_SOC_RT1318_SDW
+ imply SND_SOC_RT1320_SDW
+ imply SND_SOC_RT9120
+ imply SND_SOC_RT9123
+ imply SND_SOC_RTQ9124
+ imply SND_SOC_RTQ9128
+ 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_SMA1307
+ 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_TAS2781_I2C
+ imply SND_SOC_TAS2783_SDW
+ 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_UDA1342
+ imply SND_SOC_UDA1380
+ imply SND_SOC_WCD9335
+ imply SND_SOC_WCD934X
+ imply SND_SOC_WCD937X_SDW
+ imply SND_SOC_WCD938X_SDW
+ imply SND_SOC_WCD939X_SDW
+ imply SND_SOC_LPASS_MACRO_COMMON
+ imply SND_SOC_LPASS_RX_MACRO
+ imply SND_SOC_LPASS_TX_MACRO
+ 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_WSA884X
+ 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
@@ -255,18 +397,34 @@ config SND_SOC_WM_HUBS
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 y if SND_SOC_CS48L32=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
+ default m if SND_SOC_CS48L32=m
config SND_SOC_AB8500_CODEC
tristate
+ depends on ABX500_CORE
config SND_SOC_AC97_CODEC
tristate "Build generic ASoC AC97 CODEC driver"
@@ -275,21 +433,25 @@ config SND_SOC_AC97_CODEC
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
- select REGMAP_AC97
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_AD73311
tristate
@@ -297,10 +459,27 @@ config SND_SOC_AD73311
config SND_SOC_ADAU_UTILS
tristate
-config SND_SOC_ADAU1373
+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 "Analog Devices ADAU1373 CODEC"
+ depends on I2C
+ select SND_SOC_ADAU_UTILS
+
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
depends on I2C
@@ -333,11 +512,13 @@ config SND_SOC_ADAU1781
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
@@ -346,26 +527,57 @@ config SND_SOC_ADAU1977
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
@@ -375,8 +587,29 @@ config SND_SOC_AK4104
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_AK4554
tristate "AKM AK4554 CODEC"
@@ -385,8 +618,14 @@ config SND_SOC_AK4613
tristate "AKM AK4613 CODEC"
depends on I2C
+config SND_SOC_AK4619
+ tristate "AKM AK4619 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
@@ -394,23 +633,169 @@ config SND_SOC_AK4642
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"
+ tristate "Realtek ALC5623 CODEC"
depends on I2C
config SND_SOC_ALC5632
tristate
+ depends on I2C
+
+config SND_SOC_AUDIO_IIO_AUX
+ tristate "Audio IIO Auxiliary device"
+ depends on IIO
+ help
+ Enable support for Industrial I/O devices as audio auxiliary devices.
+ This allows to have an IIO device present in the audio path and
+ controlled using mixer controls.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-audio-iio-aux.
+
+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
+ select CRC8
+ tristate
+
+config SND_SOC_AW88395
+ tristate "Soc Audio for awinic aw88395"
+ depends on I2C
+ 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_AW88166
+ tristate "Soc Audio for awinic aw88166"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88166 Smart PA.
+ The awinic AW88166 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with sound quality
+ enhancement algorithms and speaker protection.
+
+config SND_SOC_AW88261
+ tristate "Soc Audio for awinic aw88261"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88261 Smart PA.
+ The awinic AW88261 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. The output voltage of
+ boost converter can be adjusted smartly according to
+ the input amplitude.
+
+config SND_SOC_AW88081
+ tristate "Soc Audio for awinic aw88081/aw88083"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88081 Smart PA.
+ The awinic AW88081 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. Due to its 9uV noise
+ floor and ultra-low distortion, clean listening is guaranteed.
+
+config SND_SOC_AW87390
+ tristate "Soc Audio for awinic aw87390"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ The awinic aw87390 is specifically designed to improve
+ the musical output dynamic range, enhance the overall
+ sound quality, which is a new high efficiency, low
+ noise, constant large volume, 6th Smart K audio amplifier.
+
+config SND_SOC_AW88399
+ tristate "Soc Audio for awinic aw88399"
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88399 Smart PA.
+ The awinic AW88399 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier and SKTune speaker
+ protection algorithms.
+
+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_CHV3_CODEC
+ tristate "Google Chameleon v3 codec driver"
+ help
+ Enable support for the Google Chameleon v3 audio codec.
+ This codec does not have a control interface, it always outputs
+ 8 channel S32_LE audio.
+
+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_CS_AMP_LIB
+ tristate
+
+config SND_SOC_CS_AMP_LIB_TEST
+ tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS_AMP_LIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the Cirrus Logic common
+ amplifier library.
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
config SND_SOC_CS35L32
tristate "Cirrus Logic CS35L32 CODEC"
depends on I2C
@@ -427,9 +812,130 @@ 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
+ select REGMAP_IRQ
+
+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
+ select SND_SOC_CS_AMP_LIB
+ 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_CS40L50
+ tristate "Cirrus Logic CS40L50 CODEC"
+ depends on MFD_CS40L50_CORE
+ help
+ This option enables support for I2S streaming to Cirrus Logic CS40L50.
+
+ CS40L50 is a haptic driver with waveform memory, an integrated
+ DSP, and closed-loop algorithms. If built as a module, it will be
+ called snd-soc-cs40l50.
+
+config SND_SOC_CS42L42_CORE
+ tristate
+
config SND_SOC_CS42L42
- tristate "Cirrus Logic CS42L42 CODEC"
+ 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_CS42L43
+ tristate "Cirrus Logic CS42L43 CODEC"
+ depends on MFD_CS42L43
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC.
+
+config SND_SOC_CS42L43_SDW
+ tristate "Cirrus Logic CS42L43 CODEC (SoundWire)"
+ depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC over SoundWire.
config SND_SOC_CS42L51
tristate
@@ -451,6 +957,24 @@ 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_CS42L84
+ tristate "Cirrus Logic CS42L84 CODEC"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+
+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
@@ -495,57 +1019,156 @@ config SND_SOC_CS42XX8_I2C
# Cirrus Logic CS43130 HiFi DAC
config SND_SOC_CS43130
- tristate "Cirrus Logic CS43130 CODEC"
- depends on I2C
+ 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
+
+config SND_SOC_CS48L32
+ tristate "Cirrus Logic CS48L32 audio DSP"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ help
+ Build the codec driver for the Cirrus Logic CS48L32 audio DSP.
# Cirrus Logic Quad-Channel ADC
config SND_SOC_CS53L30
tristate "Cirrus Logic CS53L30 CODEC"
depends on I2C
+config SND_SOC_CS530X
+ tristate
+
+config SND_SOC_CS530X_I2C
+ tristate "Cirrus Logic CS530x ADCs (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS530X
+ help
+ Enable support for Cirrus Logic CS530X ADCs
+ with I2C control.
+
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
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
select REGMAP_MMIO
- tristate
+ 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.
-config SND_SOC_L3
- tristate
+ 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_DA7210
- tristate
+ tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_DA7213
- tristate
+ tristate "Dialog DA7213 CODEC"
+ depends on I2C
config SND_SOC_DA7218
tristate
+ depends on I2C
config SND_SOC_DA7219
- tristate
+ tristate
+ depends on I2C
config SND_SOC_DA732X
- tristate
+ tristate
+ depends on I2C
config SND_SOC_DA9055
tristate
-
-config SND_SOC_DIO2125
- tristate "Dioo DIO2125 Amplifier"
- select GPIOLIB
+ depends on I2C
config SND_SOC_DMIC
- tristate
+ 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
@@ -554,12 +1177,31 @@ config SND_SOC_HDMI_CODEC
select HDMI
config SND_SOC_ES7134
- tristate "Everest Semi ES7134 CODEC"
+ tristate "Everest Semi ES7134 CODEC"
+
+config SND_SOC_ES7241
+ tristate "Everest Semi ES7241 CODEC"
+
+config SND_SOC_ES83XX_DSM_COMMON
+ depends on ACPI
+ tristate
+
+config SND_SOC_ES8311
+ tristate "Everest Semi ES8311 CODEC"
+ depends on I2C
config SND_SOC_ES8316
tristate "Everest Semi ES8316 CODEC"
depends on I2C
+config SND_SOC_ES8323
+ tristate "Everest Semi ES8323 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8326
+ tristate "Everest Semi ES8326 CODEC"
+ depends on I2C
+
config SND_SOC_ES8328
tristate
@@ -573,6 +1215,43 @@ config SND_SOC_ES8328_SPI
depends on SPI_MASTER
select SND_SOC_ES8328
+config SND_SOC_ES8375
+ tristate "Everest Semi ES8375 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8389
+ tristate "Everest Semi ES8389 CODEC"
+ depends on I2C
+
+config SND_SOC_FRAMER
+ tristate "Framer codec"
+ depends on GENERIC_FRAMER
+ help
+ Enable support for the framer codec.
+ The framer codec uses the generic framer infrastructure to transport
+ some audio data over an analog E1/T1/J1 line.
+ This codec allows to use some of the time slots available on the TDM
+ bus on which the framer is connected to transport the audio data.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-framer.
+
+config SND_SOC_FS_AMP_LIB
+ select CRC16
+ tristate
+
+config SND_SOC_FS210X
+ tristate 'FourSemi FS2104/5S digital audio amplifier'
+ depends on I2C
+ select GPIOLIB
+ select REGMAP_I2C
+ select SND_SOC_FS_AMP_LIB
+ help
+ Enable support for FourSemi FS2104/5S digital audio amplifier.
+ The FS2104/5S are Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifiers with Enhanced Signal Processing.
+ The amplifiers support I2C and I2S/TDM.
+
config SND_SOC_GTM601
tristate 'GTM601 UMTS modem audio codec'
@@ -582,57 +1261,172 @@ config SND_SOC_HDAC_HDMI
select SND_PCM_ELD
select HDMI
-config SND_SOC_ICS43432
+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"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
select REGMAP_MMIO
config SND_SOC_ISABELLE
- tristate
+ 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
+ tristate "Maxim MAX98090 CODEC"
+ depends on I2C
config SND_SOC_MAX98095
- tristate
+ tristate
+ depends on I2C
config SND_SOC_MAX98357A
- tristate
+ tristate "Maxim MAX98357A CODEC"
config SND_SOC_MAX98371
- tristate
+ tristate
+ depends on I2C
config SND_SOC_MAX98504
tristate "Maxim MAX98504 speaker amplifier"
depends on I2C
config SND_SOC_MAX9867
- tristate
+ tristate "Maxim MAX9867 CODEC"
+ depends on I2C
config SND_SOC_MAX98925
- tristate
+ 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_MAX98388
+ tristate "Analog Devices MAX98388 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Analog Devices MAX98388 audio
+ amplifier. The device provides a PCM interface for
+ audio data and a standard I2C interface for control
+ data communication.
+
+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"
@@ -645,11 +1439,27 @@ config SND_SOC_MSM8916_WCD_ANALOG
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_PCM1754
+ tristate "Texas Instruments PCM1754 CODEC"
+ depends on GPIOLIB
+
+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
@@ -685,7 +1495,22 @@ config SND_SOC_PCM186X_SPI
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
@@ -703,7 +1528,7 @@ config SND_SOC_PCM3168A_SPI
select REGMAP_SPI
config SND_SOC_PCM5102A
- tristate
+ tristate "Texas Instruments PCM5102A CODEC"
config SND_SOC_PCM512x
tristate
@@ -720,6 +1545,64 @@ config SND_SOC_PCM512x_SPI
select SND_SOC_PCM512x
select REGMAP_SPI
+config SND_SOC_PCM6240
+ tristate "Texas Instruments PCM6240 Family Audio chips based on I2C"
+ depends on I2C
+ help
+ Enable support for Texas Instruments PCM6240 Family Audio chips.
+ Note the PCM6240 driver implements a flexible and configurable
+ setting for register and filter coefficients, to one, two or
+ even multiple PCM6240 Family Audio chips.
+
+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_PM4125
+ depends on SND_SOC_PM4125_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_PM4125_SDW
+ tristate "PM4125 audio codec - SDW"
+ select SND_SOC_PM4125
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The PMIC PM4125 has an in-built audio codec IC used with SoCs
+ like QCM2290, and it is connected via soundwire and SPMI.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_RK3308
+ tristate "Rockchip RK3308 audio CODEC"
+ depends on ARM64 || COMPILE_TEST
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This is a device driver for the audio codec embedded in the
+ Rockchip RK3308 SoC.
+
+ It has 8 24-bit ADCs and 2 24-bit DACs. The maximum supported
+ sampling rate is 192 kHz.
+
+config SND_SOC_RK3328
+ tristate "Rockchip RK3328 audio CODEC"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+
+config SND_SOC_RK817
+ tristate "Rockchip RK817 audio CODEC"
+ depends on MFD_RK8XX || COMPILE_TEST
+
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5514=y
@@ -731,8 +1614,16 @@ config SND_SOC_RL6231
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
@@ -742,8 +1633,21 @@ config SND_SOC_RL6231
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_RT_SDW_COMMON
+ tristate
+ default y if SND_SOC_RT721_SDCA_SDW=y
+ default m if SND_SOC_RT721_SDCA_SDW=m
config SND_SOC_RL6347A
tristate
@@ -766,11 +1670,70 @@ 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_RT1017_SDCA_SDW
+ tristate "Realtek RT1017 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+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
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1318_SDW
+ tristate "Realtek RT1318 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1320_SDW
+ tristate "Realtek RT1320 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
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
@@ -785,31 +1748,44 @@ config SND_SOC_RT5631
depends on I2C
config SND_SOC_RT5640
- tristate
+ tristate "Realtek RT5640/RT5639 Codec"
+ depends on I2C
config SND_SOC_RT5645
- tristate
+ tristate
+ depends on I2C
config SND_SOC_RT5651
tristate
+ depends on I2C
config SND_SOC_RT5659
- tristate
+ 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
@@ -817,6 +1793,148 @@ 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_RT721_SDCA_SDW
+ tristate "Realtek RT721 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT722_SDCA_SDW
+ tristate "Realtek RT722 SDCA 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_RT9123
+ tristate "Richtek RT9123 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the I2C control mode of Richtek RT9123 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RT9123P
+ tristate "Richtek RT9123P Mono Class-D Amplifier"
+ help
+ Enable support for the HW control mode of Richtek RT9123P 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RTQ9124
+ tristate "Richtek RTQ9124 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9124 1x30W digital input automotive
+ audio amplifier with current sense and real-time load diagnostics.
+
+config SND_SOC_RTQ9128
+ tristate "Richtek RTQ9128 45W Digital Input Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9128 digital input 4-channel
+ automotive audio amplifier. It is a ultra-low output noise,
+ high-efficiency, four-channel class-D audio power amplifier
+ that can deliver over 87% power efficienty at 4x75W into 4Ohm,
+ 25V supply in automotive applications.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-rtq9128.
+
+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"
@@ -824,6 +1942,7 @@ config SND_SOC_SGTL5000
config SND_SOC_SI476X
tristate
+ depends on MFD_SI476X_CORE
config SND_SOC_SIGMADSP
tristate
@@ -837,16 +1956,53 @@ config SND_SOC_SIGMADSP_REGMAP
tristate
select SND_SOC_SIGMADSP
-config SND_SOC_SIRF_AUDIO_CODEC
- tristate "SiRF SoC internal audio codec"
- select REGMAP_MMIO
+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_SMA1307
+ tristate "Iron Device SMA1307 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1307 boosted digital speaker
+ amplifier with feedback-loop.
+ If you are using a system with an SMA1307 amplifier connected
+ via I2C, enable this option.
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
-config SND_SOC_SSM2518
+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
@@ -862,6 +2018,12 @@ config SND_SOC_SSM2602_I2C
select SND_SOC_SSM2602
select REGMAP_I2C
+config SND_SOC_SSM3515
+ tristate "Analog Devices SSM3515 amplifier driver"
+ select REGMAP_I2C
+ depends on I2C
+ depends on OF
+
config SND_SOC_SSM4567
tristate "Analog Devices ssm4567 amplifier driver support"
depends on I2C
@@ -877,9 +2039,12 @@ config SND_SOC_STA350
config SND_SOC_STA529
tristate
+ depends on I2C
config SND_SOC_STAC9766
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
@@ -888,13 +2053,75 @@ 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_TAS2781_COMLIB
+ tristate
+
+config SND_SOC_TAS2781_COMLIB_I2C
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ tristate
+
+config SND_SOC_TAS2781_FMWLIB
+ select SND_SOC_TAS2781_COMLIB
+ select CRC8
+ tristate
+ default n
+
+config SND_SOC_TAS2781_I2C
+ tristate "Texas Instruments TAS2781 speaker amplifier based on I2C"
+ depends on I2C
+ select SND_SOC_TAS2781_COMLIB_I2C
+ select SND_SOC_TAS2781_FMWLIB
+ help
+ Enable support for Texas Instruments TAS2781 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ Note the TAS2781 driver implements a flexible and configurable
+ algo coefficient setting, for one, two or even multiple TAS2781
+ chips.
+
+config SND_SOC_TAS2783_SDW
+ tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
+ depends on SOUNDWIRE
+ depends on EFI
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+ select CRC32
+ help
+ Enable support for Texas Instruments TAS2783A Digital input
+ mono Class-D and DSP-inside audio power amplifiers. TAS2783
+ driver implements a flexible and configurable algorithm
+ cofficient setting, for one, two or multiple TAS2783 chips.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
config SND_SOC_TAS571X
- tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers"
+ 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"
@@ -903,6 +2130,15 @@ config SND_SOC_TAS5720
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
@@ -910,10 +2146,33 @@ config SND_SOC_TAS6424
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
@@ -938,23 +2197,47 @@ config SND_SOC_TLV320AIC31XX
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 "Texas Instruments TLV320AIC3x CODECs"
+ 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
+ depends on GPIOLIB_LEGACY
+
+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"
@@ -967,49 +2250,171 @@ config SND_SOC_TSCS42XX
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 MFD_TWL4030_AUDIO
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
+config SND_SOC_UDA1342
+ tristate "NXP UDA1342 CODEC"
+ depends on I2C
+ help
+ The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
+ mic inputs), stereo audio DAC, with basic audio processing.
config SND_SOC_UDA1380
- tristate
+ tristate
depends on I2C
+ depends on GPIOLIB_LEGACY
-config SND_SOC_WL1273
+config SND_SOC_WCD_CLASSH
tristate
+config SND_SOC_WCD_COMMON
+ tristate
+
+config SND_SOC_WCD9335
+ tristate "WCD9335 Codec"
+ depends on SLIMBUS
+ select REGMAP_SLIMBUS
+ select REGMAP_IRQ
+ select SND_SOC_WCD_CLASSH
+ 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_IRQ
+ select REGMAP_SLIMBUS
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+ 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_WCD937X
+ depends on SND_SOC_WCD937X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD937X_SDW
+ tristate "WCD9370/WCD9375 Codec - SDW"
+ select SND_SOC_WCD937X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9370/9375 is an audio codec IC used with SoCs
+ like SC7280 or QCM6490 chipsets, and it connected
+ via soundwire.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_WCD938X
+ depends on SND_SOC_WCD938X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+ select MULTIPLEXER
+
+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_WCD939X
+ depends on SND_SOC_WCD939X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD939X_SDW
+ tristate "WCD9390/WCD9395 Codec - SDW"
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD939X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9390/9395 is a audio codec IC Integrated in
+ Qualcomm SoCs like SM8650.
+
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 "Wolfson Microelectronics WM8510 CODEC"
@@ -1039,15 +2444,26 @@ config SND_SOC_WM8728
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
- tristate "Wolfson Microelectronics WM8731 CODEC"
- depends on SND_SOC_I2C_AND_SPI
+ 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 "Wolfson Microelectronics WM8737 DAC"
+ tristate "Wolfson Microelectronics WM8741 DAC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
@@ -1067,7 +2483,7 @@ config SND_SOC_WM8776
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8782
- tristate
+ tristate "Wolfson Microelectronics WM8782 ADC"
config SND_SOC_WM8804
tristate
@@ -1086,26 +2502,31 @@ config SND_SOC_WM8804_SPI
config SND_SOC_WM8900
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8903
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 "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 "Wolfson Microelectronics WM8962 CODEC"
@@ -1113,6 +2534,7 @@ config SND_SOC_WM8962
config SND_SOC_WM8971
tristate
+ depends on I2C
config SND_SOC_WM8974
tristate "Wolfson Microelectronics WM8974 codec"
@@ -1124,6 +2546,7 @@ config SND_SOC_WM8978
config SND_SOC_WM8983
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8985
tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver"
@@ -1131,30 +2554,39 @@ config SND_SOC_WM8985
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
+ depends on MFD_WM8994
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
+ tristate "Wolfson Microelectronics WM8998 codec driver"
+ depends on MFD_WM8998 && MFD_ARIZONA
config SND_SOC_WM9081
tristate
@@ -1162,60 +2594,193 @@ config SND_SOC_WM9081
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_ZX_AUD96P22
- tristate "ZTE ZX AUD96P22 CODEC"
- depends on I2C
- select REGMAP_I2C
+config SND_SOC_WSA881X
+ tristate "WSA881X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ 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
+ help
+ This enables support for Qualcomm WSA8830/WSA8835 Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_WSA884X
+ tristate "WSA884X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ This enables support for Qualcomm WSA8840/WSA8845/WSA8845H 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_MC13783
tristate
+ depends on MFD_MC13XXX
config SND_SOC_ML26124
tristate
+ depends on I2C
+
+config SND_SOC_MT6351
+ tristate "MediaTek MT6351 Codec"
+
+config SND_SOC_MT6357
+ tristate "MediaTek MT6357 Codec"
+ help
+ Enable support for the platform which uses MT6357 as
+ external codec device.
+
+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
+ 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_NTPFW
+ tristate
+
+config SND_SOC_NTP8918
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8918 amplifier"
+ depends on I2C
+
+config SND_SOC_NTP8835
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8835 and NTP8835C amplifiers"
+ 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
+ select SND_SOC_LPASS_MACRO_COMMON
+ 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 da1571336f1e..bd95a7c911d5 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,246 +1,433 @@
# 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-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-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-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-arizona-objs := arizona.o
-snd-soc-bt-sco-objs := bt-sco.o
-snd-soc-cq93vc-objs := cq93vc.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-cs42l42-objs := cs42l42.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-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-cs4349-objs := cs4349.o
-snd-soc-cs47l24-objs := cs47l24.o
-snd-soc-cs53l30-objs := cs53l30.o
-snd-soc-cx20442-objs := cx20442.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-es8316-objs := es8316.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-ics43432-objs := ics43432.o
-snd-soc-inno-rk3036-objs := inno_rk3036.o
-snd-soc-isabelle-objs := isabelle.o
-snd-soc-jz4740-codec-objs := jz4740.o
-snd-soc-l3-objs := l3.o
-snd-soc-lm4857-objs := lm4857.o
-snd-soc-lm49453-objs := lm49453.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-max98373-objs := max98373.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-nau8540-objs := nau8540.o
-snd-soc-nau8810-objs := nau8810.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-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-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-rl6231-objs := rl6231.o
-snd-soc-rl6347a-objs := rl6347a.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-rt5670-objs := rt5670.o
-snd-soc-rt5677-objs := rt5677.o
-snd-soc-rt5677-spi-objs := rt5677-spi.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-sirf-audio-codec-objs := sirf-audio-codec.o
-snd-soc-spdif-tx-objs := spdif_transmitter.o
-snd-soc-spdif-rx-objs := spdif_receiver.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-tas6424-objs := tas6424.o
-snd-soc-tfa9879-objs := tfa9879.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
-snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
-snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
-snd-soc-tlv320aic3x-objs := tlv320aic3x.o
-snd-soc-tlv320dac33-objs := tlv320dac33.o
-snd-soc-tscs42xx-objs := tscs42xx.o
-snd-soc-ts3a227e-objs := ts3a227e.o
-snd-soc-twl4030-objs := twl4030.o
-snd-soc-twl6040-objs := twl6040.o
-snd-soc-uda134x-objs := uda134x.o
-snd-soc-uda1380-objs := uda1380.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-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
-snd-soc-wm8961-objs := wm8961.o
-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 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-zx-aud96p22-objs := zx_aud96p22.o
+snd-soc-88pm860x-y := 88pm860x-codec.o
+snd-soc-ab8500-codec-y := ab8500-codec.o
+snd-soc-ac97-y := ac97.o
+snd-soc-ad1836-y := ad1836.o
+snd-soc-ad193x-y := ad193x.o
+snd-soc-ad193x-spi-y := ad193x-spi.o
+snd-soc-ad193x-i2c-y := ad193x-i2c.o
+snd-soc-ad1980-y := ad1980.o
+snd-soc-ad73311-y := ad73311.o
+snd-soc-adau-utils-y := adau-utils.o
+snd-soc-adau1372-y := adau1372.o
+snd-soc-adau1372-i2c-y := adau1372-i2c.o
+snd-soc-adau1372-spi-y := adau1372-spi.o
+snd-soc-adau1373-y := adau1373.o
+snd-soc-adau1701-y := adau1701.o
+snd-soc-adau17x1-y := adau17x1.o
+snd-soc-adau1761-y := adau1761.o
+snd-soc-adau1761-i2c-y := adau1761-i2c.o
+snd-soc-adau1761-spi-y := adau1761-spi.o
+snd-soc-adau1781-y := adau1781.o
+snd-soc-adau1781-i2c-y := adau1781-i2c.o
+snd-soc-adau1781-spi-y := adau1781-spi.o
+snd-soc-adau1977-y := adau1977.o
+snd-soc-adau1977-spi-y := adau1977-spi.o
+snd-soc-adau1977-i2c-y := adau1977-i2c.o
+snd-soc-adau7002-y := adau7002.o
+snd-soc-adau7118-y := adau7118.o
+snd-soc-adau7118-i2c-y := adau7118-i2c.o
+snd-soc-adau7118-hw-y := adau7118-hw.o
+snd-soc-adav80x-y := adav80x.o
+snd-soc-adav801-y := adav801.o
+snd-soc-adav803-y := adav803.o
+snd-soc-ads117x-y := ads117x.o
+snd-soc-ak4104-y := ak4104.o
+snd-soc-ak4118-y := ak4118.o
+snd-soc-ak4375-y := ak4375.o
+snd-soc-ak4458-y := ak4458.o
+snd-soc-ak4535-y := ak4535.o
+snd-soc-ak4554-y := ak4554.o
+snd-soc-ak4613-y := ak4613.o
+snd-soc-ak4619-y := ak4619.o
+snd-soc-ak4641-y := ak4641.o
+snd-soc-ak4642-y := ak4642.o
+snd-soc-ak4671-y := ak4671.o
+snd-soc-ak5386-y := ak5386.o
+snd-soc-ak5558-y := ak5558.o
+snd-soc-arizona-y := arizona.o arizona-jack.o
+snd-soc-audio-iio-aux-y := audio-iio-aux.o
+snd-soc-aw8738-y := aw8738.o
+snd-soc-aw87390-y := aw87390.o
+snd-soc-aw88081-y := aw88081.o
+snd-soc-aw88395-lib-y := aw88395/aw88395_lib.o
+snd-soc-aw88395-y := aw88395/aw88395.o \
+ aw88395/aw88395_device.o
+snd-soc-aw88166-y := aw88166.o
+snd-soc-aw88261-y := aw88261.o
+snd-soc-aw88399-y := aw88399.o
+snd-soc-bd28623-y := bd28623.o
+snd-soc-bt-sco-y := bt-sco.o
+snd-soc-chv3-codec-y := chv3-codec.o
+snd-soc-cpcap-y := cpcap.o
+snd-soc-cq93vc-y := cq93vc.o
+snd-soc-cros-ec-codec-y := cros_ec_codec.o
+snd-soc-cs-amp-lib-y := cs-amp-lib.o
+snd-soc-cs-amp-lib-test-y := cs-amp-lib-test.o
+snd-soc-cs35l32-y := cs35l32.o
+snd-soc-cs35l33-y := cs35l33.o
+snd-soc-cs35l34-y := cs35l34.o
+snd-soc-cs35l35-y := cs35l35.o
+snd-soc-cs35l36-y := cs35l36.o
+snd-soc-cs35l41-lib-y := cs35l41-lib.o
+snd-soc-cs35l41-y := cs35l41.o
+snd-soc-cs35l41-spi-y := cs35l41-spi.o
+snd-soc-cs35l41-i2c-y := cs35l41-i2c.o
+snd-soc-cs35l45-y := cs35l45.o cs35l45-tables.o
+snd-soc-cs35l45-spi-y := cs35l45-spi.o
+snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
+snd-soc-cs35l56-y := cs35l56.o
+snd-soc-cs35l56-shared-y := cs35l56-shared.o
+snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
+snd-soc-cs35l56-spi-y := cs35l56-spi.o
+snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
+snd-soc-cs40l50-y := cs40l50-codec.o
+snd-soc-cs42l42-y := cs42l42.o
+snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
+snd-soc-cs42l42-sdw-y := cs42l42-sdw.o
+snd-soc-cs42l43-y := cs42l43.o cs42l43-jack.o
+snd-soc-cs42l43-sdw-y := cs42l43-sdw.o
+snd-soc-cs42l51-y := cs42l51.o
+snd-soc-cs42l51-i2c-y := cs42l51-i2c.o
+snd-soc-cs42l52-y := cs42l52.o
+snd-soc-cs42l56-y := cs42l56.o
+snd-soc-cs42l73-y := cs42l73.o
+snd-soc-cs42l83-i2c-y := cs42l83-i2c.o
+snd-soc-cs42l84-y := cs42l84.o
+snd-soc-cs4234-y := cs4234.o
+snd-soc-cs4265-y := cs4265.o
+snd-soc-cs4270-y := cs4270.o
+snd-soc-cs4271-y := cs4271.o
+snd-soc-cs4271-i2c-y := cs4271-i2c.o
+snd-soc-cs4271-spi-y := cs4271-spi.o
+snd-soc-cs42xx8-y := cs42xx8.o
+snd-soc-cs42xx8-i2c-y := cs42xx8-i2c.o
+snd-soc-cs43130-y := cs43130.o
+snd-soc-cs4341-y := cs4341.o
+snd-soc-cs4349-y := cs4349.o
+snd-soc-cs47l15-y := cs47l15.o
+snd-soc-cs47l24-y := cs47l24.o
+snd-soc-cs47l35-y := cs47l35.o
+snd-soc-cs47l85-y := cs47l85.o
+snd-soc-cs47l90-y := cs47l90.o
+snd-soc-cs47l92-y := cs47l92.o
+snd-soc-cs48l32-y := cs48l32.o cs48l32-tables.o
+snd-soc-cs53l30-y := cs53l30.o
+snd-soc-cs530x-y := cs530x.o
+snd-soc-cs530x-i2c-y := cs530x-i2c.o
+snd-soc-cx20442-y := cx20442.o
+snd-soc-cx2072x-y := cx2072x.o
+snd-soc-da7210-y := da7210.o
+snd-soc-da7213-y := da7213.o
+snd-soc-da7218-y := da7218.o
+snd-soc-da7219-y := da7219.o da7219-aad.o
+snd-soc-da732x-y := da732x.o
+snd-soc-da9055-y := da9055.o
+snd-soc-dmic-y := dmic.o
+snd-soc-es7134-y := es7134.o
+snd-soc-es7241-y := es7241.o
+snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o
+snd-soc-es8311-y := es8311.o
+snd-soc-es8316-y := es8316.o
+snd-soc-es8323-y := es8323.o
+snd-soc-es8326-y := es8326.o
+snd-soc-es8328-y := es8328.o
+snd-soc-es8328-i2c-y := es8328-i2c.o
+snd-soc-es8328-spi-y := es8328-spi.o
+snd-soc-es8375-y := es8375.o
+snd-soc-es8389-y := es8389.o
+snd-soc-framer-y := framer-codec.o
+snd-soc-fs-amp-lib-y := fs-amp-lib.o
+snd-soc-fs210x-y := fs210x.o
+snd-soc-gtm601-y := gtm601.o
+snd-soc-hdac-hdmi-y := hdac_hdmi.o
+snd-soc-hdac-hda-y := hdac_hda.o
+snd-soc-hda-codec-y := hda.o hda-dai.o
+snd-soc-ics43432-y := ics43432.o
+snd-soc-idt821034-y := idt821034.o
+snd-soc-inno-rk3036-y := inno_rk3036.o
+snd-soc-isabelle-y := isabelle.o
+snd-soc-jz4740-codec-y := jz4740.o
+snd-soc-jz4725b-codec-y := jz4725b.o
+snd-soc-jz4760-codec-y := jz4760.o
+snd-soc-jz4770-codec-y := jz4770.o
+snd-soc-lm4857-y := lm4857.o
+snd-soc-lm49453-y := lm49453.o
+snd-soc-lochnagar-sc-y := lochnagar-sc.o
+snd-soc-lpass-macro-common-y := lpass-macro-common.o
+snd-soc-lpass-rx-macro-y := lpass-rx-macro.o
+snd-soc-lpass-tx-macro-y := lpass-tx-macro.o
+snd-soc-lpass-wsa-macro-y := lpass-wsa-macro.o
+snd-soc-lpass-va-macro-y := lpass-va-macro.o
+snd-soc-madera-y := madera.o
+snd-soc-max9759-y := max9759.o
+snd-soc-max9768-y := max9768.o
+snd-soc-max98088-y := max98088.o
+snd-soc-max98090-y := max98090.o
+snd-soc-max98095-y := max98095.o
+snd-soc-max98357a-y := max98357a.o
+snd-soc-max98371-y := max98371.o
+snd-soc-max9867-y := max9867.o
+snd-soc-max98925-y := max98925.o
+snd-soc-max98926-y := max98926.o
+snd-soc-max98927-y := max98927.o
+snd-soc-max98520-y := max98520.o
+snd-soc-max98363-y := max98363.o
+snd-soc-max98373-y := max98373.o
+snd-soc-max98373-i2c-y := max98373-i2c.o
+snd-soc-max98373-sdw-y := max98373-sdw.o
+snd-soc-max98388-y := max98388.o
+snd-soc-max98390-y := max98390.o
+snd-soc-max98396-y := max98396.o
+snd-soc-max9850-y := max9850.o
+snd-soc-max9860-y := max9860.o
+snd-soc-mc13783-y := mc13783.o
+snd-soc-ml26124-y := ml26124.o
+snd-soc-msm8916-analog-y := msm8916-wcd-analog.o
+snd-soc-msm8916-digital-y := msm8916-wcd-digital.o
+snd-soc-mt6351-y := mt6351.o
+snd-soc-mt6357-y := mt6357.o
+snd-soc-mt6358-y := mt6358.o
+snd-soc-mt6359-y := mt6359.o
+snd-soc-mt6359-accdet-y := mt6359-accdet.o
+snd-soc-mt6660-y := mt6660.o
+snd-soc-nau8315-y := nau8315.o
+snd-soc-nau8540-y := nau8540.o
+snd-soc-nau8810-y := nau8810.o
+snd-soc-nau8821-y := nau8821.o
+snd-soc-nau8822-y := nau8822.o
+snd-soc-nau8824-y := nau8824.o
+snd-soc-nau8825-y := nau8825.o
+snd-soc-ntp8835-y := ntp8835.o
+snd-soc-ntp8918-y := ntp8918.o
+snd-soc-ntpfw-y := ntpfw.o
+snd-soc-hdmi-codec-y := hdmi-codec.o
+snd-soc-pcm1681-y := pcm1681.o
+snd-soc-pcm1754-y := pcm1754.o
+snd-soc-pcm1789-codec-y := pcm1789.o
+snd-soc-pcm1789-i2c-y := pcm1789-i2c.o
+snd-soc-pcm179x-codec-y := pcm179x.o
+snd-soc-pcm179x-i2c-y := pcm179x-i2c.o
+snd-soc-pcm179x-spi-y := pcm179x-spi.o
+snd-soc-pcm186x-y := pcm186x.o
+snd-soc-pcm186x-i2c-y := pcm186x-i2c.o
+snd-soc-pcm186x-spi-y := pcm186x-spi.o
+snd-soc-pcm3008-y := pcm3008.o
+snd-soc-pcm3060-y := pcm3060.o
+snd-soc-pcm3060-i2c-y := pcm3060-i2c.o
+snd-soc-pcm3060-spi-y := pcm3060-spi.o
+snd-soc-pcm3168a-y := pcm3168a.o
+snd-soc-pcm3168a-i2c-y := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-y := pcm3168a-spi.o
+snd-soc-pcm5102a-y := pcm5102a.o
+snd-soc-pcm512x-y := pcm512x.o
+snd-soc-pcm512x-i2c-y := pcm512x-i2c.o
+snd-soc-pcm512x-spi-y := pcm512x-spi.o
+snd-soc-pcm6240-y := pcm6240.o
+snd-soc-peb2466-y := peb2466.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
+snd-soc-rk3308-y := rk3308_codec.o
+snd-soc-rk3328-y := rk3328_codec.o
+snd-soc-rk817-y := rk817_codec.o
+snd-soc-rl6231-y := rl6231.o
+snd-soc-rt-sdw-common-y := rt-sdw-common.o
+snd-soc-rl6347a-y := rl6347a.o
+snd-soc-rt1011-y := rt1011.o
+snd-soc-rt1015-y := rt1015.o
+snd-soc-rt1015p-y := rt1015p.o
+snd-soc-rt1016-y := rt1016.o
+snd-soc-rt1017-sdca-y := rt1017-sdca-sdw.o
+snd-soc-rt1019-y := rt1019.o
+snd-soc-rt1305-y := rt1305.o
+snd-soc-rt1308-y := rt1308.o
+snd-soc-rt1308-sdw-y := rt1308-sdw.o
+snd-soc-rt1316-sdw-y := rt1316-sdw.o
+snd-soc-rt1318-y := rt1318.o
+snd-soc-rt1318-sdw-y := rt1318-sdw.o
+snd-soc-rt1320-sdw-y := rt1320-sdw.o
+snd-soc-rt274-y := rt274.o
+snd-soc-rt286-y := rt286.o
+snd-soc-rt298-y := rt298.o
+snd-soc-rt5514-y := rt5514.o
+snd-soc-rt5514-spi-y := rt5514-spi.o
+snd-soc-rt5616-y := rt5616.o
+snd-soc-rt5631-y := rt5631.o
+snd-soc-rt5640-y := rt5640.o
+snd-soc-rt5645-y := rt5645.o
+snd-soc-rt5651-y := rt5651.o
+snd-soc-rt5659-y := rt5659.o
+snd-soc-rt5660-y := rt5660.o
+snd-soc-rt5663-y := rt5663.o
+snd-soc-rt5665-y := rt5665.o
+snd-soc-rt5668-y := rt5668.o
+snd-soc-rt5670-y := rt5670.o
+snd-soc-rt5677-y := rt5677.o
+snd-soc-rt5677-spi-y := rt5677-spi.o
+snd-soc-rt5682-y := rt5682.o
+snd-soc-rt5682-sdw-y := rt5682-sdw.o
+snd-soc-rt5682-i2c-y := rt5682-i2c.o
+snd-soc-rt5682s-y := rt5682s.o
+snd-soc-rt700-y := rt700.o rt700-sdw.o
+snd-soc-rt711-y := rt711.o rt711-sdw.o
+snd-soc-rt711-sdca-y := rt711-sdca.o rt711-sdca-sdw.o
+snd-soc-rt712-sdca-y := rt712-sdca.o rt712-sdca-sdw.o
+snd-soc-rt712-sdca-dmic-y := rt712-sdca-dmic.o
+snd-soc-rt715-y := rt715.o rt715-sdw.o
+snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o
+snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o
+snd-soc-rt9120-y := rt9120.o
+snd-soc-rt9123-y := rt9123.o
+snd-soc-rt9123p-y := rt9123p.o
+snd-soc-rtq9124-y := rtq9124.o
+snd-soc-rtq9128-y := rtq9128.o
+snd-soc-sdw-mockup-y := sdw-mockup.o
+snd-soc-sgtl5000-y := sgtl5000.o
+snd-soc-alc5623-y := alc5623.o
+snd-soc-alc5632-y := alc5632.o
+snd-soc-sigmadsp-y := sigmadsp.o
+snd-soc-sigmadsp-i2c-y := sigmadsp-i2c.o
+snd-soc-sigmadsp-regmap-y := sigmadsp-regmap.o
+snd-soc-si476x-y := si476x.o
+snd-soc-sma1303-y := sma1303.o
+snd-soc-sma1307-y := sma1307.o
+snd-soc-spdif-tx-y := spdif_transmitter.o
+snd-soc-spdif-rx-y := spdif_receiver.o
+snd-soc-src4xxx-y := src4xxx.o
+snd-soc-src4xxx-i2c-y := src4xxx-i2c.o
+snd-soc-ssm2305-y := ssm2305.o
+snd-soc-ssm2518-y := ssm2518.o
+snd-soc-ssm2602-y := ssm2602.o
+snd-soc-ssm2602-spi-y := ssm2602-spi.o
+snd-soc-ssm2602-i2c-y := ssm2602-i2c.o
+snd-soc-ssm3515-y := ssm3515.o
+snd-soc-ssm4567-y := ssm4567.o
+snd-soc-sta32x-y := sta32x.o
+snd-soc-sta350-y := sta350.o
+snd-soc-sta529-y := sta529.o
+snd-soc-stac9766-y := stac9766.o
+snd-soc-sti-sas-y := sti-sas.o
+snd-soc-tas5086-y := tas5086.o
+snd-soc-tas571x-y := tas571x.o
+snd-soc-tas5720-y := tas5720.o
+snd-soc-tas5805m-y := tas5805m.o
+snd-soc-tas6424-y := tas6424.o
+snd-soc-tda7419-y := tda7419.o
+snd-soc-tas2770-y := tas2770.o
+snd-soc-tas2781-comlib-y := tas2781-comlib.o
+snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
+snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
+snd-soc-tas2781-i2c-y := tas2781-i2c.o
+snd-soc-tas2783-sdw-y := tas2783-sdw.o
+snd-soc-tfa9879-y := tfa9879.o
+snd-soc-tfa989x-y := tfa989x.o
+snd-soc-tlv320adc3xxx-y := tlv320adc3xxx.o
+snd-soc-tlv320aic23-y := tlv320aic23.o
+snd-soc-tlv320aic23-i2c-y := tlv320aic23-i2c.o
+snd-soc-tlv320aic23-spi-y := tlv320aic23-spi.o
+snd-soc-tlv320aic26-y := tlv320aic26.o
+snd-soc-tlv320aic31xx-y := tlv320aic31xx.o
+snd-soc-tlv320aic32x4-y := tlv320aic32x4.o tlv320aic32x4-clk.o
+snd-soc-tlv320aic32x4-i2c-y := tlv320aic32x4-i2c.o
+snd-soc-tlv320aic32x4-spi-y := tlv320aic32x4-spi.o
+snd-soc-tlv320aic3x-y := tlv320aic3x.o
+snd-soc-tlv320aic3x-i2c-y := tlv320aic3x-i2c.o
+snd-soc-tlv320aic3x-spi-y := tlv320aic3x-spi.o
+snd-soc-tlv320dac33-y := tlv320dac33.o
+snd-soc-tlv320adcx140-y := tlv320adcx140.o
+snd-soc-tscs42xx-y := tscs42xx.o
+snd-soc-tscs454-y := tscs454.o
+snd-soc-ts3a227e-y := ts3a227e.o
+snd-soc-twl4030-y := twl4030.o
+snd-soc-twl6040-y := twl6040.o
+snd-soc-uda1334-y := uda1334.o
+snd-soc-uda1342-y := uda1342.o
+snd-soc-uda1380-y := uda1380.o
+snd-soc-wcd-classh-y := wcd-clsh-v2.o
+snd-soc-wcd-common-y := wcd-common.o
+snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
+snd-soc-wcd9335-y := wcd9335.o
+snd-soc-wcd934x-y := wcd934x.o
+snd-soc-wcd937x-y := wcd937x.o
+snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
+snd-soc-wcd938x-y := wcd938x.o
+snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
+snd-soc-wcd939x-y := wcd939x.o
+snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
+snd-soc-wm-adsp-y := wm_adsp.o
+snd-soc-wm0010-y := wm0010.o
+snd-soc-wm1250-ev1-y := wm1250-ev1.o
+snd-soc-wm2000-y := wm2000.o
+snd-soc-wm2200-y := wm2200.o
+snd-soc-wm5100-y := wm5100.o wm5100-tables.o
+snd-soc-wm5102-y := wm5102.o
+snd-soc-wm5110-y := wm5110.o
+snd-soc-wm8350-y := wm8350.o
+snd-soc-wm8400-y := wm8400.o
+snd-soc-wm8510-y := wm8510.o
+snd-soc-wm8523-y := wm8523.o
+snd-soc-wm8524-y := wm8524.o
+snd-soc-wm8580-y := wm8580.o
+snd-soc-wm8711-y := wm8711.o
+snd-soc-wm8727-y := wm8727.o
+snd-soc-wm8728-y := wm8728.o
+snd-soc-wm8731-y := wm8731.o
+snd-soc-wm8731-i2c-y := wm8731-i2c.o
+snd-soc-wm8731-spi-y := wm8731-spi.o
+snd-soc-wm8737-y := wm8737.o
+snd-soc-wm8741-y := wm8741.o
+snd-soc-wm8750-y := wm8750.o
+snd-soc-wm8753-y := wm8753.o
+snd-soc-wm8770-y := wm8770.o
+snd-soc-wm8776-y := wm8776.o
+snd-soc-wm8782-y := wm8782.o
+snd-soc-wm8804-y := wm8804.o
+snd-soc-wm8804-i2c-y := wm8804-i2c.o
+snd-soc-wm8804-spi-y := wm8804-spi.o
+snd-soc-wm8900-y := wm8900.o
+snd-soc-wm8903-y := wm8903.o
+snd-soc-wm8904-y := wm8904.o
+snd-soc-wm8996-y := wm8996.o
+snd-soc-wm8940-y := wm8940.o
+snd-soc-wm8955-y := wm8955.o
+snd-soc-wm8960-y := wm8960.o
+snd-soc-wm8961-y := wm8961.o
+snd-soc-wm8962-y := wm8962.o
+snd-soc-wm8971-y := wm8971.o
+snd-soc-wm8974-y := wm8974.o
+snd-soc-wm8978-y := wm8978.o
+snd-soc-wm8983-y := wm8983.o
+snd-soc-wm8985-y := wm8985.o
+snd-soc-wm8988-y := wm8988.o
+snd-soc-wm8990-y := wm8990.o
+snd-soc-wm8991-y := wm8991.o
+snd-soc-wm8993-y := wm8993.o
+snd-soc-wm8994-y := wm8994.o wm8958-dsp2.o
+snd-soc-wm8995-y := wm8995.o
+snd-soc-wm8997-y := wm8997.o
+snd-soc-wm8998-y := wm8998.o
+snd-soc-wm9081-y := wm9081.o
+snd-soc-wm9090-y := wm9090.o
+snd-soc-wm9705-y := wm9705.o
+snd-soc-wm9712-y := wm9712.o
+snd-soc-wm9713-y := wm9713.o
+snd-soc-wm-hubs-y := wm_hubs.o
+snd-soc-wsa881x-y := wsa881x.o
+snd-soc-wsa883x-y := wsa883x.o
+snd-soc-wsa884x-y := wsa884x.o
+snd-soc-zl38060-y := zl38060.o
# Amp
-snd-soc-dio2125-objs := dio2125.o
-snd-soc-max9877-objs := max9877.o
-snd-soc-max98504-objs := max98504.o
-snd-soc-tpa6130a2-objs := tpa6130a2.o
-snd-soc-tas2552-objs := tas2552.o
+snd-soc-max9877-y := max9877.o
+snd-soc-max98504-y := max98504.o
+snd-soc-simple-amplifier-y := simple-amplifier.o
+snd-soc-tpa6130a2-y := tpa6130a2.o
+snd-soc-tas2552-y := tas2552.o
+snd-soc-tas2562-y := tas2562.o
+snd-soc-tas2764-y := tas2764.o
+snd-soc-tas2780-y := tas2780.o
+# Mux
+snd-soc-simple-mux-y := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -252,6 +439,9 @@ 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
@@ -265,33 +455,77 @@ 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_AK4619) += snd-soc-ak4619.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_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o
+obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
+obj-$(CONFIG_SND_SOC_AW87390) += snd-soc-aw87390.o
+obj-$(CONFIG_SND_SOC_AW88081) += snd-soc-aw88081.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_AW88166) +=snd-soc-aw88166.o
+obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o
+obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.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_CHV3_CODEC) += snd-soc-chv3-codec.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_CS_AMP_LIB) += snd-soc-cs-amp-lib.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.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_CS42L42) += snd-soc-cs42l42.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_CS40L50) += snd-soc-cs40l50.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_CS42L43) += snd-soc-cs42l43.o
+obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-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_CS42L84) += snd-soc-cs42l84.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
@@ -300,10 +534,20 @@ 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_CS48L32) += snd-soc-cs48l32.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
+obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o
+obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.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_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
@@ -312,19 +556,37 @@ 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_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o
+obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o
obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
+obj-$(CONFIG_SND_SOC_ES8323) += snd-soc-es8323.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_ES8375) += snd-soc-es8375.o
+obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o
+obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
+obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
+obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.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_L3) += snd-soc-l3.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_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
@@ -335,26 +597,51 @@ 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_MAX98388) += snd-soc-max98388.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_MT6357) += snd-soc-mt6357.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_NTP8835) += snd-soc-ntp8835.o
+obj-$(CONFIG_SND_SOC_NTP8918) += snd-soc-ntp8918.o
+obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.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_PCM1754) += snd-soc-pcm1754.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_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
@@ -362,8 +649,33 @@ 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_PCM6240) += snd-soc-pcm6240.o
+obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_PM4125_SDW) += snd-soc-pm4125-sdw.o
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125.o
+ifdef CONFIG_SND_SOC_PM4125_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125-sdw.o
+endif
+obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.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_RT_SDW_COMMON) += snd-soc-rt-sdw-common.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_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.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) += snd-soc-rt1318.o
+obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o
+obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-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
@@ -379,21 +691,45 @@ 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_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o
+obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o
+obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
+obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o
+obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o
+obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o
+obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.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_SN95031) +=snd-soc-sn95031.o
+obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
+obj-$(CONFIG_SND_SOC_SMA1307) += snd-soc-sma1307.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
-obj-$(CONFIG_SND_SOC_SIRF_AUDIO_CODEC) += sirf-audio-codec.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_SSM3515) += snd-soc-ssm3515.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
@@ -401,11 +737,24 @@ 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_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.o
+obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o
+obj-$(CONFIG_SND_SOC_TAS2783_SDW) += snd-soc-tas2783-sdw.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
@@ -415,14 +764,38 @@ 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_UDA134X) += snd-soc-uda134x.o
+obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
+obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
-obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o
+obj-$(CONFIG_SND_SOC_WCD_COMMON) += snd-soc-wcd-common.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_WCD937X) += snd-soc-wcd937x.o
+ifdef CONFIG_SND_SOC_WCD937X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o
+endif
+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_WCD939X) += snd-soc-wcd939x.o
+ifdef CONFIG_SND_SOC_WCD939X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
+endif
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
@@ -440,6 +813,8 @@ 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
@@ -479,10 +854,21 @@ 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_ZX_AUD96P22) += snd-soc-zx-aud96p22.o
+obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
+obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o
+obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o
+obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o
# Amp
-obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o
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_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
index 006627b8c3a8..04b5e1d5a653 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -11,12 +12,6 @@
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* Jarmo K. Kuronen <jarmo.kuronen@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * 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>
@@ -116,13 +111,6 @@ enum amic_idx {
AMIC_IDX_2
};
-struct ab8500_codec_drvdata_dbg {
- struct regulator *vaud;
- struct regulator *vamic1;
- struct regulator *vamic2;
- struct regulator *vdmic;
-};
-
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
@@ -1037,86 +1025,86 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
};
/* ANC FIR-coefficients configuration sequence */
-static void anc_fir(struct snd_soc_codec *codec,
+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_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
- snd_soc_write(codec, AB8500_ANCCONF5, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF6, val & 0xff);
+ 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_update_bits(codec, AB8500_ANCCONF1,
+ 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_codec *codec, unsigned int bnk,
+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_update_bits(codec, AB8500_ANCCONF1,
+ 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);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ 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);
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
} else {
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
}
} else if (par > 3) {
- snd_soc_write(codec, AB8500_ANCCONF7, 0);
- snd_soc_write(codec, AB8500_ANCCONF8, val >> 16 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF7, 0);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff);
}
- snd_soc_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF8, val & 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_update_bits(codec, AB8500_ANCCONF1,
+ 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_codec *codec,
+static void anc_configure(struct snd_soc_component *component,
bool apply_fir, bool apply_iir)
{
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int bnk, par, val;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
if (apply_fir)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), 0);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ 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_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_fir_values[par]);
- anc_fir(codec, bnk, par, val);
+ 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_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_iir_values[par]);
- anc_iir(codec, bnk, par, val);
+ anc_iir(component, bnk, par, val);
}
- dev_dbg(codec->dev, "%s: Exit.\n", __func__);
+ dev_dbg(component->dev, "%s: Exit.\n", __func__);
}
/*
@@ -1126,8 +1114,8 @@ static void anc_configure(struct snd_soc_codec *codec,
static int sid_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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;
@@ -1140,15 +1128,15 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
static int sid_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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(codec->dev, "%s: Enter\n", __func__);
+ dev_dbg(component->dev, "%s: Enter\n", __func__);
if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: ERROR: This control supports '%s' only!\n",
__func__, enum_sid_state[SID_APPLY_FIR]);
return -EIO;
@@ -1156,10 +1144,10 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
mutex_lock(&drvdata->ctrl_lock);
- sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
+ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
- dev_err(codec->dev, "%s: Sidetone busy while off!\n",
+ dev_err(component->dev, "%s: Sidetone busy while off!\n",
__func__);
status = -EPERM;
} else {
@@ -1168,18 +1156,18 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
goto out;
}
- snd_soc_write(codec, AB8500_SIDFIRADR, 0);
+ snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
- val = snd_soc_read(codec, drvdata->sid_fir_values[param]);
- snd_soc_write(codec, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_SIDFIRCOEF2, val & 0xff);
+ 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_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET),
BIT(AB8500_SIDFIRADR_FIRSIDSET));
- snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
drvdata->sid_status = SID_FIR_CONFIGURED;
@@ -1187,7 +1175,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
out:
mutex_unlock(&drvdata->ctrl_lock);
- dev_dbg(codec->dev, "%s: Exit\n", __func__);
+ dev_dbg(component->dev, "%s: Exit\n", __func__);
return status;
}
@@ -1195,8 +1183,8 @@ out:
static int anc_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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;
@@ -1208,10 +1196,10 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
static int anc_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
- struct device *dev = codec->dev;
+ 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;
@@ -1244,7 +1232,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
}
snd_soc_dapm_sync(dapm);
- anc_configure(codec, apply_fir, apply_iir);
+ anc_configure(component, apply_fir, apply_iir);
if (apply_fir) {
if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1291,8 +1279,8 @@ static int filter_control_info(struct snd_kcontrol *kcontrol,
static int filter_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1308,8 +1296,8 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
static int filter_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1926,11 +1914,11 @@ enum ab8500_filter {
* Extended interface for codec-driver
*/
-static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
+static int ab8500_audio_init_audioblock(struct snd_soc_component *component)
{
int status;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Reset audio-registers and disable 32kHz-clock output 2 */
status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
@@ -1943,26 +1931,26 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
return 0;
}
-static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
+static int ab8500_audio_setup_mics(struct snd_soc_component *component,
struct amic_settings *amics)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Set DMic-clocks to outputs */
- status = abx500_get_register_interruptible(codec->dev, AB8500_MISC,
+ 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(codec->dev,
+ status = abx500_set_register_interruptible(component->dev,
AB8500_MISC,
AB8500_GPIO_DIR4_REG,
value);
@@ -1970,41 +1958,41 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
return status;
/* Attach regulators to AMic DAPM-paths */
- dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
+ 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(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
+ 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(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
+ 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(codec->dev,
+ dev_err(component->dev,
"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
__func__, status);
return status;
}
/* Set AMic-configuration */
- dev_dbg(codec->dev, "%s: Mic 1 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 1 mic-type: %s\n", __func__,
amic_type_str(amics->mic1_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ?
0 : AB8500_ANAGAINX_ENSEMICX);
- dev_dbg(codec->dev, "%s: Mic 2 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 2 mic-type: %s\n", __func__,
amic_type_str(amics->mic2_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+ 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_codec *codec,
+static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,
enum ear_cm_voltage ear_cmv)
{
char *cmv_str;
@@ -2023,14 +2011,14 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
cmv_str = "1.58V";
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Unknown earpiece CM-voltage (%d)!\n",
__func__, (int)ear_cmv);
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
cmv_str);
- snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+ snd_soc_component_update_bits(component, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
ear_cmv);
return 0;
@@ -2040,7 +2028,7 @@ static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
unsigned int delay)
{
unsigned int mask, val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
val = 0;
@@ -2052,21 +2040,21 @@ static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported bit-delay (0x%x)!\n",
__func__, delay);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 Bit-delay: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n",
__func__, delay);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ 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_codec *codec,
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component,
unsigned int fmt)
{
unsigned int mask;
@@ -2079,22 +2067,22 @@ static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
case SND_SOC_DAIFMT_CONT: /* continuous clock */
- dev_dbg(codec->dev, "%s: IF0 Clock is continuous.\n",
+ 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(codec->dev, "%s: IF0 Clock is gated.\n",
+ dev_dbg(component->dev, "%s: IF0 Clock is gated.\n",
__func__);
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: ERROR: Unsupported clock mask (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
return 0;
}
@@ -2103,10 +2091,10 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
unsigned int mask;
unsigned int val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int status;
- dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+ dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
@@ -2114,34 +2102,35 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
BIT(AB8500_DIGIFCONF3_IF0MASTER);
val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 master.\n", __func__);
+ 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_CBS_CFS: /* codec clk & FRM slave */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 slave.\n", __func__);
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
- dev_err(dai->codec->dev,
- "%s: ERROR: The device is either a master or a slave.\n",
+ 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->codec->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ 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_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val);
/* Set clock gating */
- status = ab8500_codec_set_dai_clock_gate(codec, fmt);
+ status = ab8500_codec_set_dai_clock_gate(component, fmt);
if (status) {
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Failed to set clock gate (%d).\n",
__func__, status);
return status;
@@ -2157,27 +2146,27 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: /* I2S mode */
- dev_dbg(dai->codec->dev, "%s: IF0 Protocol: I2S\n", __func__);
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported format (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
@@ -2185,37 +2174,37 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- dev_dbg(dai->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported INV mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
return 0;
}
@@ -2224,7 +2213,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
@@ -2245,17 +2234,17 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF2_IF0WL0);
break;
default:
- dev_err(dai->codec->dev, "%s: Unsupported slot-width 0x%x\n",
+ dev_err(dai->component->dev, "%s: Unsupported slot-width 0x%x\n",
__func__, slot_width);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 slot-width: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n",
__func__, slot_width);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
/* Setup TDM clocking according to slot count */
- dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots);
+ dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots);
mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
switch (slots) {
@@ -2273,12 +2262,12 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported number of slots (%d)!\n",
__func__, slots);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
@@ -2289,7 +2278,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2297,26 +2286,26 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(tx_mask);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ 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_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ 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_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ 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->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode DA-from-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active TX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2330,7 +2319,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2338,29 +2327,29 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(rx_mask);
- snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode AD-to-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active RX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2386,7 +2375,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
},
{
.name = "ab8500-codec-dai.1",
@@ -2399,7 +2388,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
}
};
@@ -2458,10 +2447,10 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
}
}
-static int ab8500_codec_probe(struct snd_soc_codec *codec)
+static int ab8500_codec_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct device *dev = codec->dev;
+ 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;
@@ -2472,19 +2461,19 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
ab8500_codec_of_probe(dev, np, &codec_pdata);
- status = ab8500_audio_setup_mics(codec, &codec_pdata.amics);
+ 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(codec, codec_pdata.ear_cmv);
+ 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(codec);
+ status = ab8500_audio_init_audioblock(component);
if (status < 0) {
dev_err(dev, "%s: failed to init audio-block (%d)!\n",
__func__, status);
@@ -2492,13 +2481,13 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
}
/* Override HW-defaults */
- snd_soc_write(codec, AB8500_ANACONF5,
+ snd_soc_component_write(component, AB8500_ANACONF5,
BIT(AB8500_ANACONF5_HSAUTOEN));
- snd_soc_write(codec, AB8500_SHORTCIRCONF,
+ snd_soc_component_write(component, AB8500_SHORTCIRCONF,
BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
/* Add filter controls */
- status = snd_soc_add_codec_controls(codec, ab8500_filter_controls,
+ status = snd_soc_add_component_controls(component, ab8500_filter_controls,
ARRAY_SIZE(ab8500_filter_controls));
if (status < 0) {
dev_err(dev,
@@ -2523,16 +2512,17 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
return status;
}
-static const struct snd_soc_codec_driver ab8500_codec_driver = {
- .probe = ab8500_codec_probe,
- .component_driver = {
- .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),
- },
+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)
@@ -2561,7 +2551,8 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
- status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &ab8500_component_driver,
ab8500_codec_dai,
ARRAY_SIZE(ab8500_codec_dai));
if (status < 0)
@@ -2572,22 +2563,13 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
return status;
}
-static int ab8500_codec_driver_remove(struct platform_device *pdev)
-{
- dev_dbg(&pdev->dev, "%s Enter.\n", __func__);
-
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver ab8500_codec_platform_driver = {
.driver = {
.name = "ab8500-codec",
},
.probe = ab8500_codec_driver_probe,
- .remove = ab8500_codec_driver_remove,
};
module_platform_driver(ab8500_codec_platform_driver);
+MODULE_DESCRIPTION("ASoC AB8500 codec driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
index e2e54425d25e..2a6f6409f1f8 100644
--- a/sound/soc/codecs/ab8500-codec.h
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -10,12 +11,6 @@
* Mikko J. Lehto <mikko.lehto@symbio.com>,
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * 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 AB8500_CODEC_REGISTERS_H
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 440b4ce54376..d8444a083af2 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.
*/
@@ -17,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -36,8 +33,8 @@ static const struct snd_soc_dapm_route ac97_routes[] = {
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(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;
@@ -65,7 +62,7 @@ static struct snd_soc_dai_driver ac97_dai = {
.ops = &ac97_dai_ops,
};
-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;
@@ -73,7 +70,7 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+ ret = snd_ac97_bus(component->card->snd_card, 0, soc_ac97_ops,
NULL, &ac97_bus);
if (ret < 0)
return ret;
@@ -83,25 +80,25 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_set_drvdata(component, ac97);
return 0;
}
#ifdef CONFIG_PM
-static int ac97_soc_suspend(struct snd_soc_codec *codec)
+static int ac97_soc_suspend(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ 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)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
snd_ac97_resume(ac97);
@@ -112,38 +109,40 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
#define ac97_soc_resume NULL
#endif
-static const struct snd_soc_codec_driver soc_codec_dev_ac97 = {
- .probe = ac97_soc_probe,
- .suspend = ac97_soc_suspend,
- .resume = ac97_soc_resume,
-
- .component_driver = {
- .dapm_widgets = ac97_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
- .dapm_routes = ac97_routes,
- .num_dapm_routes = ARRAY_SIZE(ac97_routes),
- },
+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 int ac97_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ac97, &ac97_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ac97, &ac97_dai, 1);
}
-static int ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#ifdef CONFIG_OF
+static const struct of_device_id ac97_codec_of_match[] = {
+ { .compatible = "realtek,alc203", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac97_codec_of_match);
+#endif
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
+ .of_match_table = of_match_ptr(ac97_codec_of_match),
},
.probe = ac97_probe,
- .remove = ac97_remove,
};
module_platform_driver(ac97_codec_driver);
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index d0361caad09e..949077108bef 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
@@ -149,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;
@@ -164,7 +163,7 @@ 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_codec_get_drvdata(dai->codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(dai->component);
int word_len = 0;
/* bit size */
@@ -228,17 +227,17 @@ static struct snd_soc_dai_driver ad183x_dais[] = {
};
#ifdef CONFIG_PM
-static int ad1836_suspend(struct snd_soc_codec *codec)
+static int ad1836_suspend(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);
/* reset clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
-static int ad1836_resume(struct snd_soc_codec *codec)
+static int ad1836_resume(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);
/* restore clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
@@ -248,10 +247,10 @@ static int ad1836_resume(struct snd_soc_codec *codec)
#define ad1836_resume NULL
#endif
-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 snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(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;
@@ -277,7 +276,7 @@ static int ad1836_probe(struct snd_soc_codec *codec)
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
- ret = snd_soc_add_codec_controls(codec, ad1836_controls,
+ ret = snd_soc_add_component_controls(component, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
@@ -285,11 +284,11 @@ static int ad1836_probe(struct snd_soc_codec *codec)
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
}
- ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
+ ret = snd_soc_add_component_controls(component, ad183x_dac_controls, num_dacs * 2);
if (ret)
return ret;
- ret = snd_soc_add_codec_controls(codec, ad183x_adc_controls, num_adcs);
+ ret = snd_soc_add_component_controls(component, ad183x_adc_controls, num_adcs);
if (ret)
return ret;
@@ -306,35 +305,33 @@ static int ad1836_probe(struct snd_soc_codec *codec)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
- if (ret)
- return ret;
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_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
- return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
-static const struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
- .probe = ad1836_probe,
- .remove = ad1836_remove,
- .suspend = ad1836_suspend,
- .resume = ad1836_resume,
-
- .component_driver = {
- .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),
- },
+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[] = {
@@ -361,7 +358,7 @@ static const struct regmap_config ad1836_regmap_config = {
.max_register = AD1836_ADC_CTRL3,
.reg_defaults = ad1836_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int ad1836_spi_probe(struct spi_device *spi)
@@ -382,17 +379,11 @@ static int ad1836_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ad1836);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_ad1836, &ad183x_dais[ad1836->type], 1);
return ret;
}
-static int ad1836_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id ad1836_ids[] = {
{ "ad1835", AD1835 },
{ "ad1836", AD1836 },
@@ -408,7 +399,6 @@ static struct spi_driver ad1836_spi_driver = {
.name = "ad1836",
},
.probe = ad1836_spi_probe,
- .remove = ad1836_spi_remove,
.id_table = ad1836_ids,
};
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index dd7be0dbbc58..05711fab687a 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index 171313664bc8..6aa168e01fbb 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1936/AD1937 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -21,8 +20,7 @@ static const struct i2c_device_id ad193x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
-static int ad193x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ad193x_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -32,21 +30,14 @@ static int ad193x_i2c_probe(struct i2c_client *client,
return ad193x_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- (enum ad193x_type)id->driver_data);
-}
-
-static int ad193x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ (uintptr_t)i2c_get_match_data(client));
}
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
- .probe = ad193x_i2c_probe,
- .remove = ad193x_i2c_remove,
+ .probe = ad193x_i2c_probe,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
index 23c28573bdb7..bce96a3d81c9 100644
--- a/sound/soc/codecs/ad193x-spi.c
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1938/AD1939 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -29,12 +28,6 @@ static int ad193x_spi_probe(struct spi_device *spi)
(enum ad193x_type)id->driver_data);
}
-static int ad193x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id ad193x_spi_id[] = {
{ "ad193x", AD193X },
{ "ad1933", AD1933 },
@@ -51,7 +44,6 @@ static struct spi_driver ad193x_spi_driver = {
.name = "ad193x",
},
.probe = ad193x_spi_probe,
- .remove = ad193x_spi_remove,
.id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index d10988eec0c1..1d3c4d94b4ae 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -1,9 +1,8 @@
+// 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/module.h>
@@ -37,6 +36,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
+static const unsigned int ad193x_sb[] = {32};
+
+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_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
@@ -93,6 +99,15 @@ static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
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, "SYSCLK" },
{ "DAC Output", NULL, "DAC" },
@@ -101,7 +116,7 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC2OUT", NULL, "DAC Output" },
{ "DAC3OUT", NULL, "DAC Output" },
{ "DAC4OUT", NULL, "DAC Output" },
- { "SYSCLK", NULL, "PLL_PWR" },
+ { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll },
};
static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
@@ -128,9 +143,9 @@ static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
* 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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
@@ -146,7 +161,7 @@ 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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
unsigned int channels;
switch (slots) {
@@ -179,25 +194,28 @@ 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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
+ 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_serfmt |= AD193X_ADC_SERFMT_TDM;
+ dac_serfmt |= AD193X_DAC_SERFMT_STEREO;
break;
case SND_SOC_DAIFMT_DSP_A:
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
+ dac_serfmt |= AD193X_DAC_SERFMT_TDM;
break;
default:
if (ad193x_has_adc(ad193x))
return -EINVAL;
- break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -221,22 +239,26 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm 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 */
+ 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 */
+ 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 */
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -248,6 +270,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
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);
@@ -257,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:
@@ -275,8 +314,15 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int word_len = 0, master_rate = 0;
- struct snd_soc_codec *codec = dai->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ 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));
+
/* bit size */
switch (params_width(params)) {
@@ -307,6 +353,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
break;
}
+ 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);
@@ -321,12 +386,22 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+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 */
@@ -336,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,
},
@@ -351,38 +426,67 @@ static struct snd_soc_dai_driver ad193x_dai = {
.ops = &ad193x_dai_ops,
};
-static int ad193x_codec_probe(struct snd_soc_codec *codec)
-{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- int num, ret;
-
- /* default setting for ad193x */
+/* 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,
+};
- /* unmute dac channels */
- regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0);
- /* de-emphasis: 48kHz, powedown dac */
- regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
- /* dac in tdm mode */
- regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
+/* 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));
- /* adc only */
if (ad193x_has_adc(ad193x)) {
- /* high-pass filter enable */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+ regmap_multi_reg_write(ad193x->regmap, reg_adc_init,
+ ARRAY_SIZE(reg_adc_init));
}
+}
- /* pll input: mclki/xi */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+static int ad193x_component_probe(struct snd_soc_component *component)
+{
+ 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;
+
+ /* 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_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
ad193x_adc_snd_controls,
num);
if (ret)
@@ -408,16 +512,17 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_ad193x = {
- .probe = ad193x_codec_probe,
- .component_driver = {
- .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),
- },
+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,
};
const struct regmap_config ad193x_regmap_config = {
@@ -442,8 +547,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, ad193x);
- return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
- &ad193x_dai, 1);
+ 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);
}
EXPORT_SYMBOL_GPL(ad193x_probe);
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index 8b1e65f928d2..61f4648861d5 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -1,9 +1,8 @@
+/* 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__
@@ -31,8 +30,17 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
#define AD193X_PLL_INPUT_512 (2 << 1)
#define AD193X_PLL_INPUT_768 (3 << 1)
#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)
@@ -96,4 +104,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
#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 ce89bfb42094..3c1ae13c1aae 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.
*/
/*
@@ -96,7 +92,7 @@ static const struct regmap_config ad1980_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = ad1980_readable_reg,
@@ -205,9 +201,9 @@ static struct snd_soc_dai_driver ad1980_dai = {
#define AD1980_VENDOR_ID 0x41445300
#define AD1980_VENDOR_MASK 0xffffff00
-static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+static int ad1980_reset(struct snd_soc_component *component, int try_warm)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
unsigned int retry_cnt = 0;
int ret;
@@ -223,16 +219,16 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
* case the first nibble of data is eaten by the addr. (Tag is
* always 16 bit)
*/
- snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+ snd_soc_component_write(component, AC97_AD_SERIAL_CFG, 0x9900);
} while (retry_cnt++ < 10);
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+ dev_err(component->dev, "Failed to reset: AC97 link error\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;
@@ -240,10 +236,10 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
u16 vendor_id2;
u16 ext_status;
- ac97 = snd_soc_new_ac97_codec(codec, 0, 0);
+ ac97 = snd_soc_new_ac97_component(component, 0, 0);
if (IS_ERR(ac97)) {
ret = PTR_ERR(ac97);
- dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+ dev_err(component->dev, "Failed to register AC97 component: %d\n", ret);
return ret;
}
@@ -253,72 +249,65 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
goto err_free_ac97;
}
- snd_soc_codec_init_regmap(codec, regmap);
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
- ret = ad1980_reset(codec, 0);
+ ret = ad1980_reset(component, 0);
if (ret < 0)
goto reset_err;
- vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
+ vendor_id2 = snd_soc_component_read(component, AC97_VENDOR_ID2);
if (vendor_id2 == 0x5374) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
/* unmute captures and playbacks volume */
- snd_soc_write(codec, AC97_MASTER, 0x0000);
- snd_soc_write(codec, AC97_PCM, 0x0000);
- snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
- snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- snd_soc_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 = snd_soc_read(codec, AC97_EXTENDED_STATUS);
- snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
+ 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_codec_exit_regmap(codec);
+ snd_soc_component_exit_regmap(component);
err_free_ac97:
- snd_soc_free_ac97_codec(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)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- snd_soc_codec_exit_regmap(codec);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static const struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
- .probe = ad1980_soc_probe,
- .remove = ad1980_soc_remove,
-
- .component_driver = {
- .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),
- },
+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 int ad1980_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad1980, &ad1980_dai, 1);
-}
-
-static int ad1980_remove(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 = {
@@ -327,7 +316,6 @@ static struct platform_driver ad1980_codec_driver = {
},
.probe = ad1980_probe,
- .remove = ad1980_remove,
};
module_platform_driver(ad1980_codec_driver);
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index d8d86a0fea60..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>
@@ -54,25 +50,20 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static const struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
- .component_driver = {
- .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),
- },
+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 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 = {
@@ -81,7 +72,6 @@ static struct platform_driver ad73311_codec_driver = {
},
.probe = ad73311_probe,
- .remove = ad73311_remove,
};
module_platform_driver(ad73311_codec_driver);
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
index 19d6a6f41b12..836940f2ab92 100644
--- a/sound/soc/codecs/adau-utils.c
+++ b/sound/soc/codecs/adau-utils.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/gcd.h>
diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c
new file mode 100644
index 000000000000..73f83be38f74
--- /dev/null
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -0,0 +1,41 @@
+// 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" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
+
+static struct i2c_driver adau1372_i2c_driver = {
+ .driver = {
+ .name = "adau1372",
+ .of_match_table = adau1372_of_match,
+ },
+ .probe = 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..656bd1fabeb3
--- /dev/null
+++ b/sound/soc/codecs/adau1372-spi.c
@@ -0,0 +1,59 @@
+// 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",
+ .of_match_table = adau1372_of_match,
+ },
+ .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..fdee689cae53
--- /dev/null
+++ b/sound/soc/codecs/adau1372.c
@@ -0,0 +1,1073 @@
+// 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/mod_devicetable.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_MAPLE,
+};
+EXPORT_SYMBOL_GPL(adau1372_regmap_config);
+
+const struct of_device_id adau1372_of_match[] = {
+ { .compatible = "adi,adau1372" },
+ { }
+};
+EXPORT_SYMBOL_GPL(adau1372_of_match);
+MODULE_DEVICE_TABLE(of, adau1372_of_match);
+
+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..c55b1c24fe39
--- /dev/null
+++ b/sound/soc/codecs/adau1372.h
@@ -0,0 +1,22 @@
+/* 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;
+
+extern const struct of_device_id adau1372_of_match[];
+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
index a865945d776a..16b9b2658341 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1,26 +1,25 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gcd.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"
@@ -29,12 +28,31 @@ struct adau1373_dai {
unsigned int clk_src;
unsigned int sysclk;
bool enable_src;
- bool master;
+ bool clock_provider;
+};
+
+enum adau1373_micbias_voltage {
+ ADAU1373_MICBIAS_2_9V,
+ ADAU1373_MICBIAS_2_2V,
+ ADAU1373_MICBIAS_2_6V,
+ ADAU1373_MICBIAS_1_8V,
};
+#define ADAU1373_DRC_SIZE 13
+
struct adau1373 {
struct regmap *regmap;
struct adau1373_dai dais[3];
+
+ bool input_differential[4];
+ bool lineout_differential;
+ bool lineout_ground_sense;
+
+ unsigned int num_drc;
+ u8 drc_setting[3][ADAU1373_DRC_SIZE];
+
+ enum adau1373_micbias_voltage micbias1;
+ enum adau1373_micbias_voltage micbias2;
};
#define ADAU1373_INPUT_MODE 0x00
@@ -549,8 +567,8 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -821,14 +839,14 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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].master)
+ if (!adau1373->dais[dai].clock_provider)
return 0;
if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
@@ -836,14 +854,14 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
else
clk = "SYSCLK2";
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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';
@@ -1031,8 +1049,8 @@ static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
static int adau1373_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 adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1098,19 +1116,19 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream,
static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl = ADAU1373_DAI_MASTER;
- adau1373_dai->master = true;
+ adau1373_dai->clock_provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl = 0;
- adau1373_dai->master = false;
+ adau1373_dai->clock_provider = false;
break;
default:
return -EINVAL;
@@ -1158,7 +1176,7 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(dai->component);
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
switch (clk_id) {
@@ -1206,7 +1224,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 1,
@@ -1226,7 +1244,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 2,
@@ -1246,14 +1264,14 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+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_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int dpll_div = 0;
uint8_t pll_regs[5];
int ret;
@@ -1334,67 +1352,62 @@ static void adau1373_load_drc_settings(struct adau1373 *adau1373,
regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]);
}
-static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+static int adau1373_get_micbias(unsigned int val,
+ 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;
+ switch (val) {
+ case 2900000:
+ *micbias = ADAU1373_MICBIAS_2_9V;
+ return 0;
+ case 2200000:
+ *micbias = ADAU1373_MICBIAS_2_2V;
+ return 0;
+ case 2600000:
+ *micbias = ADAU1373_MICBIAS_2_6V;
+ return 0;
+ case 1800000:
+ *micbias = ADAU1373_MICBIAS_1_8V;
+ return 0;
default:
- break;
+ return -EINVAL;
}
- return false;
}
-static int adau1373_probe(struct snd_soc_codec *codec)
+static int adau1373_probe(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
- struct adau1373_platform_data *pdata = codec->dev->platform_data;
- bool lineout_differential = false;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
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]);
- }
+ for (i = 0; i < adau1373->num_drc; ++i) {
+ adau1373_load_drc_settings(adau1373, i,
+ adau1373->drc_setting[i]);
+ }
- snd_soc_add_codec_controls(codec, adau1373_drc_controls,
- pdata->num_drc);
+ snd_soc_add_component_controls(component, adau1373_drc_controls,
+ adau1373->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;
+ for (i = 0; i < ARRAY_SIZE(adau1373->input_differential); ++i) {
+ if (adau1373->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);
+ val = 0;
+ if (adau1373->lineout_differential)
+ val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+ if (adau1373->lineout_ground_sense)
+ val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
- lineout_differential = pdata->lineout_differential;
+ regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val);
- regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
- (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
- (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
- }
+ regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
+ (adau1373->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+ (adau1373->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
- if (!lineout_differential) {
- snd_soc_add_codec_controls(codec, adau1373_lineout2_controls,
+ if (!adau1373->lineout_differential) {
+ snd_soc_add_component_controls(component, adau1373_lineout2_controls,
ARRAY_SIZE(adau1373_lineout2_controls));
}
@@ -1404,10 +1417,10 @@ static int adau1373_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adau1373_set_bias_level(struct snd_soc_codec *codec,
+static int adau1373_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1426,9 +1439,9 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int adau1373_resume(struct snd_soc_codec *codec)
+static int adau1373_resume(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
regcache_sync(adau1373->regmap);
@@ -1453,33 +1466,94 @@ static const struct regmap_config adau1373_regmap_config = {
.volatile_reg = adau1373_register_volatile,
.max_register = ADAU1373_SOFT_RESET,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1373_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults),
};
-static const struct snd_soc_codec_driver adau1373_codec_driver = {
- .probe = adau1373_probe,
- .resume = adau1373_resume,
- .set_bias_level = adau1373_set_bias_level,
- .idle_bias_off = true,
-
- .set_pll = adau1373_set_pll,
-
- .component_driver = {
- .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),
- },
+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,
- const struct i2c_device_id *id)
+static void adau1373_reset(void *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
+
+static int adau1373_parse_fw(struct device *dev, struct adau1373 *adau1373)
+{
+ int ret, drc_count;
+ unsigned int val;
+
+ if (device_property_present(dev, "adi,input1-differential"))
+ adau1373->input_differential[0] = true;
+ if (device_property_present(dev, "adi,input2-differential"))
+ adau1373->input_differential[1] = true;
+ if (device_property_present(dev, "adi,input3-differential"))
+ adau1373->input_differential[2] = true;
+ if (device_property_present(dev, "adi,input4-differential"))
+ adau1373->input_differential[3] = true;
+
+ if (device_property_present(dev, "adi,lineout-differential"))
+ adau1373->lineout_differential = true;
+ if (device_property_present(dev, "adi,lineout-gnd-sense"))
+ adau1373->lineout_ground_sense = true;
+
+ ret = device_property_read_u32(dev, "adi,micbias1-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias1);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias1(%u)\n", val);
+ }
+
+ ret = device_property_read_u32(dev, "adi,micbias2-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias2);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias2(%u)\n", val);
+ }
+
+ drc_count = device_property_count_u8(dev, "adi,drc-settings");
+ if (drc_count < 0)
+ return 0;
+ if (drc_count % ADAU1373_DRC_SIZE != 0)
+ return dev_err_probe(dev, -EINVAL,
+ "DRC count(%u) not multiple of %u\n",
+ drc_count, ADAU1373_DRC_SIZE);
+
+ adau1373->num_drc = drc_count / ADAU1373_DRC_SIZE;
+ if (adau1373->num_drc > ARRAY_SIZE(adau1373->drc_setting))
+ return dev_err_probe(dev, -EINVAL,
+ "Too many DRC settings(%u)\n",
+ adau1373->num_drc);
+
+ ret = device_property_read_u8_array(dev, "adi,drc-settings",
+ (u8 *)&adau1373->drc_setting[0],
+ drc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read DRC settings\n");
+
+ return 0;
+}
+
+static int adau1373_i2c_probe(struct i2c_client *client)
{
struct adau1373 *adau1373;
+ struct gpio_desc *gpiod;
int ret;
adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL);
@@ -1491,33 +1565,57 @@ static int adau1373_i2c_probe(struct i2c_client *client,
if (IS_ERR(adau1373->regmap))
return PTR_ERR(adau1373->regmap);
- regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ /*
+ * If the powerdown GPIO is specified, we use it for reset. Otherwise
+ * a software reset is done.
+ */
+ gpiod = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ if (gpiod) {
+ gpiod_set_value_cansleep(gpiod, 0);
+ fsleep(10);
+
+ ret = devm_add_action_or_reset(&client->dev, adau1373_reset,
+ gpiod);
+ if (ret)
+ return ret;
+ } else {
+ regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ }
dev_set_drvdata(&client->dev, adau1373);
- ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+ ret = adau1373_parse_fw(&client->dev, adau1373);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1373_component_driver,
adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
return ret;
}
-static int adau1373_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id adau1373_i2c_id[] = {
- { "adau1373", 0 },
+ { "adau1373" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+static const struct of_device_id adau1373_of_match[] = {
+ { .compatible = "adi,adau1373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1373_of_match);
+
static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
+ .of_match_table = adau1373_of_match,
},
.probe = adau1373_i2c_probe,
- .remove = adau1373_i2c_remove,
.id_table = adau1373_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 805afac8146b..6876462d8bdb 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -1,11 +1,10 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -14,8 +13,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -23,7 +21,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
#include "adau1701.h"
@@ -107,8 +105,8 @@ static const char * const supply_names[] = {
};
struct adau1701 {
- int gpio_nreset;
- int gpio_pll_mode[2];
+ struct gpio_desc *gpio_nreset;
+ struct gpio_descs *gpio_pll_mode;
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
@@ -298,45 +296,45 @@ static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
.safeload = adau1701_safeload,
};
-static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+static int adau1701_reset(struct snd_soc_component *component, unsigned int clkdiv,
unsigned int rate)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
int ret;
+ DECLARE_BITMAP(values, 2);
sigmadsp_reset(adau1701->sigmadsp);
- if (clkdiv != ADAU1707_CLKDIV_UNSET &&
- gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
- gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) {
switch (clkdiv) {
case 64:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 0);
break;
case 256:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 1);
break;
case 384:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 0);
break;
- case 0: /* fallback */
+ case 0: /* fallback */
case 512:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 1);
break;
}
+ gpiod_multi_set_value_cansleep(adau1701->gpio_pll_mode, values);
}
adau1701->pll_clkdiv = clkdiv;
- if (gpio_is_valid(adau1701->gpio_nreset)) {
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset) {
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
- gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
@@ -348,7 +346,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
if (clkdiv != ADAU1707_CLKDIV_UNSET) {
ret = sigmadsp_setup(adau1701->sigmadsp, rate);
if (ret) {
- dev_warn(codec->dev, "Failed to load firmware\n");
+ dev_warn(component->dev, "Failed to load firmware\n");
return ret;
}
}
@@ -362,10 +360,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
return 0;
}
-static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_capture_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
unsigned int val;
@@ -403,10 +401,10 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_playback_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
@@ -435,8 +433,8 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adau1701_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 adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -447,7 +445,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
* firmware upload.
*/
if (clkdiv != adau1701->pll_clkdiv) {
- ret = adau1701_reset(codec, clkdiv, params_rate(params));
+ ret = adau1701_reset(component, clkdiv, params_rate(params));
if (ret < 0)
return ret;
}
@@ -470,26 +468,26 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
ADAU1701_DSPCTRL_SR_MASK, val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return adau1701_set_playback_pcm_format(codec, params);
+ return adau1701_set_playback_pcm_format(component, params);
else
- return adau1701_set_capture_pcm_format(codec, params);
+ 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_codec *codec = codec_dai->codec;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -548,11 +546,11 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int adau1701_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -574,11 +572,11 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
+static int adau1701_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (mute)
@@ -591,11 +589,11 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
return 0;
}
-static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -618,7 +616,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
static int adau1701_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(dai->component);
return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
}
@@ -632,8 +630,9 @@ static int adau1701_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
- .digital_mute = adau1701_digital_mute,
+ .mute_stream = adau1701_mute_stream,
.startup = adau1701_startup,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver adau1701_dai = {
@@ -653,7 +652,7 @@ static struct snd_soc_dai_driver adau1701_dai = {
.formats = ADAU1701_FORMATS,
},
.ops = &adau1701_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_OF
@@ -664,20 +663,20 @@ static const struct of_device_id adau1701_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
#endif
-static int adau1701_probe(struct snd_soc_codec *codec)
+static int adau1701_probe(struct snd_soc_component *component)
{
int i, ret;
unsigned int val;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- ret = sigmadsp_attach(adau1701->sigmadsp, &codec->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(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
@@ -690,7 +689,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
/* initalize with pre-configured pll mode settings */
- ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ ret = adau1701_reset(component, adau1701->pll_clkdiv, 0);
if (ret < 0)
goto exit_regulators_disable;
@@ -715,22 +714,20 @@ exit_regulators_disable:
return ret;
}
-static int adau1701_remove(struct snd_soc_codec *codec)
+static void adau1701_remove(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(adau1701->gpio_nreset))
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset)
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
-
- return 0;
}
#ifdef CONFIG_PM
-static int adau1701_suspend(struct snd_soc_codec *codec)
+static int adau1701_suspend(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies),
adau1701->supplies);
@@ -738,61 +735,56 @@ static int adau1701_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int adau1701_resume(struct snd_soc_codec *codec)
+static int adau1701_resume(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
- return adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ 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_codec_driver adau1701_codec_drv = {
+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,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+ .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,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = adau1701_volatile_reg,
.reg_write = adau1701_reg_write,
.reg_read = adau1701_reg_read,
};
-static int adau1701_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1701_i2c_probe(struct i2c_client *client)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
- int gpio_nreset = -EINVAL;
- int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret, i;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
@@ -826,26 +818,6 @@ static int adau1701_i2c_probe(struct i2c_client *client,
if (dev->of_node) {
- gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
- if (gpio_nreset < 0 && gpio_nreset != -ENOENT) {
- ret = gpio_nreset;
- goto exit_regulators_disable;
- }
-
- gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 0);
- if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) {
- ret = gpio_pll_mode[0];
- goto exit_regulators_disable;
- }
-
- gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 1);
- if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) {
- ret = gpio_pll_mode[1];
- goto exit_regulators_disable;
- }
-
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
@@ -854,32 +826,20 @@ static int adau1701_i2c_probe(struct i2c_client *client,
ARRAY_SIZE(adau1701->pin_config));
}
- if (gpio_is_valid(gpio_nreset)) {
- ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
- "ADAU1701 Reset");
- if (ret < 0)
- goto exit_regulators_disable;
+ 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;
}
- if (gpio_is_valid(gpio_pll_mode[0]) &&
- gpio_is_valid(gpio_pll_mode[1])) {
- ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 0");
- if (ret < 0)
- goto exit_regulators_disable;
+ adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW);
- ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 1");
- if (ret < 0)
- goto exit_regulators_disable;
+ if (IS_ERR(adau1701->gpio_pll_mode)) {
+ ret = PTR_ERR(adau1701->gpio_pll_mode);
+ goto exit_regulators_disable;
}
- adau1701->gpio_nreset = gpio_nreset;
- adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
- adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
-
i2c_set_clientdata(client, adau1701);
adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
@@ -889,7 +849,8 @@ static int adau1701_i2c_probe(struct i2c_client *client,
goto exit_regulators_disable;
}
- ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1701_component_drv,
&adau1701_dai, 1);
exit_regulators_disable:
@@ -898,17 +859,11 @@ exit_regulators_disable:
return ret;
}
-static int adau1701_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id adau1701_i2c_id[] = {
- { "adau1401", 0 },
- { "adau1401a", 0 },
- { "adau1701", 0 },
- { "adau1702", 0 },
+ { "adau1401" },
+ { "adau1401a" },
+ { "adau1701" },
+ { "adau1702" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
@@ -919,7 +874,6 @@ static struct i2c_driver adau1701_i2c_driver = {
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
- .remove = adau1701_i2c_remove,
.id_table = adau1701_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h
index 8d0949a2aec9..19b41e453a36 100644
--- a/sound/soc/codecs/adau1701.h
+++ b/sound/soc/codecs/adau1701.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAU1701_H
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 9e7f257f17f8..eba7e4f42c78 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1761.h"
-static int adau1761_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1761_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1761_i2c_probe(struct i2c_client *client,
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1761_i2c_remove(struct i2c_client *client)
+static void adau1761_i2c_remove(struct i2c_client *client)
{
adau17x1_remove(&client->dev);
- return 0;
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index a0b214be759a..7c9242c2ff94 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1761_spi_probe(struct spi_device *spi)
id->driver_data, adau1761_spi_switch_mode);
}
-static int adau1761_spi_remove(struct spi_device *spi)
+static void adau1761_spi_remove(struct spi_device *spi)
{
adau17x1_remove(&spi->dev);
- return 0;
}
static const struct spi_device_id adau1761_spi_id[] = {
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 3bc3cc559dde..1f09ea385f8a 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -29,6 +28,10 @@
#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
@@ -72,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = {
{ 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 },
@@ -122,6 +129,10 @@ 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,
};
@@ -148,6 +159,103 @@ 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),
@@ -162,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
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[] = {
@@ -255,8 +379,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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. */
@@ -432,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
- { "DSP", NULL, "Digital Clock 0" },
-
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
@@ -445,10 +567,60 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Digital Clock 1", NULL, "SYSCLK" },
};
-static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -460,7 +632,7 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(adau->regmap);
break;
case SND_SOC_BIAS_OFF:
@@ -474,9 +646,9 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
}
static enum adau1761_output_mode adau1761_get_lineout_mode(
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
if (pdata)
return pdata->lineout_mode;
@@ -484,11 +656,11 @@ static enum adau1761_output_mode adau1761_get_lineout_mode(
return ADAU1761_OUTPUT_MODE_LINE;
}
-static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -513,12 +685,13 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
if (pdata->jackdetect_active_low)
val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
adau1761_jack_detect_controls,
ARRAY_SIZE(adau1761_jack_detect_controls));
if (ret)
return ret;
- case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
+ 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)
@@ -546,11 +719,11 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
return 0;
}
-static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+static int adau1761_setup_headphone_mode(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ 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;
@@ -568,7 +741,7 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
- /* fallthrough */
+ fallthrough;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
@@ -588,7 +761,7 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
- ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
+ ret = snd_soc_add_component_controls(component, adau1761_mono_controls,
ARRAY_SIZE(adau1761_mono_controls));
if (ret)
return ret;
@@ -632,6 +805,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
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;
@@ -640,14 +817,14 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
return adau17x1_readable_register(dev, reg);
}
-static int adau1761_codec_probe(struct snd_soc_codec *codec)
+static int adau1761_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ ret = adau17x1_add_widgets(component);
if (ret < 0)
return ret;
@@ -658,20 +835,20 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
- ret = snd_soc_add_codec_controls(codec,
+ 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_codec_controls(codec,
+ 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(codec)) {
+ switch (adau1761_get_lineout_mode(component)) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
@@ -686,15 +863,19 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
return -EINVAL;
}
- ret = adau1761_setup_headphone_mode(codec);
+ ret = adau1761_setup_headphone_mode(component);
if (ret)
return ret;
- ret = adau1761_setup_digmic_jackdetect(codec);
+ ret = adau1761_setup_digmic_jackdetect(component);
if (ret)
return ret;
- if (adau->type == ADAU1761) {
+ /*
+ * 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)
@@ -705,28 +886,50 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
}
-
- ret = adau17x1_add_routes(codec);
+ /*
+ * 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_codec_driver adau1761_codec_driver = {
- .probe = adau1761_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1761_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 | \
@@ -789,11 +992,16 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
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 snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+ return devm_snd_soc_register_component(dev, &adau1761_component_driver,
+ dai_drv, 1);
}
EXPORT_SYMBOL_GPL(adau1761_probe);
@@ -806,7 +1014,7 @@ const struct regmap_config adau1761_regmap_config = {
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
index a9e0d288301e..7beabf448ad1 100644
--- a/sound/soc/codecs/adau1761.h
+++ b/sound/soc/codecs/adau1761.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 7b9d1802d159..cb67fde8d9a8 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1781.h"
-static int adau1781_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1781_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1781_i2c_probe(struct i2c_client *client,
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1781_i2c_remove(struct i2c_client *client)
+static void adau1781_i2c_remove(struct i2c_client *client)
{
adau17x1_remove(&client->dev);
- return 0;
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 9b233544d2e8..1a09633d5a88 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1781_spi_probe(struct spi_device *spi)
id->driver_data, adau1781_spi_switch_mode);
}
-static int adau1781_spi_remove(struct spi_device *spi)
+static void adau1781_spi_remove(struct spi_device *spi)
{
adau17x1_remove(&spi->dev);
- return 0;
}
static const struct spi_device_id adau1781_spi_id[] = {
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index 546071c6c0d0..faad2f9f8dd2 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -174,8 +173,8 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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. */
@@ -314,10 +313,10 @@ static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "Right Decimator", NULL, "DMIC Select" },
};
-static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+static int adau1781_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -380,14 +379,14 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
-static int adau1781_codec_probe(struct snd_soc_codec *codec)
+static int adau1781_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ ret = adau17x1_add_widgets(component);
if (ret)
return ret;
@@ -419,27 +418,27 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
return ret;
}
- ret = adau17x1_add_routes(codec);
+ ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
return 0;
}
-static const struct snd_soc_codec_driver adau1781_codec_driver = {
- .probe = adau1781_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1781_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 | \
@@ -473,7 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
@@ -498,7 +497,7 @@ int adau1781_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1781_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1781_component_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h
index 2b96e0a9ff2e..ac8b8acbdbbd 100644
--- a/sound/soc/codecs/adau1781.h
+++ b/sound/soc/codecs/adau1781.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1381/ADAU1781 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 6758f789b712..0b6b0d2115eb 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -1,10 +1,9 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -17,15 +16,21 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
+#include <linux/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",
};
@@ -60,11 +65,14 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -93,8 +101,8 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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
@@ -177,11 +185,11 @@ static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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 = { 0 };
+ struct snd_soc_dapm_update update = {};
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
@@ -205,7 +213,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
- change = snd_soc_test_bits(codec, reg, 0xff, val);
+ change = snd_soc_component_test_bits(component, reg, 0xff, val);
if (change) {
update.kcontrol = kcontrol;
update.mask = 0xff;
@@ -222,8 +230,8 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -247,7 +255,7 @@ static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
#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_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)
@@ -299,6 +307,7 @@ static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
{ "DSP", NULL, "Left Decimator" },
{ "DSP", NULL, "Right Decimator" },
+ { "DSP", NULL, "Playback" },
};
static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
@@ -312,7 +321,7 @@ static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
{ "Capture", NULL, "Right Decimator" },
};
-bool adau17x1_has_dsp(struct adau *adau)
+static bool adau17x1_has_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761:
@@ -323,13 +332,34 @@ bool adau17x1_has_dsp(struct adau *adau)
return false;
}
}
-EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+/* 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_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -353,8 +383,8 @@ static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
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_codec_get_dapm(dai->codec);
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ 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;
@@ -365,7 +395,7 @@ static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
case ADAU17X1_CLK_SRC_PLL_AUTO:
if (!adau->mclk)
return -EINVAL;
- /* Fall-through */
+ fallthrough;
case ADAU17X1_CLK_SRC_PLL:
is_pll = true;
break;
@@ -438,8 +468,8 @@ static int adau17x1_auto_pll(struct snd_soc_dai *dai,
static int adau17x1_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 adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -449,7 +479,7 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
ret = adau17x1_auto_pll(dai, params);
if (ret)
return ret;
- /* Fall-through */
+ fallthrough;
case ADAU17X1_CLK_SRC_PLL:
freq = adau->pll_freq;
break;
@@ -496,13 +526,14 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
- if (adau17x1_has_dsp(adau)) {
+
+ 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(adau, params_rate(params));
+ ret = adau17x1_setup_firmware(component, params_rate(params));
if (ret < 0)
return ret;
}
@@ -531,16 +562,17 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl0 = 0;
adau->master = false;
break;
@@ -592,8 +624,16 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
if (lrclk_pol)
ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+ /* 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;
@@ -603,7 +643,7 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
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_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
unsigned int ser_ctrl0, ser_ctrl1;
unsigned int conv_ctrl0, conv_ctrl1;
@@ -634,7 +674,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
switch (slot_width * slots) {
case 32:
- if (adau->type == ADAU1761)
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@@ -709,7 +749,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
- if (!adau17x1_has_dsp(adau))
+ if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
@@ -728,7 +768,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
static int adau17x1_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
if (adau->sigmadsp)
return sigmadsp_restrict_params(adau->sigmadsp, substream);
@@ -746,10 +786,10 @@ const struct snd_soc_dai_ops adau17x1_dai_ops = {
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
-int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
enum adau17x1_micbias_voltage micbias)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (micbias) {
case ADAU17X1_MICBIAS_0_90_AVDD:
@@ -835,36 +875,58 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate)
{
int ret;
- int dspsr;
+ 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)
- return 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);
- return ret;
+ goto err;
}
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, dsp_run);
- return 0;
+err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
-int adau17x1_add_widgets(struct snd_soc_codec *codec)
+int adau17x1_add_widgets(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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_codec_controls(codec, adau17x1_controls,
+ ret = snd_soc_add_component_controls(component, adau17x1_controls,
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
@@ -882,9 +944,9 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
if (!adau->sigmadsp)
return 0;
- ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+ ret = sigmadsp_attach(adau->sigmadsp, component);
if (ret) {
- dev_err(codec->dev, "Failed to attach firmware: %d\n",
+ dev_err(component->dev, "Failed to attach firmware: %d\n",
ret);
return ret;
}
@@ -894,10 +956,10 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
-int adau17x1_add_routes(struct snd_soc_codec *codec)
+int adau17x1_add_routes(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -920,12 +982,12 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
-int adau17x1_resume(struct snd_soc_codec *codec)
+int adau17x1_resume(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
if (adau->switch_mode)
- adau->switch_mode(codec->dev);
+ adau->switch_mode(component->dev);
regcache_sync(adau->regmap);
@@ -933,6 +995,56 @@ int adau17x1_resume(struct snd_soc_codec *codec)
}
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)
@@ -947,13 +1059,12 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
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) {
+ /* Clock is optional (for the driver) */
+ adau->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(adau->mclk))
+ return PTR_ERR(adau->mclk);
+
+ if (adau->mclk) {
adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
/*
@@ -978,8 +1089,13 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, adau);
if (firmware_name) {
- adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
- 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));
@@ -998,9 +1114,7 @@ void adau17x1_remove(struct device *dev)
{
struct adau *adau = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
- if (adau->mclk)
- clk_disable_unprepare(adau->mclk);
+ clk_disable_unprepare(adau->mclk);
}
EXPORT_SYMBOL_GPL(adau17x1_remove);
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index eaf8f933bab8..5e58abfffc3d 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -10,6 +10,7 @@
enum adau17x1_type {
ADAU1361,
ADAU1761,
+ ADAU1761_AS_1361,
ADAU1381,
ADAU1781,
};
@@ -53,24 +54,21 @@ struct adau {
struct sigmadsp *sigmadsp;
};
-int adau17x1_add_widgets(struct snd_soc_codec *codec);
-int adau17x1_add_routes(struct snd_soc_codec *codec);
+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_codec *codec,
+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_codec *codec);
+int adau17x1_resume(struct snd_soc_component *component);
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate);
-bool adau17x1_has_dsp(struct adau *adau);
-
#define ADAU17X1_CLOCK_CONTROL 0x4000
#define ADAU17X1_PLL_CONTROL 0x4002
#define ADAU17X1_REC_POWER_MGMT 0x4009
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index 21e7394a972a..441c8079246a 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1977.h"
-static int adau1977_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1977_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client,
return adau1977_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
-}
-
-static int adau1977_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
static const struct i2c_device_id adau1977_i2c_ids[] = {
@@ -48,7 +40,6 @@ static struct i2c_driver adau1977_i2c_driver = {
.name = "adau1977",
},
.probe = adau1977_i2c_probe,
- .remove = adau1977_i2c_remove,
.id_table = adau1977_i2c_ids,
};
module_i2c_driver(adau1977_i2c_driver);
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index 0b46d88b481c..e7e95e5d1911 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/of.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
@@ -46,12 +46,6 @@ static int adau1977_spi_probe(struct spi_device *spi)
id->driver_data, adau1977_spi_switch_mode);
}
-static int adau1977_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id adau1977_spi_ids[] = {
{ "adau1977", ADAU1977 },
{ "adau1978", ADAU1978 },
@@ -60,12 +54,20 @@ static const struct spi_device_id adau1977_spi_ids[] = {
};
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,
- .remove = adau1977_spi_remove,
.id_table = adau1977_spi_ids,
};
module_spi_driver(adau1977_spi_driver);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index e384f212beb2..c193a9f22f59 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/delay.h>
@@ -13,7 +12,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/platform_data/adau1977.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -25,6 +23,8 @@
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <dt-bindings/sound/adi,adau1977.h>
+
#include "adau1977.h"
#define ADAU1977_REG_POWER 0x00
@@ -124,10 +124,10 @@ struct adau1977 {
struct device *dev;
void (*switch_mode)(struct device *dev);
- unsigned int max_master_fs;
+ unsigned int max_clock_provider_fs;
unsigned int slot_width;
bool enabled;
- bool master;
+ bool clock_provider;
};
static const struct reg_default adau1977_reg_defaults[] = {
@@ -236,8 +236,6 @@ static int adau1977_reset(struct adau1977 *adau1977)
ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
ADAU1977_POWER_RESET);
regcache_cache_bypass(adau1977->regmap, false);
- if (ret)
- return ret;
return ret;
}
@@ -294,8 +292,8 @@ static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
static int adau1977_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 adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -332,7 +330,7 @@ static int adau1977_hw_params(struct snd_pcm_substream *substream,
ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
}
- if (adau1977->master) {
+ if (adau1977->clock_provider) {
switch (params_width(params)) {
case 16:
ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
@@ -471,10 +469,10 @@ err_disable_avdd:
return ret;
}
-static int adau1977_set_bias_level(struct snd_soc_codec *codec,
+static int adau1977_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@@ -483,7 +481,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = adau1977_power_enable(adau1977);
break;
case SND_SOC_BIAS_OFF:
@@ -497,7 +495,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
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_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl0, ctrl1, drv;
unsigned int slot[4];
unsigned int i;
@@ -506,7 +504,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slots == 0) {
/* 0 = No fixed slot width */
adau1977->slot_width = 0;
- adau1977->max_master_fs = 192000;
+ 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);
@@ -535,7 +533,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 24:
/* We can only generate 16 bit or 32 bit wide slots */
- if (adau1977->master)
+ if (adau1977->clock_provider)
return -EINVAL;
ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
break;
@@ -595,15 +593,15 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
adau1977->slot_width = width;
- /* In master mode the maximum bitclock is 24.576 MHz */
- adau1977->max_master_fs = min(192000, 24576000 / width / slots);
+ /* 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_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -617,18 +615,18 @@ static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- adau1977->master = false;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1977->clock_provider = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
- adau1977->master = true;
+ adau1977->clock_provider = true;
break;
default:
return -EINVAL;
@@ -704,7 +702,7 @@ static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1977_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
u64 formats = 0;
if (adau1977->slot_width == 16)
@@ -716,9 +714,10 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
- if (adau1977->master)
+ if (adau1977->clock_provider)
snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+ SNDRV_PCM_HW_PARAM_RATE, 8000,
+ adau1977->max_clock_provider_fs);
if (formats != 0)
snd_pcm_hw_constraint_mask64(substream->runtime,
@@ -729,7 +728,7 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (tristate)
@@ -790,13 +789,13 @@ static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
return true;
}
-static int adau1977_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
unsigned int mask = 0;
unsigned int clk_src;
- unsigned int ret;
+ int ret;
if (dir != SND_SOC_CLOCK_IN)
return -EINVAL;
@@ -844,10 +843,10 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adau1977_codec_probe(struct snd_soc_codec *codec)
+static int adau1977_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -865,34 +864,30 @@ static int adau1977_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver adau1977_codec_driver = {
- .probe = adau1977_codec_probe,
- .set_bias_level = adau1977_set_bias_level,
- .set_sysclk = adau1977_set_sysclk,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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)
{
- struct adau1977_platform_data *pdata = adau1977->dev->platform_data;
unsigned int micbias;
- if (pdata) {
- micbias = pdata->micbias;
- if (micbias > ADAU1977_MICBIAS_9V0)
- return -EINVAL;
-
- } else {
+ 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,
@@ -918,7 +913,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->type = type;
adau1977->regmap = regmap;
adau1977->switch_mode = switch_mode;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
adau1977->constraints.list = adau1977_rates;
adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
@@ -968,7 +963,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1977_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1977_component_driver,
&adau1977_dai, 1);
err_poweroff:
@@ -996,7 +991,7 @@ const struct regmap_config adau1977_regmap_config = {
.max_register = ADAU1977_REG_DC_HPF_CAL,
.volatile_reg = adau1977_register_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1977_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
};
diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h
index 95e714345a86..80baeb4f39b9 100644
--- a/sound/soc/codecs/adau1977.h
+++ b/sound/soc/codecs/adau1977.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
index 6384c5491de8..c9134e1de0b2 100644
--- a/sound/soc/codecs/adau7002.c
+++ b/sound/soc/codecs/adau7002.c
@@ -1,12 +1,13 @@
+// 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>
- *
- * Licensed under the GPL-2.
*/
+#include <linux/acpi.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -14,12 +15,55 @@
#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" },
};
@@ -38,27 +82,24 @@ static struct snd_soc_dai_driver adau7002_dai = {
},
};
-static const struct snd_soc_codec_driver adau7002_codec_driver = {
- .component_driver = {
- .dapm_widgets = adau7002_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
- .dapm_routes = adau7002_routes,
- .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
- },
+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 snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &adau7002_component_driver,
&adau7002_dai, 1);
}
-static int adau7002_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id adau7002_dt_ids[] = {
{ .compatible = "adi,adau7002", },
@@ -67,13 +108,21 @@ static const struct of_device_id adau7002_dt_ids[] = {
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,
- .remove = adau7002_remove,
};
module_platform_driver(adau7002_driver);
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..f9dc8f4ef9a4
--- /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_MAPLE,
+ .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"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adau7118_id);
+
+static struct i2c_driver adau7118_driver = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = 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..14259807c872
--- /dev/null
+++ b/sound/soc/codecs/adau7118.c
@@ -0,0 +1,576 @@
+// 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,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const 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;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ 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
index 055f1228c2b4..f734c71211da 100644
--- a/sound/soc/codecs/adav801.c
+++ b/sound/soc/codecs/adav801.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV801 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,18 +29,11 @@ static int adav80x_spi_probe(struct spi_device *spi)
return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int adav80x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver adav80x_spi_driver = {
.driver = {
.name = "adav801",
},
.probe = adav80x_spi_probe,
- .remove = adav80x_spi_remove,
.id_table = adav80x_spi_id,
};
module_spi_driver(adav80x_spi_driver);
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
index 52881faedcf6..8b96c41f0354 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV803 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -15,30 +14,22 @@
#include "adav80x.h"
static const struct i2c_device_id adav803_id[] = {
- { "adav803", 0 },
+ { "adav803" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
-static int adav803_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adav803_probe(struct i2c_client *client)
{
return adav80x_bus_probe(&client->dev,
devm_regmap_init_i2c(client, &adav80x_regmap_config));
}
-static int adav803_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
},
.probe = adav803_probe,
- .remove = adav803_remove,
.id_table = adav803_id,
};
module_i2c_driver(adav803_driver);
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index da7ca81f47cf..c8c0fc928211 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -1,11 +1,10 @@
+// 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>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -212,8 +211,8 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -230,14 +229,14 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
return 0;
}
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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;
}
@@ -281,9 +280,9 @@ static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
{ "AIFAUXIN", NULL, "SYSCLK" },
};
-static int adav80x_set_deemph(struct snd_soc_codec *codec)
+static int adav80x_set_deemph(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->deemph) {
@@ -315,8 +314,8 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec)
static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -324,14 +323,14 @@ static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
adav80x->deemph = deemph;
- return adav80x_set_deemph(codec);
+ return adav80x_set_deemph(component);
}
static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -365,16 +364,17 @@ static unsigned int adav80x_port_ctrl_regs[2][2] = {
static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
capture |= ADAV80X_CAPTURE_MODE_MASTER;
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
- case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -415,10 +415,10 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+static int adav80x_set_adc_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -432,10 +432,10 @@ static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+static int adav80x_set_dac_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -450,10 +450,10 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
switch (params_width(params)) {
@@ -479,10 +479,10 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
@@ -514,32 +514,32 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adav80x_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 adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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(codec, dai, params);
- adav80x_set_dac_clock(codec, rate);
+ adav80x_set_playback_pcm_format(component, dai, params);
+ adav80x_set_dac_clock(component, rate);
} else {
- adav80x_set_capture_pcm_format(codec, dai, params);
- adav80x_set_adc_clock(codec, rate);
+ adav80x_set_capture_pcm_format(component, dai, params);
+ adav80x_set_adc_clock(component, rate);
}
adav80x->rate = rate;
- adav80x_set_deemph(codec);
+ adav80x_set_deemph(component);
return 0;
}
-static int adav80x_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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) {
@@ -619,11 +619,11 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
+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_codec_get_dapm(codec);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -648,6 +648,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
break;
}
+ fallthrough;
default:
return -EINVAL;
}
@@ -694,10 +695,10 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
return 0;
}
-static int adav80x_set_bias_level(struct snd_soc_codec *codec,
+static int adav80x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAV80X_DAC_CTRL1_PD;
switch (level) {
@@ -722,10 +723,10 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
static int adav80x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
+ if (!snd_soc_component_active(component) || !adav80x->rate)
return 0;
return snd_pcm_hw_constraint_single(substream->runtime,
@@ -735,10 +736,10 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
adav80x->rate = 0;
}
@@ -799,10 +800,10 @@ static struct snd_soc_dai_driver adav80x_dais[] = {
},
};
-static int adav80x_probe(struct snd_soc_codec *codec)
+static int adav80x_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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");
@@ -816,32 +817,31 @@ static int adav80x_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adav80x_resume(struct snd_soc_codec *codec)
+static int adav80x_resume(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
regcache_sync(adav80x->regmap);
return 0;
}
-static const struct snd_soc_codec_driver adav80x_codec_driver = {
- .probe = adav80x_probe,
- .resume = adav80x_resume,
- .set_bias_level = adav80x_set_bias_level,
- .suspend_bias_off = true,
-
- .set_pll = adav80x_set_pll,
- .set_sysclk = adav80x_set_sysclk,
-
- .component_driver = {
- .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),
- },
+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)
@@ -858,7 +858,7 @@ int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, adav80x);
adav80x->regmap = regmap;
- return snd_soc_register_codec(dev, &adav80x_codec_driver,
+ return devm_snd_soc_register_component(dev, &adav80x_component_driver,
adav80x_dais, ARRAY_SIZE(adav80x_dais));
}
EXPORT_SYMBOL_GPL(adav80x_bus_probe);
@@ -870,7 +870,7 @@ const struct regmap_config adav80x_regmap_config = {
.max_register = ADAV80X_PLL_OUTE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adav80x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
};
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
index 8a1d7c09dca5..fb50ff57e317 100644
--- a/sound/soc/codecs/adav80x.h
+++ b/sound/soc/codecs/adav80x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAV80X parts
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAV80X_H
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index b7f0057c0239..44aa06e03486 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -1,13 +1,9 @@
+// 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>
@@ -58,25 +54,20 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static const struct snd_soc_codec_driver soc_codec_dev_ads117x = {
- .component_driver = {
- .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),
- },
+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 int ads117x_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ads117x, &ads117x_dai, 1);
-}
-
-static int ads117x_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ads117x, &ads117x_dai, 1);
}
#if defined(CONFIG_OF)
@@ -95,7 +86,6 @@ static struct platform_driver ads117x_codec_driver = {
},
.probe = ads117x_probe,
- .remove = ads117x_remove,
};
module_platform_driver(ads117x_codec_driver);
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index dbb184118f2e..a33cb329865c 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -1,19 +1,15 @@
+// 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -65,8 +61,8 @@ static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int val = 0;
int ret;
@@ -81,12 +77,12 @@ 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;
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
@@ -102,8 +98,8 @@ static int ak4104_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 ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -141,7 +137,7 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
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;
}
@@ -174,14 +170,14 @@ 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);
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(ak4104->regulator);
if (ret < 0) {
- dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
+ dev_err(component->dev, "Unable to enable regulator: %d\n", ret);
return ret;
}
@@ -205,30 +201,28 @@ exit_disable_regulator:
return ret;
}
-static int ak4104_remove(struct snd_soc_codec *codec)
+static void ak4104_remove(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ 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);
-
- return 0;
}
#ifdef CONFIG_PM
-static int ak4104_soc_suspend(struct snd_soc_codec *codec)
+static int ak4104_soc_suspend(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
regulator_disable(priv->regulator);
return 0;
}
-static int ak4104_soc_resume(struct snd_soc_codec *codec)
+static int ak4104_soc_resume(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(priv->regulator);
@@ -242,18 +236,18 @@ static int ak4104_soc_resume(struct snd_soc_codec *codec)
#define ak4104_soc_resume NULL
#endif /* CONFIG_PM */
-static const struct snd_soc_codec_driver soc_codec_device_ak4104 = {
- .probe = ak4104_probe,
- .remove = ak4104_remove,
- .suspend = ak4104_soc_suspend,
- .resume = ak4104_soc_resume,
-
- .component_driver = {
- .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),
- }
+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 = {
@@ -269,8 +263,8 @@ static const struct regmap_config ak4104_regmap = {
static int ak4104_spi_probe(struct spi_device *spi)
{
- struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
+ struct gpio_desc *reset_gpiod;
unsigned int val;
int ret;
@@ -298,19 +292,10 @@ static int ak4104_spi_probe(struct spi_device *spi)
return ret;
}
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&spi->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "ak4104 reset");
- if (ret < 0)
- 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
@@ -323,17 +308,11 @@ static int ak4104_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ak4104);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_device_ak4104, &ak4104_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_device_ak4104, &ak4104_dai, 1);
return ret;
}
-static int ak4104_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ak4104_of_match[] = {
{ .compatible = "asahi-kasei,ak4104", },
{ }
@@ -353,7 +332,6 @@ static struct spi_driver ak4104_spi_driver = {
},
.id_table = ak4104_id_table,
.probe = ak4104_spi_probe,
- .remove = ak4104_spi_remove,
};
module_spi_driver(ak4104_spi_driver);
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
new file mode 100644
index 000000000000..23e868e4e3fb
--- /dev/null
+++ b/sound/soc/codecs/ak4118.c
@@ -0,0 +1,416 @@
+// 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.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;
+ unsigned int i;
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {
+ kctl_new = &ak4118_iec958_controls[i];
+
+ snd_soc_component_notify_control(component, kctl_new->name);
+ }
+
+ 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" },
+ {}
+};
+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 = 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..452559d8c97b
--- /dev/null
+++ b/sound/soc/codecs/ak4375.c
@@ -0,0 +1,606 @@
+// 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.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 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 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 = {
+ RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL)
+ 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 = pm_ptr(&ak4375_pm),
+ .of_match_table = ak4375_of_match,
+ },
+ .probe = 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..57cf601d3df3
--- /dev/null
+++ b/sound/soc/codecs/ak4458.c
@@ -0,0 +1,813 @@
+// 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.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 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)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak4458_rate_constraints);
+}
+
+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 (!IS_ERR_OR_NULL(ak4458->reset)) {
+ if (active)
+ reset_control_assert(ak4458->reset);
+ else
+ reset_control_deassert(ak4458->reset);
+ usleep_range(1000, 2000);
+ }
+}
+
+static int 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 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);
+}
+
+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 = {
+ RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL)
+ 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->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 = pm_ptr(&ak4458_pm),
+ .of_match_table = ak4458_of_match,
+ },
+ .probe = 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 e3c157dc88db..aadc46a47280 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>
@@ -251,8 +248,8 @@ static const struct snd_soc_dapm_route ak4535_audio_map[] = {
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;
@@ -262,9 +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_codec *codec = dai->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
- u8 mode2 = snd_soc_read(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)
@@ -283,14 +280,14 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
}
/* set rate */
- snd_soc_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 */
@@ -308,37 +305,38 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* use 32 fs for BCLK to save power */
mode1 |= 0x4;
- snd_soc_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 = snd_soc_read(codec, AK4535_DAC);
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, AK4535_DAC);
+
if (!mute)
- snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg & ~0x20);
else
- snd_soc_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)
{
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4535_PM2, 0x80, 0);
+ 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:
- snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0);
break;
}
return 0;
@@ -351,8 +349,9 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
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 = {
@@ -372,9 +371,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
.ops = &ak4535_dai_ops,
};
-static int ak4535_resume(struct snd_soc_codec *codec)
+static int ak4535_resume(struct snd_soc_component *component)
{
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -390,23 +389,22 @@ static const struct regmap_config ak4535_regmap = {
.num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
};
-static const struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
- .resume = ak4535_resume,
- .set_bias_level = ak4535_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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,
};
-static 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;
@@ -425,20 +423,14 @@ static int ak4535_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4535);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4535, &ak4535_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4535, &ak4535_dai, 1);
return ret;
}
-static int ak4535_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4535_i2c_id[] = {
- { "ak4535", 0 },
+ { "ak4535" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
@@ -447,8 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
},
- .probe = ak4535_i2c_probe,
- .remove = ak4535_i2c_remove,
+ .probe = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
index 402de1d274bf..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
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
index 0bb4fe5c122a..b9607de5a191 100644
--- a/sound/soc/codecs/ak4554.c
+++ b/sound/soc/codecs/ak4554.c
@@ -1,13 +1,8 @@
-/*
- * ak4554.c
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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.
- */
+// 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>
@@ -61,31 +56,26 @@ static struct snd_soc_dai_driver ak4554_dai = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static const struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
- .component_driver = {
- .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),
- },
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ak4554,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ak4554,
&ak4554_dai, 1);
}
-static int ak4554_soc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static const struct of_device_id ak4554_of_match[] = {
{ .compatible = "asahi-kasei,ak4554" },
{},
@@ -98,10 +88,9 @@ static struct platform_driver ak4554_driver = {
.of_match_table = ak4554_of_match,
},
.probe = ak4554_soc_probe,
- .remove = ak4554_soc_remove,
};
module_platform_driver(ak4554_driver);
-MODULE_LICENSE("GPL");
+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
index 3d1cf4784e87..de9e43185555 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -1,24 +1,106 @@
+// 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.c -- Asahi Kasei ALSA Soc Audio driver
+ * +-------+
+ * |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|
+ * +-----------+
*
- * Copyright (C) 2015 Renesas Electronics Corporation
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Renesas SoC can handle [2, 6,8] channels.
+ * Ak4613 can handle [2,4, 8,12] channels.
*
- * 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
+ * Because of above HW connection and available channels number,
+ * Renesas could test are ...
*
- * 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.
+ * [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.h>
+#include <linux/of_graph.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -82,29 +164,74 @@
/* OCTRL */
#define OCTRL_MASK (0x3F)
-struct ak4613_formats {
- unsigned int width;
- unsigned int fmt;
-};
+/*
+ * 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 {
- struct ak4613_formats capture;
- struct ak4613_formats playback;
+ unsigned int width;
+ unsigned int fmt;
+ u8 dif;
};
struct ak4613_priv {
struct mutex lock;
- const struct ak4613_interface *iface;
- struct snd_pcm_hw_constraint_list constraint;
+ 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;
- int cnt;
};
/*
@@ -141,14 +268,24 @@ static const struct reg_default ak4613_reg[] = {
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
};
-#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
-#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+/*
+ * 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[] = {
- /* capture */ /* playback */
- /* [0] - [2] are not supported */
- [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
- [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+ /* 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,
@@ -243,9 +380,9 @@ static const struct snd_soc_dapm_route ak4613_intercon[] = {
static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct device *dev = codec->dev;
+ 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--;
@@ -254,13 +391,14 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
priv->cnt = 0;
}
if (!priv->cnt)
- priv->iface = NULL;
+ priv->ctrl1 = 0;
mutex_unlock(&priv->lock);
}
static void ak4613_hw_constraints(struct ak4613_priv *priv,
- struct snd_pcm_runtime *runtime)
+ struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
static const unsigned int ak4613_rates[] = {
32000,
44100,
@@ -271,10 +409,36 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
176400,
192000,
};
- struct snd_pcm_hw_constraint_list *constraint = &priv->constraint;
+#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;
@@ -300,17 +464,53 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
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_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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++;
-
- ak4613_hw_constraints(priv, substream->runtime);
+ mutex_unlock(&priv->lock);
return 0;
}
@@ -318,21 +518,21 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream,
static int ak4613_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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 fmt)
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
-
- fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+ 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:
@@ -342,40 +542,34 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- return 0;
-}
-
-static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
- int is_play,
- unsigned int fmt, unsigned int width)
-{
- const struct ak4613_formats *fmts;
-
- fmts = (is_play) ? &iface->playback : &iface->capture;
-
- if (fmts->fmt != fmt)
- return false;
-
- if (fmts->width != width)
- return false;
+ 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 true;
+ 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_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
- const struct ak4613_interface *iface;
- struct device *dev = codec->dev;
+ 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 is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i, ret;
- u8 fmt_ctrl, ctrl2;
+ u8 ctrl2;
rate = params_rate(params);
switch (rate) {
@@ -401,44 +595,55 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
/*
* FIXME
*
- * It doesn't support TDM at this point
+ * It doesn't have full TDM suppert yet
*/
- fmt_ctrl = NO_FMT;
ret = -EINVAL;
- iface = NULL;
mutex_lock(&priv->lock);
- if (priv->iface) {
- if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
- iface = priv->iface;
+ 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--) {
- if (!ak4613_dai_fmt_matching(ak4613_iface + i,
- is_play,
- fmt, width))
- continue;
- iface = ak4613_iface + i;
- break;
+ 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;
+ }
}
}
-
- if ((priv->iface == NULL) ||
- (priv->iface == iface)) {
- priv->iface = iface;
- ret = 0;
- }
mutex_unlock(&priv->lock);
if (ret < 0)
goto hw_params_end;
- fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
+ snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1);
+ snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2);
- snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
- snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
-
- snd_soc_update_bits(codec, ICTRL, ICTRL_MASK, priv->ic);
- snd_soc_update_bits(codec, OCTRL, OCTRL_MASK, priv->oc);
+ 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)
@@ -447,7 +652,7 @@ hw_params_end:
return ret;
}
-static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+static int ak4613_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
u8 mgmt1 = 0;
@@ -455,19 +660,19 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
mgmt1 |= RSTN;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_PREPARE:
mgmt1 |= PMADC | PMDAC;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_STANDBY:
mgmt1 |= PMVR;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_OFF:
default:
break;
}
- snd_soc_write(codec, PW_MGMT1, mgmt1);
+ snd_soc_component_write(component, PW_MGMT1, mgmt1);
return 0;
}
@@ -494,8 +699,8 @@ static void ak4613_dummy_write(struct work_struct *work)
*/
udelay(5000000 / priv->rate);
- snd_soc_component_read(component, PW_MGMT1, &mgmt1);
- snd_soc_component_read(component, PW_MGMT3, &mgmt3);
+ 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);
@@ -504,8 +709,8 @@ static void ak4613_dummy_write(struct work_struct *work)
static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
/*
* FIXME
@@ -537,12 +742,21 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
- priv->component = &codec->component;
+ 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 const 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,
@@ -550,6 +764,8 @@ static const struct snd_soc_dai_ops ak4613_dai_ops = {
.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 |\
@@ -567,50 +783,50 @@ static struct snd_soc_dai_driver ak4613_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 12,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 4,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.ops = &ak4613_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ak4613_suspend(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ 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_codec *codec)
+static int ak4613_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
-static const struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+static const struct snd_soc_component_driver soc_component_dev_ak4613 = {
.suspend = ak4613_suspend,
.resume = ak4613_resume,
.set_bias_level = ak4613_set_bias_level,
- .component_driver = {
- .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),
- },
+ .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,
@@ -618,43 +834,57 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
{
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))
+ if (!of_property_read_bool(np, prop))
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))
+ if (!of_property_read_bool(np, prop))
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,
- const struct i2c_device_id *id)
+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) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(ak4613_of_match, dev);
- if (of_id)
- regmap_cfg = of_id->data;
- } else {
- regmap_cfg = (const struct regmap_config *)id->driver_data;
- }
-
+ regmap_cfg = i2c_get_match_data(i2c);
if (!regmap_cfg)
return -EINVAL;
@@ -664,7 +894,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
ak4613_parse_of(priv, dev);
- priv->iface = NULL;
+ priv->ctrl1 = 0;
priv->cnt = 0;
priv->sysclk = 0;
INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
@@ -677,23 +907,16 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ak4613,
&ak4613_dai, 1);
}
-static int ak4613_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver ak4613_i2c_driver = {
.driver = {
.name = "ak4613-codec",
.of_match_table = ak4613_of_match,
},
.probe = ak4613_i2c_probe,
- .remove = ak4613_i2c_remove,
.id_table = ak4613_i2c_id,
};
diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c
new file mode 100644
index 000000000000..8f2442482f72
--- /dev/null
+++ b/sound/soc/codecs/ak4619.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4619.c -- Asahi Kasei ALSA SoC Audio driver
+ *
+ * Copyright (C) 2023 Renesas Electronics Corporation
+ * Khanh Le <khanh.le.xr@renesas.com>
+ *
+ * Based on ak4613.c by Kuninori Morimoto
+ * Based on da7213.c by Adam Thomson
+ * Based on ak4641.c by Harald Welte
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+/*
+ * Registers
+ */
+
+#define PWR_MGMT 0x00 /* Power Management */
+#define AU_IFF1 0x01 /* Audio I/F Format */
+#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */
+#define SYS_CLK 0x03 /* System Clock Setting */
+#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */
+#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */
+#define LADC1 0x06 /* ADC1 Lch Digital Volume */
+#define RADC1 0x07 /* ADC1 Rch Digital Volume */
+#define LADC2 0x08 /* ADC2 Lch Digital Volume */
+#define RADC2 0x09 /* ADC2 Rch Digital Volume */
+#define ADC_DF 0x0a /* ADC Digital Filter Setting */
+#define ADC_AI 0x0b /* ADC Analog Input Setting */
+#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */
+#define LDAC1 0x0E /* DAC1 Lch Digital Volume */
+#define RDAC1 0x0F /* DAC1 Rch Digital Volume */
+#define LDAC2 0x10 /* DAC2 Lch Digital Volume */
+#define RDAC2 0x11 /* DAC2 Rch Digital Volume */
+#define DAC_IS 0x12 /* DAC Input Select Setting */
+#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */
+#define DAC_MF 0x14 /* DAC Mute & Filter Setting */
+
+/*
+ * Bit fields
+ */
+
+/* Power Management */
+#define PMAD2 BIT(5)
+#define PMAD1 BIT(4)
+#define PMDA2 BIT(2)
+#define PMDA1 BIT(1)
+#define RSTN BIT(0)
+
+/* Audio_I/F Format */
+#define DCF_STEREO_I2S (0x0 << 4)
+#define DCF_STEREO_MSB (0x5 << 4)
+#define DCF_PCM_SF (0x6 << 4)
+#define DCF_PCM_LF (0x7 << 4)
+#define DSL_32 (0x3 << 2)
+#define DCF_MASK (0x7 << 4)
+#define DSL_MASK (0x3 << 2)
+#define BCKP BIT(1)
+
+/* Audio_I/F Format (Extended) */
+#define DIDL_24 (0x0 << 2)
+#define DIDL_20 (0x1 << 2)
+#define DIDL_16 (0x2 << 2)
+#define DIDL_32 (0x3 << 2)
+#define DODL_24 (0x0 << 0)
+#define DODL_20 (0x1 << 0)
+#define DODL_16 (0x2 << 0)
+#define DIDL_MASK (0x3 << 2)
+#define DODL_MASK (0x3 << 0)
+#define SLOT BIT(4)
+
+/* System Clock Setting */
+#define FS_MASK 0x7
+
+/* MIC AMP Gain */
+#define MGNL_SHIFT 4
+#define MGNR_SHIFT 0
+#define MGN_MAX 0xB
+
+/* ADC Digital Volume */
+#define VOLAD_SHIFT 0
+#define VOLAD_MAX 0xFF
+
+/* ADC Digital Filter Setting */
+#define AD1SL_SHIFT 0
+#define AD2SL_SHIFT 4
+
+/* Analog Input Select */
+#define AD1LSEL_SHIFT 6
+#define AD1RSEL_SHIFT 4
+#define AD2LSEL_SHIFT 2
+#define AD2RSEL_SHIFT 0
+
+/* ADC Mute & HPF Control */
+#define ATSPAD_SHIFT 7
+#define AD1MUTE_SHIFT 5
+#define AD2MUTE_SHIFT 6
+#define AD1MUTE_MAX 1
+#define AD2MUTE_MAX 1
+#define AD1MUTE_EN BIT(5)
+#define AD2MUTE_EN BIT(6)
+#define AD1HPFN_SHIFT 1
+#define AD1HPFN_MAX 1
+#define AD2HPFN_SHIFT 2
+#define AD2HPFN_MAX 1
+
+/* DAC Digital Volume */
+#define VOLDA_SHIFT 0
+#define VOLDA_MAX 0xFF
+
+/* DAC Input Select Setting */
+#define DAC1SEL_SHIFT 0
+#define DAC2SEL_SHIFT 2
+
+/* DAC De-Emphasis Setting */
+#define DEM1_32000 (0x3 << 0)
+#define DEM1_44100 (0x0 << 0)
+#define DEM1_48000 (0x2 << 0)
+#define DEM1_OFF (0x1 << 0)
+#define DEM2_32000 (0x3 << 2)
+#define DEM2_44100 (0x0 << 2)
+#define DEM2_48000 (0x2 << 2)
+#define DEM2_OFF (0x1 << 2)
+#define DEM1_MASK (0x3 << 0)
+#define DEM2_MASK (0x3 << 2)
+#define DEM1_SHIFT 0
+#define DEM2_SHIFT 2
+
+/* DAC Mute & Filter Setting */
+#define DA1MUTE_SHIFT 4
+#define DA1MUTE_MAX 1
+#define DA2MUTE_SHIFT 5
+#define DA2MUTE_MAX 1
+#define DA1MUTE_EN BIT(4)
+#define DA2MUTE_EN BIT(5)
+#define ATSPDA_SHIFT 7
+#define DA1SL_SHIFT 0
+#define DA2SL_SHIFT 2
+
+/* Codec private data */
+struct ak4619_priv {
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list constraint;
+ int deemph_en;
+ unsigned int playback_rate;
+ unsigned int sysclk;
+};
+
+/*
+ * DAC Volume
+ *
+ * max : 0x00 : +12.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -115.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1);
+
+/*
+ * MIC Volume
+ *
+ * max : 0x0B : +27.0 dB
+ * ( 3 dB step )
+ * min: 0x00 : -6.0 dB
+ */
+static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0);
+
+/*
+ * ADC Volume
+ *
+ * max : 0x00 : +24.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -103.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+/* ADC & DAC Volume Level Transition Time select */
+static const char * const ak4619_vol_trans_txt[] = {
+ "4/fs", "16/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt);
+
+/* ADC Digital Filter select */
+static const char * const ak4619_adc_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter",
+ "Voice Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt);
+
+/* DAC De-Emphasis Filter select */
+static const char * const ak4619_dac_de_emp_txt[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt);
+
+/* DAC Digital Filter select */
+static const char * const ak4619_dac_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt);
+
+/*
+ * Control functions
+ */
+
+static void ak4619_set_deemph(struct snd_soc_component *component)
+{
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ u8 dem = 0;
+
+ if (!ak4619->deemph_en)
+ return;
+
+ switch (ak4619->playback_rate) {
+ case 32000:
+ dem |= DEM1_32000 | DEM2_32000;
+ break;
+ case 44100:
+ dem |= DEM1_44100 | DEM2_44100;
+ break;
+ case 48000:
+ dem |= DEM1_48000 | DEM2_48000;
+ break;
+ default:
+ dem |= DEM1_OFF | DEM2_OFF;
+ break;
+ }
+ snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem);
+}
+
+static int ak4619_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ int deemph_en = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ switch (deemph_en) {
+ case 0:
+ case 1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4619->deemph_en != deemph_en)
+ ret = 1; /* The value changed */
+
+ ak4619->deemph_en = deemph_en;
+ ak4619_set_deemph(component);
+
+ return ret;
+}
+
+static int ak4619_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = ak4619->deemph_en;
+
+ return 0;
+};
+
+/*
+ * KControls
+ */
+static const struct snd_kcontrol_new ak4619_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+ SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+
+ SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+ SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+
+ /* Volume Level Transition Time controls */
+ SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans),
+ SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans),
+
+ /* Mute controls */
+ SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1),
+ SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1),
+
+ SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1),
+ SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1),
+
+ /* Filter controls */
+ SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil),
+ SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil),
+
+ SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1),
+ SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1),
+
+ SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp),
+ SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp),
+
+ SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil),
+ SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph),
+};
+
+/*
+ * DAPM
+ */
+
+/* Analog input mode */
+static const char * const ak4619_analog_in_txt[] = {
+ "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt);
+
+static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in);
+static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in);
+static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in);
+static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in);
+
+/* DAC source mux */
+static const char * const ak4619_dac_in_txt[] = {
+ "SDIN1", "SDIN2", "SDOUT1", "SDOUT2"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt);
+
+static const struct snd_kcontrol_new ak4619_dac_1_in_mux =
+ SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in);
+static const struct snd_kcontrol_new ak4619_dac_2_in_mux =
+ SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in);
+
+static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = {
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN5L"),
+
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("AIN5R"),
+
+ SND_SOC_DAPM_INPUT("MIC1L"),
+ SND_SOC_DAPM_INPUT("MIC1R"),
+ SND_SOC_DAPM_INPUT("MIC2L"),
+ SND_SOC_DAPM_INPUT("MIC2R"),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux),
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux),
+ SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux),
+};
+
+static const struct snd_soc_dapm_route ak4619_intercon[] = {
+ /* Dest Connecting Widget Source */
+
+ /* Output path */
+ {"AOUT1L", NULL, "DAC1"},
+ {"AOUT2L", NULL, "DAC2"},
+
+ {"AOUT1R", NULL, "DAC1"},
+ {"AOUT2R", NULL, "DAC2"},
+
+ {"DAC1", NULL, "DAC 1 Source MUX"},
+ {"DAC2", NULL, "DAC 2 Source MUX"},
+
+ {"DAC 1 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 1 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"},
+
+ {"DAC 2 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 2 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"},
+
+ /* Input path */
+ {"SDOUT1", NULL, "ADC1"},
+ {"SDOUT2", NULL, "ADC2"},
+
+ {"ADC1", NULL, "Analog Input 1 Left MUX"},
+ {"ADC1", NULL, "Analog Input 1 Right MUX"},
+
+ {"ADC2", NULL, "Analog Input 2 Left MUX"},
+ {"ADC2", NULL, "Analog Input 2 Right MUX"},
+
+ {"Analog Input 1 Left MUX", "Differential", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"},
+
+ {"Analog Input 1 Right MUX", "Differential", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"},
+
+ {"Analog Input 2 Left MUX", "Differential", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"},
+
+ {"Analog Input 2 Right MUX", "Differential", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"},
+
+ {"MIC1L", NULL, "AIN1L"},
+ {"MIC1L", NULL, "AIN2L"},
+
+ {"MIC1R", NULL, "AIN1R"},
+ {"MIC1R", NULL, "AIN2R"},
+
+ {"MIC2L", NULL, "AIN4L"},
+ {"MIC2L", NULL, "AIN5L"},
+
+ {"MIC2R", NULL, "AIN4R"},
+ {"MIC2R", NULL, "AIN5R"},
+};
+
+static const struct reg_default ak4619_reg_defaults[] = {
+ { PWR_MGMT, 0x00 },
+ { AU_IFF1, 0x0C },
+ { AU_IFF2, 0x0C },
+ { SYS_CLK, 0x00 },
+ { MIC_AMP1, 0x22 },
+ { MIC_AMP2, 0x22 },
+ { LADC1, 0x30 },
+ { RADC1, 0x30 },
+ { LADC2, 0x30 },
+ { RADC2, 0x30 },
+ { ADC_DF, 0x00 },
+ { ADC_AI, 0x00 },
+ { ADC_MHPF, 0x00 },
+ { LDAC1, 0x18 },
+ { RDAC1, 0x18 },
+ { LDAC2, 0x18 },
+ { RDAC2, 0x18 },
+ { DAC_IS, 0x04 },
+ { DAC_DEMP, 0x05 },
+ { DAC_MF, 0x0A },
+};
+
+static int ak4619_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ u8 pwr_ctrl = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pwr_ctrl |= RSTN;
+ fallthrough;
+ case SND_SOC_BIAS_PREPARE:
+ pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2;
+ fallthrough;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_component_write(component, PWR_MGMT, pwr_ctrl);
+
+ return 0;
+}
+
+static int ak4619_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 ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ unsigned int width;
+ unsigned int rate;
+ unsigned int fs;
+ bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 dai_ctrl = 0;
+ u8 clk_mode = 0;
+
+ width = params_width(params);
+ switch (width) {
+ case 16:
+ dai_ctrl |= is_play ? DIDL_16 : DODL_16;
+ break;
+ case 20:
+ dai_ctrl |= is_play ? DIDL_20 : DODL_20;
+ break;
+ case 24:
+ dai_ctrl |= is_play ? DIDL_24 : DODL_24;
+ break;
+ case 32:
+ if (is_play)
+ dai_ctrl |= DIDL_32;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rate = params_rate(params);
+ if (rate)
+ fs = ak4619->sysclk / rate;
+ else
+ return -EINVAL;
+
+ switch (rate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ switch (fs) {
+ case 256:
+ clk_mode |= (0x0 << 0);
+ break;
+ case 384:
+ clk_mode |= (0x2 << 0);
+ break;
+ case 512:
+ clk_mode |= (0x3 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 64000:
+ case 88200:
+ case 96000:
+ if (fs == 256)
+ clk_mode |= (0x1 << 0);
+ else
+ return -EINVAL;
+ break;
+ case 176400:
+ case 192000:
+ if (fs == 128)
+ clk_mode |= (0x4 << 0);
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode);
+ snd_soc_component_update_bits(component, AU_IFF2,
+ is_play ? DIDL_MASK : DODL_MASK, dai_ctrl);
+
+ if (is_play) {
+ ak4619->playback_rate = rate;
+ ak4619_set_deemph(component);
+ }
+
+ return 0;
+}
+
+static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 dai_fmt1 = 0;
+ u8 dai_fmt2 = 0;
+
+ /* Set clock normal/inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_fmt1 |= BCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ default:
+ return -EINVAL;
+ }
+
+ /* Only Stereo modes are supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_fmt1 |= DCF_STEREO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_fmt1 |= DCF_STEREO_MSB;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+ dai_fmt1 |= DCF_PCM_SF;
+ dai_fmt2 |= SLOT;
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+ dai_fmt1 |= DCF_PCM_LF;
+ dai_fmt2 |= SLOT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only slave mode is support */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 64 BICK per LRCLK is supported */
+ dai_fmt1 |= DSL_32;
+
+ snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK |
+ DSL_MASK | BCKP, dai_fmt1);
+ snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2);
+
+ return 0;
+}
+
+static int ak4619_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 ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619->sysclk = freq;
+
+ return 0;
+}
+
+static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0);
+ snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0);
+
+ return 0;
+}
+
+static void ak4619_hw_constraints(struct ak4619_priv *ak4619,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint;
+ int ak4619_rate_mask = 0;
+ unsigned int fs;
+ int i;
+ static const unsigned int ak4619_sr[] = {
+ 8000,
+ 11025,
+ 12000,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ };
+
+ /*
+ * [8kHz - 48kHz] : 256fs, 384fs or 512fs
+ * [64kHz - 96kHz] : 256fs
+ * [176.4kHz, 192kHz] : 128fs
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) {
+ fs = ak4619->sysclk / ak4619_sr[i];
+
+ switch (fs) {
+ case 512:
+ case 384:
+ case 256:
+ ak4619_rate_mask |= (1 << i);
+ break;
+ case 128:
+ switch (i) {
+ case (ARRAY_SIZE(ak4619_sr) - 1):
+ case (ARRAY_SIZE(ak4619_sr) - 2):
+ ak4619_rate_mask |= (1 << i);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ constraint->list = ak4619_sr;
+ constraint->mask = ak4619_rate_mask;
+ constraint->count = ARRAY_SIZE(ak4619_sr);
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint);
+};
+
+#define PLAYBACK_MODE 0
+#define CAPTURE_MODE 1
+
+static int ak4619_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619_hw_constraints(ak4619, substream->runtime);
+
+ return 0;
+}
+
+static u64 ak4619_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 */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops ak4619_dai_ops = {
+ .startup = ak4619_dai_startup,
+ .set_sysclk = ak4619_dai_set_sysclk,
+ .set_fmt = ak4619_dai_set_fmt,
+ .hw_params = ak4619_dai_hw_params,
+ .mute_stream = ak4619_dai_mute,
+ .auto_selectable_formats = ak4619_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats),
+};
+
+static const struct snd_soc_component_driver soc_component_dev_ak4619 = {
+ .set_bias_level = ak4619_set_bias_level,
+ .controls = ak4619_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4619_snd_controls),
+ .dapm_widgets = ak4619_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets),
+ .dapm_routes = ak4619_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4619_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4619_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x14,
+ .reg_defaults = ak4619_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct of_device_id ak4619_of_match[] = {
+ { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4619_of_match);
+
+static const struct i2c_device_id ak4619_i2c_id[] = {
+ { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id);
+
+#define AK4619_RATES SNDRV_PCM_RATE_8000_192000
+
+#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4619_dai = {
+ .name = "ak4619-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_DAC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_ADC_FORMATS,
+ },
+ .ops = &ak4619_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int ak4619_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct ak4619_priv *ak4619;
+ int ret;
+
+ ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL);
+ if (!ak4619)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4619);
+
+ ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg);
+ if (IS_ERR(ak4619->regmap)) {
+ ret = PTR_ERR(ak4619->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619,
+ &ak4619_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register ak4619 component: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver ak4619_i2c_driver = {
+ .driver = {
+ .name = "ak4619-codec",
+ .of_match_table = ak4619_of_match,
+ },
+ .probe = ak4619_i2c_probe,
+ .id_table = ak4619_i2c_id,
+};
+module_i2c_driver(ak4619_i2c_driver);
+
+MODULE_DESCRIPTION("SoC AK4619 driver");
+MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 60142ff32d4f..ec33e7d73c6c 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4641.c -- AK4641 ALSA Soc Audio driver
*
@@ -5,10 +6,6 @@
* Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
*
* Based on ak4535.c by Richard Purdie
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -70,9 +67,9 @@ static const struct reg_default ak4641_reg_defaults[] = {
static const int deemph_settings[] = {44100, 0, 48000, 32000};
-static int ak4641_set_deemph(struct snd_soc_codec *codec)
+static int ak4641_set_deemph(struct snd_soc_component *component)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
int i, best = 0;
for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
@@ -86,16 +83,16 @@ static int ak4641_set_deemph(struct snd_soc_codec *codec)
best = i;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+ dev_dbg(component->dev, "Set deemphasis %d\n", best);
- return snd_soc_update_bits(codec, AK4641_DAC, 0x3, 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -103,14 +100,14 @@ static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
ak4641->deemph = deemph;
- return ak4641_set_deemph(codec);
+ return ak4641_set_deemph(component);
}
static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -307,8 +304,8 @@ static const struct snd_soc_dapm_route ak4641_audio_map[] = {
static int ak4641_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 ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
ak4641->sysclk = freq;
return 0;
@@ -318,8 +315,8 @@ 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_codec *codec = dai->codec;
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -340,16 +337,16 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
mode2 = (0x0 << 5);
break;
default:
- dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
return -EINVAL;
}
- snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+ 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(codec);
+ ak4641_set_deemph(component);
}
return 0;
@@ -358,7 +355,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
static int ak4641_pcm_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 btif;
int ret;
@@ -380,7 +377,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- ret = snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+ ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
if (ret < 0)
return ret;
@@ -390,7 +387,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ak4641_i2s_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 */
@@ -405,34 +402,34 @@ static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, AK4641_MODE1, mode1);
+ return snd_soc_component_write(component, AK4641_MODE1, mode1);
}
-static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
}
-static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+static int ak4641_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ 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_update_bits(codec, AK4641_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* mute */
- snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ 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);
@@ -442,16 +439,16 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
ret = regcache_sync(ak4641->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ 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_update_bits(codec, AK4641_PM1, 0x80, 0);
+ 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))
@@ -470,15 +467,17 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
.hw_params = ak4641_i2s_hw_params,
.set_fmt = ak4641_i2s_set_dai_fmt,
- .digital_mute = ak4641_mute,
+ .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,
- .digital_mute = ak4641_mute,
+ .mute_stream = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4641_dai[] = {
@@ -500,7 +499,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "ak4641-voice",
@@ -520,21 +519,22 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_pcm_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
- .component_driver = {
- .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),
- },
+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 = true,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4641_regmap = {
@@ -547,8 +547,7 @@ static const struct regmap_config ak4641_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4641_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4641_i2c_probe(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
@@ -583,7 +582,8 @@ static int ak4641_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4641);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_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;
@@ -604,12 +604,10 @@ err_out:
return ret;
}
-static int ak4641_i2c_remove(struct i2c_client *i2c)
+static void ak4641_i2c_remove(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
- snd_soc_unregister_codec(&i2c->dev);
-
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 0);
@@ -618,12 +616,10 @@ static int ak4641_i2c_remove(struct i2c_client *i2c)
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
-
- return 0;
}
static const struct i2c_device_id ak4641_i2c_id[] = {
- { "ak4641", 0 },
+ { "ak4641" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 29530c567bd9..fe035d2fc913 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -1,17 +1,13 @@
-/*
- * 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 **
*
@@ -28,7 +24,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -178,19 +174,19 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = {
static int ak4642_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
case SND_SOC_DAPM_PRE_PMU:
/* Power save mode ON */
- snd_soc_update_bits(codec, SG_SL2, LOPS, LOPS);
+ 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_update_bits(codec, SG_SL2, LOPS, 0);
+ snd_soc_component_update_bits(component, SG_SL2, LOPS, 0);
break;
}
@@ -282,7 +278,7 @@ 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) {
/*
@@ -295,8 +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_write(codec, L_IVC, 0x91); /* volume */
- snd_soc_write(codec, R_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, L_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, R_IVC, 0x91); /* volume */
} else {
/*
* start stereo input
@@ -311,11 +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.
*/
- snd_soc_update_bits(codec, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
- snd_soc_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
- snd_soc_write(codec, ALC_CTL1, ALC | LMTH0);
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, 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;
@@ -325,22 +321,22 @@ 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) {
} 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 ak4642_priv *priv = snd_soc_codec_get_drvdata(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;
@@ -382,33 +378,33 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
if (extended_freq && !priv->drvdata->extended_frequencies)
return -EINVAL;
- snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
+ 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 | MCKO | PMPLL, 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;
@@ -425,12 +421,12 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+ snd_soc_component_update_bits(component, MD_CTL1, DIF_MASK, data);
return 0;
}
-static int ak4642_set_mcko(struct snd_soc_codec *codec,
+static int ak4642_set_mcko(struct snd_soc_component *component,
u32 frequency)
{
static const u32 fs_list[] = {
@@ -458,7 +454,7 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec,
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_write(codec, MD_CTL2,
+ snd_soc_component_write(component, MD_CTL2,
PSs(ps) | FSs(fs));
return 0;
}
@@ -472,25 +468,25 @@ 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;
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
u32 rate = clk_get_rate(priv->mcko);
if (!rate)
rate = params_rate(params) * 256;
- return ak4642_set_mcko(codec, rate);
+ return ak4642_set_mcko(component, rate);
}
-static int ak4642_set_bias_level(struct snd_soc_codec *codec,
+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_write(codec, PW_MGMT1, 0x00);
+ snd_soc_component_write(component, PW_MGMT1, 0x00);
break;
default:
- snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMVCM, PMVCM);
break;
}
@@ -520,49 +516,49 @@ static struct snd_soc_dai_driver ak4642_dai = {
.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_suspend(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
return 0;
}
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
regcache_cache_only(regmap, false);
regcache_sync(regmap);
return 0;
}
-static int ak4642_probe(struct snd_soc_codec *codec)
+static int ak4642_probe(struct snd_soc_component *component)
{
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
if (priv->mcko)
- ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+ ak4642_set_mcko(component, clk_get_rate(priv->mcko));
return 0;
}
-static const 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,
.set_bias_level = ak4642_set_bias_level,
- .component_driver = {
- .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),
- },
+ .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 = {
@@ -632,35 +628,23 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
#define ak4642_of_parse_mcko(d) 0
#endif
-static const struct of_device_id ak4642_of_match[];
-static int ak4642_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4642_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
- struct device_node *np = dev->of_node;
- const struct ak4642_drvdata *drvdata = NULL;
+ const struct ak4642_drvdata *drvdata;
struct regmap *regmap;
struct ak4642_priv *priv;
struct clk *mcko = NULL;
- if (np) {
- const struct of_device_id *of_id;
-
+ if (dev_fwnode(dev)) {
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 {
- drvdata = (const struct ak4642_drvdata *)id->driver_data;
}
- if (!drvdata) {
- dev_err(dev, "Unknown device type\n");
- return -EINVAL;
- }
+ drvdata = i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(dev, -EINVAL, "Unknown device type\n");
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -675,21 +659,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(dev,
- &soc_codec_dev_ak4642, &ak4642_dai, 1);
-}
-
-static int ak4642_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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);
@@ -697,7 +675,7 @@ static const struct i2c_device_id ak4642_i2c_id[] = {
{ "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);
@@ -707,7 +685,6 @@ static struct i2c_driver ak4642_i2c_driver = {
.of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
- .remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
@@ -715,4 +692,4 @@ 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 dcfdff56fc5a..d545aa2e0a39 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -1,14 +1,9 @@
+// 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>
@@ -163,15 +158,15 @@ 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 = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, AK4671_MUTEN);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, 0);
break;
}
@@ -427,10 +422,10 @@ 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)) {
@@ -465,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;
}
@@ -473,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) {
@@ -511,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:
@@ -537,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) {
@@ -557,24 +552,24 @@ 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)
{
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+ 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;
}
return 0;
@@ -610,16 +605,17 @@ static struct snd_soc_dai_driver ak4671_dai = {
.ops = &ak4671_dai_ops,
};
-static const struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
- .set_bias_level = ak4671_set_bias_level,
- .component_driver = {
- .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),
- },
+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 const struct regmap_config ak4671_regmap = {
@@ -632,8 +628,7 @@ static const struct regmap_config ak4671_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4671_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -645,19 +640,13 @@ static int ak4671_i2c_probe(struct i2c_client *client,
return ret;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ak4671, &ak4671_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_ak4671, &ak4671_dai, 1);
return ret;
}
-static int ak4671_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4671_i2c_id[] = {
- { "ak4671", 0 },
+ { "ak4671" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
@@ -667,7 +656,6 @@ static struct i2c_driver ak4671_i2c_driver = {
.name = "ak4671-codec",
},
.probe = ak4671_i2c_probe,
- .remove = ak4671_i2c_remove,
.id_table = ak4671_i2c_id,
};
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
index 394a34d3f50a..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
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index d0e16c03815c..6525d50b7ab2 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -1,20 +1,18 @@
+// 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>
- *
- * 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/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#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 <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -24,7 +22,7 @@ static const char * const supply_names[] = {
};
struct ak5386_priv {
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@@ -38,30 +36,29 @@ static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
{ "Capture", NULL, "AINR" },
};
-static int ak5386_soc_probe(struct snd_soc_codec *codec)
+static int ak5386_soc_probe(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
-static int ak5386_soc_remove(struct snd_soc_codec *codec)
+static void ak5386_soc_remove(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
- return 0;
}
#ifdef CONFIG_PM
-static int ak5386_soc_suspend(struct snd_soc_codec *codec)
+static int ak5386_soc_suspend(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int ak5386_soc_resume(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
#else
@@ -69,28 +66,29 @@ static int ak5386_soc_resume(struct snd_soc_codec *codec)
#define ak5386_soc_resume NULL
#endif /* CONFIG_PM */
-static const struct snd_soc_codec_driver soc_codec_ak5386 = {
- .probe = ak5386_soc_probe,
- .remove = ak5386_soc_remove,
- .suspend = ak5386_soc_suspend,
- .resume = ak5386_soc_resume,
- .component_driver = {
- .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),
- },
+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_codec *codec = codec_dai->codec;
+ 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(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -101,8 +99,8 @@ static int ak5386_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 ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
/*
* From the datasheet:
@@ -114,8 +112,7 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
* the AK5386 in power-down mode (PDN pin = “L”).
*/
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 1);
+ gpiod_set_value(priv->reset_gpio, 1);
return 0;
}
@@ -123,11 +120,10 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
static int ak5386_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
+ gpiod_set_value(priv->reset_gpio, 0);
return 0;
}
@@ -171,7 +167,6 @@ static int ak5386_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->reset_gpio = -EINVAL;
dev_set_drvdata(dev, priv);
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
@@ -182,29 +177,19 @@ static int ak5386_probe(struct platform_device *pdev)
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);
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get AK5386 reset GPIO\n");
- 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;
+ gpiod_set_consumer_name(priv->reset_gpio, "AK5386 Reset");
- return snd_soc_register_codec(dev, &soc_codec_ak5386,
+ return devm_snd_soc_register_component(dev, &soc_component_ak5386,
&ak5386_dai, 1);
}
-static int ak5386_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver ak5386_driver = {
.probe = ak5386_probe,
- .remove = ak5386_remove,
.driver = {
.name = "ak5386",
.of_match_table = of_match_ptr(ak5386_dt_ids),
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
new file mode 100644
index 000000000000..683f3e472f50
--- /dev/null
+++ b/sound/soc/codecs/ak5558.c
@@ -0,0 +1,508 @@
+// 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.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 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 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 = {
+ RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL)
+ 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 = pm_ptr(&ak5558_pm),
+ },
+ .probe = 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
index 1db965a93632..fbf723758079 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5623.c -- alc562[123] ALSA Soc Audio driver
*
@@ -6,13 +7,7 @@
*
* Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
- *
* Based on WM8753.c
- *
- * 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>
@@ -47,28 +42,28 @@ struct alc5623_priv {
unsigned int jack_det_ctrl;
};
-static inline int alc5623_reset(struct snd_soc_codec *codec)
+static inline int alc5623_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, ALC5623_RESET, 0);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_write(codec, ALC5623_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0);
break;
}
@@ -526,7 +521,7 @@ 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -534,12 +529,12 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -ENODEV;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ 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_read(codec, ALC5623_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
return 0;
@@ -575,13 +570,13 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (!pll_div)
return -EINVAL;
- snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
- snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ 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_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
return 0;
}
@@ -604,9 +599,9 @@ static const struct _coeff_div coeff_div[] = {
{384*1, 0x0c6b},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -622,8 +617,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5623_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 alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 8192000:
@@ -643,15 +638,15 @@ static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5623_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;
u16 iface = 0;
- /* 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:
iface = ALC5623_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5623_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -695,18 +690,18 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ 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_codec *codec = dai->codec;
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, ALC5623_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
iface &= ~ALC5623_DAI_I2S_DL_MASK;
/* bit size */
@@ -728,30 +723,30 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- dev_dbg(codec->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
+ dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
__func__, alc5623->sysclk, rate, coeff);
- snd_soc_write(codec, ALC5623_STEREO_AD_DA_CLK_CTRL, 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)
+static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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_read(codec, ALC5623_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5623_MISC_CTRL, mute_reg);
}
#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
@@ -769,60 +764,60 @@ static int alc5623_mute(struct snd_soc_dai *dai, int mute)
(ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
| ALC5623_PWR_ADD1_HP_OUT_AMP)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_PWR_ADD1_SOFTGEN_EN,
ALC5623_PWR_ADD1_SOFTGEN_EN);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ 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_write(codec, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
+ 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_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN_5622);
else
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ 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_codec *codec,
+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(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2,
ALC5623_PWR_ADD2_VREF);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3,
+ 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_write(codec, ALC5623_PWR_MANAG_ADD2, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
+ 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;
@@ -834,10 +829,11 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5623_dai_ops = {
.hw_params = alc5623_pcm_hw_params,
- .digital_mute = alc5623_mute,
+ .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 = {
@@ -862,25 +858,25 @@ static struct snd_soc_dai_driver alc5623_dai = {
.ops = &alc5623_dai_ops,
};
-static int alc5623_suspend(struct snd_soc_codec *codec)
+static int alc5623_suspend(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int alc5623_resume(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to sync register cache: %d\n",
+ dev_err(component->dev, "Failed to sync register cache: %d\n",
ret);
regcache_cache_only(alc5623->regmap, true);
return ret;
@@ -889,41 +885,41 @@ static int alc5623_resume(struct snd_soc_codec *codec)
return 0;
}
-static int alc5623_probe(struct snd_soc_codec *codec)
+static int alc5623_probe(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
- alc5623_reset(codec);
+ alc5623_reset(component);
if (alc5623->add_ctrl) {
- snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
+ snd_soc_component_write(component, ALC5623_ADD_CTRL_REG,
alc5623->add_ctrl);
}
if (alc5623->jack_det_ctrl) {
- snd_soc_write(codec, 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_codec_controls(codec, alc5621_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5621_vol_snd_controls,
ARRAY_SIZE(alc5621_vol_snd_controls));
break;
case 0x22:
- snd_soc_add_codec_controls(codec, alc5622_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5622_vol_snd_controls,
ARRAY_SIZE(alc5622_vol_snd_controls));
break;
case 0x23:
- snd_soc_add_codec_controls(codec, alc5623_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_vol_snd_controls,
ARRAY_SIZE(alc5623_vol_snd_controls));
break;
default:
return -EINVAL;
}
- snd_soc_add_codec_controls(codec, alc5623_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_snd_controls,
ARRAY_SIZE(alc5623_snd_controls));
snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets,
@@ -951,12 +947,15 @@ static int alc5623_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_device_alc5623 = {
- .probe = alc5623_probe,
- .suspend = alc5623_suspend,
- .resume = alc5623_resume,
- .set_bias_level = alc5623_set_bias_level,
- .suspend_bias_off = true,
+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 = {
@@ -968,19 +967,27 @@ static const struct regmap_config alc5623_regmap = {
.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,
- const struct i2c_device_id *id)
+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;
+ unsigned int matched_id;
int ret;
u32 val32;
@@ -1009,10 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client,
}
vid2 >>= 8;
- if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10ec) || (vid2 != matched_id)) {
dev_err(&client->dev, "unknown or wrong codec\n");
- dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
- 0x10ec, id->driver_data,
+ dev_err(&client->dev, "Expected %x:%x, got %x:%x\n",
+ 0x10ec, matched_id,
vid1, vid2);
return -ENODEV;
}
@@ -1052,33 +1061,21 @@ static int alc5623_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, alc5623);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5623, &alc5623_dai, 1);
+ 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;
}
-static int alc5623_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5623_i2c_table[] = {
- {"alc5621", 0x21},
- {"alc5622", 0x22},
- {"alc5623", 0x23},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
-
+#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 = {
@@ -1087,7 +1084,6 @@ static struct i2c_driver alc5623_i2c_driver = {
.of_match_table = of_match_ptr(alc5623_of_match),
},
.probe = alc5623_i2c_probe,
- .remove = alc5623_i2c_remove,
.id_table = alc5623_i2c_table,
};
diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h
index f3d68260d425..1dd88c772509 100644
--- a/sound/soc/codecs/alc5623.h
+++ b/sound/soc/codecs/alc5623.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5623.h -- alc562[123] ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
*
* Author: flove <flove@realtek.com>
* Arnaud Patard <arnaud.patard@rtp-net.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef _ALC5623_H
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index adb80d8719bd..72f4622204ff 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5632.c -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.c by Arnaud Patard
-*
-* 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>
@@ -116,20 +113,20 @@ static inline int alc5632_reset(struct regmap *map)
static int amp_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_write(codec, ALC5632_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0);
break;
}
@@ -681,7 +678,7 @@ 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -689,15 +686,15 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_read(codec, ALC5632_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
return 0;
@@ -745,19 +742,19 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* choose MCLK/BCLK/VBCLK */
- snd_soc_write(codec, ALC5632_GPCR2, gbl_clk);
+ snd_soc_component_write(component, ALC5632_GPCR2, gbl_clk);
/* choose PLL1 clock rate */
- snd_soc_write(codec, ALC5632_PLL1_CTRL, pll_div);
+ snd_soc_component_write(component, ALC5632_PLL1_CTRL, pll_div);
/* enable PLL1 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
ALC5632_PWR_ADD2_PLL1);
/* enable PLL2 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_update_bits(codec, ALC5632_GPCR1,
+ snd_soc_component_update_bits(component, ALC5632_GPCR1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);
@@ -775,9 +772,9 @@ static const struct _coeff_div coeff_div[] = {
{512*1, 0x3075},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -793,8 +790,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5632_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 alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 4096000:
@@ -815,15 +812,15 @@ static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5632_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;
u16 iface = 0;
- /* 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:
iface = ALC5632_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5632_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -864,17 +861,17 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int coeff, rate;
u16 iface;
- iface = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
iface &= ~ALC5632_DAI_I2S_DL_MASK;
/* bit size */
@@ -893,29 +890,29 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- snd_soc_write(codec, ALC5632_DAC_CLK_CTRL1, coeff);
+ snd_soc_component_write(component, ALC5632_DAC_CLK_CTRL1, coeff);
return 0;
}
-static int alc5632_mute(struct snd_soc_dai *dai, int mute)
+static int alc5632_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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_read(codec, ALC5632_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5632_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5632_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5632_MISC_CTRL, mute_reg);
}
#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF)
@@ -929,73 +926,73 @@ static int alc5632_mute(struct snd_soc_dai *dai, int mute)
| ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \
| ALC5632_PWR_ADD1_MAIN_BIAS)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_ADD1_SOFTGEN_EN,
ALC5632_PWR_ADD1_SOFTGEN_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_ADD3_POWER_EN,
ALC5632_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ 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_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
0);
msleep(500);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_ADD2_POWER_EN,
ALC5632_ADD2_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_ADD1_POWER_EN,
ALC5632_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ 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_codec *codec,
+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(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK,
ALC5632_PWR_ADD1_MAIN_BIAS);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ 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_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_MANAG_ADD2_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_PWR_MANAG_ADD3_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK, 0);
break;
}
@@ -1008,10 +1005,11 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5632_dai_ops = {
.hw_params = alc5632_pcm_hw_params,
- .digital_mute = alc5632_mute,
+ .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 = {
@@ -1034,13 +1032,13 @@ static struct snd_soc_dai_driver alc5632_dai = {
.formats = ALC5632_FORMATS,},
.ops = &alc5632_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_PM
-static int alc5632_resume(struct snd_soc_codec *codec)
+static int alc5632_resume(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
regcache_sync(alc5632->regmap);
@@ -1050,13 +1048,13 @@ static int alc5632_resume(struct snd_soc_codec *codec)
#define alc5632_resume NULL
#endif
-static int alc5632_probe(struct snd_soc_codec *codec)
+static int alc5632_probe(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (alc5632->id) {
case 0x5c:
- snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5632_vol_snd_controls,
ARRAY_SIZE(alc5632_vol_snd_controls));
break;
default:
@@ -1066,20 +1064,20 @@ static int alc5632_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_device_alc5632 = {
- .probe = alc5632_probe,
- .resume = alc5632_resume,
- .set_bias_level = alc5632_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -1093,18 +1091,24 @@ static const struct regmap_config alc5632_regmap = {
.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,
- const struct i2c_device_id *id)
+static int alc5632_i2c_probe(struct i2c_client *client)
{
struct alc5632_priv *alc5632;
int ret, ret1, ret2;
unsigned int vid1, vid2;
+ unsigned int matched_id;
alc5632 = devm_kzalloc(&client->dev,
sizeof(struct alc5632_priv), GFP_KERNEL);
@@ -1130,7 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client,
vid2 >>= 8;
- if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10EC) || (vid2 != matched_id)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
return -EINVAL;
@@ -1151,34 +1157,24 @@ static int alc5632_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5632, &alc5632_dai, 1);
+ 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 codec: %d\n", ret);
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int alc5632_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5632_i2c_table[] = {
- {"alc5632", 0x5c},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
-
+#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 = {
@@ -1187,7 +1183,6 @@ static struct i2c_driver alc5632_i2c_driver = {
.of_match_table = of_match_ptr(alc5632_of_match),
},
.probe = alc5632_i2c_probe,
- .remove = alc5632_i2c_remove,
.id_table = alc5632_i2c_table,
};
diff --git a/sound/soc/codecs/alc5632.h b/sound/soc/codecs/alc5632.h
index 1b5bda594ea3..a2bb5f9c7109 100644
--- a/sound/soc/codecs/alc5632.h
+++ b/sound/soc/codecs/alc5632.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5632.h -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.h by Arnaud Patard
-*
-* 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 _ALC5632_H
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
new file mode 100644
index 000000000000..6b55610ad535
--- /dev/null
+++ b/sound/soc/codecs/arizona-jack.c
@@ -0,0 +1,1670 @@
+// 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_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;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#else
+ int id_gpio = 0;
+#endif
+
+ 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;
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ /* 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;
+ }
+#endif
+
+ /* 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;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#endif
+ 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);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ if (id_gpio)
+ gpio_set_value_cansleep(id_gpio, 0);
+#endif
+
+ /* 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_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);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ 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
+#endif
+ {
+ 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;
+ }
+ }
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ 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;
+ }
+ }
+#endif
+
+ 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
index b3375e19598a..f2f47f1c1ac8 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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/delay.h>
@@ -84,13 +81,14 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ 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_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
+ 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");
@@ -169,10 +167,10 @@ static const struct snd_soc_dapm_widget arizona_spkr =
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_codec *codec)
+int arizona_init_spk(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -238,10 +236,10 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT6R", NULL, "OUT6L" },
};
-int arizona_init_mono(struct snd_soc_codec *codec)
+int arizona_init_mono(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -255,11 +253,9 @@ int arizona_init_mono(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_mono);
-int arizona_init_gpio(struct snd_soc_codec *codec)
+int arizona_init_gpio(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
@@ -643,7 +639,6 @@ const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
};
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,
@@ -882,9 +877,9 @@ const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
};
EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
-static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
+static void arizona_in_set_vu(struct snd_soc_component *component, int ena)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int val;
int i;
@@ -894,15 +889,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
val = 0;
for (i = 0; i < priv->num_inputs; i++)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4),
ARIZONA_IN_VU, val);
}
-bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+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_read(codec, reg);
+ unsigned int val = snd_soc_component_read(component, reg);
return !(val & ARIZONA_IN1_MODE_MASK);
}
@@ -911,8 +906,8 @@ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -925,25 +920,26 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
priv->in_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, reg, ARIZONA_IN1L_MUTE, 0);
+ 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(codec, 1);
+ arizona_in_set_vu(component, 1);
}
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, reg,
+ 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_read(codec, ARIZONA_INPUT_ENABLES);
+ reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES);
if (reg == 0)
- arizona_in_set_vu(codec, 0);
+ arizona_in_set_vu(component, 0);
break;
default:
break;
@@ -957,8 +953,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -971,7 +967,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_up_pending++;
- priv->out_up_delay += 17;
+ priv->out_up_delay += 17000;
break;
case ARIZONA_OUT4L_ENA_SHIFT:
case ARIZONA_OUT4R_ENA_SHIFT:
@@ -981,7 +977,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case WM8997:
break;
default:
- priv->out_up_delay += 10;
+ priv->out_up_delay += 10000;
break;
}
break;
@@ -1001,9 +997,9 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_up_pending--;
if (!priv->out_up_pending && priv->out_up_delay) {
- dev_dbg(codec->dev, "Power up delay: %d\n",
+ dev_dbg(component->dev, "Power up delay: %d\n",
priv->out_up_delay);
- msleep(priv->out_up_delay);
+ fsleep(priv->out_up_delay);
priv->out_up_delay = 0;
}
break;
@@ -1021,7 +1017,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_down_pending++;
- priv->out_down_delay++;
+ priv->out_down_delay += 1000;
break;
case ARIZONA_OUT4L_ENA_SHIFT:
case ARIZONA_OUT4R_ENA_SHIFT:
@@ -1032,12 +1028,13 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
break;
case WM8998:
case WM1814:
- priv->out_down_delay += 5;
+ priv->out_down_delay += 5000;
break;
default:
- priv->out_down_delay++;
+ priv->out_down_delay += 1000;
break;
}
+ break;
default:
break;
}
@@ -1054,9 +1051,9 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_down_pending--;
if (!priv->out_down_pending && priv->out_down_delay) {
- dev_dbg(codec->dev, "Power down delay: %d\n",
+ dev_dbg(component->dev, "Power down delay: %d\n",
priv->out_down_delay);
- msleep(priv->out_down_delay);
+ fsleep(priv->out_down_delay);
priv->out_down_delay = 0;
}
break;
@@ -1072,12 +1069,11 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_out_ev);
-int arizona_hp_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)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1111,15 +1107,15 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_hp_ev);
-static int arizona_dvfs_enable(struct snd_soc_codec *codec)
+static int arizona_dvfs_enable(struct snd_soc_component *component)
{
- const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to boost DCVDD: %d\n", ret);
+ dev_err(component->dev, "Failed to boost DCVDD: %d\n", ret);
return ret;
}
@@ -1128,7 +1124,7 @@ static int arizona_dvfs_enable(struct snd_soc_codec *codec)
ARIZONA_SUBSYS_MAX_FREQ,
ARIZONA_SUBSYS_MAX_FREQ);
if (ret) {
- dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
+ dev_err(component->dev, "Failed to enable subsys max: %d\n", ret);
regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
return ret;
}
@@ -1136,9 +1132,9 @@ static int arizona_dvfs_enable(struct snd_soc_codec *codec)
return 0;
}
-static int arizona_dvfs_disable(struct snd_soc_codec *codec)
+static int arizona_dvfs_disable(struct snd_soc_component *component)
{
- const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int ret;
@@ -1146,28 +1142,28 @@ static int arizona_dvfs_disable(struct snd_soc_codec *codec)
ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
ARIZONA_SUBSYS_MAX_FREQ, 0);
if (ret) {
- dev_err(codec->dev, "Failed to disable subsys max: %d\n", 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(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
+ dev_err(component->dev, "Failed to unboost DCVDD: %d\n", ret);
return ret;
}
return 0;
}
-int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
+int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ ret = arizona_dvfs_enable(component);
if (ret)
goto err;
}
@@ -1179,9 +1175,9 @@ err:
}
EXPORT_SYMBOL_GPL(arizona_dvfs_up);
-int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
+int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int old_reqs;
int ret = 0;
@@ -1191,7 +1187,7 @@ int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
priv->dvfs_reqs &= ~flags;
if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
- ret = arizona_dvfs_disable(codec);
+ ret = arizona_dvfs_disable(component);
mutex_unlock(&priv->dvfs_lock);
return ret;
@@ -1201,8 +1197,8 @@ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -1210,7 +1206,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (priv->dvfs_reqs)
- ret = arizona_dvfs_enable(codec);
+ ret = arizona_dvfs_enable(component);
priv->dvfs_cached = false;
break;
@@ -1222,7 +1218,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
priv->dvfs_cached = true;
if (priv->dvfs_reqs)
- ret = arizona_dvfs_disable(codec);
+ ret = arizona_dvfs_disable(component);
break;
default:
break;
@@ -1243,7 +1239,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int val;
switch (event) {
@@ -1257,7 +1253,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
return 0;
}
- snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val);
+ snd_soc_component_write(component, ARIZONA_CLOCK_CONTROL, val);
return 0;
}
@@ -1277,10 +1273,10 @@ static unsigned int arizona_opclk_ref_44k1_rates[] = {
45158400,
};
-static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
- unsigned int freq)
+static int arizona_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int reg;
unsigned int *rates;
int ref, div, refclk;
@@ -1304,13 +1300,13 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
rates = arizona_opclk_ref_48k_rates;
for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
- rates[ref] <= refclk; ref++) {
+ rates[ref] <= refclk; ref++) {
div = 1;
while (rates[ref] / div >= freq && div < 32) {
if (rates[ref] / div == freq) {
- dev_dbg(codec->dev, "Configured %dHz OPCLK\n",
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
freq);
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
ARIZONA_OPCLK_DIV_MASK |
ARIZONA_OPCLK_SEL_MASK,
(div <<
@@ -1322,22 +1318,22 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
}
- dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ 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(codec->dev, "Failed to check clock source: %d\n", ret);
+ dev_err(component->dev, "Failed to check clock source: %d\n", ret);
return ret;
}
@@ -1366,10 +1362,10 @@ int arizona_clk_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_clk_ev);
-int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
char *name;
unsigned int reg;
@@ -1391,7 +1387,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
break;
case ARIZONA_CLK_OPCLK:
case ARIZONA_CLK_ASYNC_OPCLK:
- return arizona_set_opclk(codec, clk_id, freq);
+ return arizona_set_opclk(component, clk_id, freq);
default:
return -EINVAL;
}
@@ -1445,8 +1441,8 @@ EXPORT_SYMBOL_GPL(arizona_set_sysclk);
static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1461,7 +1457,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_DSP_B:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
return -EINVAL;
}
@@ -1472,7 +1468,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_LEFT_J:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
return -EINVAL;
}
@@ -1485,15 +1481,15 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
@@ -1620,8 +1616,8 @@ static const struct snd_pcm_hw_constraint_list arizona_constraint = {
static int arizona_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1651,10 +1647,10 @@ static int arizona_startup(struct snd_pcm_substream *substream,
&dai_priv->constraint);
}
-static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
+static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component,
unsigned int rate)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
struct reg_sequence dac_comp[] = {
{ 0x80, 0x3 },
@@ -1680,8 +1676,8 @@ 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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1704,9 +1700,9 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
case WM5102:
case WM8997:
if (arizona_sr_vals[sr_val] >= 88200)
- ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ);
+ ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ);
else
- ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ);
+ ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ);
if (ret) {
arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
@@ -1721,26 +1717,31 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
case ARIZONA_CLK_SYSCLK:
switch (priv->arizona->type) {
case WM5102:
- arizona_wm5102_set_dac_comp(codec,
+ arizona_wm5102_set_dac_comp(component,
params_rate(params));
break;
default:
break;
}
- snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
- ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
+ ARIZONA_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK, 0);
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK, 0);
break;
case ARIZONA_CLK_ASYNCCLK:
- snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
- ARIZONA_ASYNC_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component,
+ ARIZONA_ASYNC_SAMPLE_RATE_1,
+ ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK,
- 8 << ARIZONA_AIF1_RATE_SHIFT);
+ 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);
@@ -1750,20 +1751,20 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
return 0;
}
-static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+static bool arizona_aif_cfg_changed(struct snd_soc_component *component,
int base, int bclk, int lrclk, int frame)
{
int val;
- val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL);
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL);
if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE);
- if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+ 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;
@@ -1775,8 +1776,8 @@ static int arizona_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 arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1813,7 +1814,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
}
/* Force multiple of 2 channels for I2S mode */
- val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
+ 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");
@@ -1841,19 +1842,20 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width;
- reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+ reconfig = arizona_aif_cfg_changed(component, base, bclk, lrclk, frame);
if (reconfig) {
/* Save AIF TX/RX state */
- aif_tx_state = snd_soc_read(codec,
+ aif_tx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_TX_ENABLES);
- aif_rx_state = snd_soc_read(codec,
+ 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);
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, 0x0);
regmap_update_bits(arizona->regmap,
- base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
}
ret = arizona_hw_params_rate(substream, params, dai);
@@ -1908,9 +1910,9 @@ static const char *arizona_dai_clk_str(int clk_id)
static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -1925,13 +1927,13 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
if (clk_id == dai_priv->clk)
return 0;
- if (dai->active) {
- dev_err(codec->dev, "Can't change clock on active DAI %d\n",
+ 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(codec->dev, "Setting AIF%d to %s\n", dai->id + 1,
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id + 1,
arizona_dai_clk_str(clk_id));
memset(&routes, 0, sizeof(routes));
@@ -1953,7 +1955,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int base = dai->driver->base;
unsigned int reg;
@@ -1962,16 +1964,17 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
else
reg = 0;
- return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_TRI, reg);
+ 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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1992,8 +1995,8 @@ static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -2321,7 +2324,6 @@ static int arizona_calc_fll(struct arizona_fll *fll,
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,
@@ -2567,9 +2569,8 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
fll->ref_src = source;
fll->ref_freq = Fref;
- if (fll->fout && Fref > 0) {
+ if (fll->fout && Fref > 0)
ret = arizona_enable_fll(fll);
- }
return ret;
}
@@ -2645,7 +2646,7 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
/**
* arizona_set_output_mode - Set the mode of the specified output
*
- * @codec: Device to configure
+ * @component: Device to configure
* @output: Output number
* @diff: True to set the output to differential mode
*
@@ -2658,7 +2659,8 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
* Most systems have a single static configuration and should use
* platform data instead.
*/
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
+ bool diff)
{
unsigned int reg, val;
@@ -2672,7 +2674,8 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
else
val = 0;
- return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+ return snd_soc_component_update_bits(component, reg,
+ ARIZONA_OUT1_MONO, val);
}
EXPORT_SYMBOL_GPL(arizona_set_output_mode);
@@ -2721,8 +2724,8 @@ static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ 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;
@@ -2765,8 +2768,8 @@ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ 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);
@@ -2783,15 +2786,13 @@ 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) {
+ of_property_for_each_u32(np, "wlf,inmode", val) {
if (count == ARRAY_SIZE(pdata->inmode))
break;
@@ -2800,7 +2801,7 @@ int arizona_of_get_audio_pdata(struct arizona *arizona)
}
count = 0;
- of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) {
+ of_property_for_each_u32(np, "wlf,dmic-ref", val) {
if (count == ARRAY_SIZE(pdata->dmic_ref))
break;
@@ -2809,7 +2810,7 @@ int arizona_of_get_audio_pdata(struct arizona *arizona)
}
count = 0;
- of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) {
+ of_property_for_each_u32(np, "wlf,out-mono", val) {
if (count == ARRAY_SIZE(pdata->out_mono))
break;
@@ -2818,7 +2819,7 @@ int arizona_of_get_audio_pdata(struct arizona *arizona)
}
count = 0;
- of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) {
+ of_property_for_each_u32(np, "wlf,max-channels-clocked", val) {
if (count == ARRAY_SIZE(pdata->max_channels_clocked))
break;
@@ -2827,7 +2828,7 @@ int arizona_of_get_audio_pdata(struct arizona *arizona)
}
count = 0;
- of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) {
+ of_property_for_each_u32(np, "wlf,out-volume-limit", val) {
if (count == ARRAY_SIZE(pdata->out_vol_limit))
break;
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index dfdf6d8c9687..ecd8890eefc1 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -1,13 +1,10 @@
+/* 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>
- *
- * 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 _ASOC_ARIZONA_H
@@ -94,6 +91,41 @@ struct arizona_priv {
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 {
@@ -225,6 +257,9 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#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];
@@ -273,7 +308,7 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event);
-int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source,
+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;
@@ -297,8 +332,8 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
-int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
-int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
+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);
@@ -310,9 +345,9 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
-int arizona_init_spk(struct snd_soc_codec *codec);
-int arizona_init_gpio(struct snd_soc_codec *codec);
-int arizona_init_mono(struct snd_soc_codec *codec);
+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);
@@ -320,22 +355,22 @@ 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 dai);
+int arizona_init_dai(struct arizona_priv *priv, int id);
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
bool diff);
-bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+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_codec *codec,
+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_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
nb->notifier_call = notify;
@@ -343,10 +378,10 @@ static inline int arizona_register_notifier(struct snd_soc_codec *codec,
return blocking_notifier_chain_register(&arizona->notifier, nb);
}
-static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
+static inline int arizona_unregister_notifier(struct snd_soc_component *component,
struct notifier_block *nb)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
@@ -354,4 +389,10 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
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/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c
new file mode 100644
index 000000000000..588e48044c13
--- /dev/null
+++ b/sound/soc/codecs/audio-iio-aux.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC glue to use IIO devices as audio components
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/cleanup.h>
+#include <linux/iio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct audio_iio_aux_chan {
+ struct iio_channel *iio_chan;
+ const char *name;
+ int max;
+ int min;
+ bool is_invert_range;
+};
+
+struct audio_iio_aux {
+ struct device *dev;
+ unsigned int num_chans;
+ struct audio_iio_aux_chan chans[] __counted_by(num_chans);
+};
+
+static int audio_iio_aux_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chan->max - chan->min;
+ uinfo->type = (uinfo->value.integer.max == 1) ?
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ return 0;
+}
+
+static int audio_iio_aux_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int ret;
+ int val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = val - min;
+ if (invert_range)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int audio_iio_aux_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int val;
+ int ret;
+ int tmp;
+
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ return -EINVAL;
+ if (val > max - min)
+ return -EINVAL;
+
+ val = val + min;
+ if (invert_range)
+ val = max - val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &tmp);
+ if (ret < 0)
+ return ret;
+
+ if (tmp == val)
+ return 0;
+
+ ret = iio_write_channel_raw(chan->iio_chan, val);
+ if (ret)
+ return ret;
+
+ return 1; /* The value changed */
+}
+
+static int audio_iio_aux_add_controls(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_kcontrol_new control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = chan->name,
+ .info = audio_iio_aux_info_volsw,
+ .get = audio_iio_aux_get_volsw,
+ .put = audio_iio_aux_put_volsw,
+ .private_value = (unsigned long)chan,
+ };
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+/*
+ * These data could be on stack but they are pretty big.
+ * As ASoC internally copy them and protect them against concurrent accesses
+ * (snd_soc_bind_card() protects using client_mutex), keep them in the global
+ * data area.
+ */
+static struct snd_soc_dapm_widget widgets[3];
+static struct snd_soc_dapm_route routes[2];
+
+/* Be sure sizes are correct (need 3 widgets and 2 routes) */
+static_assert(ARRAY_SIZE(widgets) >= 3, "3 widgets are needed");
+static_assert(ARRAY_SIZE(routes) >= 2, "2 routes are needed");
+
+static int audio_iio_aux_add_dapms(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ /* Allocated names are not needed afterwards (duplicated in ASoC internals) */
+ char *input_name __free(kfree) = kasprintf(GFP_KERNEL, "%s IN", chan->name);
+ if (!input_name)
+ return -ENOMEM;
+
+ char *output_name __free(kfree) = kasprintf(GFP_KERNEL, "%s OUT", chan->name);
+ if (!output_name)
+ return -ENOMEM;
+
+ char *pga_name __free(kfree) = kasprintf(GFP_KERNEL, "%s PGA", chan->name);
+ if (!pga_name)
+ return -ENOMEM;
+
+ widgets[0] = SND_SOC_DAPM_INPUT(input_name);
+ widgets[1] = SND_SOC_DAPM_OUTPUT(output_name);
+ widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0);
+ ret = snd_soc_dapm_new_controls(dapm, widgets, 3);
+ if (ret)
+ return ret;
+
+ routes[0].sink = pga_name;
+ routes[0].control = NULL;
+ routes[0].source = input_name;
+ routes[1].sink = output_name;
+ routes[1].control = NULL;
+ routes[1].source = pga_name;
+
+ return snd_soc_dapm_add_routes(dapm, routes, 2);
+}
+
+static int audio_iio_aux_component_probe(struct snd_soc_component *component)
+{
+ struct audio_iio_aux *iio_aux = snd_soc_component_get_drvdata(component);
+ struct audio_iio_aux_chan *chan;
+ int ret;
+ int i;
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ chan = iio_aux->chans + i;
+
+ ret = iio_read_max_channel_raw(chan->iio_chan, &chan->max);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get max raw value\n",
+ i, chan->name);
+
+ ret = iio_read_min_channel_raw(chan->iio_chan, &chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get min raw value\n",
+ i, chan->name);
+
+ if (chan->min > chan->max) {
+ /*
+ * This should never happen but to avoid any check
+ * later, just swap values here to ensure that the
+ * minimum value is lower than the maximum value.
+ */
+ dev_dbg(component->dev, "chan[%d] %s: Swap min and max\n",
+ i, chan->name);
+ swap(chan->min, chan->max);
+ }
+
+ /* Set initial value */
+ ret = iio_write_channel_raw(chan->iio_chan,
+ chan->is_invert_range ? chan->max : chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot set initial value\n",
+ i, chan->name);
+
+ ret = audio_iio_aux_add_controls(component, chan);
+ if (ret)
+ return ret;
+
+ ret = audio_iio_aux_add_dapms(component, chan);
+ if (ret)
+ return ret;
+
+ dev_dbg(component->dev, "chan[%d]: Added %s (min=%d, max=%d, invert=%s)\n",
+ i, chan->name, chan->min, chan->max,
+ str_on_off(chan->is_invert_range));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver audio_iio_aux_component_driver = {
+ .probe = audio_iio_aux_component_probe,
+};
+
+static int audio_iio_aux_probe(struct platform_device *pdev)
+{
+ struct audio_iio_aux_chan *iio_aux_chan;
+ struct device *dev = &pdev->dev;
+ struct audio_iio_aux *iio_aux;
+ int count;
+ int ret;
+ int i;
+
+ count = device_property_string_array_count(dev, "io-channel-names");
+ if (count < 0)
+ return dev_err_probe(dev, count, "failed to count io-channel-names\n");
+
+ iio_aux = devm_kzalloc(dev, struct_size(iio_aux, chans, count), GFP_KERNEL);
+ if (!iio_aux)
+ return -ENOMEM;
+
+ iio_aux->dev = dev;
+
+ iio_aux->num_chans = count;
+
+ const char **names __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*names),
+ GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+
+ u32 *invert_ranges __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*invert_ranges),
+ GFP_KERNEL);
+ if (!invert_ranges)
+ return -ENOMEM;
+
+ ret = device_property_read_string_array(dev, "io-channel-names",
+ names, iio_aux->num_chans);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read io-channel-names\n");
+
+ /*
+ * snd-control-invert-range is optional and can contain fewer items
+ * than the number of channels. Unset values default to 0.
+ */
+ count = device_property_count_u32(dev, "snd-control-invert-range");
+ if (count > 0) {
+ count = min_t(unsigned int, count, iio_aux->num_chans);
+ ret = device_property_read_u32_array(dev, "snd-control-invert-range",
+ invert_ranges, count);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n");
+ }
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ iio_aux_chan = iio_aux->chans + i;
+ iio_aux_chan->name = names[i];
+ iio_aux_chan->is_invert_range = invert_ranges[i];
+
+ iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name);
+ if (IS_ERR(iio_aux_chan->iio_chan))
+ return dev_err_probe(dev, PTR_ERR(iio_aux_chan->iio_chan),
+ "get IIO channel '%s' failed\n",
+ iio_aux_chan->name);
+ }
+
+ platform_set_drvdata(pdev, iio_aux);
+
+ return devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver,
+ NULL, 0);
+}
+
+static const struct of_device_id audio_iio_aux_ids[] = {
+ { .compatible = "audio-iio-aux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, audio_iio_aux_ids);
+
+static struct platform_driver audio_iio_aux_driver = {
+ .driver = {
+ .name = "audio-iio-aux",
+ .of_match_table = audio_iio_aux_ids,
+ },
+ .probe = audio_iio_aux_probe,
+};
+module_platform_driver(audio_iio_aux_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IIO ALSA SoC aux driver");
+MODULE_LICENSE("GPL");
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/aw87390.c b/sound/soc/codecs/aw87390.c
new file mode 100644
index 000000000000..ef6f64856988
--- /dev/null
+++ b/sound/soc/codecs/aw87390.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.c -- AW87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw87390.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw87390_remap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = AW87390_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw87390_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int i, ret;
+
+ if (!data) {
+ dev_err(aw_dev->dev, "data is NULL\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len-1; i += 2) {
+ if (data[i] == AW87390_DELAY_REG_ADDR) {
+ usleep_range(data[i + 1] * AW87390_REG_DELAY_TIME,
+ data[i + 1] * AW87390_REG_DELAY_TIME + 10);
+ continue;
+ }
+ ret = regmap_write(aw_dev->regmap, data[i], data[i + 1]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw87390_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw87390_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;
+}
+
+static int aw87390_dev_fw_update(struct aw_device *aw_dev)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw87390_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw87390_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw87390_dev_get_prof_data failed\n");
+ return ret;
+ }
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw87390_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\n");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static int aw87390_power_off(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off\n");
+ return 0;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ return 0;
+}
+
+static int aw87390_power_on(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on\n");
+ return 0;
+ }
+
+ if (!aw_dev->fw_status) {
+ dev_err(aw_dev->dev, "fw not load\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw87390_dev_fw_update(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s load profile failed\n", __func__);
+ return ret;
+ }
+ aw_dev->status = AW87390_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw87390_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw87390_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw87390->aw_pa->prof_info.count;
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw87390_dev_get_prof_name(aw87390->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw87390_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw87390->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw87390_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw87390->lock);
+ ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change\n");
+ mutex_unlock(&aw87390->lock);
+ return 0;
+ }
+
+ if (aw87390->aw_pa->status == AW87390_DEV_PW_ON) {
+ aw87390_power_off(aw87390->aw_pa);
+ aw87390_power_on(aw87390->aw_pa);
+ }
+
+ mutex_unlock(&aw87390->lock);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new aw87390_controls[] = {
+ AW87390_PROFILE_EXT("AW87390 Profile Set", aw87390_profile_info,
+ aw87390_profile_get, aw87390_profile_set),
+};
+
+static int aw87390_request_firmware_file(struct aw87390 *aw87390)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW87390_ACF_FILE, aw87390->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "load [%s] failed!\n", AW87390_ACF_FILE);
+
+ dev_dbg(aw87390->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW87390_ACF_FILE, cont ? cont->size : 0);
+
+ aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev,
+ struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw87390->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+
+ aw87390->aw_cfg->len = cont->size;
+ memcpy(aw87390->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret) {
+ dev_err(aw87390->aw_pa->dev, "load [%s] failed!\n", AW87390_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw87390->lock);
+
+ ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret)
+ dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n");
+
+ mutex_unlock(&aw87390->lock);
+
+ return ret;
+}
+
+static int aw87390_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 aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = aw87390_power_on(aw_dev);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = aw87390_power_off(aw_dev);
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
+ { "SPK PA", NULL, "IN" },
+ { "OUT", NULL, "SPK PA" },
+};
+
+static int aw87390_codec_probe(struct snd_soc_component *component)
+{
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = aw87390_request_firmware_file(aw87390);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "aw87390_request_firmware_file failed\n");
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
+ .probe = aw87390_codec_probe,
+ .dapm_widgets = aw87390_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw87390_dapm_widgets),
+ .dapm_routes = aw87390_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+ .controls = aw87390_controls,
+ .num_controls = ARRAY_SIZE(aw87390_controls),
+};
+
+static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
+{
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW87390_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw87390_init(struct aw87390 **aw87390, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW87390_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (chip_id != AW87390_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ (*aw87390)->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW87390_CHIP_ID;
+ 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 = AW87390_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW87390_DEV_FW_FAILED;
+ aw_dev->prof_index = AW87390_INIT_PROFILE;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ aw87390_parse_channel_dt(*aw87390);
+
+ return 0;
+}
+
+static int aw87390_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw87390 *aw87390;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw87390 = devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL);
+ if (!aw87390)
+ return -ENOMEM;
+
+ mutex_init(&aw87390->lock);
+
+ i2c_set_clientdata(i2c, aw87390);
+
+ aw87390->regmap = devm_regmap_init_i2c(i2c, &aw87390_remap_config);
+ if (IS_ERR(aw87390->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw87390->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw87390_init(&aw87390, i2c, aw87390->regmap);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw87390->regmap, AW87390_ID_REG, AW87390_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw87390, NULL, 0);
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw87390_i2c_id[] = {
+ { AW87390_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
+
+static struct i2c_driver aw87390_i2c_driver = {
+ .driver = {
+ .name = AW87390_I2C_NAME,
+ },
+ .probe = aw87390_i2c_probe,
+ .id_table = aw87390_i2c_id,
+};
+module_i2c_driver(aw87390_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW87390 PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h
new file mode 100644
index 000000000000..d0d049e65991
--- /dev/null
+++ b/sound/soc/codecs/aw87390.h
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.h -- aw87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW87390_H__
+#define __AW87390_H__
+
+#define AW87390_ID_REG (0x00)
+#define AW87390_SYSCTRL_REG (0x01)
+#define AW87390_MDCTRL_REG (0x02)
+#define AW87390_CPOVP_REG (0x03)
+#define AW87390_CPP_REG (0x04)
+#define AW87390_PAG_REG (0x05)
+#define AW87390_AGC3P_REG (0x06)
+#define AW87390_AGC3PA_REG (0x07)
+#define AW87390_AGC2P_REG (0x08)
+#define AW87390_AGC2PA_REG (0x09)
+#define AW87390_AGC1PA_REG (0x0A)
+#define AW87390_SYSST_REG (0x59)
+#define AW87390_SYSINT_REG (0x60)
+#define AW87390_DFT_SYSCTRL_REG (0x61)
+#define AW87390_DFT_MDCTRL_REG (0x62)
+#define AW87390_DFT_CPADP_REG (0x63)
+#define AW87390_DFT_AGCPA_REG (0x64)
+#define AW87390_DFT_POFR_REG (0x65)
+#define AW87390_DFT_OC_REG (0x66)
+#define AW87390_DFT_ADP1_REG (0x67)
+#define AW87390_DFT_REF_REG (0x68)
+#define AW87390_DFT_LDO_REG (0x69)
+#define AW87390_ADP1_REG (0x70)
+#define AW87390_ADP2_REG (0x71)
+#define AW87390_NG1_REG (0x72)
+#define AW87390_NG2_REG (0x73)
+#define AW87390_NG3_REG (0x74)
+#define AW87390_CP_REG (0x75)
+#define AW87390_AB_REG (0x76)
+#define AW87390_TEST_REG (0x77)
+#define AW87390_ENCR_REG (0x78)
+#define AW87390_DELAY_REG_ADDR (0xFE)
+
+#define AW87390_SOFT_RESET_VALUE (0xAA)
+#define AW87390_POWER_DOWN_VALUE (0x00)
+#define AW87390_REG_MAX (0xFF)
+#define AW87390_DEV_DEFAULT_CH (0)
+#define AW87390_INIT_PROFILE (0)
+#define AW87390_REG_DELAY_TIME (1000)
+#define AW87390_I2C_NAME "aw87390"
+#define AW87390_ACF_FILE "aw87390_acf.bin"
+
+#define AW87390_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 aw87390_id {
+ AW87390_CHIP_ID = 0x76,
+};
+
+enum {
+ AW87390_DEV_FW_FAILED = 0,
+ AW87390_DEV_FW_OK,
+};
+
+enum {
+ AW87390_DEV_PW_OFF = 0,
+ AW87390_DEV_PW_ON,
+};
+
+struct aw87390 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c
new file mode 100644
index 000000000000..d61a7b8c5470
--- /dev/null
+++ b/sound/soc/codecs/aw88081.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.c -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88081.h"
+#include "aw88395/aw88395_device.h"
+
+enum aw8808x_type {
+ AW88081,
+ AW88083,
+};
+
+struct aw88081 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+ enum aw8808x_type devtype;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88081_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88081_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config aw88083_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88083_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw88081_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88081_BIT_PLL_CHECK) != AW88081_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88081_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88081_CCO_MUX_MASK);
+ if (reg_val == AW88081_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, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88081_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ unsigned int value;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PWMCTRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88081_NOISE_GATE_EN_MASK))
+ check_val = AW88081_NO_SWS_SYSST_CHECK;
+ else
+ check_val = AW88081_SWS_SYSST_CHECK;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ value = reg_val & (~AW88081_BIT_SYSST_CHECK_MASK) & check_val;
+ if (value != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, check_val);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88081_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88081_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_WORKING_VALUE);
+}
+
+static void aw88081_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_WORKING_VALUE);
+}
+
+static void aw88083_i2c_wen(struct aw88081 *aw88081, bool flag)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw88081->devtype != AW88083)
+ return;
+
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_DISABLE_VALUE);
+}
+
+static void aw88083_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_WORKING_VALUE);
+}
+
+static void aw88083_dev_pllpd(struct aw_device *aw_dev, bool pllpd)
+{
+ if (pllpd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_WORKING_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_POWER_DOWN_VALUE);
+}
+
+static void aw88081_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static void aw88081_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int volume;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88081_MUTE_VOL);
+
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL2_REG, ~AW88081_VOL_MASK, volume);
+}
+
+static void aw88081_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88081_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88081_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88081_MUTE_VOL; i += fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88081_MUTE_VOL)
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+}
+
+static void aw88081_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88081_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_DISABLE_VALUE);
+ aw88081_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88081_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static int aw88081_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88081_EN_PA_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88081_ULS_HMUTE_MASK);
+
+ *reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88081_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88081_I2SCTRL3_REG) {
+ /* close tx */
+ *reg_val &= AW88081_I2STXEN_MASK;
+ *reg_val |= AW88081_I2STXEN_DISABLE_VALUE;
+ }
+
+ return 0;
+}
+
+static int aw88083_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88083_AMPPD_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88083_I2C_WEN_MASK);
+
+ *reg_val |= AW88083_AMPPD_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88083_I2C_WEN_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ return 0;
+}
+
+static int aw88081_reg_value_check(struct aw88081 *aw88081,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ case AW88083:
+ ret = aw88083_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_reg_update(struct aw88081 *aw88081,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u16 reg_val;
+ u8 reg_addr;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ 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];
+
+ ret = aw88081_reg_value_check(aw88081, reg_addr, &reg_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88081_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return 0;
+}
+
+static int aw88081_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88081_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;
+}
+
+static int aw88081_dev_fw_update(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw88081_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88081_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 = aw88081_dev_reg_update(aw88081, 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;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static int aw88081_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ ret = aw88081_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88081_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88081_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close uls mute */
+ aw88081_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ aw88081_dev_clear_int_status(aw_dev);
+ aw88081_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88081_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88083_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88083_i2c_wen(aw88081, true);
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ aw88083_dev_pllpd(aw_dev, true);
+ /* amppd on */
+ aw88083_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 50);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw88081_device_start(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_start(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_start(aw88081);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+
+ aw88081_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 100);
+
+ /* enable amppd */
+ aw88081_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88083_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ aw88083_i2c_wen(aw88081, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 100);
+
+ /* enable amppd */
+ aw88083_dev_amppd(aw_dev, true);
+
+ aw88083_dev_pllpd(aw_dev, false);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_stop(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_stop(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_stop(aw88081);
+ break;
+ default:
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_reg_update(struct aw88081 *aw88081, bool force)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static void aw88081_start_pa(struct aw88081 *aw88081)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_START_RETRIES; i++) {
+ ret = aw88081_reg_update(aw88081, aw88081->phase_sync);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88081_device_start(aw88081);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "aw88081 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_dbg(aw88081->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88081_startup_work(struct work_struct *work)
+{
+ struct aw88081 *aw88081 =
+ container_of(work, struct aw88081, start_work.work);
+
+ mutex_lock(&aw88081->lock);
+ aw88081_start_pa(aw88081);
+ mutex_unlock(&aw88081->lock);
+}
+
+static void aw88081_start(struct aw88081 *aw88081, bool sync_start)
+{
+ if (aw88081->aw_pa->fw_status != AW88081_DEV_FW_OK)
+ return;
+
+ if (aw88081->aw_pa->status == AW88081_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88081_SYNC_START)
+ aw88081_start_pa(aw88081);
+ else
+ queue_delayed_work(system_wq,
+ &aw88081->start_work,
+ AW88081_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88081_dai[] = {
+ {
+ .name = "aw88081-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ },
+};
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->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 aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->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 aw88081_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 -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw88081_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88081->aw_pa->prof_info.count;
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88081_dev_get_prof_name(aw88081->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88081_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88081_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88081->lock);
+ return 0;
+ }
+
+ if (aw88081->aw_pa->status) {
+ aw88081_stop(aw88081);
+ aw88081_start(aw88081, AW88081_SYNC_START);
+ }
+
+ mutex_unlock(&aw88081->lock);
+
+ return 1;
+}
+
+static int aw88081_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88081_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->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;
+
+ aw88083_i2c_wen(aw88081, true);
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88081_dev_set_volume(aw88081->aw_pa, vol_desc->ctl_volume);
+ return 1;
+ }
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88081_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = 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 (aw88081->aw_pa->fade_step != value) {
+ aw88081->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88081_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88081_SYSCTRL2_REG,
+ 0, AW88081_MUTE_VOL, 0, aw88081_volume_get,
+ aw88081_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88081_MUTE_VOL, 0,
+ aw88081_get_fade_step, aw88081_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_in_time, aw88081_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_out_time, aw88081_set_fade_out_time),
+ AW88081_PROFILE_EXT("Profile Set", aw88081_profile_info,
+ aw88081_profile_get, aw88081_profile_set),
+};
+
+static void aw88081_parse_channel_dt(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88081_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88081->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW88081_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88081_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ case AW88083_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ default:
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ aw88081->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = chip_id;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->fade_step = AW88081_VOLUME_STEP_DB;
+ aw_dev->volume_desc.mute_volume = AW88081_MUTE_VOL;
+ aw88081_parse_channel_dt(aw88081);
+
+ return 0;
+}
+
+static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88081_500_US;
+ aw_dev->fade_out_time = AW88081_500_US;
+ aw_dev->prof_cur = AW88081_INIT_PROFILE;
+ aw_dev->prof_index = AW88081_INIT_PROFILE;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+ aw88081_stop(aw88081);
+
+ return 0;
+}
+
+static int aw88081_request_firmware_file(struct aw88081 *aw88081)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88081->aw_pa->fw_status = AW88081_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88081_ACF_FILE, aw88081->aw_pa->dev);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw88081->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88081_ACF_FILE, cont ? cont->size : 0);
+
+ aw88081->aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88081->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88081->aw_cfg->len = (int)cont->size;
+ memcpy(aw88081->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg);
+ if (ret)
+ return ret;
+
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_init(aw88081, aw88081->aw_cfg);
+ mutex_unlock(&aw88081->lock);
+
+ return ret;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88081->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88081_start(aw88081, AW88081_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88081_stop(aw88081);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88081->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88081_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88081_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 aw88081_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88081_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88081->start_work, aw88081_startup_work);
+
+ ret = aw88081_request_firmware_file(aw88081);
+ if (ret)
+ dev_err(aw88081->aw_pa->dev, "%s: request firmware failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88081_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88081->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88081 = {
+ .probe = aw88081_codec_probe,
+ .remove = aw88081_codec_remove,
+ .dapm_widgets = aw88081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88081_dapm_widgets),
+ .dapm_routes = aw88081_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88081_audio_map),
+ .controls = aw88081_controls,
+ .num_controls = ARRAY_SIZE(aw88081_controls),
+};
+
+static const struct i2c_device_id aw88081_i2c_id[] = {
+ { AW88081_I2C_NAME, AW88081},
+ { AW88083_I2C_NAME, AW88083},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
+
+static int aw88081_i2c_probe(struct i2c_client *i2c)
+{
+ const struct regmap_config *regmap_config;
+ const struct i2c_device_id *id;
+ struct aw88081 *aw88081;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88081 = devm_kzalloc(&i2c->dev, sizeof(*aw88081), GFP_KERNEL);
+ if (!aw88081)
+ return -ENOMEM;
+
+ id = i2c_match_id(aw88081_i2c_id, i2c);
+ aw88081->devtype = id->driver_data;
+
+ mutex_init(&aw88081->lock);
+
+ i2c_set_clientdata(i2c, aw88081);
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ regmap_config = &aw88081_regmap_config;
+ break;
+ case AW88083:
+ regmap_config = &aw88083_regmap_config;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aw88081->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(aw88081->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88081->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88081_init(aw88081, i2c, aw88081->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88081,
+ aw88081_dai, ARRAY_SIZE(aw88081_dai));
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id aw88081_of_match[] = {
+ { .compatible = "awinic,aw88081" },
+ { .compatible = "awinic,aw88083" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw88081_of_match);
+#endif
+
+static struct i2c_driver aw88081_i2c_driver = {
+ .driver = {
+ .name = AW88081_I2C_NAME,
+ .of_match_table = of_match_ptr(aw88081_of_match),
+ },
+ .probe = aw88081_i2c_probe,
+ .id_table = aw88081_i2c_id,
+};
+module_i2c_driver(aw88081_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88081 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88081.h b/sound/soc/codecs/aw88081.h
new file mode 100644
index 000000000000..7a4564270ab3
--- /dev/null
+++ b/sound/soc/codecs/aw88081.h
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.h -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88081_H__
+#define __AW88081_H__
+
+#define AW88081_ID_REG (0x00)
+#define AW88081_SYSST_REG (0x01)
+#define AW88081_SYSINT_REG (0x02)
+#define AW88081_SYSINTM_REG (0x03)
+#define AW88081_SYSCTRL_REG (0x04)
+#define AW88081_SYSCTRL2_REG (0x05)
+#define AW88081_I2SCTRL1_REG (0x06)
+#define AW88081_I2SCTRL2_REG (0x07)
+#define AW88081_I2SCTRL3_REG (0x08)
+#define AW88081_DACCFG1_REG (0x09)
+#define AW88081_DACCFG2_REG (0x0A)
+#define AW88081_DACCFG3_REG (0x0B)
+#define AW88081_DACCFG4_REG (0x0C)
+#define AW88081_DACCFG5_REG (0x0D)
+#define AW88081_DACCFG6_REG (0x0E)
+#define AW88081_DACCFG7_REG (0x11)
+#define AW88081_PWMCTRL1_REG (0x13)
+#define AW88081_PWMCTRL2_REG (0x14)
+#define AW88081_PWMCTRL3_REG (0x15)
+#define AW88081_PWMCTRL4_REG (0x16)
+#define AW88081_I2SCFG1_REG (0x17)
+#define AW88081_DBGCTRL_REG (0x18)
+#define AW88081_PDMCTRL_REG (0x19)
+#define AW88081_DACST_REG (0x20)
+#define AW88081_PATTERNST_REG (0x21)
+#define AW88081_I2SINT_REG (0x26)
+#define AW88081_I2SCAPCNT_REG (0x27)
+#define AW88081_ANASTA1_REG (0x28)
+#define AW88081_ANASTA2_REG (0x29)
+#define AW88081_ANASTA3_REG (0x2A)
+#define AW88081_VBAT_REG (0x21)
+#define AW88081_TEMP_REG (0x22)
+#define AW88081_PVDD_REG (0x23)
+#define AW88081_ISNDAT_REG (0x24)
+#define AW88081_VSNDAT_REG (0x25)
+#define AW88081_DSMCFG1_REG (0x30)
+#define AW88081_DSMCFG2_REG (0x31)
+#define AW88081_DSMCFG3_REG (0x32)
+#define AW88081_DSMCFG4_REG (0x33)
+#define AW88081_DSMCFG5_REG (0x34)
+#define AW88081_DSMCFG6_REG (0x35)
+#define AW88081_DSMCFG7_REG (0x36)
+#define AW88081_DSMCFG8_REG (0x37)
+#define AW88081_TESTIN_REG (0x38)
+#define AW88081_TESTOUT_REG (0x39)
+#define AW88081_BOPCTRL1_REG (0x40)
+#define AW88081_BOPCTRL2_REG (0x41)
+#define AW88081_BOPCTRL3_REG (0x42)
+#define AW88081_BOPSTA_REG (0x43)
+#define AW88081_PLLCTRL1_REG (0x54)
+#define AW88081_PLLCTRL2_REG (0x55)
+#define AW88081_PLLCTRL3_REG (0x56)
+#define AW88081_CDACTRL1_REG (0x57)
+#define AW88081_CDACTRL2_REG (0x58)
+#define AW88081_CDACTRL3_REG (0x59)
+#define AW88081_DITHERCFG1_REG (0x5A)
+#define AW88081_DITHERCFG2_REG (0x5B)
+#define AW88081_DITHERCFG3_REG (0x5C)
+#define AW88081_TM_REG (0x6E)
+#define AW88081_TM2_REG (0x6F)
+#define AW88081_TESTCTRL1_REG (0x70)
+#define AW88081_TESTCTRL2_REG (0x71)
+
+#define AW88081_REG_MAX (0x72)
+
+#define AW88081_UVLS_START_BIT (14)
+#define AW88081_UVLS_UVLO (1)
+#define AW88081_UVLS_UVLO_VALUE \
+ (AW88081_UVLS_UVLO << AW88081_UVLS_START_BIT)
+
+#define AW88081_SWS_START_BIT (8)
+#define AW88081_SWS_SWITCHING (1)
+#define AW88081_SWS_SWITCHING_VALUE \
+ (AW88081_SWS_SWITCHING << AW88081_SWS_START_BIT)
+
+#define AW88081_NOCLKS_START_BIT (5)
+#define AW88081_NOCLKS_NO_CLOCK (1)
+#define AW88081_NOCLKS_NO_CLOCK_VALUE \
+ (AW88081_NOCLKS_NO_CLOCK << AW88081_NOCLKS_START_BIT)
+
+#define AW88081_CLKS_START_BIT (4)
+#define AW88081_CLKS_STABLE (1)
+#define AW88081_CLKS_STABLE_VALUE \
+ (AW88081_CLKS_STABLE << AW88081_CLKS_START_BIT)
+
+#define AW88081_OCDS_START_BIT (3)
+#define AW88081_OCDS_OC (1)
+#define AW88081_OCDS_OC_VALUE \
+ (AW88081_OCDS_OC << AW88081_OCDS_START_BIT)
+
+#define AW88081_OTHS_START_BIT (1)
+#define AW88081_OTHS_OT (1)
+#define AW88081_OTHS_OT_VALUE \
+ (AW88081_OTHS_OT << AW88081_OTHS_START_BIT)
+
+#define AW88081_PLLS_START_BIT (0)
+#define AW88081_PLLS_LOCKED (1)
+#define AW88081_PLLS_LOCKED_VALUE \
+ (AW88081_PLLS_LOCKED << AW88081_PLLS_START_BIT)
+
+#define AW88081_BIT_PLL_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_BIT_SYSST_CHECK_MASK \
+ (~(AW88081_UVLS_UVLO_VALUE | \
+ AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_NOCLKS_NO_CLOCK_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_OCDS_OC_VALUE | \
+ AW88081_OTHS_OT_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE))
+
+#define AW88081_NO_SWS_SYSST_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_SWS_SYSST_CHECK \
+ (AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_ULS_HMUTE_START_BIT (14)
+#define AW88081_ULS_HMUTE_BITS_LEN (1)
+#define AW88081_ULS_HMUTE_MASK \
+ (~(((1<<AW88081_ULS_HMUTE_BITS_LEN)-1) << AW88081_ULS_HMUTE_START_BIT))
+
+#define AW88081_ULS_HMUTE_DISABLE (0)
+#define AW88081_ULS_HMUTE_DISABLE_VALUE \
+ (AW88081_ULS_HMUTE_DISABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_ULS_HMUTE_ENABLE (1)
+#define AW88081_ULS_HMUTE_ENABLE_VALUE \
+ (AW88081_ULS_HMUTE_ENABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_START_BIT (8)
+#define AW88081_HMUTE_BITS_LEN (1)
+#define AW88081_HMUTE_MASK \
+ (~(((1<<AW88081_HMUTE_BITS_LEN)-1) << AW88081_HMUTE_START_BIT))
+
+#define AW88081_HMUTE_DISABLE (0)
+#define AW88081_HMUTE_DISABLE_VALUE \
+ (AW88081_HMUTE_DISABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_ENABLE (1)
+#define AW88081_HMUTE_ENABLE_VALUE \
+ (AW88081_HMUTE_ENABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_EN_PA_START_BIT (1)
+#define AW88081_EN_PA_BITS_LEN (1)
+#define AW88081_EN_PA_MASK \
+ (~(((1<<AW88081_EN_PA_BITS_LEN)-1) << AW88081_EN_PA_START_BIT))
+
+#define AW88081_EN_PA_WORKING (1)
+#define AW88081_EN_PA_WORKING_VALUE \
+ (AW88081_EN_PA_WORKING << AW88081_EN_PA_START_BIT)
+
+#define AW88081_EN_PA_POWER_DOWN (0)
+#define AW88081_EN_PA_POWER_DOWN_VALUE \
+ (AW88081_EN_PA_POWER_DOWN << AW88081_EN_PA_START_BIT)
+
+#define AW88081_PWDN_START_BIT (0)
+#define AW88081_PWDN_BITS_LEN (1)
+#define AW88081_PWDN_MASK \
+ (~(((1<<AW88081_PWDN_BITS_LEN)-1) << AW88081_PWDN_START_BIT))
+
+#define AW88081_PWDN_WORKING (0)
+#define AW88081_PWDN_WORKING_VALUE \
+ (AW88081_PWDN_WORKING << AW88081_PWDN_START_BIT)
+
+#define AW88081_PWDN_POWER_DOWN (1)
+#define AW88081_PWDN_POWER_DOWN_VALUE \
+ (AW88081_PWDN_POWER_DOWN << AW88081_PWDN_START_BIT)
+
+#define AW88081_VOL_START_BIT (0)
+#define AW88081_VOL_BITS_LEN (10)
+#define AW88081_VOL_MASK \
+ (~(((1<<AW88081_VOL_BITS_LEN)-1) << AW88081_VOL_START_BIT))
+
+#define AW88081_VOLUME_STEP_DB (64)
+#define AW88081_MUTE_VOL (1023)
+
+#define AW88081_I2STXEN_START_BIT (6)
+#define AW88081_I2STXEN_BITS_LEN (1)
+#define AW88081_I2STXEN_MASK \
+ (~(((1<<AW88081_I2STXEN_BITS_LEN)-1) << AW88081_I2STXEN_START_BIT))
+
+#define AW88081_I2STXEN_DISABLE (0)
+#define AW88081_I2STXEN_DISABLE_VALUE \
+ (AW88081_I2STXEN_DISABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_I2STXEN_ENABLE (1)
+#define AW88081_I2STXEN_ENABLE_VALUE \
+ (AW88081_I2STXEN_ENABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_START_BIT (13)
+#define AW88081_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88081_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88081_NOISE_GATE_EN_BITS_LEN)-1) << AW88081_NOISE_GATE_EN_START_BIT))
+
+#define AW88081_NOISE_GATE_EN_DISABLE (0)
+#define AW88081_NOISE_GATE_EN_DISABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_DISABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_ENABLE (1)
+#define AW88081_NOISE_GATE_EN_ENABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_ENABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_CCO_MUX_START_BIT (13)
+#define AW88081_CCO_MUX_BITS_LEN (1)
+#define AW88081_CCO_MUX_MASK \
+ (~(((1<<AW88081_CCO_MUX_BITS_LEN)-1) << AW88081_CCO_MUX_START_BIT))
+
+#define AW88081_CCO_MUX_DIVIDED (0)
+#define AW88081_CCO_MUX_DIVIDED_VALUE \
+ (AW88081_CCO_MUX_DIVIDED << AW88081_CCO_MUX_START_BIT)
+
+#define AW88081_CCO_MUX_BYPASS (1)
+#define AW88081_CCO_MUX_BYPASS_VALUE \
+ (AW88081_CCO_MUX_BYPASS << AW88081_CCO_MUX_START_BIT)
+
+#define AW88083_I2C_WEN_START_BIT (14)
+#define AW88083_I2C_WEN_BITS_LEN (2)
+#define AW88083_I2C_WEN_MASK \
+ (~(((1<<AW88083_I2C_WEN_BITS_LEN)-1) << AW88083_I2C_WEN_START_BIT))
+
+#define AW88083_I2C_WEN_DISABLE (0)
+#define AW88083_I2C_WEN_DISABLE_VALUE \
+ (AW88083_I2C_WEN_DISABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_I2C_WEN_ENABLE (2)
+#define AW88083_I2C_WEN_ENABLE_VALUE \
+ (AW88083_I2C_WEN_ENABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_PLL_PD_START_BIT (2)
+#define AW88083_PLL_PD_BITS_LEN (1)
+#define AW88083_PLL_PD_MASK \
+ (~(((1<<AW88083_PLL_PD_BITS_LEN)-1) << AW88083_PLL_PD_START_BIT))
+
+#define AW88083_PLL_PD_POWER_DOWN (1)
+#define AW88083_PLL_PD_POWER_DOWN_VALUE \
+ (AW88083_PLL_PD_POWER_DOWN << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_PLL_PD_WORKING (0)
+#define AW88083_PLL_PD_WORKING_VALUE \
+ (AW88083_PLL_PD_WORKING << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_AMPPD_START_BIT (1)
+#define AW88083_AMPPD_BITS_LEN (1)
+#define AW88083_AMPPD_MASK \
+ (~(((1<<AW88083_AMPPD_BITS_LEN)-1) << AW88083_AMPPD_START_BIT))
+
+#define AW88083_AMPPD_WORKING (0)
+#define AW88083_AMPPD_WORKING_VALUE \
+ (AW88083_AMPPD_WORKING << AW88083_AMPPD_START_BIT)
+
+#define AW88083_AMPPD_POWER_DOWN (1)
+#define AW88083_AMPPD_POWER_DOWN_VALUE \
+ (AW88083_AMPPD_POWER_DOWN << AW88083_AMPPD_START_BIT)
+
+#define AW88083_REG_MAX (0x7D)
+#define AW88083_I2C_NAME "aw88083"
+#define AW88083_CHIP_ID 0x2407
+
+#define AW88081_START_RETRIES (5)
+#define AW88081_START_WORK_DELAY_MS (0)
+
+#define AW88081_I2C_NAME "aw88081"
+#define AW88081_CHIP_ID 0x2116
+
+#define AW88081_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88081_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+
+#define AW88081_DEV_DEFAULT_CH (0)
+#define AW88081_ACF_FILE "aw88081_acf.bin"
+#define AW88081_DEV_SYSST_CHECK_MAX (10)
+#define AW88081_SOFT_RESET_VALUE (0x55aa)
+
+#define AW88081_INIT_PROFILE (0)
+
+#define AW88081_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 {
+ AW88081_SYNC_START = 0,
+ AW88081_ASYNC_START,
+};
+
+enum {
+ AW88081_500_US = 500,
+ AW88081_1000_US = 1000,
+ AW88081_2000_US = 2000,
+ AW88081_5000_US = 5000,
+};
+
+enum {
+ AW88081_DEV_PW_OFF = 0,
+ AW88081_DEV_PW_ON,
+};
+
+enum {
+ AW88081_DEV_FW_FAILED = 0,
+ AW88081_DEV_FW_OK,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c
new file mode 100644
index 000000000000..28f62b991ef2
--- /dev/null
+++ b/sound/soc/codecs/aw88166.c
@@ -0,0 +1,1928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.c -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88166.h"
+#include "aw88395/aw88395_device.h"
+
+struct aw88166 {
+ 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;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int re_init_val;
+ unsigned int dither_st;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88166_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88166_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+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, AW88166_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, AW88166_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_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, AW88166_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, AW88166_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, AW88166_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, AW88166_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, AW88166_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 AW88166_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 failed", (u32)dsp_addr);
+ break;
+ case AW88166_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] 32-bit failed", (u32)dsp_addr);
+ 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, AW88166_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. ret=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ 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, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88166_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_dbg(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, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88166_BIT_PLL_CHECK) != AW88166_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 < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_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, AW88166_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88166_CCO_MUX_MASK);
+ if (reg_val == AW88166_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, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88166_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(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_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 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88166_NOISE_GATE_EN_MASK))
+ check_val = AW88166_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88166_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88166_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88166_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88166_dev_get_icalk(struct aw88166 *aw88166, int16_t *icalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_isn_geslp, ef_isn_h5bits;
+ uint16_t icalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_geslp = (efrm_reg_val & (~AW88166_EF_ISN_GESLP_MASK)) >>
+ AW88166_EF_ISN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_h5bits = (efrl_reg_val & (~AW88166_EF_ISN_H5BITS_MASK)) >>
+ AW88166_EF_ISN_H5BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ icalk_val = ef_isn_geslp & (ef_isn_h5bits | AW88166_EF_ISN_H5BITS_SIGN_MASK);
+ else
+ icalk_val = ef_isn_geslp | (ef_isn_h5bits & (~AW88166_EF_ISN_H5BITS_SIGN_MASK));
+
+ if (icalk_val & (~AW88166_ICALK_SIGN_MASK))
+ icalk_val = icalk_val | AW88166_ICALK_NEG_MASK;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_get_vcalk(struct aw88166 *aw88166, int16_t *vcalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_vsn_geslp, ef_vsn_h3bits;
+ uint16_t vcalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_geslp = (efrm_reg_val & (~AW88166_EF_VSN_GESLP_MASK)) >>
+ AW88166_EF_VSN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_h3bits = (efrl_reg_val & (~AW88166_EF_VSN_H3BITS_MASK)) >>
+ AW88166_EF_VSN_H3BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ vcalk_val = ef_vsn_geslp & (ef_vsn_h3bits | AW88166_EF_VSN_H3BITS_SIGN_MASK);
+ else
+ vcalk_val = ef_vsn_geslp | (ef_vsn_h3bits & (~AW88166_EF_VSN_H3BITS_SIGN_MASK));
+
+ if (vcalk_val & (~AW88166_VCALK_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88166_VCALK_NEG_MASK;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_set_vcalb(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ unsigned int reg_val;
+ int ret;
+
+ ret = aw88166_dev_get_icalk(aw88166, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+ ical_k = icalk * AW88166_ICABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ ret = aw88166_dev_get_vcalk(aw88166, &vcalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vbcalk failed\n");
+ return ret;
+ }
+ vcal_k = vcalk * AW88166_VCABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ vcalb = AW88166_VCALB_ACCURACY * AW88166_VSCAL_FACTOR /
+ AW88166_ISCAL_FACTOR * ical_k / vcal_k * aw88166->vcalb_init_val;
+
+ vcalb = vcalb >> AW88166_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int aw_dev_init_vcalb_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, aw88166->vcalb_init_val);
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_DSPVCALB_REG, &aw88166->vcalb_init_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_init_re_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int re_temp_h, re_temp_l;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, aw88166->re_init_val >> 16);
+ if (ret)
+ return ret;
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG,
+ (uint16_t)aw88166->re_init_val);
+ if (ret)
+ return ret;
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR1_REG, &re_temp_h);
+ if (ret)
+ return ret;
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR2_REG, &re_temp_l);
+ if (ret)
+ return ret;
+ aw88166->re_init_val = (re_temp_h << 16) + re_temp_l;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_backup_sec_record(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECORD_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static void aw_dev_backup_sec_recovery(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static int 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);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88166_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88166_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88166_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88166_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88166_CALI_RE_HBITS_MASK)) >> AW88166_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88166_CALI_RE_LBITS_MASK)) >> AW88166_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x\n",
+ __func__, check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x\n",
+ check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_CRCCTRL_REG, aw88166->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+ 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, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88166_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88166_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88166_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ for (i = 0; i < AW88166_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(AW88166_2000_US, AW88166_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88166_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88166_VOL_START_BIT) | (reg_value & AW88166_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88166_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+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 (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88166_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88166_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 != AW88166_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88166_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88166_dev_set_dither(struct aw88166 *aw88166, bool dither)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88166_dev_start(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88166_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88166_dev_set_dither(aw88166, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed\n");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK) {
+ aw_dev_backup_sec_recovery(aw88166);
+ ret = aw_dev_hw_crc_check(aw88166);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed\n");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw88166_dev_set_vcalb(aw88166);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed\n");
+ 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);
+
+ if (aw88166->dither_st == AW88166_DITHER_EN_ENABLE_VALUE)
+ aw88166_dev_set_dither(aw88166, true);
+
+ /* close mute */
+ aw88166_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88166_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+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 = AW88166_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88166_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+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, AW88166_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88166_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error\n");
+ return ret;
+ }
+
+ cali_desc->ra = AW88166_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88166_DSP_RE_SHIFT);
+
+ 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\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ 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\n");
+ return -EINVAL;
+ }
+
+ aw_dev->dsp_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_ROM_CHECK_ADDR, &reg_val);
+ if (reg_val != AW88166_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]\n",
+ reg_val, AW88166_DSP_ROM_CHECK_DATA);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88166_DSP_CFG_ADDR, AW88166_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88166_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]\n",
+ reg_val, AW88166_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88166_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed\n");
+ break;
+ case AW88166_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_OSCCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed\n");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x\n", flag);
+ break;
+ }
+}
+
+static int aw_dev_update_reg_container(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported\n", 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 == AW88166_DSPVCALB_REG) {
+ aw88166->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL_REG) {
+ if (reg_val & (~AW88166_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_WORK;
+
+ reg_val &= (AW88166_HMUTE_MASK | AW88166_PWDN_MASK |
+ AW88166_DSPBY_MASK);
+ reg_val |= (AW88166_HMUTE_ENABLE_VALUE | AW88166_PWDN_POWER_DOWN_VALUE |
+ AW88166_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88166_I2SCTRL3_REG) {
+ reg_val &= AW88166_I2STXEN_MASK;
+ reg_val |= AW88166_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88166_VOL_MASK)) >>
+ AW88166_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88166_DBGCTRL_REG) {
+ if ((reg_val & (~AW88166_EF_DBMD_MASK)) == AW88166_EF_DBMD_OR_VALUE)
+ aw88166->check_val = AW_EF_OR_CHECK;
+ else
+ aw88166->check_val = AW_EF_AND_CHECK;
+
+ aw88166->dither_st = reg_val & (~AW88166_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88166_ACR1_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val << 16;
+ continue;
+ }
+
+ if (reg_addr == AW88166_ACR2_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_CRCCTRL_REG)
+ aw88166->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88166->aw_pa->dev, "reg data is null or len is 0\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88166, data, len);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "reg update failed\n");
+
+ return ret;
+}
+
+static int aw88166_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88166_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;
+}
+
+static int aw88166_dev_fw_update(struct aw88166 *aw88166, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ 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 == AW88166_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88166_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error\n", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88166_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88166_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(aw88166, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed\n");
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_recovery(aw88166);
+
+ if (up_dsp_fw_en) {
+ 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\n");
+ 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\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_record(aw88166);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88166_start_pa(struct aw88166 *aw88166)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88166_START_RETRIES; i++) {
+ ret = aw88166_dev_start(aw88166);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "aw88166 device start failed. retry = %d", i);
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88166->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88166_startup_work(struct work_struct *work)
+{
+ struct aw88166 *aw88166 =
+ container_of(work, struct aw88166, start_work.work);
+
+ mutex_lock(&aw88166->lock);
+ aw88166_start_pa(aw88166);
+ mutex_unlock(&aw88166->lock);
+}
+
+static void aw88166_start(struct aw88166 *aw88166, bool sync_start)
+{
+ int ret;
+
+ if (aw88166->aw_pa->fw_status != AW88166_DEV_FW_OK)
+ return;
+
+ if (aw88166->aw_pa->status == AW88166_DEV_PW_ON)
+ return;
+
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_OFF, aw88166->phase_sync);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed\n");
+ return;
+ }
+
+ if (sync_start == AW88166_SYNC_START)
+ aw88166_start_pa(aw88166);
+ else
+ queue_delayed_work(system_wq,
+ &aw88166->start_work,
+ AW88166_START_WORK_DELAY_MS);
+}
+
+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 & AW88166_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x\n", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88166_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;
+
+ if (aw_dev->status == AW88166_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88166_DEV_PW_OFF;
+
+ aw88166_dev_mute(aw_dev, true);
+ usleep_range(AW88166_4000_US, AW88166_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88166_dai[] = {
+ {
+ .name = "aw88166-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ },
+};
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 aw88166_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;
+}
+
+static int aw88166_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88166->aw_pa->prof_info.count;
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88166_dev_get_prof_name(aw88166->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88166_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88166_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88166->lock);
+ ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88166->lock);
+ return 0;
+ }
+
+ if (aw88166->aw_pa->status) {
+ aw88166_stop(aw88166->aw_pa);
+ aw88166_start(aw88166, AW88166_SYNC_START);
+ }
+
+ mutex_unlock(&aw88166->lock);
+
+ return 1;
+}
+
+static int aw88166_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88166_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->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;
+ aw_dev_set_volume(aw88166->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88166_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = 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 (aw88166->aw_pa->fade_step != value) {
+ aw88166->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88166_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed\n");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88166_1000_US / 10;
+ aw_dev->fade_out_time = AW88166_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 = aw88166_dev_fw_update(aw88166, AW88166_FORCE_UPDATE_ON, AW88166_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88166_request_firmware_file(struct aw88166 *aw88166)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88166->aw_pa->fw_status = AW88166_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88166_ACF_FILE, aw88166->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88166->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88166_ACF_FILE, cont ? cont->size : 0);
+
+ aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev,
+ struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88166->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88166->aw_cfg->len = (int)cont->size;
+ memcpy(aw88166->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88166->lock);
+ /* aw device init */
+ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "dev init failed\n");
+ mutex_unlock(&aw88166->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88166_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88166_SYSCTRL2_REG,
+ 6, AW88166_MUTE_VOL, 0, aw88166_volume_get,
+ aw88166_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88166_MUTE_VOL, 0,
+ aw88166_get_fade_step, aw88166_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_in_time, aw88166_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_out_time, aw88166_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88166_CALI_RE_MAX, 0,
+ aw88166_re_get, aw88166_re_set),
+ AW88166_PROFILE_EXT("AW88166 Profile Set", aw88166_profile_info,
+ aw88166_profile_get, aw88166_profile_set),
+};
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88166->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88166_start(aw88166, AW88166_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88166_stop(aw88166->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88166->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88166_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88166_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 aw88166_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88166_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88166->start_work, aw88166_startup_work);
+
+ ret = aw88166_request_firmware_file(aw88166);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88166_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88166->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88166 = {
+ .probe = aw88166_codec_probe,
+ .remove = aw88166_codec_remove,
+ .dapm_widgets = aw88166_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88166_dapm_widgets),
+ .dapm_routes = aw88166_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88166_audio_map),
+ .controls = aw88166_controls,
+ .num_controls = ARRAY_SIZE(aw88166_controls),
+};
+
+static void aw88166_hw_reset(struct aw88166 *aw88166)
+{
+ if (aw88166->reset_gpio) {
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 1);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 0);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ }
+}
+
+static void aw88166_parse_channel_dt(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+ aw88166->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+}
+
+static int aw88166_init(struct aw88166 *aw88166, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88166_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88166->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = chip_id;
+ 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 = AW88166_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88166_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88166_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88166_VOL_DEFAULT_VALUE;
+
+ aw88166_parse_channel_dt(aw88166);
+
+ return 0;
+}
+
+static int aw88166_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88166 *aw88166;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw88166 = devm_kzalloc(&i2c->dev, sizeof(*aw88166), GFP_KERNEL);
+ if (!aw88166)
+ return -ENOMEM;
+
+ mutex_init(&aw88166->lock);
+
+ i2c_set_clientdata(i2c, aw88166);
+
+ aw88166->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88166->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->reset_gpio),
+ "reset gpio not defined\n");
+ aw88166_hw_reset(aw88166);
+
+ aw88166->regmap = devm_regmap_init_i2c(i2c, &aw88166_remap_config);
+ if (IS_ERR(aw88166->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88166_init(aw88166, i2c, aw88166->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88166,
+ aw88166_dai, ARRAY_SIZE(aw88166_dai));
+}
+
+static const struct i2c_device_id aw88166_i2c_id[] = {
+ { AW88166_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id);
+
+static struct i2c_driver aw88166_i2c_driver = {
+ .driver = {
+ .name = AW88166_I2C_NAME,
+ },
+ .probe = aw88166_i2c_probe,
+ .id_table = aw88166_i2c_id,
+};
+module_i2c_driver(aw88166_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88166 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88166.h b/sound/soc/codecs/aw88166.h
new file mode 100644
index 000000000000..3a53ba0ac625
--- /dev/null
+++ b/sound/soc/codecs/aw88166.h
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.h -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88166_H__
+#define __AW88166_H__
+
+/* registers list */
+#define AW88166_ID_REG (0x00)
+#define AW88166_SYSST_REG (0x01)
+#define AW88166_SYSINT_REG (0x02)
+#define AW88166_SYSINTM_REG (0x03)
+#define AW88166_SYSCTRL_REG (0x04)
+#define AW88166_SYSCTRL2_REG (0x05)
+#define AW88166_I2SCTRL1_REG (0x06)
+#define AW88166_I2SCTRL2_REG (0x07)
+#define AW88166_I2SCTRL3_REG (0x08)
+#define AW88166_DACCFG1_REG (0x09)
+#define AW88166_DACCFG2_REG (0x0A)
+#define AW88166_DACCFG3_REG (0x0B)
+#define AW88166_DACCFG4_REG (0x0C)
+#define AW88166_DACCFG5_REG (0x0D)
+#define AW88166_DACCFG6_REG (0x0E)
+#define AW88166_DACCFG7_REG (0x0F)
+#define AW88166_MPDCFG1_REG (0x10)
+#define AW88166_MPDCFG2_REG (0x11)
+#define AW88166_MPDCFG3_REG (0x12)
+#define AW88166_MPDCFG4_REG (0x13)
+#define AW88166_PWMCTRL1_REG (0x14)
+#define AW88166_PWMCTRL2_REG (0x15)
+#define AW88166_PWMCTRL3_REG (0x16)
+#define AW88166_I2SCFG1_REG (0x17)
+#define AW88166_DBGCTRL_REG (0x18)
+#define AW88166_HAGCST_REG (0x20)
+#define AW88166_VBAT_REG (0x21)
+#define AW88166_TEMP_REG (0x22)
+#define AW88166_PVDD_REG (0x23)
+#define AW88166_ISNDAT_REG (0x24)
+#define AW88166_I2SINT_REG (0x25)
+#define AW88166_I2SCAPCNT_REG (0x26)
+#define AW88166_ANASTA1_REG (0x27)
+#define AW88166_ANASTA2_REG (0x28)
+#define AW88166_ANASTA3_REG (0x29)
+#define AW88166_TESTDET_REG (0x2A)
+#define AW88166_TESTIN_REG (0x38)
+#define AW88166_TESTOUT_REG (0x39)
+#define AW88166_MEMTEST_REG (0x3A)
+#define AW88166_DSPMADD_REG (0x40)
+#define AW88166_DSPMDAT_REG (0x41)
+#define AW88166_WDT_REG (0x42)
+#define AW88166_ACR1_REG (0x43)
+#define AW88166_ACR2_REG (0x44)
+#define AW88166_ASR1_REG (0x45)
+#define AW88166_ASR2_REG (0x46)
+#define AW88166_DSPCFG_REG (0x47)
+#define AW88166_ASR3_REG (0x48)
+#define AW88166_ASR4_REG (0x49)
+#define AW88166_DSPVCALB_REG (0x4A)
+#define AW88166_CRCCTRL_REG (0x4B)
+#define AW88166_DSPDBG1_REG (0x4C)
+#define AW88166_DSPDBG2_REG (0x4D)
+#define AW88166_DSPDBG3_REG (0x4E)
+#define AW88166_ISNCTRL1_REG (0x50)
+#define AW88166_PLLCTRL1_REG (0x51)
+#define AW88166_PLLCTRL2_REG (0x52)
+#define AW88166_PLLCTRL3_REG (0x53)
+#define AW88166_CDACTRL1_REG (0x54)
+#define AW88166_CDACTRL2_REG (0x55)
+#define AW88166_CDACTRL3_REG (0x56)
+#define AW88166_SADCCTRL1_REG (0x57)
+#define AW88166_SADCCTRL2_REG (0x58)
+#define AW88166_BOPCTRL1_REG (0x59)
+#define AW88166_BOPCTRL2_REG (0x5A)
+#define AW88166_BOPCTRL3_REG (0x5B)
+#define AW88166_BOPCTRL4_REG (0x5C)
+#define AW88166_BOPCTRL5_REG (0x5D)
+#define AW88166_BOPCTRL6_REG (0x5E)
+#define AW88166_BOPCTRL7_REG (0x5F)
+#define AW88166_BSTCTRL1_REG (0x60)
+#define AW88166_BSTCTRL2_REG (0x61)
+#define AW88166_BSTCTRL3_REG (0x62)
+#define AW88166_BSTCTRL4_REG (0x63)
+#define AW88166_BSTCTRL5_REG (0x64)
+#define AW88166_BSTCTRL6_REG (0x65)
+#define AW88166_DSMCFG1_REG (0x66)
+#define AW88166_DSMCFG2_REG (0x67)
+#define AW88166_DSMCFG3_REG (0x68)
+#define AW88166_DSMCFG4_REG (0x69)
+#define AW88166_DSMCFG5_REG (0x6A)
+#define AW88166_DSMCFG6_REG (0x6B)
+#define AW88166_DSMCFG7_REG (0x6C)
+#define AW88166_DSMCFG8_REG (0x6D)
+#define AW88166_TESTCTRL1_REG (0x70)
+#define AW88166_TESTCTRL2_REG (0x71)
+#define AW88166_EFCTRL1_REG (0x72)
+#define AW88166_EFCTRL2_REG (0x73)
+#define AW88166_EFWH_REG (0x74)
+#define AW88166_EFWM2_REG (0x75)
+#define AW88166_EFWM1_REG (0x76)
+#define AW88166_EFRH_REG (0x77)
+#define AW88166_EFRM2_REG (0x78)
+#define AW88166_EFRM1_REG (0x79)
+#define AW88166_EFRL_REG (0x7A)
+#define AW88166_TM_REG (0x7C)
+#define AW88166_TM2_REG (0x7D)
+
+#define AW88166_REG_MAX (0x7E)
+#define AW88166_MUTE_VOL (1023)
+
+#define AW88166_DSP_CFG_ADDR (0x9B00)
+#define AW88166_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88166_DSP_FW_ADDR (0x8980)
+#define AW88166_DSP_ROM_CHECK_ADDR (0x1F40)
+
+#define AW88166_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88166_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88166_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88166_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88166_I2STXEN_START_BIT (9)
+#define AW88166_I2STXEN_BITS_LEN (1)
+#define AW88166_I2STXEN_MASK \
+ (~(((1<<AW88166_I2STXEN_BITS_LEN)-1) << AW88166_I2STXEN_START_BIT))
+
+#define AW88166_I2STXEN_DISABLE (0)
+#define AW88166_I2STXEN_DISABLE_VALUE \
+ (AW88166_I2STXEN_DISABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_I2STXEN_ENABLE (1)
+#define AW88166_I2STXEN_ENABLE_VALUE \
+ (AW88166_I2STXEN_ENABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_VOL_START_BIT (0)
+#define AW88166_VOL_BITS_LEN (10)
+#define AW88166_VOL_MASK \
+ (~(((1<<AW88166_VOL_BITS_LEN)-1) << AW88166_VOL_START_BIT))
+
+#define AW88166_PWDN_START_BIT (0)
+#define AW88166_PWDN_BITS_LEN (1)
+#define AW88166_PWDN_MASK \
+ (~(((1<<AW88166_PWDN_BITS_LEN)-1) << AW88166_PWDN_START_BIT))
+
+#define AW88166_PWDN_POWER_DOWN (1)
+#define AW88166_PWDN_POWER_DOWN_VALUE \
+ (AW88166_PWDN_POWER_DOWN << AW88166_PWDN_START_BIT)
+
+#define AW88166_PWDN_WORKING (0)
+#define AW88166_PWDN_WORKING_VALUE \
+ (AW88166_PWDN_WORKING << AW88166_PWDN_START_BIT)
+
+#define AW88166_DSPBY_START_BIT (2)
+#define AW88166_DSPBY_BITS_LEN (1)
+#define AW88166_DSPBY_MASK \
+ (~(((1<<AW88166_DSPBY_BITS_LEN)-1) << AW88166_DSPBY_START_BIT))
+
+#define AW88166_DSPBY_WORKING (0)
+#define AW88166_DSPBY_WORKING_VALUE \
+ (AW88166_DSPBY_WORKING << AW88166_DSPBY_START_BIT)
+
+#define AW88166_DSPBY_BYPASS (1)
+#define AW88166_DSPBY_BYPASS_VALUE \
+ (AW88166_DSPBY_BYPASS << AW88166_DSPBY_START_BIT)
+
+#define AW88166_MEM_CLKSEL_START_BIT (3)
+#define AW88166_MEM_CLKSEL_BITS_LEN (1)
+#define AW88166_MEM_CLKSEL_MASK \
+ (~(((1<<AW88166_MEM_CLKSEL_BITS_LEN)-1) << AW88166_MEM_CLKSEL_START_BIT))
+
+#define AW88166_MEM_CLKSEL_OSCCLK (0)
+#define AW88166_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88166_MEM_CLKSEL_OSCCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_MEM_CLKSEL_DAPHCLK (1)
+#define AW88166_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88166_MEM_CLKSEL_DAPHCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_DITHER_EN_START_BIT (15)
+#define AW88166_DITHER_EN_BITS_LEN (1)
+#define AW88166_DITHER_EN_MASK \
+ (~(((1<<AW88166_DITHER_EN_BITS_LEN)-1) << AW88166_DITHER_EN_START_BIT))
+
+#define AW88166_DITHER_EN_DISABLE (0)
+#define AW88166_DITHER_EN_DISABLE_VALUE \
+ (AW88166_DITHER_EN_DISABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_DITHER_EN_ENABLE (1)
+#define AW88166_DITHER_EN_ENABLE_VALUE \
+ (AW88166_DITHER_EN_ENABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_HMUTE_START_BIT (8)
+#define AW88166_HMUTE_BITS_LEN (1)
+#define AW88166_HMUTE_MASK \
+ (~(((1<<AW88166_HMUTE_BITS_LEN)-1) << AW88166_HMUTE_START_BIT))
+
+#define AW88166_HMUTE_DISABLE (0)
+#define AW88166_HMUTE_DISABLE_VALUE \
+ (AW88166_HMUTE_DISABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_HMUTE_ENABLE (1)
+#define AW88166_HMUTE_ENABLE_VALUE \
+ (AW88166_HMUTE_ENABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_EF_DBMD_START_BIT (2)
+#define AW88166_EF_DBMD_BITS_LEN (1)
+#define AW88166_EF_DBMD_MASK \
+ (~(((1<<AW88166_EF_DBMD_BITS_LEN)-1) << AW88166_EF_DBMD_START_BIT))
+
+#define AW88166_EF_DBMD_OR (1)
+#define AW88166_EF_DBMD_OR_VALUE \
+ (AW88166_EF_DBMD_OR << AW88166_EF_DBMD_START_BIT)
+
+#define AW88166_CLKI_START_BIT (4)
+#define AW88166_NOCLKI_START_BIT (5)
+#define AW88166_PLLI_START_BIT (0)
+#define AW88166_PLLI_INT_VALUE (1)
+#define AW88166_PLLI_INT_INTERRUPT \
+ (AW88166_PLLI_INT_VALUE << AW88166_PLLI_START_BIT)
+
+#define AW88166_CLKI_INT_VALUE (1)
+#define AW88166_CLKI_INT_INTERRUPT \
+ (AW88166_CLKI_INT_VALUE << AW88166_CLKI_START_BIT)
+
+#define AW88166_NOCLKI_INT_VALUE (1)
+#define AW88166_NOCLKI_INT_INTERRUPT \
+ (AW88166_NOCLKI_INT_VALUE << AW88166_NOCLKI_START_BIT)
+
+#define AW88166_BIT_SYSINT_CHECK \
+ (AW88166_PLLI_INT_INTERRUPT | \
+ AW88166_CLKI_INT_INTERRUPT | \
+ AW88166_NOCLKI_INT_INTERRUPT)
+
+#define AW88166_CRC_CHECK_START_BIT (12)
+#define AW88166_CRC_CHECK_BITS_LEN (3)
+#define AW88166_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88166_CRC_CHECK_BITS_LEN)-1) << AW88166_CRC_CHECK_START_BIT))
+
+#define AW88166_RCV_MODE_RECEIVER (1)
+#define AW88166_RCV_MODE_RECEIVER_VALUE \
+ (AW88166_RCV_MODE_RECEIVER << AW88166_RCV_MODE_START_BIT)
+
+#define AW88166_AMPPD_START_BIT (1)
+#define AW88166_AMPPD_BITS_LEN (1)
+#define AW88166_AMPPD_MASK \
+ (~(((1<<AW88166_AMPPD_BITS_LEN)-1) << AW88166_AMPPD_START_BIT))
+
+#define AW88166_AMPPD_WORKING (0)
+#define AW88166_AMPPD_WORKING_VALUE \
+ (AW88166_AMPPD_WORKING << AW88166_AMPPD_START_BIT)
+
+#define AW88166_AMPPD_POWER_DOWN (1)
+#define AW88166_AMPPD_POWER_DOWN_VALUE \
+ (AW88166_AMPPD_POWER_DOWN << AW88166_AMPPD_START_BIT)
+
+#define AW88166_RAM_CG_BYP_START_BIT (0)
+#define AW88166_RAM_CG_BYP_BITS_LEN (1)
+#define AW88166_RAM_CG_BYP_MASK \
+ (~(((1<<AW88166_RAM_CG_BYP_BITS_LEN)-1) << AW88166_RAM_CG_BYP_START_BIT))
+
+#define AW88166_RAM_CG_BYP_WORK (0)
+#define AW88166_RAM_CG_BYP_WORK_VALUE \
+ (AW88166_RAM_CG_BYP_WORK << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_RAM_CG_BYP_BYPASS (1)
+#define AW88166_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88166_RAM_CG_BYP_BYPASS << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_CRC_END_ADDR_START_BIT (0)
+#define AW88166_CRC_END_ADDR_BITS_LEN (12)
+#define AW88166_CRC_END_ADDR_MASK \
+ (~(((1<<AW88166_CRC_END_ADDR_BITS_LEN)-1) << AW88166_CRC_END_ADDR_START_BIT))
+
+#define AW88166_CRC_CODE_EN_START_BIT (13)
+#define AW88166_CRC_CODE_EN_BITS_LEN (1)
+#define AW88166_CRC_CODE_EN_MASK \
+ (~(((1<<AW88166_CRC_CODE_EN_BITS_LEN)-1) << AW88166_CRC_CODE_EN_START_BIT))
+
+#define AW88166_CRC_CODE_EN_DISABLE (0)
+#define AW88166_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88166_CRC_CODE_EN_DISABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CODE_EN_ENABLE (1)
+#define AW88166_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88166_CRC_CODE_EN_ENABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_START_BIT (12)
+#define AW88166_CRC_CFG_EN_BITS_LEN (1)
+#define AW88166_CRC_CFG_EN_MASK \
+ (~(((1<<AW88166_CRC_CFG_EN_BITS_LEN)-1) << AW88166_CRC_CFG_EN_START_BIT))
+
+#define AW88166_CRC_CFG_EN_DISABLE (0)
+#define AW88166_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88166_CRC_CFG_EN_DISABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_ENABLE (1)
+#define AW88166_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88166_CRC_CFG_EN_ENABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_OCDS_START_BIT (3)
+#define AW88166_OCDS_OC (1)
+#define AW88166_OCDS_OC_VALUE \
+ (AW88166_OCDS_OC << AW88166_OCDS_START_BIT)
+
+#define AW88166_NOCLKS_START_BIT (5)
+#define AW88166_NOCLKS_NO_CLOCK (1)
+#define AW88166_NOCLKS_NO_CLOCK_VALUE \
+ (AW88166_NOCLKS_NO_CLOCK << AW88166_NOCLKS_START_BIT)
+
+#define AW88166_SWS_START_BIT (8)
+#define AW88166_SWS_SWITCHING (1)
+#define AW88166_SWS_SWITCHING_VALUE \
+ (AW88166_SWS_SWITCHING << AW88166_SWS_START_BIT)
+
+#define AW88166_BSTS_START_BIT (9)
+#define AW88166_BSTS_FINISHED (1)
+#define AW88166_BSTS_FINISHED_VALUE \
+ (AW88166_BSTS_FINISHED << AW88166_BSTS_START_BIT)
+
+#define AW88166_UVLS_START_BIT (14)
+#define AW88166_UVLS_NORMAL (0)
+#define AW88166_UVLS_NORMAL_VALUE \
+ (AW88166_UVLS_NORMAL << AW88166_UVLS_START_BIT)
+
+#define AW88166_BSTOCS_START_BIT (11)
+#define AW88166_BSTOCS_OVER_CURRENT (1)
+#define AW88166_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88166_BSTOCS_OVER_CURRENT << AW88166_BSTOCS_START_BIT)
+
+#define AW88166_OTHS_START_BIT (1)
+#define AW88166_OTHS_OT (1)
+#define AW88166_OTHS_OT_VALUE \
+ (AW88166_OTHS_OT << AW88166_OTHS_START_BIT)
+
+#define AW88166_PLLS_START_BIT (0)
+#define AW88166_PLLS_LOCKED (1)
+#define AW88166_PLLS_LOCKED_VALUE \
+ (AW88166_PLLS_LOCKED << AW88166_PLLS_START_BIT)
+
+#define AW88166_CLKS_START_BIT (4)
+#define AW88166_CLKS_STABLE (1)
+#define AW88166_CLKS_STABLE_VALUE \
+ (AW88166_CLKS_STABLE << AW88166_CLKS_START_BIT)
+
+#define AW88166_BIT_PLL_CHECK \
+ (AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_CHECK_MASK \
+ (~(AW88166_UVLS_NORMAL_VALUE | \
+ AW88166_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE | \
+ AW88166_NOCLKS_NO_CLOCK_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_OCDS_OC_VALUE | \
+ AW88166_OTHS_OT_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE))
+
+#define AW88166_BIT_SYSST_NOSWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_SWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE)
+
+#define AW88166_CCO_MUX_START_BIT (14)
+#define AW88166_CCO_MUX_BITS_LEN (1)
+#define AW88166_CCO_MUX_MASK \
+ (~(((1<<AW88166_CCO_MUX_BITS_LEN)-1) << AW88166_CCO_MUX_START_BIT))
+
+#define AW88166_CCO_MUX_DIVIDED (0)
+#define AW88166_CCO_MUX_DIVIDED_VALUE \
+ (AW88166_CCO_MUX_DIVIDED << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_CCO_MUX_BYPASS (1)
+#define AW88166_CCO_MUX_BYPASS_VALUE \
+ (AW88166_CCO_MUX_BYPASS << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_NOISE_GATE_EN_START_BIT (13)
+#define AW88166_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88166_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88166_NOISE_GATE_EN_BITS_LEN)-1) << AW88166_NOISE_GATE_EN_START_BIT))
+
+#define AW88166_WDT_CNT_START_BIT (0)
+#define AW88166_WDT_CNT_BITS_LEN (8)
+#define AW88166_WDT_CNT_MASK \
+ (~(((1<<AW88166_WDT_CNT_BITS_LEN)-1) << AW88166_WDT_CNT_START_BIT))
+
+#define AW88166_EF_ISN_GESLP_START_BIT (0)
+#define AW88166_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88166_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88166_EF_ISN_GESLP_BITS_LEN)-1) << AW88166_EF_ISN_GESLP_START_BIT))
+#define AW88166_EF_ISN_GESLP_SHIFT (0)
+
+#define AW88166_EF_VSN_GESLP_START_BIT (10)
+#define AW88166_EF_VSN_GESLP_BITS_LEN (6)
+#define AW88166_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88166_EF_VSN_GESLP_BITS_LEN)-1) << AW88166_EF_VSN_GESLP_START_BIT))
+#define AW88166_EF_VSN_GESLP_SHIFT (10)
+
+#define AW88166_EF_VSN_H3BITS_START_BIT (13)
+#define AW88166_EF_VSN_H3BITS_BITS_LEN (3)
+#define AW88166_EF_VSN_H3BITS_MASK \
+ (~(((1<<AW88166_EF_VSN_H3BITS_BITS_LEN)-1) << AW88166_EF_VSN_H3BITS_START_BIT))
+#define AW88166_EF_VSN_H3BITS_SHIFT (10)
+#define AW88166_EF_VSN_H3BITS_SIGN_MASK (0x7)
+
+#define AW88166_EF_ISN_H5BITS_START_BIT (8)
+#define AW88166_EF_ISN_H5BITS_BITS_LEN (5)
+#define AW88166_EF_ISN_H5BITS_MASK \
+ (~(((1<<AW88166_EF_ISN_H5BITS_BITS_LEN)-1) << AW88166_EF_ISN_H5BITS_START_BIT))
+#define AW88166_EF_ISN_H5BITS_SIGN_MASK (0x1F)
+#define AW88166_EF_ISN_H5BITS_SHIFT (3)
+
+#define AW88166_VSCAL_FACTOR (65300)
+#define AW88166_ISCAL_FACTOR (34667)
+#define AW88166_CABL_BASE_VALUE (1000)
+#define AW88166_VCALK_SIGN_MASK (~(1 << 5))
+#define AW88166_VCALK_NEG_MASK (0xFFE0)
+#define AW88166_ICALK_SIGN_MASK (~(1 << 9))
+#define AW88166_ICALK_NEG_MASK (0xFE00)
+#define AW88166_ICABLK_FACTOR (1)
+#define AW88166_VCABLK_FACTOR (2)
+#define AW88166_VCALB_ADJ_FACTOR (12)
+#define AW88166_VCALB_ACCURACY (1 << 12)
+#define AW88166_DSP_RE_SHIFT (12)
+#define AW88166_CALI_RE_MAX (15000)
+#define AW88166_CALI_RE_MIN (4000)
+#define AW88166_VOLUME_STEP_DB (64)
+#define AW88166_VOL_DEFAULT_VALUE (0)
+#define AW88166_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88166_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88166_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88166_DSP_ROM_CHECK_DATA (0xFF99)
+
+#define AW88166_DEV_DEFAULT_CH (0)
+#define AW88166_DEV_DSP_CHECK_MAX (5)
+#define AW88166_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW_FW_ADDR_LEN (4)
+#define AW88166_CRC_CHECK_PASS_VAL (0x4)
+#define AW88166_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88166_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88166_DEV_SYSST_CHECK_MAX (10)
+#define AW88166_START_RETRIES (5)
+#define AW88166_START_WORK_DELAY_MS (0)
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+#define AW88166_CHIP_ID (0x2066)
+#define AW88166_I2C_NAME "aw88166"
+#define AW88166_ACF_FILE "aw88166_acf.bin"
+
+#define AW88166_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88166_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AW88166_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 {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88166_DSP_FW_UPDATE_OFF = 0,
+ AW88166_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_FORCE_UPDATE_OFF = 0,
+ AW88166_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_1000_US = 1000,
+ AW88166_2000_US = 2000,
+ AW88166_3000_US = 3000,
+ AW88166_4000_US = 4000,
+};
+
+enum AW88166_DEV_STATUS {
+ AW88166_DEV_PW_OFF = 0,
+ AW88166_DEV_PW_ON,
+};
+
+enum AW88166_DEV_FW_STATUS {
+ AW88166_DEV_FW_FAILED = 0,
+ AW88166_DEV_FW_OK,
+};
+
+enum AW88166_DEV_MEMCLK {
+ AW88166_DEV_MEMCLK_OSC = 0,
+ AW88166_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88166_DEV_DSP_CFG {
+ AW88166_DEV_DSP_WORK = 0,
+ AW88166_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88166_DSP_16_DATA = 0,
+ AW88166_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88166_SYNC_START = 0,
+ AW88166_ASYNC_START,
+};
+
+enum {
+ AW88166_RECORD_SEC_DATA = 0,
+ AW88166_RECOVERY_SEC_DATA = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
new file mode 100644
index 000000000000..de11ae8dd9d9
--- /dev/null
+++ b/sound/soc/codecs/aw88261.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.c -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88261.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88261_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88261_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int real_value, volume;
+ unsigned int reg_value;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
+ real_value = DB_TO_REG_VAL(volume);
+
+ regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
+
+ real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
+
+ dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
+
+ regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
+}
+
+static void aw88261_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time,
+ aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88261_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88261_MUTE_VOL) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE);
+}
+
+static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE);
+}
+
+static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88261_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
+ aw88261_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88261_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return ret;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88261_CCO_MUX_MASK);
+ if (reg_val == AW88261_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, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88261_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
+ & AW88261_BIT_SYSST_CHECK;
+ if (check_val != AW88261_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, AW88261_BIT_SYSST_CHECK);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static void aw88261_reg_force_set(struct aw88261 *aw88261)
+{
+ if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
+ /* set FORCE_PWM */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
+ AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+ /* set BOOST_OS_WIDTH */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
+ AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+ /* set BURST_LOOPR */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
+ AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+ /* set RSQN_DLY */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
+ AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+ /* set BURST_SSMODE */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
+ AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+ /* set BST_BURST */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
+ AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+ } else {
+ dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
+ }
+}
+
+static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ u16 reg_icalk, reg_icalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK);
+
+ reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT);
+
+ if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ u16 reg_vcalk, reg_vcalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK);
+
+ reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT);
+
+ if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG);
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw88261_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vcalk, vcalb;
+ u32 reg_val;
+ int ret;
+
+ ret = aw88261_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+
+ icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val;
+ vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val;
+ if (!vcalk)
+ return -EINVAL;
+
+ vcalb = AW88261_VCAL_FACTOR * icalk / vcalk;
+ reg_val = (unsigned int)vcalb;
+
+ dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x",
+ icalk, vcalk, vcalb, reg_val);
+ ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val);
+
+ return ret;
+}
+
+static int aw88261_dev_reg_update(struct aw88261 *aw88261,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val, efcheck_val, read_vol;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u16 reg_val;
+ u8 reg_addr;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ 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 == AW88261_SYSCTRL_REG) {
+ aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK);
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+
+ read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) |
+ (~AW88261_HMUTE_MASK);
+ reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK);
+ reg_val |= read_val;
+
+ /* enable uls hmute */
+ reg_val &= AW88261_ULS_HMUTE_MASK;
+ reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_DBGCTRL_REG) {
+ efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK);
+ if (efcheck_val == AW88261_OR_VALUE)
+ aw88261->efuse_check = AW88261_EF_OR_CHECK;
+ else
+ aw88261->efuse_check = AW88261_EF_AND_CHECK;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88261_I2SCTRL3_REG) {
+ /* close tx */
+ reg_val &= AW88261_I2STXEN_MASK;
+ reg_val |= AW88261_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88261_VOL_MASK)) >>
+ AW88261_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ REG_VAL_TO_DB(read_vol);
+ }
+
+ if (reg_addr == AW88261_VSNTM1_REG)
+ continue;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+ }
+
+ ret = aw88261_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return ret;
+}
+
+static int aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88261_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;
+}
+
+static int aw88261_dev_fw_update(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88261_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 = aw88261_dev_reg_update(aw88261, 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;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static int aw88261_dev_start(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88261_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88261_dev_pwd(aw_dev, false);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+
+ ret = aw88261_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88261_dev_amppd(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88261_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, true);
+
+ if (aw88261->amppd_st)
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_reg_force_set(aw88261);
+
+ /* close uls mute */
+ aw88261_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ if (!aw88261->mute_st)
+ aw88261_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88261_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ aw88261_dev_clear_int_status(aw_dev);
+ aw88261_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88261_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88261_dev_stop(struct aw_device *aw_dev)
+{
+ if (aw_dev->status == AW88261_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88261_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ /* enable amppd */
+ aw88261_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_reg_update(struct aw88261 *aw88261, bool force)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ ret = 0;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static void aw88261_start_pa(struct aw88261 *aw88261)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_START_RETRIES; i++) {
+ ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88261_dev_start(aw88261);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_info(aw88261->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88261_startup_work(struct work_struct *work)
+{
+ struct aw88261 *aw88261 =
+ container_of(work, struct aw88261, start_work.work);
+
+ mutex_lock(&aw88261->lock);
+ aw88261_start_pa(aw88261);
+ mutex_unlock(&aw88261->lock);
+}
+
+static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+{
+ if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
+ return;
+
+ if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88261_SYNC_START)
+ aw88261_start_pa(aw88261);
+ else
+ queue_delayed_work(system_wq,
+ &aw88261->start_work,
+ AW88261_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88261_dai[] = {
+ {
+ .name = "aw88261-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ },
+};
+
+static int aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->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 aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->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 aw88261_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 -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw88261_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88261->aw_pa->prof_info.count;
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88261_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88261->lock);
+ ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88261->lock);
+ return 0;
+ }
+
+ if (aw88261->aw_pa->status) {
+ aw88261_dev_stop(aw88261->aw_pa);
+ aw88261_start(aw88261, AW88261_SYNC_START);
+ }
+
+ mutex_unlock(&aw88261->lock);
+
+ return 1;
+}
+
+static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->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;
+ aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = 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 (aw88261->aw_pa->fade_step != value) {
+ aw88261->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88261_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+ 6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
+ aw88261_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
+ aw88261_get_fade_step, aw88261_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_in_time, aw88261_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_out_time, aw88261_set_fade_out_time),
+ AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
+ aw88261_profile_get, aw88261_profile_set),
+};
+
+static int aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88261->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88261_start(aw88261, AW88261_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88261_dev_stop(aw88261->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88261->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88261_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 aw88261_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88261_frcset_check(struct aw88261 *aw88261)
+{
+ unsigned int reg_val;
+ u16 temh, teml, tem;
+ int ret;
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+ temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+ teml = ((u16)reg_val & (~AW88261_TEML_MASK));
+
+ if (aw88261->efuse_check == AW88261_EF_OR_CHECK)
+ tem = (temh | teml);
+ else
+ tem = (temh & teml);
+
+ if (tem == AW88261_DEFAULT_CFG)
+ aw88261->frcset_en = AW88261_FRCSET_ENABLE;
+ else
+ aw88261->frcset_en = AW88261_FRCSET_DISABLE;
+
+ dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d",
+ tem, aw88261->frcset_en);
+
+ return ret;
+}
+
+static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88261_500_US;
+ aw_dev->fade_out_time = AW88261_500_US;
+ aw_dev->prof_cur = AW88261_INIT_PROFILE;
+ aw_dev->prof_index = AW88261_INIT_PROFILE;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = aw88261_frcset_check(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+
+ aw88261_dev_mute(aw_dev, true);
+
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_request_firmware_file(struct aw88261 *aw88261)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "load [%s] failed!", AW88261_ACF_FILE);
+
+ dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88261_ACF_FILE, cont ? cont->size : 0);
+
+ aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88261->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88261->aw_cfg->len = (int)cont->size;
+ memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88261->lock);
+ /* aw device init */
+ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg);
+ if (ret)
+ dev_err(aw88261->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88261->lock);
+
+ return ret;
+}
+
+static int aw88261_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
+
+ ret = aw88261_request_firmware_file(aw88261);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "aw88261_request_firmware_file failed\n");
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
+ ARRAY_SIZE(aw88261_dapm_widgets));
+ if (ret)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
+ ARRAY_SIZE(aw88261_audio_map));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88261_controls,
+ ARRAY_SIZE(aw88261_controls));
+
+ return ret;
+}
+
+static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88261->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
+ .probe = aw88261_codec_probe,
+ .remove = aw88261_codec_remove,
+};
+
+static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88261_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88261->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw88261_init(struct aw88261 **aw88261, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88261_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ dev_info(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ (*aw88261)->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW88261_CHIP_ID;
+ 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 = AW88261_DEV_FW_FAILED;
+ aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+ aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
+ aw88261_parse_channel_dt(*aw88261);
+
+ return ret;
+}
+
+static int aw88261_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88261 *aw88261;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL);
+ if (!aw88261)
+ return -ENOMEM;
+
+ mutex_init(&aw88261->lock);
+
+ i2c_set_clientdata(i2c, aw88261);
+
+ aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
+ if (IS_ERR(aw88261->regmap)) {
+ ret = PTR_ERR(aw88261->regmap);
+ return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret);
+ }
+
+ /* aw pa init */
+ ret = aw88261_init(&aw88261, i2c, aw88261->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88261,
+ aw88261_dai, ARRAY_SIZE(aw88261_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88261_i2c_id[] = {
+ { AW88261_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+
+static struct i2c_driver aw88261_i2c_driver = {
+ .driver = {
+ .name = AW88261_I2C_NAME,
+ },
+ .probe = aw88261_i2c_probe,
+ .id_table = aw88261_i2c_id,
+};
+module_i2c_driver(aw88261_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
new file mode 100644
index 000000000000..734d0f93ced9
--- /dev/null
+++ b/sound/soc/codecs/aw88261.h
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.h -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_H__
+#define __AW88261_H__
+
+#define AW88261_ID_REG (0x00)
+#define AW88261_SYSST_REG (0x01)
+#define AW88261_SYSINT_REG (0x02)
+#define AW88261_SYSINTM_REG (0x03)
+#define AW88261_SYSCTRL_REG (0x04)
+#define AW88261_SYSCTRL2_REG (0x05)
+#define AW88261_I2SCTRL1_REG (0x06)
+#define AW88261_I2SCTRL2_REG (0x07)
+#define AW88261_I2SCTRL3_REG (0x08)
+#define AW88261_DACCFG1_REG (0x09)
+#define AW88261_DACCFG2_REG (0x0A)
+#define AW88261_DACCFG3_REG (0x0B)
+#define AW88261_DACCFG4_REG (0x0C)
+#define AW88261_DACCFG5_REG (0x0D)
+#define AW88261_DACCFG6_REG (0x0E)
+#define AW88261_DACCFG7_REG (0x0F)
+#define AW88261_DACCFG8_REG (0x10)
+#define AW88261_PWMCTRL1_REG (0x11)
+#define AW88261_PWMCTRL2_REG (0x12)
+#define AW88261_I2SCFG1_REG (0x13)
+#define AW88261_DBGCTRL_REG (0x14)
+#define AW88261_DACCFG9_REG (0x15)
+#define AW88261_DACCFG10_REG (0x16)
+#define AW88261_DACST_REG (0x20)
+#define AW88261_VBAT_REG (0x21)
+#define AW88261_TEMP_REG (0x22)
+#define AW88261_PVDD_REG (0x23)
+#define AW88261_ISNDAT_REG (0x24)
+#define AW88261_VSNDAT_REG (0x25)
+#define AW88261_I2SINT_REG (0x26)
+#define AW88261_I2SCAPCNT_REG (0x27)
+#define AW88261_ANASTA1_REG (0x28)
+#define AW88261_ANASTA2_REG (0x29)
+#define AW88261_ANASTA3_REG (0x2A)
+#define AW88261_TESTDET_REG (0x2B)
+#define AW88261_DSMCFG1_REG (0x30)
+#define AW88261_DSMCFG2_REG (0x31)
+#define AW88261_DSMCFG3_REG (0x32)
+#define AW88261_DSMCFG4_REG (0x33)
+#define AW88261_DSMCFG5_REG (0x34)
+#define AW88261_DSMCFG6_REG (0x35)
+#define AW88261_DSMCFG7_REG (0x36)
+#define AW88261_DSMCFG8_REG (0x37)
+#define AW88261_TESTIN_REG (0x38)
+#define AW88261_TESTOUT_REG (0x39)
+#define AW88261_SADCCTRL1_REG (0x3A)
+#define AW88261_SADCCTRL2_REG (0x3B)
+#define AW88261_SADCCTRL3_REG (0x3C)
+#define AW88261_SADCCTRL4_REG (0x3D)
+#define AW88261_SADCCTRL5_REG (0x3E)
+#define AW88261_SADCCTRL6_REG (0x3F)
+#define AW88261_SADCCTRL7_REG (0x40)
+#define AW88261_VSNTM1_REG (0x50)
+#define AW88261_VSNTM2_REG (0x51)
+#define AW88261_ISNCTRL1_REG (0x52)
+#define AW88261_ISNCTRL2_REG (0x53)
+#define AW88261_PLLCTRL1_REG (0x54)
+#define AW88261_PLLCTRL2_REG (0x55)
+#define AW88261_PLLCTRL3_REG (0x56)
+#define AW88261_CDACTRL1_REG (0x57)
+#define AW88261_CDACTRL2_REG (0x58)
+#define AW88261_DITHERCFG1_REG (0x59)
+#define AW88261_DITHERCFG2_REG (0x5A)
+#define AW88261_DITHERCFG3_REG (0x5B)
+#define AW88261_CPCTRL_REG (0x5C)
+#define AW88261_BSTCTRL1_REG (0x60)
+#define AW88261_BSTCTRL2_REG (0x61)
+#define AW88261_BSTCTRL3_REG (0x62)
+#define AW88261_BSTCTRL4_REG (0x63)
+#define AW88261_BSTCTRL5_REG (0x64)
+#define AW88261_BSTCTRL6_REG (0x65)
+#define AW88261_BSTCTRL7_REG (0x66)
+#define AW88261_BSTCTRL8_REG (0x67)
+#define AW88261_BSTCTRL9_REG (0x68)
+#define AW88261_TM_REG (0x6F)
+#define AW88261_TESTCTRL1_REG (0x70)
+#define AW88261_TESTCTRL2_REG (0x71)
+#define AW88261_EFCTRL1_REG (0x72)
+#define AW88261_EFCTRL2_REG (0x73)
+#define AW88261_EFWH_REG (0x74)
+#define AW88261_EFWM2_REG (0x75)
+#define AW88261_EFWM1_REG (0x76)
+#define AW88261_EFWL_REG (0x77)
+#define AW88261_EFRH4_REG (0x78)
+#define AW88261_EFRH3_REG (0x79)
+#define AW88261_EFRH2_REG (0x7A)
+#define AW88261_EFRH1_REG (0x7B)
+#define AW88261_EFRL4_REG (0x7C)
+#define AW88261_EFRL3_REG (0x7D)
+#define AW88261_EFRL2_REG (0x7E)
+#define AW88261_EFRL1_REG (0x7F)
+
+#define AW88261_REG_MAX (0x80)
+#define AW88261_EF_DBMD_MASK (0xfff7)
+#define AW88261_OR_VALUE (0x0008)
+
+#define AW88261_TEMH_MASK (0x83ff)
+#define AW88261_TEML_MASK (0x83ff)
+#define AW88261_DEFAULT_CFG (0x0000)
+
+#define AW88261_ICALK_SHIFT (0)
+#define AW88261_ICALKL_SHIFT (0)
+#define AW88261_VCALK_SHIFT (0)
+#define AW88261_VCALKL_SHIFT (0)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_UVLS_START_BIT (14)
+#define AW88261_UVLS_NORMAL (0)
+#define AW88261_UVLS_NORMAL_VALUE \
+ (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT)
+
+#define AW88261_BSTOCS_START_BIT (11)
+#define AW88261_BSTOCS_OVER_CURRENT (1)
+#define AW88261_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT)
+
+#define AW88261_BSTS_START_BIT (9)
+#define AW88261_BSTS_FINISHED (1)
+#define AW88261_BSTS_FINISHED_VALUE \
+ (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT)
+
+#define AW88261_SWS_START_BIT (8)
+#define AW88261_SWS_SWITCHING (1)
+#define AW88261_SWS_SWITCHING_VALUE \
+ (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT)
+
+#define AW88261_NOCLKS_START_BIT (5)
+#define AW88261_NOCLKS_NO_CLOCK (1)
+#define AW88261_NOCLKS_NO_CLOCK_VALUE \
+ (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT)
+
+#define AW88261_CLKS_START_BIT (4)
+#define AW88261_CLKS_STABLE (1)
+#define AW88261_CLKS_STABLE_VALUE \
+ (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT)
+
+#define AW88261_OCDS_START_BIT (3)
+#define AW88261_OCDS_OC (1)
+#define AW88261_OCDS_OC_VALUE \
+ (AW88261_OCDS_OC << AW88261_OCDS_START_BIT)
+
+#define AW88261_OTHS_START_BIT (1)
+#define AW88261_OTHS_OT (1)
+#define AW88261_OTHS_OT_VALUE \
+ (AW88261_OTHS_OT << AW88261_OTHS_START_BIT)
+
+#define AW88261_PLLS_START_BIT (0)
+#define AW88261_PLLS_LOCKED (1)
+#define AW88261_PLLS_LOCKED_VALUE \
+ (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT)
+
+#define AW88261_BIT_PLL_CHECK \
+ (AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_BIT_SYSST_CHECK_MASK \
+ (~(AW88261_UVLS_NORMAL_VALUE | \
+ AW88261_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_NOCLKS_NO_CLOCK_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_OCDS_OC_VALUE | \
+ AW88261_OTHS_OT_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE))
+
+#define AW88261_BIT_SYSST_CHECK \
+ (AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_ULS_HMUTE_START_BIT (14)
+#define AW88261_ULS_HMUTE_BITS_LEN (1)
+#define AW88261_ULS_HMUTE_MASK \
+ (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT))
+
+#define AW88261_ULS_HMUTE_DISABLE (0)
+#define AW88261_ULS_HMUTE_DISABLE_VALUE \
+ (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_ULS_HMUTE_ENABLE (1)
+#define AW88261_ULS_HMUTE_ENABLE_VALUE \
+ (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_START_BIT (8)
+#define AW88261_HMUTE_BITS_LEN (1)
+#define AW88261_HMUTE_MASK \
+ (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT))
+
+#define AW88261_HMUTE_DISABLE (0)
+#define AW88261_HMUTE_DISABLE_VALUE \
+ (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_ENABLE (1)
+#define AW88261_HMUTE_ENABLE_VALUE \
+ (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_AMPPD_WORKING (0)
+#define AW88261_AMPPD_WORKING_VALUE \
+ (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT)
+
+#define AW88261_AMPPD_POWER_DOWN (1)
+#define AW88261_AMPPD_POWER_DOWN_VALUE \
+ (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT)
+
+#define AW88261_PWDN_START_BIT (0)
+#define AW88261_PWDN_BITS_LEN (1)
+#define AW88261_PWDN_MASK \
+ (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT))
+
+#define AW88261_PWDN_WORKING (0)
+#define AW88261_PWDN_WORKING_VALUE \
+ (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT)
+
+#define AW88261_PWDN_POWER_DOWN (1)
+#define AW88261_PWDN_POWER_DOWN_VALUE \
+ (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT)
+
+#define AW88261_MUTE_VOL (90 * 8)
+#define AW88261_VOLUME_STEP_DB (6 * 8)
+
+#define AW88261_VOL_6DB_START (6)
+
+#define AW88261_VOL_START_BIT (0)
+#define AW88261_VOL_BITS_LEN (10)
+#define AW88261_VOL_MASK \
+ (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
+
+#define AW88261_VOL_DEFAULT_VALUE (0)
+
+#define AW88261_I2STXEN_START_BIT (6)
+#define AW88261_I2STXEN_BITS_LEN (1)
+#define AW88261_I2STXEN_MASK \
+ (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT))
+
+#define AW88261_I2STXEN_DISABLE (0)
+#define AW88261_I2STXEN_DISABLE_VALUE \
+ (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_I2STXEN_ENABLE (1)
+#define AW88261_I2STXEN_ENABLE_VALUE \
+ (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT (14)
+#define AW88261_CCO_MUX_BITS_LEN (1)
+#define AW88261_CCO_MUX_MASK \
+ (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
+
+#define AW88261_CCO_MUX_DIVIDED (0)
+#define AW88261_CCO_MUX_DIVIDED_VALUE \
+ (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_CCO_MUX_BYPASS (1)
+#define AW88261_CCO_MUX_BYPASS_VALUE \
+ (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88261_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88261_FORCE_PWM_START_BIT (12)
+#define AW88261_FORCE_PWM_BITS_LEN (1)
+#define AW88261_FORCE_PWM_MASK \
+ (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT))
+
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1)
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \
+ (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT)
+
+#define AW88261_BST_OS_WIDTH_START_BIT (0)
+#define AW88261_BST_OS_WIDTH_BITS_LEN (3)
+#define AW88261_BST_OS_WIDTH_MASK \
+ (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT))
+
+#define AW88261_BST_OS_WIDTH_50NS (4)
+#define AW88261_BST_OS_WIDTH_50NS_VALUE \
+ (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT)
+
+/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */
+#define AW88261_BST_LOOPR_START_BIT (0)
+#define AW88261_BST_LOOPR_BITS_LEN (2)
+#define AW88261_BST_LOOPR_MASK \
+ (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT))
+
+#define AW88261_BST_LOOPR_340K (2)
+#define AW88261_BST_LOOPR_340K_VALUE \
+ (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT)
+
+/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */
+#define AW88261_RSQN_DLY_START_BIT (14)
+#define AW88261_RSQN_DLY_BITS_LEN (2)
+#define AW88261_RSQN_DLY_MASK \
+ (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT))
+
+#define AW88261_RSQN_DLY_35NS (2)
+#define AW88261_RSQN_DLY_35NS_VALUE \
+ (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT)
+
+/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */
+#define AW88261_BURST_SSMODE_START_BIT (3)
+#define AW88261_BURST_SSMODE_BITS_LEN (1)
+#define AW88261_BURST_SSMODE_MASK \
+ (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT))
+
+#define AW88261_BURST_SSMODE_FAST (0)
+#define AW88261_BURST_SSMODE_FAST_VALUE \
+ (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT)
+
+/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */
+#define AW88261_BST_BURST_START_BIT (7)
+#define AW88261_BST_BURST_BITS_LEN (3)
+#define AW88261_BST_BURST_MASK \
+ (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT))
+
+#define AW88261_BST_BURST_30MA (2)
+#define AW88261_BST_BURST_30MA_VALUE \
+ (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_VSN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_ISN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT))
+
+#define AW88261_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT))
+
+#define AW88261_CABL_BASE_VALUE (1000)
+#define AW88261_ICABLK_FACTOR (1)
+#define AW88261_VCABLK_FACTOR (1)
+
+#define AW88261_VCAL_FACTOR (1<<13)
+
+#define AW88261_START_RETRIES (5)
+#define AW88261_START_WORK_DELAY_MS (0)
+
+#define AW88261_I2C_NAME "aw88261"
+
+#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88261_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 AW88261_DEV_DEFAULT_CH (0)
+#define AW88261_ACF_FILE "aw88261_acf.bin"
+#define AW88261_DEV_SYSST_CHECK_MAX (10)
+#define AW88261_SOFT_RESET_VALUE (0x55aa)
+#define AW88261_REG_TO_DB (0x3f)
+#define AW88261_VOL_START_MASK (0xfc00)
+#define AW88261_INIT_PROFILE (0)
+
+#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \
+ AW88261_VOLUME_STEP_DB) + \
+ ((value) & AW88261_REG_TO_DB))
+#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \
+ AW88261_VOL_6DB_START) + \
+ ((value) % AW88261_VOLUME_STEP_DB))
+
+#define AW88261_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 {
+ AW88261_SYNC_START = 0,
+ AW88261_ASYNC_START,
+};
+
+enum aw88261_id {
+ AW88261_CHIP_ID = 0x2113,
+};
+
+enum {
+ AW88261_500_US = 500,
+ AW88261_1000_US = 1000,
+ AW88261_2000_US = 2000,
+};
+
+enum {
+ AW88261_DEV_PW_OFF = 0,
+ AW88261_DEV_PW_ON,
+};
+
+enum {
+ AW88261_DEV_FW_FAILED = 0,
+ AW88261_DEV_FW_OK,
+};
+
+enum {
+ AW88261_EF_AND_CHECK = 0,
+ AW88261_EF_OR_CHECK,
+};
+
+enum {
+ AW88261_FRCSET_DISABLE = 0,
+ AW88261_FRCSET_ENABLE,
+};
+
+struct aw88261 {
+ 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;
+
+ int efuse_check;
+ int frcset_en;
+ unsigned int mute_st;
+ unsigned int amppd_st;
+
+ bool phase_sync;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
new file mode 100644
index 000000000000..fb563b4c6971
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -0,0 +1,576 @@
+// 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/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.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);
+ char *prof_name;
+ int count, ret;
+
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88395_dev_get_prof_name(aw88395->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_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, AW88395_CALI_RE_MAX, 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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id);
+
+static struct i2c_driver aw88395_i2c_driver = {
+ .driver = {
+ .name = AW88395_I2C_NAME,
+ },
+ .probe = 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..c2a4f0cb8cd5
--- /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"
+
+#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..e1430940015d
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -0,0 +1,1720 @@
+// 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/minmax.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 (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 (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(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, "unsupported 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);
+ }
+
+ 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) {
+ tmp_len = min(len - i, 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;
+ }
+
+ ret = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ 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, "awinic,audio-channel", &channel_value);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read audio-channel failed,use default 0");
+ aw_dev->channel = AW88395_DEV_DEFAULT_CH;
+ return;
+ }
+
+ dev_dbg(aw_dev->dev, "read audio-channel value is: %d",
+ channel_value);
+ aw_dev->channel = channel_value;
+}
+
+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);
+
+ 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);
+
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+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..6f8b30b475da
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -0,0 +1,214 @@
+// 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;
+};
+
+enum {
+ CALI_RESULT_NORMAL,
+ CALI_RESULT_ERROR,
+};
+
+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;
+};
+
+#define AW_CALI_CFG_NUM (4)
+struct cali_cfg {
+ uint32_t data[AW_CALI_CFG_NUM];
+};
+
+struct aw_cali_backup_desc {
+ unsigned int dsp_ng_cfg;
+ unsigned int dsp_lp_cfg;
+};
+
+struct aw_cali_desc {
+ u32 cali_re;
+ u32 ra;
+ bool cali_switch;
+ bool cali_running;
+ uint16_t cali_result;
+ uint16_t store_vol;
+ struct cali_cfg cali_cfg;
+ struct aw_cali_backup_desc backup_info;
+};
+
+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;
+ unsigned char dsp_cfg;
+ u16 chip_id;
+
+ unsigned int channel;
+ unsigned int fade_step;
+ unsigned int prof_data_type;
+
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ char *acf;
+
+ 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);
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name);
+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..ceb7fc43d018
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -0,0 +1,1174 @@
+// 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/cleanup.h>
+#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)
+{
+ int ret;
+ int i;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(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");
+ return ret;
+ }
+
+ 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)
+ return -EINVAL;
+
+ 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)
+ return -EINVAL;
+
+ 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;
+
+ return 0;
+}
+
+static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
+ uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc)
+{
+ int ret;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(*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");
+ return ret;
+ }
+
+ if ((aw_bin->all_bin_parse_num != 1) ||
+ (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
+ dev_err(aw_dev->dev, "bin num or type error");
+ return -EINVAL;
+ }
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[0].valid_data_addr;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[0].valid_data_len;
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+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);
+ case ACF_SEC_TYPE_HDR_REG:
+ return aw_dev_parse_reg_bin_with_hdr(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;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ 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;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ 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_reg_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;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK)
+ 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) {
+ 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_cfg_get_multiple_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)
+{
+ int ret;
+
+ struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*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) {
+ return ret;
+ } 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)
+ return ret;
+ }
+
+ switch (aw_dev->prof_data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_cfg_get_multiple_valid_prof(aw_dev, all_prof_info);
+ break;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_cfg_get_reg_valid_prof(aw_dev, all_prof_info);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported data type\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ aw_dev->prof_info.prof_name_list = profile_name;
+
+ 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_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG) ||
+ (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)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ 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) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ 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;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_parse_reg_bin_with_hdr(aw_dev,
+ (uint8_t *)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 reg bin with hdr 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/aw88399.c b/sound/soc/codecs/aw88399.c
new file mode 100644
index 000000000000..58846feb013d
--- /dev/null
+++ b/sound/soc/codecs/aw88399.c
@@ -0,0 +1,2350 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.c -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <linux/sort.h>
+#include <sound/soc.h>
+#include "aw88399.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88399_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88399_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+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, AW88399_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, AW88399_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)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_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, AW88399_DSPMDAT_REG, 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, AW88399_DSPMDAT_REG, temp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+
+ return ret;
+}
+
+static int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ unsigned int 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",
+ 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",
+ 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, 0x00, &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, AW88399_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, AW88399_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, AW88399_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, AW88399_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, AW88399_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 AW88399_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 AW88399_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, AW88399_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. ret=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ 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, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88399_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_dbg(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, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_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 < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_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, AW88399_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88399_CCO_MUX_MASK);
+ if (reg_val == AW88399_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, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_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 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88399_NOISE_GATE_EN_MASK))
+ check_val = AW88399_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88399_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk)
+{
+ uint16_t icalkh_val, icalkl_val, icalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ icalk_val = icalkh_val & icalkl_val;
+ else
+ icalk_val = icalkh_val | icalkl_val;
+
+ if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK))
+ icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = vcalkh_val & vcalkl_val;
+ else
+ vcalk_val = vcalkh_val | vcalkl_val;
+
+ if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK)
+ vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) &
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+ else
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) |
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+
+ if (vcalk_val & (~AW88399_TEM4_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG;
+
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int vsense_select, vsense_value;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ uint16_t reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value);
+ if (ret)
+ return ret;
+
+ vsense_select = vsense_value & (~AW88399_VDSEL_MASK);
+
+ ret = aw88399_dev_get_icalk(aw88399, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+
+ ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+
+ switch (vsense_select) {
+ case AW88399_DEV_VDSEL_VSENSE:
+ ret = aw88399_dev_get_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR *
+ ical_k / vcal_k * aw88399->vcalb_init_val;
+ break;
+ case AW88399_DEV_VDSEL_DAC:
+ ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR /
+ AW88399_ISCAL_DAC_FACTOR * ical_k /
+ vcal_k * aw88399->vcalb_init_val;
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int 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);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x",
+ __func__, check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x",
+ check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+ 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, AW88399_I2SCTRL3_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88399_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88399_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88399_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+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 (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88399_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88399_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 != AW88399_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88399_dev_start(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88399_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88399_dev_set_dither(aw88399, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_2000_US, AW88399_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(AW88399_1000_US, AW88399_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 == AW88399_DEV_DSP_WORK) {
+ ret = aw_dev_hw_crc_check(aw88399);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_set_vcalb(aw88399);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ 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);
+
+ if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE)
+ aw88399_dev_set_dither(aw88399, true);
+
+ /* close mute */
+ aw88399_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88399_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+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 = AW88399_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+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, AW88399_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88399_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88399_DSP_RE_SHIFT);
+
+ 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;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ 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_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read_16bit(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, &reg_val);
+ if (reg_val != AW88399_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]",
+ reg_val, AW88399_DSP_ROM_CHECK_DATA);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88399_DSP_CFG_ADDR, AW88399_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88399_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88399_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88399_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88399_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_OSCCLK_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 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, AW88399_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88399_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE;
+}
+
+static int aw_dev_update_reg_container(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ 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 == AW88399_DSPVCALB_REG) {
+ aw88399->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL_REG) {
+ if (reg_val & (~AW88399_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK;
+
+ reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK |
+ AW88399_DSPBY_MASK);
+ reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE |
+ AW88399_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88399_I2SCTRL3_REG) {
+ reg_val &= AW88399_I2STXEN_MASK;
+ reg_val |= AW88399_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88399_VOL_MASK)) >>
+ AW88399_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88399_DBGCTRL_REG) {
+ if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE)
+ aw88399->check_val = AW_EF_OR_CHECK;
+ else
+ aw88399->check_val = AW_EF_AND_CHECK;
+
+ aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88399_CRCCTRL_REG)
+ aw88399->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88399, data, len);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "reg update failed");
+
+ return ret;
+}
+
+static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88399_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;
+}
+
+static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ 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 == AW88399_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88399_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(aw88399, 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;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ if (up_dsp_fw_en) {
+ 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, AW88399_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88399_start_pa(struct aw88399 *aw88399)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88399_START_RETRIES; i++) {
+ ret = aw88399_dev_start(aw88399);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i);
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88399->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88399_startup_work(struct work_struct *work)
+{
+ struct aw88399 *aw88399 =
+ container_of(work, struct aw88399, start_work.work);
+
+ mutex_lock(&aw88399->lock);
+ aw88399_start_pa(aw88399);
+ mutex_unlock(&aw88399->lock);
+}
+
+static void aw88399_start(struct aw88399 *aw88399, bool sync_start)
+{
+ int ret;
+
+ if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK)
+ return;
+
+ if (aw88399->aw_pa->status == AW88399_DEV_PW_ON)
+ return;
+
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88399_SYNC_START)
+ aw88399_start_pa(aw88399);
+ else
+ queue_delayed_work(system_wq,
+ &aw88399->start_work,
+ AW88399_START_WORK_DELAY_MS);
+}
+
+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 & AW88399_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88399_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;
+
+ if (aw_dev->status == AW88399_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88399_DEV_PW_OFF;
+
+ aw88399_dev_mute(aw_dev, true);
+ usleep_range(AW88399_4000_US, AW88399_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88399_dai[] = {
+ {
+ .name = "aw88399-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ },
+};
+
+static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result)
+{
+ if (cali_result == CALI_RESULT_ERROR)
+ aw88399_dev_mute(aw_dev, true);
+ else if (cali_result == CALI_RESULT_NORMAL)
+ aw88399_dev_mute(aw_dev, false);
+}
+
+static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev)
+{
+ struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ &cali_cfg->data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ &cali_cfg->data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ &cali_cfg->data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ &cali_cfg->data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev,
+ struct cali_cfg cali_cfg)
+{
+ int ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ cali_cfg.data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ cali_cfg.data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ cali_cfg.data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ cali_cfg.data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en)
+{
+ struct cali_cfg set_cfg;
+ int ret;
+
+ aw_dev_dsp_enable(aw_dev, false);
+ if (cali_en) {
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE);
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW88399_DSP_16_DATA);
+
+ ret = aw_cali_svc_get_cali_cfg(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get cali cfg failed\n");
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ set_cfg.data[0] = 0;
+ set_cfg.data[1] = 0;
+ set_cfg.data[2] = -1;
+ set_cfg.data[3] = 1;
+
+ ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali cfg failed\n");
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ } else {
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ }
+
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+}
+
+static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable)
+{
+ unsigned int reg_val;
+ int ret;
+
+ if (enable) {
+ ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG);
+ return ret;
+ }
+
+ aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK);
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol);
+ }
+
+ return ret;
+}
+
+static void aw_cali_svc_backup_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+ unsigned int reg_val, dsp_val;
+
+ regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, &reg_val);
+ backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK);
+
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ &dsp_val, AW88399_DSP_16_DATA);
+
+ backup_desc->dsp_lp_cfg = dsp_val;
+}
+
+static void aw_cali_svc_recover_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg);
+
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ backup_desc->dsp_lp_cfg, AW88399_DSP_16_DATA);
+}
+
+static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ return ret;
+ }
+
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status error\n");
+ return ret;
+ }
+
+ aw_cali_svc_backup_info(aw_dev);
+ ret = aw_cali_svc_cali_en(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n");
+ return ret;
+ }
+
+ ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true);
+ if (ret) {
+ aw_cali_svc_cali_en(aw_dev, false);
+ return ret;
+ }
+
+ } else {
+ aw_cali_svc_cali_run_dsp_vol(aw_dev, false);
+ aw_cali_svc_recover_info(aw_dev);
+ aw_cali_svc_cali_en(aw_dev, false);
+ }
+
+ return 0;
+}
+
+static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re)
+{
+ uint32_t dsp_re, show_re;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT);
+
+ *re = (uint32_t)(show_re - aw_dev->cali_desc.ra);
+
+ return 0;
+}
+
+static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re)
+{
+ int sum = 0, i;
+
+ for (i = 1; i < data_size - 1; i++)
+ sum += data[i];
+
+ *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM);
+}
+
+static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_data;
+ int ret, i;
+
+ for (i = 0; i < AW_GET_IV_CNT_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, &reg_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG);
+ return ret;
+ }
+
+ reg_data &= (~AW88399_REABS_MASK);
+ if (!reg_data)
+ return 0;
+ msleep(30);
+ }
+
+ dev_err(aw_dev->dev, "IV data abnormal, please check\n");
+
+ return -EINVAL;
+}
+
+static int compare_ints(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev)
+{
+ uint32_t re_temp[AW_CALI_READ_CNT_MAX];
+ uint32_t dsp_re;
+ int ret, i;
+
+ for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) {
+ ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]);
+ if (ret)
+ goto cali_re_fail;
+ msleep(30);
+ }
+
+ sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL);
+
+ aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re);
+
+ ret = aw_cali_svc_get_iv_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get iv data failed");
+ goto cali_re_fail;
+ }
+
+ if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) {
+ dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re);
+ aw_dev->cali_desc.cali_re = dsp_re;
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+
+ return 0;
+ }
+
+ aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL;
+
+ aw_dev->cali_desc.cali_re = dsp_re;
+ dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re);
+
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+
+cali_re_fail:
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+ return -EINVAL;
+}
+
+static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_cali_desc *cali_desc = &aw_dev->cali_desc;
+ int ret;
+
+ if (cali_desc->cali_running) {
+ dev_err(aw_dev->dev, "calibration in progress\n");
+ return -EINVAL;
+ }
+
+ cali_desc->cali_running = true;
+ aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL);
+
+ ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "start cali re failed\n");
+ goto re_mode_err;
+ }
+
+ msleep(1000);
+
+ ret = aw_cali_svc_get_smooth_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "get cali re failed\n");
+
+ aw_cali_svc_cali_re_mode_enable(aw_dev, false);
+
+re_mode_err:
+ cali_desc->cali_running = false;
+
+ return ret;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 aw88399_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;
+}
+
+static int aw88399_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88399->aw_pa->prof_info.count;
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88399_dev_get_prof_name(aw88399->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88399_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88399_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88399->lock);
+ ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88399->lock);
+ return 0;
+ }
+
+ if (aw88399->aw_pa->status) {
+ aw88399_stop(aw88399->aw_pa);
+ aw88399_start(aw88399, AW88399_SYNC_START);
+ }
+
+ mutex_unlock(&aw88399->lock);
+
+ return 1;
+}
+
+static int aw88399_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88399_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->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;
+ aw_dev_set_volume(aw88399->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88399_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = 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 (aw88399->aw_pa->fade_step != value) {
+ aw88399->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88399_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch;
+
+ return 0;
+}
+
+static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int aw88399_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* do nothing */
+ return 0;
+}
+
+static int aw88399_calib_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->status && aw_dev->cali_desc.cali_switch)
+ aw_cali_svc_dev_cali_re(aw88399);
+
+ return 0;
+}
+
+static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ 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 = AW88399_1000_US / 10;
+ aw_dev->fade_out_time = AW88399_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 = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88399_request_firmware_file(struct aw88399 *aw88399)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88399_ACF_FILE, cont ? cont->size : 0);
+
+ aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev,
+ struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88399->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88399->aw_cfg->len = (int)cont->size;
+ memcpy(aw88399->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88399->lock);
+ /* aw device init */
+ ret = aw88399_dev_init(aw88399, aw88399->aw_cfg);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88399->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88399_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG,
+ 6, AW88399_MUTE_VOL, 0, aw88399_volume_get,
+ aw88399_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88399_MUTE_VOL, 0,
+ aw88399_get_fade_step, aw88399_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_in_time, aw88399_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_out_time, aw88399_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0,
+ aw88399_re_get, aw88399_re_set),
+ SOC_SINGLE_BOOL_EXT("Calib Switch", 0,
+ aw88399_calib_switch_get, aw88399_calib_switch_set),
+ SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0,
+ aw88399_calib_get, aw88399_calib_set),
+ AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info,
+ aw88399_profile_get, aw88399_profile_set),
+};
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88399->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88399_start(aw88399, AW88399_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88399_stop(aw88399->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88399->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88399_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88399_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 aw88399_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88399_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88399->start_work, aw88399_startup_work);
+
+ ret = aw88399_request_firmware_file(aw88399);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88399_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88399->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88399 = {
+ .probe = aw88399_codec_probe,
+ .remove = aw88399_codec_remove,
+ .dapm_widgets = aw88399_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88399_dapm_widgets),
+ .dapm_routes = aw88399_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88399_audio_map),
+ .controls = aw88399_controls,
+ .num_controls = ARRAY_SIZE(aw88399_controls),
+};
+
+static void aw88399_hw_reset(struct aw88399 *aw88399)
+{
+ if (aw88399->reset_gpio) {
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 0);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ }
+}
+
+static void aw88399_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88399_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88399_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+ dev_dbg(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88399->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = chip_id;
+ 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 = AW88399_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88399_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88399_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE;
+
+ aw88399_parse_channel_dt(aw_dev);
+
+ return 0;
+}
+
+static int aw88399_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88399 *aw88399;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88399 = devm_kzalloc(&i2c->dev, sizeof(*aw88399), GFP_KERNEL);
+ if (!aw88399)
+ return -ENOMEM;
+
+ mutex_init(&aw88399->lock);
+
+ i2c_set_clientdata(i2c, aw88399);
+
+ aw88399->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88399->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->reset_gpio),
+ "reset gpio not defined\n");
+ aw88399_hw_reset(aw88399);
+
+ aw88399->regmap = devm_regmap_init_i2c(i2c, &aw88399_remap_config);
+ if (IS_ERR(aw88399->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88399_init(aw88399, i2c, aw88399->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88399,
+ aw88399_dai, ARRAY_SIZE(aw88399_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88399: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88399_i2c_id[] = {
+ { AW88399_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id aw88399_acpi_match[] = {
+ { "AWDZ8399", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, aw88399_acpi_match);
+#endif
+
+static struct i2c_driver aw88399_i2c_driver = {
+ .driver = {
+ .name = AW88399_I2C_NAME,
+ .acpi_match_table = ACPI_PTR(aw88399_acpi_match),
+ },
+ .probe = aw88399_i2c_probe,
+ .id_table = aw88399_i2c_id,
+};
+module_i2c_driver(aw88399_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88399 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h
new file mode 100644
index 000000000000..cacc03b1eefa
--- /dev/null
+++ b/sound/soc/codecs/aw88399.h
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.h -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88399_H__
+#define __AW88399_H__
+
+/* registers list */
+#define AW88399_ID_REG (0x00)
+#define AW88399_SYSST_REG (0x01)
+#define AW88399_SYSINT_REG (0x02)
+#define AW88399_SYSINTM_REG (0x03)
+#define AW88399_SYSCTRL_REG (0x04)
+#define AW88399_SYSCTRL2_REG (0x05)
+#define AW88399_I2SCTRL1_REG (0x06)
+#define AW88399_I2SCTRL2_REG (0x07)
+#define AW88399_I2SCTRL3_REG (0x08)
+#define AW88399_DACCFG1_REG (0x09)
+#define AW88399_DACCFG2_REG (0x0A)
+#define AW88399_DACCFG3_REG (0x0B)
+#define AW88399_DACCFG4_REG (0x0C)
+#define AW88399_DACCFG5_REG (0x0D)
+#define AW88399_DACCFG6_REG (0x0E)
+#define AW88399_DACCFG7_REG (0x0F)
+#define AW88399_MPDCFG1_REG (0x10)
+#define AW88399_MPDCFG2_REG (0x11)
+#define AW88399_MPDCFG3_REG (0x12)
+#define AW88399_MPDCFG4_REG (0x13)
+#define AW88399_PWMCTRL1_REG (0x14)
+#define AW88399_PWMCTRL2_REG (0x15)
+#define AW88399_PWMCTRL3_REG (0x16)
+#define AW88399_I2SCFG1_REG (0x17)
+#define AW88399_DBGCTRL_REG (0x18)
+#define AW88399_HAGCST_REG (0x20)
+#define AW88399_VBAT_REG (0x21)
+#define AW88399_TEMP_REG (0x22)
+#define AW88399_PVDD_REG (0x23)
+#define AW88399_ISNDAT_REG (0x24)
+#define AW88399_VSNDAT_REG (0x25)
+#define AW88399_I2SINT_REG (0x26)
+#define AW88399_I2SCAPCNT_REG (0x27)
+#define AW88399_ANASTA1_REG (0x28)
+#define AW88399_ANASTA2_REG (0x29)
+#define AW88399_ANASTA3_REG (0x2A)
+#define AW88399_TESTDET_REG (0x2B)
+#define AW88399_DSMCFG1_REG (0x30)
+#define AW88399_DSMCFG2_REG (0x31)
+#define AW88399_DSMCFG3_REG (0x32)
+#define AW88399_DSMCFG4_REG (0x33)
+#define AW88399_DSMCFG5_REG (0x34)
+#define AW88399_DSMCFG6_REG (0x35)
+#define AW88399_DSMCFG7_REG (0x36)
+#define AW88399_DSMCFG8_REG (0x37)
+#define AW88399_TESTIN_REG (0x38)
+#define AW88399_TESTOUT_REG (0x39)
+#define AW88399_MEMTEST_REG (0x3A)
+#define AW88399_VSNCTRL1_REG (0x3B)
+#define AW88399_ISNCTRL1_REG (0x3C)
+#define AW88399_ISNCTRL2_REG (0x3D)
+#define AW88399_DSPMADD_REG (0x40)
+#define AW88399_DSPMDAT_REG (0x41)
+#define AW88399_WDT_REG (0x42)
+#define AW88399_ACR1_REG (0x43)
+#define AW88399_ACR2_REG (0x44)
+#define AW88399_ASR1_REG (0x45)
+#define AW88399_ASR2_REG (0x46)
+#define AW88399_DSPCFG_REG (0x47)
+#define AW88399_ASR3_REG (0x48)
+#define AW88399_ASR4_REG (0x49)
+#define AW88399_DSPVCALB_REG (0x4A)
+#define AW88399_CRCCTRL_REG (0x4B)
+#define AW88399_DSPDBG1_REG (0x4C)
+#define AW88399_DSPDBG2_REG (0x4D)
+#define AW88399_DSPDBG3_REG (0x4E)
+#define AW88399_PLLCTRL1_REG (0x50)
+#define AW88399_PLLCTRL2_REG (0x51)
+#define AW88399_PLLCTRL3_REG (0x52)
+#define AW88399_CDACTRL1_REG (0x53)
+#define AW88399_CDACTRL2_REG (0x54)
+#define AW88399_CDACTRL3_REG (0x55)
+#define AW88399_SADCCTRL1_REG (0x56)
+#define AW88399_SADCCTRL2_REG (0x57)
+#define AW88399_BOPCTRL1_REG (0x58)
+#define AW88399_BOPCTRL2_REG (0x5A)
+#define AW88399_BOPCTRL3_REG (0x5B)
+#define AW88399_BOPCTRL4_REG (0x5C)
+#define AW88399_BOPCTRL5_REG (0x5D)
+#define AW88399_BOPCTRL6_REG (0x5E)
+#define AW88399_BOPCTRL7_REG (0x5F)
+#define AW88399_BSTCTRL1_REG (0x60)
+#define AW88399_BSTCTRL2_REG (0x61)
+#define AW88399_BSTCTRL3_REG (0x62)
+#define AW88399_BSTCTRL4_REG (0x63)
+#define AW88399_BSTCTRL5_REG (0x64)
+#define AW88399_BSTCTRL6_REG (0x65)
+#define AW88399_BSTCTRL7_REG (0x66)
+#define AW88399_BSTCTRL8_REG (0x67)
+#define AW88399_BSTCTRL9_REG (0x68)
+#define AW88399_BSTCTRL10_REG (0x69)
+#define AW88399_CPCTRL_REG (0x6A)
+#define AW88399_EFWH_REG (0x6C)
+#define AW88399_EFWM2_REG (0x6D)
+#define AW88399_EFWM1_REG (0x6E)
+#define AW88399_EFWL_REG (0x6F)
+#define AW88399_TESTCTRL1_REG (0x70)
+#define AW88399_TESTCTRL2_REG (0x71)
+#define AW88399_EFCTRL1_REG (0x72)
+#define AW88399_EFCTRL2_REG (0x73)
+#define AW88399_EFRH4_REG (0x74)
+#define AW88399_EFRH3_REG (0x75)
+#define AW88399_EFRH2_REG (0x76)
+#define AW88399_EFRH1_REG (0x77)
+#define AW88399_EFRL4_REG (0x78)
+#define AW88399_EFRL3_REG (0x79)
+#define AW88399_EFRL2_REG (0x7A)
+#define AW88399_EFRL1_REG (0x7B)
+#define AW88399_TM_REG (0x7C)
+#define AW88399_TM2_REG (0x7D)
+
+#define AW88399_REG_MAX (0x7E)
+#define AW88399_MUTE_VOL (1023)
+
+#define AW88399_DSP_CFG_ADDR (0x9B00)
+#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88399_DSP_FW_ADDR (0x8980)
+#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40)
+#define AW88399_DSP_ROM_CHECK_DATA (0x4638)
+
+#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88399_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88399_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88399_I2STXEN_START_BIT (9)
+#define AW88399_I2STXEN_BITS_LEN (1)
+#define AW88399_I2STXEN_MASK \
+ (~(((1<<AW88399_I2STXEN_BITS_LEN)-1) << AW88399_I2STXEN_START_BIT))
+
+#define AW88399_I2STXEN_DISABLE (0)
+#define AW88399_I2STXEN_DISABLE_VALUE \
+ (AW88399_I2STXEN_DISABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_I2STXEN_ENABLE (1)
+#define AW88399_I2STXEN_ENABLE_VALUE \
+ (AW88399_I2STXEN_ENABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_VOL_START_BIT (0)
+#define AW88399_VOL_BITS_LEN (10)
+#define AW88399_VOL_MASK \
+ (~(((1<<AW88399_VOL_BITS_LEN)-1) << AW88399_VOL_START_BIT))
+
+#define AW88399_PWDN_START_BIT (0)
+#define AW88399_PWDN_BITS_LEN (1)
+#define AW88399_PWDN_MASK \
+ (~(((1<<AW88399_PWDN_BITS_LEN)-1) << AW88399_PWDN_START_BIT))
+
+#define AW88399_PWDN_POWER_DOWN (1)
+#define AW88399_PWDN_POWER_DOWN_VALUE \
+ (AW88399_PWDN_POWER_DOWN << AW88399_PWDN_START_BIT)
+
+#define AW88399_PWDN_WORKING (0)
+#define AW88399_PWDN_WORKING_VALUE \
+ (AW88399_PWDN_WORKING << AW88399_PWDN_START_BIT)
+
+#define AW88399_DSPBY_START_BIT (2)
+#define AW88399_DSPBY_BITS_LEN (1)
+#define AW88399_DSPBY_MASK \
+ (~(((1<<AW88399_DSPBY_BITS_LEN)-1) << AW88399_DSPBY_START_BIT))
+
+#define AW88399_DSPBY_WORKING (0)
+#define AW88399_DSPBY_WORKING_VALUE \
+ (AW88399_DSPBY_WORKING << AW88399_DSPBY_START_BIT)
+
+#define AW88399_DSPBY_BYPASS (1)
+#define AW88399_DSPBY_BYPASS_VALUE \
+ (AW88399_DSPBY_BYPASS << AW88399_DSPBY_START_BIT)
+
+#define AW88399_MEM_CLKSEL_START_BIT (3)
+#define AW88399_MEM_CLKSEL_BITS_LEN (1)
+#define AW88399_MEM_CLKSEL_MASK \
+ (~(((1<<AW88399_MEM_CLKSEL_BITS_LEN)-1) << AW88399_MEM_CLKSEL_START_BIT))
+
+#define AW88399_MEM_CLKSEL_OSCCLK (0)
+#define AW88399_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88399_MEM_CLKSEL_OSCCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_MEM_CLKSEL_DAPHCLK (1)
+#define AW88399_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88399_MEM_CLKSEL_DAPHCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_DITHER_EN_START_BIT (15)
+#define AW88399_DITHER_EN_BITS_LEN (1)
+#define AW88399_DITHER_EN_MASK \
+ (~(((1<<AW88399_DITHER_EN_BITS_LEN)-1) << AW88399_DITHER_EN_START_BIT))
+
+#define AW88399_DITHER_EN_DISABLE (0)
+#define AW88399_DITHER_EN_DISABLE_VALUE \
+ (AW88399_DITHER_EN_DISABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_DITHER_EN_ENABLE (1)
+#define AW88399_DITHER_EN_ENABLE_VALUE \
+ (AW88399_DITHER_EN_ENABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_HMUTE_START_BIT (8)
+#define AW88399_HMUTE_BITS_LEN (1)
+#define AW88399_HMUTE_MASK \
+ (~(((1<<AW88399_HMUTE_BITS_LEN)-1) << AW88399_HMUTE_START_BIT))
+
+#define AW88399_HMUTE_DISABLE (0)
+#define AW88399_HMUTE_DISABLE_VALUE \
+ (AW88399_HMUTE_DISABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_HMUTE_ENABLE (1)
+#define AW88399_HMUTE_ENABLE_VALUE \
+ (AW88399_HMUTE_ENABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_EF_DBMD_START_BIT (2)
+#define AW88399_EF_DBMD_BITS_LEN (1)
+#define AW88399_EF_DBMD_MASK \
+ (~(((1<<AW88399_EF_DBMD_BITS_LEN)-1) << AW88399_EF_DBMD_START_BIT))
+
+#define AW88399_EF_DBMD_OR (1)
+#define AW88399_EF_DBMD_OR_VALUE \
+ (AW88399_EF_DBMD_OR << AW88399_EF_DBMD_START_BIT)
+
+#define AW88399_VDSEL_START_BIT (5)
+#define AW88399_VDSEL_BITS_LEN (1)
+#define AW88399_VDSEL_MASK \
+ (~(((1<<AW88399_VDSEL_BITS_LEN)-1) << AW88399_VDSEL_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_H_START_BIT))
+
+/* EF_VSN_GESLP_H bit 9:0 (EFRH3 0x75) */
+#define AW88399_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_L_START_BIT))
+
+/* EF_VSN_GESLP_L bit 9:0 (EFRL3 0x79) */
+#define AW88399_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_H_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_H_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_H_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_L_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_L_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_L_START_BIT))
+
+#define AW88399_RCV_MODE_START_BIT (7)
+#define AW88399_RCV_MODE_BITS_LEN (1)
+#define AW88399_RCV_MODE_MASK \
+ (~(((1<<AW88399_RCV_MODE_BITS_LEN)-1) << AW88399_RCV_MODE_START_BIT))
+
+#define AW88399_CLKI_START_BIT (4)
+#define AW88399_NOCLKI_START_BIT (5)
+#define AW88399_PLLI_START_BIT (0)
+#define AW88399_PLLI_INT_VALUE (1)
+#define AW88399_PLLI_INT_INTERRUPT \
+ (AW88399_PLLI_INT_VALUE << AW88399_PLLI_START_BIT)
+
+#define AW88399_CLKI_INT_VALUE (1)
+#define AW88399_CLKI_INT_INTERRUPT \
+ (AW88399_CLKI_INT_VALUE << AW88399_CLKI_START_BIT)
+
+#define AW88399_NOCLKI_INT_VALUE (1)
+#define AW88399_NOCLKI_INT_INTERRUPT \
+ (AW88399_NOCLKI_INT_VALUE << AW88399_NOCLKI_START_BIT)
+
+#define AW88399_BIT_SYSINT_CHECK \
+ (AW88399_PLLI_INT_INTERRUPT | \
+ AW88399_CLKI_INT_INTERRUPT | \
+ AW88399_NOCLKI_INT_INTERRUPT)
+
+#define AW88399_CRC_CHECK_START_BIT (12)
+#define AW88399_CRC_CHECK_BITS_LEN (3)
+#define AW88399_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88399_CRC_CHECK_BITS_LEN)-1) << AW88399_CRC_CHECK_START_BIT))
+
+#define AW88399_RCV_MODE_RECEIVER (1)
+#define AW88399_RCV_MODE_RECEIVER_VALUE \
+ (AW88399_RCV_MODE_RECEIVER << AW88399_RCV_MODE_START_BIT)
+
+#define AW88399_AMPPD_START_BIT (1)
+#define AW88399_AMPPD_BITS_LEN (1)
+#define AW88399_AMPPD_MASK \
+ (~(((1<<AW88399_AMPPD_BITS_LEN)-1) << AW88399_AMPPD_START_BIT))
+
+#define AW88399_AMPPD_WORKING (0)
+#define AW88399_AMPPD_WORKING_VALUE \
+ (AW88399_AMPPD_WORKING << AW88399_AMPPD_START_BIT)
+
+#define AW88399_AMPPD_POWER_DOWN (1)
+#define AW88399_AMPPD_POWER_DOWN_VALUE \
+ (AW88399_AMPPD_POWER_DOWN << AW88399_AMPPD_START_BIT)
+
+#define AW88399_RAM_CG_BYP_START_BIT (0)
+#define AW88399_RAM_CG_BYP_BITS_LEN (1)
+#define AW88399_RAM_CG_BYP_MASK \
+ (~(((1<<AW88399_RAM_CG_BYP_BITS_LEN)-1) << AW88399_RAM_CG_BYP_START_BIT))
+
+#define AW88399_RAM_CG_BYP_WORK (0)
+#define AW88399_RAM_CG_BYP_WORK_VALUE \
+ (AW88399_RAM_CG_BYP_WORK << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_RAM_CG_BYP_BYPASS (1)
+#define AW88399_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88399_RAM_CG_BYP_BYPASS << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_CRC_END_ADDR_START_BIT (0)
+#define AW88399_CRC_END_ADDR_BITS_LEN (12)
+#define AW88399_CRC_END_ADDR_MASK \
+ (~(((1<<AW88399_CRC_END_ADDR_BITS_LEN)-1) << AW88399_CRC_END_ADDR_START_BIT))
+
+#define AW88399_CRC_CODE_EN_START_BIT (13)
+#define AW88399_CRC_CODE_EN_BITS_LEN (1)
+#define AW88399_CRC_CODE_EN_MASK \
+ (~(((1<<AW88399_CRC_CODE_EN_BITS_LEN)-1) << AW88399_CRC_CODE_EN_START_BIT))
+
+#define AW88399_CRC_CODE_EN_DISABLE (0)
+#define AW88399_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88399_CRC_CODE_EN_DISABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CODE_EN_ENABLE (1)
+#define AW88399_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88399_CRC_CODE_EN_ENABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_START_BIT (12)
+#define AW88399_CRC_CFG_EN_BITS_LEN (1)
+#define AW88399_CRC_CFG_EN_MASK \
+ (~(((1<<AW88399_CRC_CFG_EN_BITS_LEN)-1) << AW88399_CRC_CFG_EN_START_BIT))
+
+#define AW88399_CRC_CFG_EN_DISABLE (0)
+#define AW88399_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88399_CRC_CFG_EN_DISABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_ENABLE (1)
+#define AW88399_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88399_CRC_CFG_EN_ENABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_OCDS_START_BIT (3)
+#define AW88399_OCDS_OC (1)
+#define AW88399_OCDS_OC_VALUE \
+ (AW88399_OCDS_OC << AW88399_OCDS_START_BIT)
+
+#define AW88399_NOCLKS_START_BIT (5)
+#define AW88399_NOCLKS_NO_CLOCK (1)
+#define AW88399_NOCLKS_NO_CLOCK_VALUE \
+ (AW88399_NOCLKS_NO_CLOCK << AW88399_NOCLKS_START_BIT)
+
+#define AW88399_SWS_START_BIT (8)
+#define AW88399_SWS_SWITCHING (1)
+#define AW88399_SWS_SWITCHING_VALUE \
+ (AW88399_SWS_SWITCHING << AW88399_SWS_START_BIT)
+
+#define AW88399_BSTS_START_BIT (9)
+#define AW88399_BSTS_FINISHED (1)
+#define AW88399_BSTS_FINISHED_VALUE \
+ (AW88399_BSTS_FINISHED << AW88399_BSTS_START_BIT)
+
+#define AW88399_UVLS_START_BIT (14)
+#define AW88399_UVLS_NORMAL (0)
+#define AW88399_UVLS_NORMAL_VALUE \
+ (AW88399_UVLS_NORMAL << AW88399_UVLS_START_BIT)
+
+#define AW88399_BSTOCS_START_BIT (11)
+#define AW88399_BSTOCS_OVER_CURRENT (1)
+#define AW88399_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88399_BSTOCS_OVER_CURRENT << AW88399_BSTOCS_START_BIT)
+
+#define AW88399_OTHS_START_BIT (1)
+#define AW88399_OTHS_OT (1)
+#define AW88399_OTHS_OT_VALUE \
+ (AW88399_OTHS_OT << AW88399_OTHS_START_BIT)
+
+#define AW88399_PLLS_START_BIT (0)
+#define AW88399_PLLS_LOCKED (1)
+#define AW88399_PLLS_LOCKED_VALUE \
+ (AW88399_PLLS_LOCKED << AW88399_PLLS_START_BIT)
+
+#define AW88399_CLKS_START_BIT (4)
+#define AW88399_CLKS_STABLE (1)
+#define AW88399_CLKS_STABLE_VALUE \
+ (AW88399_CLKS_STABLE << AW88399_CLKS_START_BIT)
+
+#define AW88399_BIT_PLL_CHECK \
+ (AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_CHECK_MASK \
+ (~(AW88399_UVLS_NORMAL_VALUE | \
+ AW88399_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE | \
+ AW88399_NOCLKS_NO_CLOCK_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_OCDS_OC_VALUE | \
+ AW88399_OTHS_OT_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE))
+
+#define AW88399_BIT_SYSST_NOSWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_SWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE)
+
+#define AW88399_CCO_MUX_START_BIT (14)
+#define AW88399_CCO_MUX_BITS_LEN (1)
+#define AW88399_CCO_MUX_MASK \
+ (~(((1<<AW88399_CCO_MUX_BITS_LEN)-1) << AW88399_CCO_MUX_START_BIT))
+
+#define AW88399_CCO_MUX_DIVIDED (0)
+#define AW88399_CCO_MUX_DIVIDED_VALUE \
+ (AW88399_CCO_MUX_DIVIDED << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_CCO_MUX_BYPASS (1)
+#define AW88399_CCO_MUX_BYPASS_VALUE \
+ (AW88399_CCO_MUX_BYPASS << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_NOISE_GATE_EN_START_BIT (13)
+#define AW88399_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88399_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88399_NOISE_GATE_EN_BITS_LEN)-1) << AW88399_NOISE_GATE_EN_START_BIT))
+
+#define AW88399_WDT_CNT_START_BIT (0)
+#define AW88399_WDT_CNT_BITS_LEN (8)
+#define AW88399_WDT_CNT_MASK \
+ (~(((1<<AW88399_WDT_CNT_BITS_LEN)-1) << AW88399_WDT_CNT_START_BIT))
+
+#define AW88399_REABS_START_BIT (3)
+#define AW88399_REABS_BITS_LEN (1)
+#define AW88399_REABS_MASK \
+ (~(((1<<AW88399_REABS_BITS_LEN)-1) << AW88399_REABS_START_BIT))
+
+#define AW88399_DSP_VOL_START_BIT (8)
+#define AW88399_DSP_VOL_BITS_LEN (8)
+#define AW88399_DSP_VOL_MASK \
+ (~(((1<<AW88399_DSP_VOL_BITS_LEN)-1) << AW88399_DSP_VOL_START_BIT))
+
+#define AW883XX_DSP_NG_EN_START (13)
+#define AW883XX_DSP_NG_EN_LEN (1)
+#define AW883XX_DSP_NG_EN_MASK \
+ (~(((1 << AW883XX_DSP_NG_EN_LEN) - 1) << AW883XX_DSP_NG_EN_START))
+#define AW883XX_DSP_NG_EN_DISABLE (0)
+#define AW883XX_DSP_NG_EN_DISABLE_VALUE \
+ (AW883XX_DSP_NG_EN_DISABLE << AW883XX_DSP_NG_EN_START)
+
+#define AW88399_VOLUME_STEP_DB (64)
+#define AW88399_VOL_DEFAULT_VALUE (0)
+#define AW88399_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88399_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_TEM4_SIGN_MASK (~(1 << 5))
+#define AW88399_TEM4_SIGN_NEG (0xffc0)
+
+#define AW88399_ICABLK_FACTOR (1)
+#define AW88399_VCABLK_FACTOR (1)
+#define AW88399_VCABLK_DAC_FACTOR (2)
+
+#define AW88399_VCALB_ADJ_FACTOR (12)
+#define AW88399_VCALB_ACCURACY (1 << 12)
+
+#define AW88399_ISCAL_FACTOR (3125)
+#define AW88399_VSCAL_FACTOR (18875)
+#define AW88399_ISCAL_DAC_FACTOR (3125)
+#define AW88399_VSCAL_DAC_FACTOR (12600)
+#define AW88399_CABL_BASE_VALUE (1000)
+
+#define AW88399_DEV_DEFAULT_CH (0)
+#define AW88399_DEV_DSP_CHECK_MAX (5)
+#define AW88399_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88399_DSP_RE_SHIFT (12)
+#define AW88399_CALI_RE_MAX (15000)
+#define AW88399_CALI_RE_MIN (4000)
+#define AW_FW_ADDR_LEN (4)
+#define AW88399_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+#define AW88399_CRC_CHECK_PASS_VAL (0x4)
+
+#define AW88399_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88399_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88399_ACF_FILE "aw88399_acf.bin"
+#define AW88399_DEV_SYSST_CHECK_MAX (10)
+#define AW88399_CHIP_ID 0x2183
+
+#define AW88399_I2C_NAME "aw88399"
+
+#define AW88399_START_RETRIES (5)
+#define AW88399_START_WORK_DELAY_MS (0)
+
+#define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88399_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 AW_CALI_READ_CNT_MAX (8)
+#define AW88399_DSP_REG_CALRE (0x8141)
+#define AW88399_DSP_REG_CALRE_SHIFT (10)
+#define AW_CALI_DATA_SUM_RM (2)
+
+#define AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH (0x9B4C)
+#define AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH (0x9B4E)
+#define AW88399_DSP_REG_CFG_ADPZ_USTEPN (0x9B6E)
+#define AW88399_DSP_REG_CFG_RE_ALPHA (0x9BD4)
+#define AW_GET_IV_CNT_MAX (6)
+
+#define AW88399_DSP_VOL_MUTE (0XFF00)
+
+#define AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR (0x9BEC)
+#define AW88399_DSP_LOW_POWER_SWITCH_DISABLE (0x110b)
+
+#define AW88399_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 {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88399_DEV_VDSEL_DAC = 0,
+ AW88399_DEV_VDSEL_VSENSE = 32,
+};
+
+enum {
+ AW88399_DSP_CRC_NA = 0,
+ AW88399_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88399_DSP_FW_UPDATE_OFF = 0,
+ AW88399_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_FORCE_UPDATE_OFF = 0,
+ AW88399_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_1000_US = 1000,
+ AW88399_2000_US = 2000,
+ AW88399_3000_US = 3000,
+ AW88399_4000_US = 4000,
+};
+
+enum AW88399_DEV_STATUS {
+ AW88399_DEV_PW_OFF = 0,
+ AW88399_DEV_PW_ON,
+};
+
+enum AW88399_DEV_FW_STATUS {
+ AW88399_DEV_FW_FAILED = 0,
+ AW88399_DEV_FW_OK,
+};
+
+enum AW88399_DEV_MEMCLK {
+ AW88399_DEV_MEMCLK_OSC = 0,
+ AW88399_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88399_DEV_DSP_CFG {
+ AW88399_DEV_DSP_WORK = 0,
+ AW88399_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88399_DSP_16_DATA = 0,
+ AW88399_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88399_NOT_RCV_MODE = 0,
+ AW88399_RCV_MODE = 1,
+};
+
+enum {
+ AW88399_SYNC_START = 0,
+ AW88399_ASYNC_START,
+};
+
+struct aw88399 {
+ 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;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int dither_st;
+};
+
+#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
index 806191addb44..3afcef2dfa35 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -1,12 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic Bluetooth SCO link
* Copyright 2011 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -18,11 +13,15 @@
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[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
+ { "BT_SCO_TX", NULL, "RX" },
+ { "TX", NULL, "BT_SCO_RX" },
};
static struct snd_soc_dai_driver bt_sco_dai[] = {
@@ -62,28 +61,23 @@ static struct snd_soc_dai_driver bt_sco_dai[] = {
}
};
-static const struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
- .component_driver = {
- .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),
- },
+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 snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_bt_sco,
bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
-static int bt_sco_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static const struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
@@ -110,7 +104,6 @@ static struct platform_driver bt_sco_driver = {
.of_match_table = of_match_ptr(bt_sco_codec_of_match),
},
.probe = bt_sco_probe,
- .remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c
new file mode 100644
index 000000000000..40020500b1fe
--- /dev/null
+++ b/sound/soc/codecs/chv3-codec.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver chv3_codec_dai = {
+ .name = "chv3-codec-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_chv3_codec = {
+};
+
+static int chv3_codec_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_chv3_codec, &chv3_codec_dai, 1);
+}
+
+static const struct of_device_id chv3_codec_of_match[] = {
+ { .compatible = "google,chv3-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, chv3_codec_of_match);
+
+static struct platform_driver chv3_codec_platform_driver = {
+ .driver = {
+ .name = "chv3-codec",
+ .of_match_table = chv3_codec_of_match,
+ },
+ .probe = chv3_codec_probe,
+};
+module_platform_driver(chv3_codec_platform_driver);
+
+MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver");
+MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>");
+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..3eb862643b53
--- /dev/null
+++ b/sound/soc/codecs/cpcap.c
@@ -0,0 +1,1887 @@
+// 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/regulator/consumer.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <sound/core.h>
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Register 8 - CPCAP_REG_INTS1 --- Interrupt Sense 1 */
+#define CPCAP_BIT_HS_S 9 /* Headset */
+#define CPCAP_BIT_MB2_S 10 /* Mic Bias2 */
+
+/* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */
+#define CPCAP_BIT_PTT_S 11 /* Push To Talk */
+
+/* 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;
+ struct regulator *vaudio;
+ int hsirq;
+ int mb2irq;
+ struct snd_soc_jack jack;
+};
+
+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 irqreturn_t cpcap_hs_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mask = SND_JACK_HEADSET;
+ int val;
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S))) {
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_TXI, val, val);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_RXOA, val, val);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+
+ /* Give PTTS time to settle */
+ msleep(20);
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS2,
+ BIT(CPCAP_BIT_PTT_S))) {
+ /* Headphones detected. (May also be a headset with the
+ * MFB pressed.)
+ */
+ status = SND_JACK_HEADPHONE;
+ dev_info(component->dev, "HP plugged in\n");
+ } else if (regmap_test_bits(regmap, CPCAP_REG_INTS1,
+ BIT(CPCAP_BIT_MB2_S)) == 1) {
+ status = SND_JACK_HEADSET;
+ dev_info(component->dev, "HS plugged in\n");
+ } else
+ dev_info(component->dev, "Unsupported HS plugged in\n");
+ } else {
+ bool mic = cpcap->jack.status & SND_JACK_MICROPHONE;
+
+ dev_info(component->dev, "H%s disconnect\n", mic ? "S" : "P");
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, val, 0);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, val, 0);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+
+ mask |= SND_JACK_BTN_0;
+ }
+
+ snd_soc_jack_report(&cpcap->jack, status, mask);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_mb2_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mb2;
+ int ptt;
+
+ if (regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S)) == 1)
+ return IRQ_HANDLED;
+
+ mb2 = regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_MB2_S));
+ ptt = regmap_test_bits(regmap, CPCAP_REG_INTS2, BIT(CPCAP_BIT_PTT_S));
+
+ /* Initial detection might have been with MFB pressed */
+ if (!(cpcap->jack.status & SND_JACK_MICROPHONE)) {
+ if (ptt == 1 && mb2 == 1) {
+ dev_info(component->dev, "MIC plugged in\n");
+ snd_soc_jack_report(&cpcap->jack, SND_JACK_MICROPHONE,
+ SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ if (!mb2 || !ptt)
+ status = SND_JACK_BTN_0;
+
+ snd_soc_jack_report(&cpcap->jack, status, SND_JACK_BTN_0);
+
+ return IRQ_HANDLED;
+}
+
+static int cpcap_soc_probe(struct snd_soc_component *component)
+{
+ struct platform_device *pdev = to_platform_device(component->dev);
+ struct snd_soc_card *card = component->card;
+ 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->vaudio = devm_regulator_get(component->dev, "VAUDIO");
+ if (IS_ERR(cpcap->vaudio))
+ return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio),
+ "Cannot get VAUDIO regulator\n");
+
+ err = snd_soc_card_jack_new(card, "Headphones",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &cpcap->jack);
+ if (err < 0) {
+ dev_err(component->dev, "Cannot create HS jack: %i\n", err);
+ return err;
+ }
+
+ snd_jack_set_key(cpcap->jack.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
+ 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;
+
+ cpcap->hsirq = platform_get_irq_byname(pdev, "hs");
+ if (cpcap->hsirq < 0)
+ return cpcap->hsirq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL,
+ cpcap_hs_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-hs",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no HS irq%i: %i\n",
+ cpcap->hsirq, err);
+ return err;
+ }
+
+ cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2");
+ if (cpcap->mb2irq < 0)
+ return cpcap->mb2irq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL,
+ cpcap_mb2_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-mb2",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no MB2 irq%i: %i\n",
+ cpcap->mb2irq, err);
+ return err;
+ }
+
+ err = cpcap_audio_reset(component, false);
+ if (err)
+ return err;
+
+ cpcap_hs_irq_thread(cpcap->hsirq, component);
+
+ enable_irq_wake(cpcap->hsirq);
+ enable_irq_wake(cpcap->mb2irq);
+
+ return 0;
+}
+
+static void cpcap_soc_remove(struct snd_soc_component *component)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ disable_irq_wake(cpcap->hsirq);
+ disable_irq_wake(cpcap->mb2irq);
+}
+
+static int cpcap_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ /* VAIDIO should be kept in normal mode in order MIC/PTT to work */
+ if (cpcap->jack.status & SND_JACK_MICROPHONE)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cpcap = {
+ .probe = cpcap_soc_probe,
+ .remove = cpcap_soc_remove,
+ .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),
+ .set_bias_level = cpcap_set_bias_level,
+ .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 3bf93652bb31..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>
@@ -43,9 +30,9 @@ static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
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;
+ struct snd_soc_component *component = dai->component;
u8 reg;
if (mute)
@@ -53,7 +40,7 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
+ snd_soc_component_update_bits(component, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
reg);
return 0;
@@ -72,23 +59,23 @@ static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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:
- snd_soc_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:
- snd_soc_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 */
- snd_soc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
@@ -100,8 +87,9 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dai_ops cq93vc_dai_ops = {
- .digital_mute = cq93vc_mute,
+ .mute_stream = cq93vc_mute,
.set_sysclk = cq93vc_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cq93vc_dai = {
@@ -130,25 +118,20 @@ static int cq93vc_probe(struct snd_soc_component *component)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
- .set_bias_level = cq93vc_set_bias_level,
- .component_driver = {
- .probe = cq93vc_probe,
- .controls = cq93vc_snd_controls,
- .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
- },
+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 = {
@@ -157,7 +140,6 @@ static struct platform_driver cq93vc_codec_driver = {
},
.probe = cq93vc_platform_probe,
- .remove = cq93vc_platform_remove,
};
module_platform_driver(cq93vc_codec_driver);
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
new file mode 100644
index 000000000000..937c8cec682a
--- /dev/null
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -0,0 +1,1066 @@
+// 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/of_reserved_mem.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/string_choices.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",
+ str_enable_disable(enabled));
+ 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 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);
+ }
+
+ ret = of_reserved_mem_region_to_resource(dev->of_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);
+ }
+#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/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
new file mode 100644
index 000000000000..2fde84309338
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus common amplifier library.
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/device/faux.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <sound/cs-amp-lib.h>
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+struct cs_amp_lib_test_priv {
+ struct faux_device *amp_dev;
+
+ struct cirrus_amp_efi_data *cal_blob;
+ struct list_head ctl_write_list;
+};
+
+struct cs_amp_lib_test_ctl_write_entry {
+ struct list_head list;
+ unsigned int value;
+ char name[16];
+};
+
+struct cs_amp_lib_test_param {
+ int num_amps;
+ int amp_index;
+};
+
+static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ unsigned int blob_size;
+ int i;
+
+ blob_size = struct_size(priv->cal_blob, data, num_amps);
+
+ priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+
+ priv->cal_blob->size = blob_size;
+ priv->cal_blob->count = num_amps;
+
+ get_random_bytes(priv->cal_blob->data, flex_array_size(priv->cal_blob, data, num_amps));
+
+ /* Ensure all timestamps are non-zero to mark the entry valid. */
+ for (i = 0; i < num_amps; i++)
+ priv->cal_blob->data[i].calTime[0] |= 1;
+
+ /* Ensure that all UIDs are non-zero and unique. */
+ for (i = 0; i < num_amps; i++)
+ *(u8 *)&priv->cal_blob->data[i].calTarget[0] = i + 1;
+}
+
+static u64 cs_amp_lib_test_get_target_uid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ u64 uid;
+
+ uid = priv->cal_blob->data[param->amp_index].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[param->amp_index].calTarget[0];
+
+ return uid;
+}
+
+/* Redirected get_efi_variable to simulate that the file is too short */
+static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ if (!buf) {
+ *size = offsetof(struct cirrus_amp_efi_data, data) - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/* Should return -EOVERFLOW if the header is larger than the EFI data */
+static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_nohead);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the count is larger than the file */
+static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ if (!buf) {
+ /*
+ * Return a size that is shorter than required for the
+ * declared number of entries.
+ */
+ *size = priv->cal_blob->size - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1);
+
+ return EFI_SUCCESS;
+}
+
+/* Should return -EOVERFLOW if the entry count is larger than the EFI data */
+static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_bad_count);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the variable not found */
+static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_NOT_FOUND;
+}
+
+/* If EFI doesn't contain a cal data variable the result should be -ENOENT */
+static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate reading a cal data blob */
+static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ return EFI_SUCCESS;
+}
+
+/* Get cal data block from HP variable. */
+static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_hp_cal_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data));
+}
+
+/* Get cal data block for a given amp, matched by target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL);
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index without checking target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index with checked target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * Get cal data block for a given amp index with checked target UID.
+ * The UID does not match so the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Get a target UID that won't match the entry */
+ target_uid = ~cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * Get cal data block for a given amp, where the cal data does not
+ * specify calTarget so the lookup falls back to using the index
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and there isn't an
+ * index to fall back do, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, -1,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and the index is
+ * out of range, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, 99,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't given, and the index is out of range, the
+ * result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 99, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* If neither the target UID or the index is given the result should be -ENOENT. */
+static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the UID is passed as 0 this must not match an entry with an
+ * unpopulated calTarget
+ */
+static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If an entry has a timestamp of 0 it should be ignored even if it has
+ * a matching target UID.
+ */
+static void cs_amp_lib_test_get_efi_cal_empty_entry_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ u64 uid;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Mark the 3rd entry invalid by zeroing calTime */
+ priv->cal_blob->data[2].calTime[0] = 0;
+ priv->cal_blob->data[2].calTime[1] = 0;
+
+ /* Get the UID value of the 3rd entry */
+ uid = priv->cal_blob->data[2].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[2].calTarget[0];
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Lookup by UID should not find it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ uid, -1,
+ &result_data),
+ -ENOENT);
+
+ /* Get by index should ignore it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ 0, 2,
+ &result_data),
+ -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
+ KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
+
+ entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry);
+
+ INIT_LIST_HEAD(&entry->list);
+ strscpy(entry->name, ctl_name, sizeof(entry->name));
+ entry->value = val;
+
+ list_add_tail(&entry->list, &priv->ctl_write_list);
+
+ return 0;
+}
+
+static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+ struct cirrus_amp_cal_data data;
+ struct cs_dsp *dsp;
+ int ret;
+
+ dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+ dsp->dev = &priv->amp_dev->dev;
+
+ get_random_bytes(&data, sizeof(data));
+
+ /* Redirect calls to write firmware controls */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->write_cal_coeff,
+ cs_amp_lib_test_write_cal_coeff);
+
+ ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff);
+
+ KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4);
+
+ /* Checksum control must be written last */
+ entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
+}
+
+static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev));
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd0;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd1;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID));
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d0);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d1);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_00);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_BUFFER_TOO_SMALL;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_buf_too_small);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x30;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x31;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_hp_30(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_30);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_31);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static int cs_amp_lib_test_case_init(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+ INIT_LIST_HEAD(&priv->ctl_write_list);
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs_amp_lib_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ return 0;
+}
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
+ { .num_amps = 2, .amp_index = 0 },
+ { .num_amps = 2, .amp_index = 1 },
+
+ { .num_amps = 3, .amp_index = 0 },
+ { .num_amps = 3, .amp_index = 1 },
+ { .num_amps = 3, .amp_index = 2 },
+
+ { .num_amps = 4, .amp_index = 0 },
+ { .num_amps = 4, .amp_index = 1 },
+ { .num_amps = 4, .amp_index = 2 },
+ { .num_amps = 4, .amp_index = 3 },
+
+ { .num_amps = 5, .amp_index = 0 },
+ { .num_amps = 5, .amp_index = 1 },
+ { .num_amps = 5, .amp_index = 2 },
+ { .num_amps = 5, .amp_index = 3 },
+ { .num_amps = 5, .amp_index = 4 },
+
+ { .num_amps = 6, .amp_index = 0 },
+ { .num_amps = 6, .amp_index = 1 },
+ { .num_amps = 6, .amp_index = 2 },
+ { .num_amps = 6, .amp_index = 3 },
+ { .num_amps = 6, .amp_index = 4 },
+ { .num_amps = 6, .amp_index = 5 },
+
+ { .num_amps = 8, .amp_index = 0 },
+ { .num_amps = 8, .amp_index = 1 },
+ { .num_amps = 8, .amp_index = 2 },
+ { .num_amps = 8, .amp_index = 3 },
+ { .num_amps = 8, .amp_index = 4 },
+ { .num_amps = 8, .amp_index = 5 },
+ { .num_amps = 8, .amp_index = 6 },
+ { .num_amps = 8, .amp_index = 7 },
+};
+
+static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d",
+ param->num_amps, param->amp_index);
+}
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
+ cs_amp_lib_test_get_cal_param_desc);
+
+static struct kunit_case cs_amp_lib_test_cases[] = {
+ /* Tests for getting calibration data from EFI */
+ KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
+ KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test),
+ KUNIT_CASE(cs_amp_lib_test_no_cal_data_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+ KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_empty_entry_test),
+
+ /* Tests for writing calibration data */
+ KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
+
+ /* Test cases for speaker ID */
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+
+ { } /* terminator */
+};
+
+static struct kunit_suite cs_amp_lib_test_suite = {
+ .name = "snd-soc-cs-amp-lib-test",
+ .init = cs_amp_lib_test_case_init,
+ .test_cases = cs_amp_lib_test_cases,
+};
+
+kunit_test_suite(cs_amp_lib_test_suite);
+
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
new file mode 100644
index 000000000000..8434d5196107
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus Logic Smart Amplifiers
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <asm/byteorder.h>
+#include <kunit/static_stub.h>
+#include <linux/dev_printk.h>
+#include <linux/efi.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+static const struct cs_amp_lib_cal_efivar {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+} cs_amp_lib_cal_efivars[] = {
+ {
+ .name = HP_CALIBRATION_EFI_NAME,
+ .guid = &HP_CALIBRATION_EFI_GUID,
+ },
+ {
+ .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
+ .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
+ },
+};
+
+static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ __be32 beval = cpu_to_be32(val);
+ int ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
+
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
+ mutex_lock(&dsp->pwr_lock);
+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
+ mutex_unlock(&dsp->pwr_lock);
+
+ if (ret < 0) {
+ dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ int ret;
+
+ dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
+ data->calAmbient, data->calStatus, data->calR);
+
+ if (list_empty(&dsp->ctl_list)) {
+ dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
+ return -ENOENT;
+ }
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
+ * @dsp: Pointer to struct cs_dsp.
+ * @controls: Pointer to definition of firmware controls to be written.
+ * @data: Pointer to calibration data.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_write_cal_coeffs(dsp, controls, data);
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
+
+static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ u32 attr;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf);
+
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+ return efi.get_variable(name, guid, &attr, size, buf);
+
+ return EFI_NOT_FOUND;
+}
+
+static int cs_amp_convert_efi_status(efi_status_t status)
+{
+ switch (status) {
+ case EFI_SUCCESS:
+ return 0;
+ case EFI_NOT_FOUND:
+ return -ENOENT;
+ case EFI_BUFFER_TOO_SMALL:
+ return -EFBIG;
+ case EFI_UNSUPPORTED:
+ case EFI_ACCESS_DENIED:
+ case EFI_SECURITY_VIOLATION:
+ return -EACCES;
+ default:
+ return -EIO;
+ }
+}
+
+static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
+{
+ struct cirrus_amp_efi_data *efi_data;
+ unsigned long data_size = 0;
+ u8 *data;
+ efi_status_t status;
+ int i, ret;
+
+ /* Find EFI variable and get size */
+ for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL)
+ break;
+ }
+
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ERR_PTR(-ENOENT);
+
+ if (data_size < sizeof(*efi_data)) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ return ERR_PTR(-EOVERFLOW);
+ }
+
+ /* Get variable contents into buffer */
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, data);
+ if (status != EFI_SUCCESS) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ efi_data = (struct cirrus_amp_efi_data *)data;
+ dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
+
+ if ((efi_data->count > 128) ||
+ struct_size(efi_data, data, efi_data->count) > data_size) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ ret = -EOVERFLOW;
+ goto err;
+ }
+
+ return efi_data;
+
+err:
+ kfree(data);
+ dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
+
+ return ERR_PTR(ret);
+}
+
+static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
+{
+ return ((u64)data->calTarget[1] << 32) | data->calTarget[0];
+}
+
+static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ struct cirrus_amp_efi_data *efi_data;
+ struct cirrus_amp_cal_data *cal = NULL;
+ int i, ret;
+
+ efi_data = cs_amp_get_cal_efi_buffer(dev);
+ if (IS_ERR(efi_data))
+ return PTR_ERR(efi_data);
+
+ if (target_uid) {
+ for (i = 0; i < efi_data->count; ++i) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
+
+ /* Skip empty entries */
+ if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
+ continue;
+
+ /* Skip entries with unpopulated silicon ID */
+ if (cal_target == 0)
+ continue;
+
+ if (cal_target == target_uid) {
+ cal = &efi_data->data[i];
+ break;
+ }
+ }
+ }
+
+ if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
+ (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
+
+ /*
+ * Treat unpopulated cal_target as a wildcard.
+ * If target_uid != 0 we can only get here if cal_target == 0
+ * or it didn't match any cal_target value.
+ * If target_uid == 0 it is a wildcard.
+ */
+ if ((cal_target == 0) || (target_uid == 0))
+ cal = &efi_data->data[amp_index];
+ else
+ dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
+ }
+
+ if (cal) {
+ memcpy(out_data, cal, sizeof(*out_data));
+ ret = 0;
+ } else {
+ dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
+ ret = -ENOENT;
+ }
+
+ kfree(efi_data);
+
+ return ret;
+}
+
+/**
+ * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
+ * @dev: struct device of the caller.
+ * @target_uid: UID to match, or zero to ignore UID matching.
+ * @amp_index: Entry index to use, or -1 to prevent lookup by index.
+ * @out_data: struct cirrus_amp_cal_data where the entry will be copied.
+ *
+ * This function can perform 3 types of lookup:
+ *
+ * (target_uid > 0, amp_index >= 0)
+ * UID search with fallback to using the array index.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else, if the entry at
+ * [amp_index] has calTarget == 0, return that entry. Else fail.
+ *
+ * (target_uid > 0, amp_index < 0)
+ * UID search only.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else fail.
+ *
+ * (target_uid == 0, amp_index >= 0)
+ * Array index fetch only.
+ * Return the entry at [amp_index].
+ *
+ * An array lookup will be skipped if amp_index exceeds the number of
+ * entries in the calibration array, and in this case the return will
+ * be -ENOENT. An out-of-range amp_index does not prevent matching by
+ * target_uid - it has the same effect as passing amp_index < 0.
+ *
+ * If the EFI data is too short to be a valid entry, or the entry count
+ * in the EFI data overflows the actual length of the data, this function
+ * returns -EOVERFLOW.
+ *
+ * Return: 0 if the entry was found, -ENOENT if no entry was found,
+ * -EOVERFLOW if the EFI file is corrupt, else other error value.
+ */
+int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
+ else
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
+
+struct cs_amp_spkid_efi {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+ u8 values[2];
+};
+
+static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
+{
+ efi_status_t status;
+ unsigned long size;
+ u8 spkid;
+ int i, ret;
+
+ size = sizeof(spkid);
+ status = cs_amp_get_efi_variable(info->name, info->guid, &size, &spkid);
+ ret = cs_amp_convert_efi_status(status);
+ if (ret < 0)
+ return ret;
+
+ if (size == 0)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(info->values); i++) {
+ if (info->values[i] == spkid)
+ return i;
+ }
+
+ dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
+
+ return -EINVAL;
+}
+
+static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
+ {
+ .name = LENOVO_SPEAKER_ID_EFI_NAME,
+ .guid = &LENOVO_SPEAKER_ID_EFI_GUID,
+ .values = { 0xd0, 0xd1 },
+ },
+ {
+ .name = HP_SPEAKER_ID_EFI_NAME,
+ .guid = &HP_SPEAKER_ID_EFI_GUID,
+ .values = { 0x30, 0x31 },
+ },
+};
+
+/**
+ * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
+ * @dev: pointer to struct device
+ *
+ * Known vendor-specific methods of speaker ID are checked and if one is
+ * found its speaker ID value is returned.
+ *
+ * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
+ * was not found. -EACCES if the vendor-specific storage could not
+ * be read. Other error values indicate that the data from the
+ * vendor-specific storage was found but could not be understood.
+ */
+int cs_amp_get_vendor_spkid(struct device *dev)
+{
+ int i, ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
+ ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
+ if (ret != -ENOENT)
+ return ret;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+
+static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
+ .get_efi_variable = cs_amp_get_efi_variable,
+ .write_cal_coeff = cs_amp_write_cal_coeff,
+};
+
+const struct cs_amp_test_hooks * const cs_amp_test_hooks =
+ PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
+EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
+
+MODULE_DESCRIPTION("Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index bc3a72e4c4ed..0bb4bdb3deec 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * 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>
@@ -17,13 +13,12 @@
#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 <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -34,6 +29,7 @@
#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] = {
@@ -43,7 +39,7 @@ static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
struct cs35l32_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
struct cs35l32_platform_data pdata;
struct gpio_desc *reset_gpio;
@@ -154,16 +150,16 @@ static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
static int cs35l32_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;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK, 0);
break;
default:
@@ -175,9 +171,9 @@ static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
+ return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
CS35L32_SDOUT_3ST, tristate << 3);
}
@@ -198,11 +194,11 @@ static struct snd_soc_dai_driver cs35l32_dai[] = {
.formats = CS35L32_FORMATS,
},
.ops = &cs35l32_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
+static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir)
{
unsigned int val;
@@ -224,21 +220,21 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
return -EINVAL;
}
- return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
+ return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
- .set_sysclk = cs35l32_codec_set_sysclk,
-
- .component_driver = {
- .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),
- },
+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 */
@@ -263,7 +259,10 @@ static const struct regmap_config cs35l32_regmap = {
.volatile_reg = cs35l32_volatile_register,
.readable_reg = cs35l32_readable_register,
.precious_reg = cs35l32_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
@@ -345,14 +344,12 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
- unsigned int devid = 0;
+ int ret, i, devid;
unsigned int reg;
cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
@@ -407,40 +404,40 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
/* Reset the Device */
cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs35l32->reset_gpio))
- return PTR_ERR(cs35l32->reset_gpio);
+ 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 */
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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);
- return ret;
+ 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");
- return ret;
+ 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");
- return ret;
+ goto err_disable;
}
dev_info(&i2c_client->dev,
@@ -481,10 +478,10 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
CS35L32_PDN_AMP);
/* Clear MCLK Error Bit since we don't have the clock yet */
- ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+ regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs35l32, cs35l32_dai,
+ 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;
@@ -492,24 +489,21 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
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 int cs35l32_i2c_remove(struct i2c_client *i2c_client)
+static void cs35l32_i2c_remove(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
- snd_soc_unregister_codec(&i2c_client->dev);
-
/* Hold down reset */
gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
-
- return 0;
}
-#ifdef CONFIG_PM
static int cs35l32_runtime_suspend(struct device *dev)
{
struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
@@ -548,11 +542,9 @@ static int cs35l32_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops cs35l32_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, NULL)
};
static const struct of_device_id cs35l32_of_match[] = {
@@ -563,7 +555,7 @@ MODULE_DEVICE_TABLE(of, cs35l32_of_match);
static const struct i2c_device_id cs35l32_id[] = {
- {"cs35l32", 0},
+ {"cs35l32"},
{}
};
@@ -572,7 +564,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id);
static struct i2c_driver cs35l32_i2c_driver = {
.driver = {
.name = "cs35l32",
- .pm = &cs35l32_runtime_pm,
+ .pm = pm_ptr(&cs35l32_runtime_pm),
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h
index 1d6c2508cd41..9471a30e9105 100644
--- a/sound/soc/codecs/cs35l32.h
+++ b/sound/soc/codecs/cs35l32.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS35L32_H__
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 854cf8f27605..c927592f90c9 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -26,23 +22,20 @@
#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_codec *codec;
+ struct snd_soc_component *component;
struct cs35l33_pdata pdata;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
@@ -201,8 +194,8 @@ static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -211,15 +204,15 @@ static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
priv->amp_cal = true;
regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
CS35L33_AMP_CAL, 0);
- dev_dbg(codec->dev, "Amp calibration done\n");
+ dev_dbg(component->dev, "Amp calibration done\n");
}
- dev_dbg(codec->dev, "Amp turned on\n");
+ dev_dbg(component->dev, "Amp turned on\n");
break;
case SND_SOC_DAPM_POST_PMD:
- dev_dbg(codec->dev, "Amp turned off\n");
+ dev_dbg(component->dev, "Amp turned off\n");
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
break;
}
@@ -229,8 +222,8 @@ static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -240,14 +233,14 @@ static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
CS35L33_PDN_TDM, val);
- dev_dbg(codec->dev, "BST turned on\n");
+ dev_dbg(component->dev, "BST turned on\n");
break;
case SND_SOC_DAPM_POST_PMU:
- dev_dbg(codec->dev, "SDIN turned on\n");
+ 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(codec->dev, "Amp calibration started\n");
+ dev_dbg(component->dev, "Amp calibration started\n");
usleep_range(10000, 11000);
}
break;
@@ -257,10 +250,10 @@ static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
usleep_range(4000, 4100);
regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
CS35L33_PDN_BST, CS35L33_PDN_BST);
- dev_dbg(codec->dev, "BST and SDIN turned off\n");
+ dev_dbg(component->dev, "BST and SDIN turned off\n");
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
}
@@ -270,8 +263,8 @@ static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -289,15 +282,15 @@ static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
/* set sdout_3st_tdm */
val2 = CS35L33_SDOUT_3ST_TDM;
}
- dev_dbg(codec->dev, "SDOUT turned on\n");
+ 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(codec->dev, "SDOUT turned off\n");
+ dev_dbg(component->dev, "SDOUT turned off\n");
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
return 0;
}
@@ -360,11 +353,11 @@ static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
{"VBSTMON", NULL, "MON"},
};
-static int cs35l33_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -441,19 +434,19 @@ static int cs35l33_get_mclk_coeff(int mclk, int srate)
static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
CS35L33_MS_MASK, CS35L33_MS_MASK);
- dev_dbg(codec->dev, "Audio port in master mode\n");
+ dev_dbg(component->dev, "Audio port in master mode\n");
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
CS35L33_MS_MASK, 0);
- dev_dbg(codec->dev, "Audio port in slave mode\n");
+ dev_dbg(component->dev, "Audio port in slave mode\n");
break;
default:
return -EINVAL;
@@ -466,11 +459,11 @@ static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
* closely, it is dsp-a with fsync shifted left by half bclk
*/
priv->is_tdm_mode = true;
- dev_dbg(codec->dev, "Audio port in TDM mode\n");
+ dev_dbg(component->dev, "Audio port in TDM mode\n");
break;
case SND_SOC_DAIFMT_I2S:
priv->is_tdm_mode = false;
- dev_dbg(codec->dev, "Audio port in I2S mode\n");
+ dev_dbg(component->dev, "Audio port in I2S mode\n");
break;
default:
return -EINVAL;
@@ -483,8 +476,8 @@ 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_codec *codec = dai->codec;
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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));
@@ -505,7 +498,7 @@ static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream,
sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
}
- dev_dbg(codec->dev, "sample rate=%d, bits per sample=%d\n",
+ dev_dbg(component->dev, "sample rate=%d, bits per sample=%d\n",
params_rate(params), params_width(params));
return 0;
@@ -532,8 +525,8 @@ static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -553,9 +546,9 @@ static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
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_codec *codec = dai->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -567,7 +560,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slot >= 0) {
regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
CS35L33_X_LOC, slot);
- dev_dbg(codec->dev, "Audio starts from slots %d", slot);
+ dev_dbg(component->dev, "Audio starts from slots %d", slot);
}
/*
@@ -593,7 +586,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slot_num == 0) {
regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
CS35L33_X_STATE | CS35L33_X_LOC, slot);
- dev_dbg(codec->dev, "VMON enabled in slots %d-%d",
+ dev_dbg(component->dev, "VMON enabled in slots %d-%d",
slot, slot + 1);
}
@@ -601,7 +594,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slot_num == 3) {
regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
CS35L33_X_STATE | CS35L33_X_LOC, slot);
- dev_dbg(codec->dev, "IMON enabled in slots %d-%d",
+ dev_dbg(component->dev, "IMON enabled in slots %d-%d",
slot, slot + 1);
}
@@ -611,7 +604,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
CS35L33_X_STATE | CS35L33_X_LOC, slot);
snd_soc_dapm_add_routes(dapm,
&cs35l33_vp_vbst_mon_route[0], 2);
- dev_dbg(codec->dev, "VPMON enabled in slots %d", slot);
+ dev_dbg(component->dev, "VPMON enabled in slots %d", slot);
}
/* configure VBSTMON_TX_LOC */
@@ -620,7 +613,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
CS35L33_X_STATE | CS35L33_X_LOC, slot);
snd_soc_dapm_add_routes(dapm,
&cs35l33_vp_vbst_mon_route[2], 2);
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"VBSTMON enabled in slots %d", slot);
}
@@ -638,10 +631,10 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int cs35l33_codec_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
switch (freq) {
case CS35L33_MCLK_5644:
@@ -663,7 +656,7 @@ static int cs35l33_codec_set_sysclk(struct snd_soc_codec *codec,
return -EINVAL;
}
- dev_dbg(codec->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+ dev_dbg(component->dev, "external mclk freq=%d, internal mclk freq=%d\n",
freq, cs35l33->mclk_int);
return 0;
@@ -695,15 +688,15 @@ static struct snd_soc_dai_driver cs35l33_dai = {
.formats = CS35L33_FORMATS,
},
.ops = &cs35l33_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
+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_codec_get_dapm(codec);
- struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -747,20 +740,20 @@ static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
return 0;
}
-static int cs35l33_set_bst_ipk(struct snd_soc_codec *codec, unsigned int bst)
+static int cs35l33_set_bst_ipk(struct snd_soc_component *component, unsigned int bst)
{
- struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid boost current %d\n", bst);
+ dev_err(component->dev, "Invalid boost current %d\n", bst);
ret = -EINVAL;
goto err;
}
if (bst % 15625) {
- dev_err(codec->dev, "Current not a multiple of 15625uA (%d)\n",
+ dev_err(component->dev, "Current not a multiple of 15625uA (%d)\n",
bst);
ret = -EINVAL;
goto err;
@@ -778,12 +771,12 @@ err:
return ret;
}
-static int cs35l33_probe(struct snd_soc_codec *codec)
+static int cs35l33_probe(struct snd_soc_component *component)
{
- struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
- cs35l33->codec = codec;
- pm_runtime_get_sync(codec->dev);
+ cs35l33->component = component;
+ pm_runtime_get_sync(component->dev);
regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
CS35L33_ALIVE_WD_DIS, 0x8);
@@ -799,24 +792,24 @@ static int cs35l33_probe(struct snd_soc_codec *codec)
cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
if (cs35l33->pdata.boost_ipk)
- cs35l33_set_bst_ipk(codec, cs35l33->pdata.boost_ipk);
+ cs35l33_set_bst_ipk(component, cs35l33->pdata.boost_ipk);
if (cs35l33->enable_soft_ramp) {
- snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
CS35L33_DIGSFT, CS35L33_DIGSFT);
- snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
} else {
- snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ 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_update_bits(codec, CS35L33_ADC_CTL,
+ snd_soc_component_update_bits(component, CS35L33_ADC_CTL,
CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
- cs35l33_set_hg_data(codec, &(cs35l33->pdata));
+ cs35l33_set_hg_data(component, &(cs35l33->pdata));
/*
* unmask important interrupts that causes the chip to enter
@@ -826,26 +819,23 @@ static int cs35l33_probe(struct snd_soc_codec *codec)
CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
CS35L33_M_CAL_ERR, 0);
- pm_runtime_put_sync(codec->dev);
+ pm_runtime_put_sync(component->dev);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l33 = {
- .probe = cs35l33_probe,
-
- .set_bias_level = cs35l33_set_bias_level,
- .set_sysclk = cs35l33_codec_set_sysclk,
-
- .component_driver = {
- .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),
- },
- .idle_bias_off = true,
+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 = {
@@ -858,11 +848,12 @@ static const struct regmap_config cs35l33_regmap = {
.volatile_reg = cs35l33_volatile_register,
.readable_reg = cs35l33_readable_register,
.writeable_reg = cs35l33_writeable_register,
- .cache_type = REGCACHE_RBTREE,
- .use_single_rw = true,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+static int cs35l33_runtime_resume(struct device *dev)
{
struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
int ret;
@@ -900,7 +891,7 @@ err:
return ret;
}
-static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+static int cs35l33_runtime_suspend(struct device *dev)
{
struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
@@ -918,9 +909,7 @@ static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops cs35l33_pm_ops = {
- SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
- cs35l33_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs35l33_runtime_suspend, cs35l33_runtime_resume, NULL)
};
static int cs35l33_get_hg_data(const struct device_node *np,
@@ -967,7 +956,7 @@ static int cs35l33_get_hg_data(const struct device_node *np,
static irqreturn_t cs35l33_irq_thread(int irq, void *data)
{
struct cs35l33_private *cs35l33 = data;
- struct snd_soc_codec *codec = cs35l33->codec;
+ 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,
@@ -989,9 +978,9 @@ static irqreturn_t cs35l33_irq_thread(int irq, void *data)
/* handle the interrupts */
if (sticky_val1 & CS35L33_AMP_SHORT) {
- dev_crit(codec->dev, "Amp short error\n");
+ dev_crit(component->dev, "Amp short error\n");
if (!(current_val & CS35L33_AMP_SHORT)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Amp short error release\n");
regmap_update_bits(cs35l33->regmap,
CS35L33_AMP_CTL,
@@ -1007,13 +996,13 @@ static irqreturn_t cs35l33_irq_thread(int irq, void *data)
}
if (sticky_val1 & CS35L33_CAL_ERR) {
- dev_err(codec->dev, "Cal error\n");
+ 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(codec->dev, "Cal error release\n");
+ dev_dbg(component->dev, "Cal error release\n");
regmap_update_bits(cs35l33->regmap,
CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
0);
@@ -1027,9 +1016,9 @@ static irqreturn_t cs35l33_irq_thread(int irq, void *data)
}
if (sticky_val1 & CS35L33_OTE) {
- dev_crit(codec->dev, "Over temperature error\n");
+ dev_crit(component->dev, "Over temperature error\n");
if (!(current_val & CS35L33_OTE)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Over temperature error release\n");
regmap_update_bits(cs35l33->regmap,
CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
@@ -1042,9 +1031,9 @@ static irqreturn_t cs35l33_irq_thread(int irq, void *data)
}
if (sticky_val1 & CS35L33_OTW) {
- dev_err(codec->dev, "Over temperature warning\n");
+ dev_err(component->dev, "Over temperature warning\n");
if (!(current_val & CS35L33_OTW)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Over temperature warning release\n");
regmap_update_bits(cs35l33->regmap,
CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
@@ -1056,21 +1045,21 @@ static irqreturn_t cs35l33_irq_thread(int irq, void *data)
}
}
if (CS35L33_ALIVE_ERR & sticky_val1)
- dev_err(codec->dev, "ERROR: ADSPCLK Interrupt\n");
+ dev_err(component->dev, "ERROR: ADSPCLK Interrupt\n");
if (CS35L33_MCLK_ERR & sticky_val1)
- dev_err(codec->dev, "ERROR: MCLK Interrupt\n");
+ dev_err(component->dev, "ERROR: MCLK Interrupt\n");
if (CS35L33_VMON_OVFL & sticky_val2)
- dev_err(codec->dev,
+ dev_err(component->dev,
"ERROR: VMON Overflow Interrupt\n");
if (CS35L33_IMON_OVFL & sticky_val2)
- dev_err(codec->dev,
+ dev_err(component->dev,
"ERROR: IMON Overflow Interrupt\n");
if (CS35L33_VPMON_OVFL & sticky_val2)
- dev_err(codec->dev,
+ dev_err(component->dev,
"ERROR: VPMON Overflow Interrupt\n");
return IRQ_HANDLED;
@@ -1120,8 +1109,7 @@ static int cs35l33_of_get_pdata(struct device *dev,
return 0;
}
-static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l33_private *cs35l33;
struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1173,7 +1161,7 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
/* 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);
+ "reset", GPIOD_OUT_HIGH);
if (IS_ERR(cs35l33->reset_gpio)) {
dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
__func__);
@@ -1195,17 +1183,18 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
regcache_cache_only(cs35l33->regmap, false);
/* initialize codec */
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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;
}
@@ -1236,10 +1225,10 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
pm_runtime_set_active(&i2c_client->dev);
pm_runtime_enable(&i2c_client->dev);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs35l33, &cs35l33_dai, 1);
+ 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 codec failed\n",
+ dev_err(&i2c_client->dev, "%s: Register component failed\n",
__func__);
goto err_enable;
}
@@ -1247,25 +1236,23 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
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 int cs35l33_i2c_remove(struct i2c_client *client)
+static void cs35l33_i2c_remove(struct i2c_client *client)
{
struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
-
gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
pm_runtime_disable(&client->dev);
regulator_bulk_disable(cs35l33->num_core_supplies,
cs35l33->core_supplies);
-
- return 0;
}
static const struct of_device_id cs35l33_of_match[] = {
@@ -1275,7 +1262,7 @@ static const struct of_device_id cs35l33_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l33_of_match);
static const struct i2c_device_id cs35l33_id[] = {
- {"cs35l33", 0},
+ {"cs35l33"},
{}
};
@@ -1284,7 +1271,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l33_id);
static struct i2c_driver cs35l33_i2c_driver = {
.driver = {
.name = "cs35l33",
- .pm = &cs35l33_pm_ops,
+ .pm = pm_ptr(&cs35l33_pm_ops),
.of_match_table = cs35l33_of_match,
},
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
index c045737d1a5f..fcb5e1723be6 100644
--- a/sound/soc/codecs/cs35l33.h
+++ b/sound/soc/codecs/cs35l33.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS35L33_H__
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index 0600d5264c4c..a5a8075598ff 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * 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>
@@ -23,27 +19,26 @@
#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.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_codec *codec;
+ struct snd_soc_component *component;
struct cs35l34_platform_data pdata;
struct regmap *regmap;
struct regulator_bulk_data core_supplies[2];
@@ -237,8 +232,8 @@ static bool cs35l34_precious_register(struct device *dev, unsigned int reg)
static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -250,7 +245,7 @@ static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
CS35L34_PDN_ALL, 0);
if (ret < 0) {
- dev_err(codec->dev, "Cannot set Power bits %d\n", ret);
+ dev_err(component->dev, "Cannot set Power bits %d\n", ret);
return ret;
}
usleep_range(5000, 5100);
@@ -272,8 +267,8 @@ static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
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_codec *codec = dai->codec;
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -284,7 +279,7 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
/* scan rx_mask for aud slot */
slot = ffs(rx_mask) - 1;
if (slot >= 0)
- snd_soc_update_bits(codec, CS35L34_TDM_RX_CTL_1_AUDIN,
+ 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)
@@ -294,10 +289,10 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
slot_num = 0;
/* disable vpmon/vbstmon: enable later if set in tx_mask */
- snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON,
+ 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_update_bits(codec, CS35L34_TDM_TX_CTL_4_VBSTMON,
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_4_VBSTMON,
CS35L34_X_STATE | CS35L34_X_LOC,
CS35L34_X_STATE | CS35L34_X_LOC);
@@ -305,22 +300,22 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
while (slot >= 0) {
/* configure VMON_TX_LOC */
if (slot_num == 0)
- snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_1_VMON,
+ 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_update_bits(codec, CS35L34_TDM_TX_CTL_2_IMON,
+ 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_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON,
+ 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_update_bits(codec,
+ snd_soc_component_update_bits(component,
CS35L34_TDM_TX_CTL_4_VBSTMON,
CS35L34_X_STATE | CS35L34_X_LOC, slot);
}
@@ -328,7 +323,7 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
/* Enable the relevant tx slot */
reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8);
bit_pos = slot - ((slot / 8) * (8));
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
1 << bit_pos, 1 << bit_pos);
tx_mask &= ~(1 << slot);
@@ -342,8 +337,8 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -382,8 +377,8 @@ static const struct snd_kcontrol_new cs35l34_snd_controls[] = {
static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -524,15 +519,15 @@ static int cs35l34_get_mclk_coeff(int mclk, int srate)
static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
0x80, 0x80);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
0x80, 0x00);
break;
@@ -546,15 +541,15 @@ 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_codec *codec = dai->codec;
- struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "ERROR: Invalid mclk %d and/or srate %d\n",
+ dev_err(component->dev, "ERROR: Invalid mclk %d and/or srate %d\n",
priv->mclk_int, srate);
return coeff;
}
@@ -562,41 +557,21 @@ static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate);
if (ret != 0)
- dev_err(codec->dev, "Failed to set clock state %d\n", ret);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (tristate)
- snd_soc_update_bits(codec, CS35L34_PWRCTL3,
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT);
else
- snd_soc_update_bits(codec, CS35L34_PWRCTL3,
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
CS35L34_PDN_SDOUT, 0);
return 0;
}
@@ -604,8 +579,8 @@ static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate)
static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
unsigned int value;
switch (freq) {
@@ -634,7 +609,7 @@ static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
cs35l34->mclk_int = freq / 2;
break;
default:
- dev_err(codec->dev, "ERROR: Invalid Frequency %d\n", freq);
+ dev_err(component->dev, "ERROR: Invalid Frequency %d\n", freq);
cs35l34->mclk_int = 0;
return -EINVAL;
}
@@ -644,7 +619,6 @@ static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
}
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,
@@ -670,13 +644,13 @@ static struct snd_soc_dai_driver cs35l34_dai = {
.formats = CS35L34_FORMATS,
},
.ops = &cs35l34_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34,
unsigned int inductor)
{
- struct snd_soc_codec *codec = cs35l34->codec;
+ struct snd_soc_component *component = cs35l34->component;
switch (inductor) {
case 1000: /* 1 uH */
@@ -708,19 +682,19 @@ static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34,
regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3);
break;
default:
- dev_err(codec->dev, "%s Invalid Inductor Value %d uH\n",
+ dev_err(component->dev, "%s Invalid Inductor Value %d uH\n",
__func__, inductor);
return -EINVAL;
}
return 0;
}
-static int cs35l34_probe(struct snd_soc_codec *codec)
+static int cs35l34_probe(struct snd_soc_component *component)
{
int ret = 0;
- struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec);
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
/* Set over temperature warning attenuation to 6 dB */
regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
@@ -773,26 +747,26 @@ static int cs35l34_probe(struct snd_soc_codec *codec)
regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL,
1, 1);
- pm_runtime_put_sync(codec->dev);
+ pm_runtime_put_sync(component->dev);
return ret;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l34 = {
- .probe = cs35l34_probe,
-
- .component_driver = {
- .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),
- },
+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 = {
+static const struct regmap_config cs35l34_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -802,7 +776,10 @@ static struct regmap_config cs35l34_regmap = {
.volatile_reg = cs35l34_volatile_register,
.readable_reg = cs35l34_readable_register,
.precious_reg = cs35l34_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
@@ -864,7 +841,7 @@ static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
static irqreturn_t cs35l34_irq_thread(int irq, void *data)
{
struct cs35l34_private *cs35l34 = data;
- struct snd_soc_codec *codec = cs35l34->codec;
+ struct snd_soc_component *component = cs35l34->component;
unsigned int sticky1, sticky2, sticky3, sticky4;
unsigned int mask1, mask2, mask3, mask4, current1;
@@ -887,11 +864,11 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &current1);
if (sticky1 & CS35L34_CAL_ERR) {
- dev_err(codec->dev, "Cal error\n");
+ dev_err(component->dev, "Cal error\n");
/* error is no longer asserted; safe to reset */
if (!(current1 & CS35L34_CAL_ERR)) {
- dev_dbg(codec->dev, "Cal error release\n");
+ dev_dbg(component->dev, "Cal error release\n");
regmap_update_bits(cs35l34->regmap,
CS35L34_PROT_RELEASE_CTL,
CS35L34_CAL_ERR_RLS, 0);
@@ -907,14 +884,14 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
}
if (sticky1 & CS35L34_ALIVE_ERR)
- dev_err(codec->dev, "Alive error\n");
+ dev_err(component->dev, "Alive error\n");
if (sticky1 & CS35L34_AMP_SHORT) {
- dev_crit(codec->dev, "Amp short error\n");
+ dev_crit(component->dev, "Amp short error\n");
/* error is no longer asserted; safe to reset */
if (!(current1 & CS35L34_AMP_SHORT)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Amp short error release\n");
regmap_update_bits(cs35l34->regmap,
CS35L34_PROT_RELEASE_CTL,
@@ -930,11 +907,11 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
}
if (sticky1 & CS35L34_OTW) {
- dev_crit(codec->dev, "Over temperature warning\n");
+ dev_crit(component->dev, "Over temperature warning\n");
/* error is no longer asserted; safe to reset */
if (!(current1 & CS35L34_OTW)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Over temperature warning release\n");
regmap_update_bits(cs35l34->regmap,
CS35L34_PROT_RELEASE_CTL,
@@ -950,11 +927,11 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
}
if (sticky1 & CS35L34_OTE) {
- dev_crit(codec->dev, "Over temperature error\n");
+ dev_crit(component->dev, "Over temperature error\n");
/* error is no longer asserted; safe to reset */
if (!(current1 & CS35L34_OTE)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Over temperature error release\n");
regmap_update_bits(cs35l34->regmap,
CS35L34_PROT_RELEASE_CTL,
@@ -970,7 +947,7 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
}
if (sticky3 & CS35L34_BST_HIGH) {
- dev_crit(codec->dev, "VBST too high error; powering off!\n");
+ 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,
@@ -978,7 +955,7 @@ static irqreturn_t cs35l34_irq_thread(int irq, void *data)
}
if (sticky3 & CS35L34_LBST_SHORT) {
- dev_crit(codec->dev, "LBST short error; powering off!\n");
+ 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,
@@ -993,15 +970,13 @@ static const char * const cs35l34_core_supplies[] = {
"VP",
};
-static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
+ int i, devid;
int ret;
- unsigned int devid = 0;
unsigned int reg;
cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL);
@@ -1042,13 +1017,15 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
} else {
pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto err_regulator;
+ }
if (i2c_client->dev.of_node) {
ret = cs35l34_handle_of_data(i2c_client, pdata);
if (ret != 0)
- return ret;
+ goto err_regulator;
}
cs35l34->pdata = *pdata;
@@ -1061,34 +1038,35 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
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))
- return PTR_ERR(cs35l34->reset_gpio);
+ "reset", 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);
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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_regulator;
+ 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_regulator;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
@@ -1108,16 +1086,18 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
pm_runtime_set_active(&i2c_client->dev);
pm_runtime_enable(&i2c_client->dev);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs35l34, &cs35l34_dai, 1);
+ 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 codec failed\n", __func__);
- goto err_regulator;
+ "%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);
@@ -1125,22 +1105,18 @@ err_regulator:
return ret;
}
-static int cs35l34_i2c_remove(struct i2c_client *client)
+static void cs35l34_i2c_remove(struct i2c_client *client)
{
struct cs35l34_private *cs35l34 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
-
gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
pm_runtime_disable(&client->dev);
regulator_bulk_disable(cs35l34->num_core_supplies,
cs35l34->core_supplies);
-
- return 0;
}
-static int __maybe_unused cs35l34_runtime_resume(struct device *dev)
+static int cs35l34_runtime_resume(struct device *dev)
{
struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
int ret;
@@ -1173,7 +1149,7 @@ err:
return ret;
}
-static int __maybe_unused cs35l34_runtime_suspend(struct device *dev)
+static int cs35l34_runtime_suspend(struct device *dev)
{
struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
@@ -1189,9 +1165,7 @@ static int __maybe_unused cs35l34_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops cs35l34_pm_ops = {
- SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend,
- cs35l34_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs35l34_runtime_suspend, cs35l34_runtime_resume, NULL)
};
static const struct of_device_id cs35l34_of_match[] = {
@@ -1201,7 +1175,7 @@ static const struct of_device_id cs35l34_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l34_of_match);
static const struct i2c_device_id cs35l34_id[] = {
- {"cs35l34", 0},
+ {"cs35l34"},
{}
};
MODULE_DEVICE_TABLE(i2c, cs35l34_id);
@@ -1209,7 +1183,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l34_id);
static struct i2c_driver cs35l34_i2c_driver = {
.driver = {
.name = "cs35l34",
- .pm = &cs35l34_pm_ops,
+ .pm = pm_ptr(&cs35l34_pm_ops),
.of_match_table = cs35l34_of_match,
},
diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h
index bcd54f127559..97959e334f9b 100644
--- a/sound/soc/codecs/cs35l34.h
+++ b/sound/soc/codecs/cs35l34.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS35L34_H__
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 129978d1243e..7a01b1d9fc9d 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -1,19 +1,14 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -22,22 +17,20 @@
#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/of.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
@@ -194,8 +187,8 @@ static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35)
static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -231,7 +224,7 @@ static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
1 << CS35L35_AMP_DIGSFT_SHIFT);
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
ret = -EINVAL;
}
return ret;
@@ -240,8 +233,8 @@ static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -301,7 +294,7 @@ static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
}
return 0;
}
@@ -369,19 +362,19 @@ static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
- 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:
regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
- cs35l35->slave_mode = false;
+ cs35l35->clock_consumer = false;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
- cs35l35->slave_mode = true;
+ cs35l35->clock_consumer = true;
break;
default:
return -EINVAL;
@@ -470,8 +463,8 @@ static int cs35l35_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -482,7 +475,7 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
if (clk_ctl < 0) {
- dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+ dev_err(component->dev, "Invalid CLK:Rate %d:%d\n",
cs35l35->sysclk, srate);
return -EINVAL;
}
@@ -490,7 +483,7 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
CS35L35_CLK_CTL2_MASK, clk_ctl);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set port config %d\n", ret);
+ dev_err(component->dev, "Failed to set port config %d\n", ret);
return ret;
}
@@ -500,16 +493,16 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
* 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;
+ errata_chk = (clk_ctl & CS35L35_SP_RATE_MASK) >> CS35L35_SP_RATE_SHIFT;
if (classh->classh_wk_fet_disable == 0x00 &&
- (errata_chk == 0x01 || errata_chk == 0x03)) {
+ (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(codec->dev, "Failed to set fet config %d\n",
+ dev_err(component->dev, "Failed to set fet config %d\n",
ret);
return ret;
}
@@ -531,7 +524,7 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
audin_format = CS35L35_SDIN_DEPTH_24;
break;
default:
- dev_err(codec->dev, "Unsupported Width %d\n",
+ dev_err(component->dev, "Unsupported Width %d\n",
params_width(params));
return -EINVAL;
}
@@ -554,31 +547,31 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
* to configure the CLOCK_CTL3 register correctly
*/
if ((cs35l35->sclk / srate) % 4) {
- dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ 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 are supported in I2S Slave Mode */
- if (cs35l35->slave_mode) {
+ /* 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(codec->dev, "ratio not supported\n");
+ dev_err(component->dev, "ratio not supported\n");
return -EINVAL;
}
} else {
- /* Only certain ratios supported in I2S MASTER Mode */
+ /* 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(codec->dev, "ratio not supported\n");
+ dev_err(component->dev, "ratio not supported\n");
return -EINVAL;
}
}
@@ -587,7 +580,7 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
CS35L35_SP_SCLKS_MASK, sp_sclks <<
CS35L35_SP_SCLKS_SHIFT);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+ dev_err(component->dev, "Failed to set fsclk %d\n", ret);
return ret;
}
}
@@ -607,8 +600,8 @@ static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
if (!substream->runtime)
return 0;
@@ -635,8 +628,8 @@ static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
if (!substream->runtime)
return 0;
@@ -655,8 +648,8 @@ static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -696,7 +689,7 @@ static struct snd_soc_dai_driver cs35l35_dai[] = {
.formats = CS35L35_FORMATS,
},
.ops = &cs35l35_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs35l35-pdm",
@@ -712,11 +705,11 @@ static struct snd_soc_dai_driver cs35l35_dai[] = {
},
};
-static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
int clksrc;
int ret = 0;
@@ -731,7 +724,7 @@ static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
clksrc = CS35L35_CLK_SOURCE_PDM;
break;
default:
- dev_err(codec->dev, "Invalid CLK Source\n");
+ dev_err(component->dev, "Invalid CLK Source\n");
return -EINVAL;
}
@@ -749,7 +742,7 @@ static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
cs35l35->sysclk = freq;
break;
default:
- dev_err(codec->dev, "Invalid CLK Frequency Input : %d\n", freq);
+ dev_err(component->dev, "Invalid CLK Frequency Input : %d\n", freq);
return -EINVAL;
}
@@ -757,7 +750,7 @@ static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
CS35L35_CLK_SOURCE_MASK,
clksrc << CS35L35_CLK_SOURCE_SHIFT);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+ dev_err(component->dev, "Failed to set sysclk %d\n", ret);
return ret;
}
@@ -834,9 +827,9 @@ static int cs35l35_boost_inductor(struct cs35l35_private *cs35l35,
return 0;
}
-static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+static int cs35l35_component_probe(struct snd_soc_component *component)
{
- struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -880,7 +873,7 @@ static int cs35l35_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
CS35L35_CH_STEREO_MASK,
1 << CS35L35_CH_STEREO_SHIFT);
- ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+ ret = snd_soc_add_component_controls(component, cs35l35_adv_controls,
ARRAY_SIZE(cs35l35_adv_controls));
if (ret)
return ret;
@@ -1079,23 +1072,21 @@ static int cs35l35_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
- .probe = cs35l35_codec_probe,
- .set_sysclk = cs35l35_codec_set_sysclk,
- .component_driver = {
- .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),
- },
-
+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 = {
+static const struct regmap_config cs35l35_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -1105,7 +1096,9 @@ static struct regmap_config cs35l35_regmap = {
.volatile_reg = cs35l35_volatile_register,
.readable_reg = cs35l35_readable_register,
.precious_reg = cs35l35_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
static irqreturn_t cs35l35_irq(int irq, void *data)
@@ -1314,7 +1307,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
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 ? true : false;
+ classh_config->classh_algo_enable = (classh != NULL);
if (classh_config->classh_algo_enable) {
classh_config->classh_bst_override =
@@ -1469,15 +1462,13 @@ static const struct reg_sequence cs35l35_errata_patch[] = {
{ 0x7F, 0x00 },
};
-static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
+ int i, devid;
int ret;
- unsigned int devid = 0;
unsigned int reg;
cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
@@ -1491,7 +1482,7 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
if (IS_ERR(cs35l35->regmap)) {
ret = PTR_ERR(cs35l35->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret);
- goto err;
+ return ret;
}
for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
@@ -1556,13 +1547,12 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
goto err;
}
/* initialize codec */
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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",
@@ -1617,10 +1607,10 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
- ret = snd_soc_register_codec(dev, &soc_codec_dev_cs35l35, cs35l35_dai,
- ARRAY_SIZE(cs35l35_dai));
+ 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 codec: %d\n", ret);
+ dev_err(dev, "Failed to register component: %d\n", ret);
goto err;
}
@@ -1634,10 +1624,12 @@ err:
return ret;
}
-static int cs35l35_i2c_remove(struct i2c_client *client)
+static void cs35l35_i2c_remove(struct i2c_client *i2c_client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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[] = {
@@ -1647,7 +1639,7 @@ static const struct of_device_id cs35l35_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l35_of_match);
static const struct i2c_device_id cs35l35_id[] = {
- {"cs35l35", 0},
+ {"cs35l35"},
{}
};
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
index 621bfef70d03..5e4509f41b32 100644
--- a/sound/soc/codecs/cs35l35.h
+++ b/sound/soc/codecs/cs35l35.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS35L35_H__
@@ -172,6 +168,7 @@
#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
@@ -286,7 +283,7 @@ struct cs35l35_private {
int sclk;
bool pdm_mode;
bool i2s_mode;
- bool slave_mode;
+ bool clock_consumer;
/* GPIO for /RST */
struct gpio_desc *reset_gpio;
struct completion pdn_done;
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
new file mode 100644
index 000000000000..b60697ff7a50
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.c
@@ -0,0 +1,1942 @@
+// 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/irq.h>
+#include <linux/of.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 <sound/cs35l36.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 const 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 struct snd_soc_dai_ops cs35l36_ops = {
+ .set_fmt = cs35l36_set_dai_fmt,
+ .hw_params = cs35l36_pcm_hw_params,
+ .set_sysclk = cs35l36_dai_set_sysclk,
+};
+
+#define CS35L36_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_384000)
+
+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 = CS35L36_RATES,
+ .formats = CS35L36_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L36_RATES,
+ .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 const 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_MAPLE,
+};
+
+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"},
+ {}
+};
+
+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 = 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..34097996b784
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -0,0 +1,96 @@
+// 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.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "cs35l41.h"
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+ { "cs35l40" },
+ { "cs35l41" },
+ { "cs35l51" },
+ { "cs35l53" },
+ {}
+};
+
+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;
+
+ 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))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ 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 = pm_ptr(&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 = 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..1702f26049d3
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -0,0 +1,1592 @@
+// 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>
+
+#define CS35L41_FIRMWARE_OLD_VERSION 0x001C00 /* v0.28.0 */
+
+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, 0x0000003B },
+ { 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, 0x81000001 },
+ { CS35L41_GPIO2_CTRL1, 0x81000001 },
+ { 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_SFT_RESET:
+ 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_MAPLE,
+};
+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_MAPLE,
+};
+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, const unsigned int *tx_slot,
+ unsigned int rx_num, const 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_start[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { 0x0000742C, 0x00000009 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_end[] = {
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_safe_to_active_start[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1
+};
+
+static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = {
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+};
+
+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);
+
+/*
+ * Enabling the CS35L41_SHD_BOOST_ACTV and CS35L41_SHD_BOOST_PASS shared boosts
+ * does also require a call to cs35l41_mdsync_up(), but not before getting the
+ * PLL Lock signal.
+ *
+ * PLL Lock seems to be triggered soon after snd_pcm_start() is executed and
+ * SNDRV_PCM_TRIGGER_START command is processed, which happens (long) after the
+ * SND_SOC_DAPM_PRE_PMU event handler is invoked as part of snd_pcm_prepare().
+ *
+ * This event handler is where cs35l41_global_enable() is normally called from,
+ * but waiting for PLL Lock here will time out. Increasing the wait duration
+ * will not help, as the only consequence of it would be to add an unnecessary
+ * delay in the invocation of snd_pcm_start().
+ *
+ * Trying to move the wait in the SNDRV_PCM_TRIGGER_START callback is not a
+ * solution either, as the trigger is executed in an IRQ-off atomic context.
+ *
+ * The current approach is to invoke cs35l41_mdsync_up() right after receiving
+ * the PLL Lock interrupt, in the IRQ handler.
+ */
+int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
+ int enable, struct cs_dsp *dsp)
+{
+ int ret;
+ unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask;
+ unsigned int pwr_ctl1_val;
+ struct reg_sequence cs35l41_mdsync_down_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_GPIO_PAD_CONTROL, 0},
+ {CS35L41_PWR_CTRL1, 0, 3000},
+ };
+
+ pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK;
+
+ ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val);
+ if (ret)
+ return ret;
+
+ if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) {
+ dev_dbg(dev, "Cannot set Global Enable - already set.\n");
+ return 0;
+ } else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) {
+ dev_dbg(dev, "Cannot unset Global Enable - not set.\n");
+ return 0;
+ }
+
+ 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));
+ /* Activation to be completed later via cs35l41_mdsync_up() */
+ if (ret || enable)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ /* Clear PUP/PDN status */
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
+ break;
+ case CS35L41_INT_BOOST:
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
+ enable << CS35L41_GLOBAL_EN_SHIFT);
+ if (ret) {
+ dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ /* Clear PUP/PDN status */
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ if (enable) {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start,
+ ARRAY_SIZE(cs35l41_safe_to_active_start));
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PUP_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK);
+
+ if (dsp->running && dsp->fw_id_version > CS35L41_FIRMWARE_OLD_VERSION)
+ ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
+ CSPL_MBOX_CMD_SPK_OUT_ENABLE);
+ else
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk,
+ ARRAY_SIZE(cs35l41_safe_to_active_en_spk));
+
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ } else {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start,
+ ARRAY_SIZE(cs35l41_active_to_safe_start));
+ if (ret) {
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PDN_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK);
+
+ /* Test Key is locked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end,
+ ARRAY_SIZE(cs35l41_active_to_safe_end));
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_global_enable);
+
+/*
+ * To be called after receiving the IRQ Lock interrupt, in order to complete
+ * any shared boost activation initiated by cs35l41_global_enable().
+ */
+int cs35l41_mdsync_up(struct regmap *regmap)
+{
+ return regmap_update_bits(regmap, CS35L41_PWR_CTRL3,
+ CS35L41_SYNC_EN_MASK, CS35L41_SYNC_EN_MASK);
+}
+EXPORT_SYMBOL_GPL(cs35l41_mdsync_up);
+
+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);
+ case CSPL_MBOX_CMD_SPK_OUT_ENABLE:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ 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 (sts == CSPL_MBOX_STS_ERROR || sts == CSPL_MBOX_STS_ERROR2) {
+ dev_err(dev, "CSPL Error Detected\n");
+ return -EINVAL;
+ }
+
+ 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..f9b6bf7bea9c
--- /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;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ spi_set_drvdata(spi, cs35l41);
+ cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l41->regmap))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ 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 = pm_ptr(&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..173d7c59b725
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.c
@@ -0,0 +1,1492 @@
+// 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 <acpi/acpi_bus.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/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, 50, 100, 0);
+
+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 };
+ unsigned int i;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(cs35l41->dev);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "pm_runtime_resume_and_get failed in %s: %d\n",
+ __func__, ret);
+ return IRQ_NONE;
+ }
+
+ ret = IRQ_NONE;
+
+ 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);
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS) {
+ ret = cs35l41_mdsync_up(cs35l41->regmap);
+ if (ret)
+ dev_err(cs35l41->dev, "MDSYNC-up failed: %d\n", ret);
+ else
+ dev_dbg(cs35l41->dev, "MDSYNC-up done\n");
+
+ dev_dbg(cs35l41->dev, "PUP-done status: %d\n",
+ !!(status[0] & CS35L41_PUP_DONE_MASK));
+ }
+
+ ret = IRQ_HANDLED;
+ }
+
+done:
+ 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);
+ 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));
+
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 1, &cs35l41->dsp.cs_dsp);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 0, &cs35l41->dsp.cs_dsp);
+
+ 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,
+ const unsigned int *tx_slot,
+ unsigned int rx_n, const 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);
+
+ 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 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 = {
+ .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,
+};
+
+#define CS35L41_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+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 = CS35L41_RATES,
+ .formats = CS35L41_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L41_RATES,
+ .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;
+ uint32_t dsp1rx5_src;
+ 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;
+ }
+
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VPMON;
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_SHD_BOOST_PASS:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VBSTMON;
+ break;
+ default:
+ dev_err(cs35l41->dev, "wm_halo_init failed - Invalid Boost Type: %d\n",
+ cs35l41->hw_cfg.bst_type);
+ goto err_dsp;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, dsp1rx5_src);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write DSP1RX5_SRC: %d failed: %d\n", dsp1rx5_src, ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_VBSTMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CS35L41_INPUT_SRC_VBSTMON 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_get_system_name(struct cs35l41_private *cs35l41)
+{
+ struct acpi_device *adev = ACPI_COMPANION(cs35l41->dev);
+ const char *sub = NULL;
+ const char *tmp;
+ int ret = 0;
+
+ /* If there is no acpi_device, there is no ACPI for this system, skip checking ACPI */
+ if (adev) {
+ acpi_handle handle = acpi_device_handle(adev);
+
+ sub = acpi_get_subsystem_id(handle);
+ ret = PTR_ERR_OR_ZERO(sub);
+ if (ret) {
+ sub = NULL;
+ /* If no _SUB, fallback to _HID, otherwise fail */
+ if (ret == -ENODATA) {
+ tmp = acpi_device_hid(adev);
+ /* If dummy hid, return 0 and fallback to legacy firmware path */
+ if (!strcmp(tmp, "device")) {
+ ret = 0;
+ goto err;
+ }
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+ } else {
+ if (!device_property_read_string(cs35l41->dev, "cirrus,subsystem-id", &tmp)) {
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+
+err:
+ if (sub) {
+ cs35l41->dsp.system_name = sub;
+ dev_info(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name);
+ } else
+ dev_warn(cs35l41->dev, "Subsystem ID not found\n");
+
+ return ret;
+}
+
+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)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to request core supplies\n");
+
+ ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to enable core supplies\n");
+
+ /* 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_probe(cs35l41->dev, ret,
+ "Failed to get reset GPIO\n");
+ 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_probe(cs35l41->dev, ret,
+ "Failed waiting for OTP_BOOT_DONE\n");
+ 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_probe(cs35l41->dev, ret, "Get Device ID failed\n");
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
+ 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_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
+ 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_probe(cs35l41->dev, ret, "Failed to request IRQ\n");
+ goto err;
+ }
+
+ ret = cs35l41_set_pdata(cs35l41);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Set pdata failed\n");
+ goto err;
+ }
+
+ ret = cs35l41_get_system_name(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ ret = cs35l41_dsp_init(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(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_probe(cs35l41->dev, ret, "Register codec failed\n");
+ 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_dont_use_autosuspend(cs35l41->dev);
+ 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_dont_use_autosuspend(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 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 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 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 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 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 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;
+}
+
+EXPORT_GPL_DEV_PM_OPS(cs35l41_pm_ops) = {
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+ SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+
+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..c85cbc1dd333
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.h
@@ -0,0 +1,41 @@
+/* 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;
+};
+
+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..a09aa3b92ae1
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// 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" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
+
+static struct i2c_driver cs35l45_i2c_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = pm_ptr(&cs35l45_pm_ops),
+ },
+ .id_table = cs35l45_id_i2c,
+ .probe = 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("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..5f91472c3fd2
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// 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 = pm_ptr(&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("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..d2ecc7b3f619
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// 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_AMP_GAIN, 0x00002300 },
+ { 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_HVLV_CONFIG:
+ case CS35L45_AMP_PCM_CONTROL:
+ case CS35L45_AMP_GAIN:
+ 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_MAPLE,
+};
+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_MAPLE,
+};
+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 },
+};
+
+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..d4dcdf37bb70
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.c
@@ -0,0 +1,1515 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// 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 int cs35l45_activate_ctl(struct snd_soc_component *component,
+ const char *ctl_name, bool active)
+{
+ struct snd_card *card = component->card->snd_card;
+ struct snd_kcontrol *kcontrol;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+
+ kcontrol = snd_soc_component_get_kcontrol(component, ctl_name);
+ if (!kcontrol) {
+ dev_err(component->dev, "Can't find kcontrol %s\n", ctl_name);
+ return -EINVAL;
+ }
+
+ index_offset = snd_ctl_get_ioff(kcontrol, &kcontrol->id);
+ vd = &kcontrol->vd[index_offset];
+ if (active)
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id);
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l45->amplifier_mode;
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int amp_state;
+ int ret;
+
+ if ((ucontrol->value.integer.value[0] == cs35l45->amplifier_mode) ||
+ (ucontrol->value.integer.value[0] > AMP_MODE_RCV))
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_BLOCK_ENABLES, &amp_state);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read AMP state: %d\n", ret);
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+ }
+
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+ snd_soc_component_disable_pin_unlocked(component, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ if (ucontrol->value.integer.value[0] == AMP_MODE_SPK) {
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_ENABLE << CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_HVLV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", true);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+
+ } else /* AMP_MODE_RCV */ {
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_DISABLE_FET_OFF <<
+ CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_FORCE_LV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ regmap_clear_bits(cs35l45->regmap,
+ CS35L45_BLOCK_ENABLES2,
+ CS35L45_AMP_DRE_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_MASK,
+ CS35L45_AMP_GAIN_PCM_13DBV <<
+ CS35L45_AMP_GAIN_PCM_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", false);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+ }
+
+ if (amp_state & CS35L45_AMP_EN_MASK)
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+
+ snd_soc_component_enable_pin_unlocked(component, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ cs35l45->amplifier_mode = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+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("DACPCM Source", cs35l45_dacpcm_enums[0]),
+};
+static const struct snd_kcontrol_new amp_en_ctl =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 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("TEMPMON_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_SUPPLY("VMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_TEMPMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BATTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BATTMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BSTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BSTMON_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, SND_SOC_NOPM, 0, 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("DACPCM Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
+
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0, &amp_en_ctl),
+
+ 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" },
+ { "TEMPMON", NULL, "TEMPMON_SRC" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+ { "VMON", NULL, "VMON_EN" },
+ { "IMON", NULL, "IMON_EN" },
+ { "TEMPMON", NULL, "TEMPMON_EN" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_EN" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_EN" },
+
+ { "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, "DACPCM 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", NULL, "VMON_EN"},
+ {"DSP1", NULL, "IMON_EN"},
+ {"DSP1", NULL, "VDD_BATTMON_EN"},
+ {"DSP1", NULL, "VDD_BSTMON_EN"},
+ {"DSP1", NULL, "TEMPMON_EN"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ CS35L45_DAC_MUX_ROUTE("DACPCM"),
+
+ { "AMP Enable", "Switch", "AMP" },
+ { "SPK", NULL, "AMP Enable"},
+};
+
+static const char * const amplifier_mode_texts[] = {"SPK", "RCV"};
+static SOC_ENUM_SINGLE_DECL(amplifier_mode_enum, SND_SOC_NOPM, 0,
+ amplifier_mode_texts);
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 1000, 300, 0);
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+ SOC_ENUM_EXT("Amplifier Mode", amplifier_mode_enum,
+ cs35l45_amplifier_mode_get, cs35l45_amplifier_mode_put),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_SHIFT,
+ CS35L45_AMP_GAIN_PCM_MASK >> CS35L45_AMP_GAIN_PCM_SHIFT,
+ 0, amp_gain_tlv),
+ /* 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);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2, CS35L45_DSP_VIRT2_MBOX_MASK);
+
+ // 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);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2,
+ CS35L45_DSP_VIRT2_MBOX_MASK);
+ 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 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 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_sys_suspend(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "System suspend, disabling IRQ\n");
+ disable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_suspend_noirq(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "Late system suspend, reenabling IRQ\n");
+ enable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_resume_noirq(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "Early system resume, disabling IRQ\n");
+ disable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_resume(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "System resume, reenabling IRQ\n");
+ enable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+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)
+ 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);
+
+ if (i < 0 || i >= ARRAY_SIZE(cs35l45_irqs))
+ dev_err(cs35l45->dev, "Unspecified global error condition (%d) detected!\n", irq);
+ else
+ 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:
+ case 0x35A460:
+ 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;
+
+ cs35l45->amplifier_mode = AMP_MODE_SPK;
+
+ 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_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");
+
+EXPORT_GPL_DEV_PM_OPS(cs35l45_pm_ops) = {
+ RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL)
+
+ SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend, cs35l45_sys_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend_noirq, cs35l45_sys_resume_noirq)
+};
+
+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("GPL");
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644
index 000000000000..7a790d2acac7
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.h
@@ -0,0 +1,514 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * 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_HVLV_CONFIG 0x00006400
+#define CS35L45_LDPM_CONFIG 0x00006404
+#define CS35L45_AMP_PCM_CONTROL 0x00007000
+#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_AMP_GAIN 0x00007800
+#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_TEMPMON_EN_SHIFT 10
+#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_RCV_EN_SHIFT 2
+#define CS35L45_RCV_EN_MASK BIT(2)
+#define CS35L45_AMP_EN_SHIFT 0
+#define CS35L45_AMP_EN_MASK BIT(0)
+
+#define CS35L45_BST_DISABLE_FET_OFF 0x00
+#define CS35L45_BST_DISABLE_FET_ON 0x01
+#define CS35L45_BST_ENABLE 0x02
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT 27
+#define CS35L45_AMP_DRE_EN_SHIFT 20
+#define CS35L45_AMP_DRE_EN_MASK BIT(20)
+#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)
+
+/* HVLV_CONFIG */
+#define CS35L45_FORCE_LV_OPERATION 0x01
+#define CS35L45_FORCE_HV_OPERATION 0x02
+#define CS35L45_HVLV_OPERATION 0x03
+#define CS35L45_HVLV_MODE_SHIFT 0
+#define CS35L45_HVLV_MODE_MASK GENMASK(1, 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
+
+/* AMP_GAIN_PCM */
+#define CS35L45_AMP_GAIN_PCM_10DBV 0x00
+#define CS35L45_AMP_GAIN_PCM_13DBV 0x01
+#define CS35L45_AMP_GAIN_PCM_16DBV 0x02
+#define CS35L45_AMP_GAIN_PCM_19DBV 0x03
+
+#define CS35L45_AMP_GAIN_PCM_SHIFT 8
+#define CS35L45_AMP_GAIN_PCM_MASK GENMASK(9, 8)
+
+/* 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,
+};
+
+enum amp_mode {
+ AMP_MODE_SPK = 0,
+ AMP_MODE_RCV = 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 amplifier_mode;
+ 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);
+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..0492ddc4102d
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -0,0 +1,108 @@
+// 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)
+{
+ unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
+ struct cs35l56_private *cs35l56;
+ struct device *dev = &client->dev;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->base.can_hibernate = true;
+
+ i2c_set_clientdata(client, cs35l56);
+
+ switch (id) {
+ case 0x3556:
+ regmap_config = &cs35l56_regmap_i2c;
+ cs35l56->base.type = 0x56;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_i2c;
+ cs35l56->base.type = 0x63;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(cs35l56->base.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->base, 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", 0x3556 },
+ { "cs35l63", 0x3563 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
+ { "CSC355C", 0x3556 },
+ { "CSC356C", 0x3563 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
+#endif
+
+static struct i2c_driver cs35l56_i2c_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&cs35l56_pm_ops_i2c_spi),
+ .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match),
+ },
+ .id_table = cs35l56_id_i2c,
+ .probe = 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..42d24ac2977f
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -0,0 +1,606 @@
+// 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
+
+/* Cirrus bus bridge registers */
+#define CS35L56_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS35L56_SDW_MEM_READ_DATA 0xd8
+
+#define CS35L56_SDW_LAST_LATE BIT(3)
+#define CS35L56_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS35L56_SDW_RDATA_RDY BIT(0)
+
+#define CS35L56_LATE_READ_POLL_US 10
+#define CS35L56_LATE_READ_TIMEOUT_US 1000
+
+static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral,
+ unsigned int mask,
+ unsigned int match)
+{
+ int ret, val;
+
+ ret = read_poll_timeout(sdw_read_no_pm, val,
+ (val < 0) || ((val & mask) == match),
+ CS35L56_LATE_READ_POLL_US, CS35L56_LATE_READ_TIMEOUT_US,
+ false, peripheral, CS35L56_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
+ u8 *buf, size_t val_size)
+{
+ int ret, i;
+
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ for (i = 0; i < val_size; i += sizeof(u32)) {
+ /* Poll for bus bridge idle */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_CMD_IN_PROGRESS,
+ 0);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "!CMD_IN_PROGRESS fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Reading LSByte triggers read of register to holding buffer */
+ sdw_read_no_pm(peripheral, reg + i);
+
+ /* Wait for data available */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_RDATA_RDY,
+ CS35L56_SDW_RDATA_RDY);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "RDATA_RDY fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Read data from buffer */
+ ret = sdw_nread_no_pm(peripheral, CS35L56_SDW_MEM_READ_DATA,
+ sizeof(u32), &buf[i]);
+ if (ret) {
+ dev_err(&peripheral->dev, "Late read @%#x failed: %d\n", reg + i, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)&buf[i]);
+ }
+
+ return 0;
+}
+
+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);
+
+ if (cs35l56_is_otp_register(reg))
+ return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size);
+
+ 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 int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0);
+ if (ret < 0)
+ return ret;
+
+ cs35l56->sdw_unique_id = ret & 0xf;
+
+ return 0;
+}
+
+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->base.dev);
+
+ ret = cs35l56_sdw_get_unique_id(cs35l56);
+ if (ret)
+ goto out;
+
+ /* SoundWire UniqueId is used to index the calibration array */
+ if (cs35l56->base.cal_index < 0)
+ cs35l56->base.cal_index = cs35l56->sdw_unique_id;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret < 0) {
+ regcache_cache_only(cs35l56->base.regmap, true);
+ goto out;
+ }
+
+ /*
+ * cs35l56_init can return with !init_done if it triggered
+ * a soft reset.
+ */
+ if (cs35l56->base.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_put_autosuspend(cs35l56->base.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->base.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->base.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->base);
+
+ /* 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->base.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->base.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->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->base.dev, "%s: ATTACHED\n", __func__);
+ if (cs35l56->sdw_attached)
+ break;
+
+ if (!cs35l56->base.init_done || cs35l56->soft_resetting)
+ cs35l56_sdw_init(peripheral);
+
+ cs35l56->sdw_attached = true;
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs35l56->base.dev, "%s: UNATTACHED\n", __func__);
+ cs35l56->sdw_attached = false;
+ break;
+ default:
+ break;
+ }
+
+ 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->base.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,
+#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->base.dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->base.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->base.init_done)
+ return 0;
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+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->base.init_done)
+ return 0;
+
+ ret = cs35l56_sdw_handle_unattach(cs35l56);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, true);
+ 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->base.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;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->sdw_peripheral = peripheral;
+ cs35l56->sdw_link_num = peripheral->bus->link_id;
+ INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+ dev_set_drvdata(dev, cs35l56);
+
+ switch ((unsigned int)id->driver_data) {
+ case 0x3556:
+ case 0x3557:
+ regmap_config = &cs35l56_regmap_sdw;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_sdw;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ cs35l56->base.type = ((unsigned int)id->driver_data) & 0xff;
+
+ cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ peripheral, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.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->base.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, 0x3556),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&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..9e6b9ca2f354
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -0,0 +1,1355 @@
+// 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/array_size.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#include "cs35l56.h"
+
+static const struct reg_sequence cs35l56_patch[] = {
+ /*
+ * Firmware can change these to non-defaults to satisfy SDCA.
+ * Ensure that they are at known 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_MASK_18, 0x1f7df0ff },
+};
+
+static const struct reg_sequence cs35l56_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l63_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
+ ARRAY_SIZE(cs35l56_patch));
+ if (ret)
+ return ret;
+
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
+ ARRAY_SIZE(cs35l56_patch_fw));
+ break;
+ case 0x63:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
+ ARRAY_SIZE(cs35l63_patch_fw));
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED");
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_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, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+static const struct reg_default cs35l63_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_MASK_1, 0x8003ffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0x8c000fff },
+ { CS35L56_IRQ1_MASK_18, 0x0760f000 },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+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_OTP_MEM_53:
+ case CS35L56_OTP_MEM_54:
+ case CS35L56_OTP_MEM_55:
+ 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_DIE_STS1:
+ case CS35L56_DIE_STS2:
+ 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_common_volatile_reg(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;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_MAIN_RENDER_USER_MUTE:
+ case CS35L56_MAIN_RENDER_USER_VOLUME:
+ case CS35L56_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L63_MAIN_RENDER_USER_MUTE:
+ case CS35L63_MAIN_RENDER_USER_VOLUME:
+ case CS35L63_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static const struct cs35l56_fw_reg cs35l56_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l56_b2_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_B2_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_B2_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l63_fw_reg = {
+ .fw_ver = CS35L63_DSP1_FW_VER,
+ .halo_state = CS35L63_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L63_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L63_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L63_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L63_MAIN_POSTURE_NUMBER,
+};
+
+static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base)
+{
+ switch (cs35l56_base->type) {
+ default:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ cs35l56_base->fw_reg = &cs35l56_fw_reg;
+ break;
+ default:
+ cs35l56_base->fw_reg = &cs35l56_b2_fw_reg;
+ break;
+ }
+ break;
+ case 0x63:
+ cs35l56_base->fw_reg = &cs35l63_fw_reg;
+ break;
+ }
+}
+
+int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ val, (val == 0),
+ CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int val;
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->pm_cur_stat,
+ val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+ val, ret);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val = 0;
+ int read_ret, poll_ret;
+
+ /*
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read of the status register.
+ */
+ poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ cs35l56_base->fw_reg->halo_state,
+ &val);
+
+ if (poll_ret) {
+ dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n",
+ read_ret, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_wait_control_port_ready(void)
+{
+ /* Wait for control port to be ready (datasheet tIRS). */
+ usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, "SND_SOC_CS35L56_SHARED");
+
+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);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, "SND_SOC_CS35L56_SHARED");
+
+static const struct {
+ u32 addr;
+ u32 value;
+} cs35l56_spi_system_reset_stages[] = {
+ { .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET },
+ /* The next write is necessary to delimit the soft reset */
+ { .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING },
+};
+
+static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base,
+ struct spi_device *spi)
+{
+ struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = sizeof(*buf),
+ };
+ struct spi_message m;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) {
+ buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr);
+ buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value);
+ spi_message_init_with_transfers(&m, &t, 1);
+ ret = spi_sync_locked(spi, &m);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret);
+
+ usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US,
+ 2 * CS35L56_SPI_RESET_TO_PORT_READY_US);
+ }
+}
+
+static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
+{
+ struct spi_device *spi = to_spi_device(cs35l56_base->dev);
+ unsigned int val;
+ int read_ret, ret;
+
+ /*
+ * There must not be any other SPI bus activity while the amp is
+ * soft-resetting.
+ */
+ ret = spi_bus_lock(spi->controller);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret);
+ return;
+ }
+
+ cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi);
+ spi_bus_unlock(spi->controller);
+
+ /*
+ * Check firmware boot by testing for a response in MBOX_2.
+ * HALO_STATE cannot be trusted yet because the reset sequence
+ * can leave it with stale state. But MBOX is reset.
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read.
+ */
+ ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val > 0) && (val < 0xffffffff),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ CS35L56_DSP_VIRTUAL1_MBOX_2,
+ &val);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n",
+ read_ret, val);
+ }
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l56_b2_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_B2_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l63_system_reset_seq[] = {
+ REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ /*
+ * 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_base->regmap, true);
+
+ if (cs35l56_is_spi(cs35l56_base)) {
+ cs35l56_spi_system_reset(cs35l56_base);
+ return;
+ }
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+ break;
+ default:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_b2_system_reset_seq,
+ ARRAY_SIZE(cs35l56_b2_system_reset_seq));
+ break;
+ }
+ break;
+ case 0x63:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l63_system_reset_seq,
+ ARRAY_SIZE(cs35l63_system_reset_seq));
+ break;
+ default:
+ break;
+ }
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (is_soundwire)
+ return;
+
+ cs35l56_wait_control_port_ready();
+
+ /* Leave in cache-only. This will be revoked when the chip has rebooted. */
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq)
+{
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs35l56", cs35l56_base);
+ if (!ret)
+ cs35l56_base->irq = irq;
+ else
+ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED");
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+ struct cs35l56_base *cs35l56_base = 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_base->init_done)
+ return IRQ_NONE;
+
+ mutex_lock(&cs35l56_base->irq_lock);
+
+ rv = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (rv < 0) {
+ dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv);
+ goto err_unlock;
+ }
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+ if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+ dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n");
+ goto err;
+ }
+
+ /* Ack interrupts */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+ status1 &= ~mask1;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+ status8 &= ~mask8;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+ status20 &= ~mask20;
+ /* We don't want EINT20 but they default to unmasked: force mask */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+ dev_dbg(cs35l56_base->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_base->dev, "Amp short error\n");
+
+ if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+ dev_crit(cs35l56_base->dev, "Overtemp error\n");
+
+ ret = IRQ_HANDLED;
+
+err:
+ pm_runtime_put(cs35l56_base->dev);
+err_unlock:
+ mutex_unlock(&cs35l56_base->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+ int ret;
+
+ /*
+ * 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_base->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts,
+ &val);
+ if (ret)
+ dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, "SND_SOC_CS35L56_SHARED");
+
+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_ALLOW_AUTO_HIBERNATE),
+};
+
+static void cs35l56_issue_wake_event(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+
+ /*
+ * Dummy transactions to trigger I2C/SPI auto-wake. Issue two
+ * transactions to meet the minimum required time from the rising edge
+ * to the last falling edge of wake.
+ *
+ * It uses bypassed read because we must wake the chip before
+ * disabling regmap cache-only.
+ */
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ usleep_range(CS35L56_WAKE_HOLD_TIME_US, 2 * CS35L56_WAKE_HOLD_TIME_US);
+
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ cs35l56_wait_control_port_ready();
+}
+
+int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56_base->init_done)
+ return 0;
+
+ /* Firmware must have entered a power-save state */
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->transducer_actual_ps,
+ val, (val >= CS35L56_PS3),
+ CS35L56_PS3_POLL_US,
+ CS35L56_PS3_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret);
+
+ /* Clear BOOT_DONE so it can be used to detect a reboot */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+ if (!cs35l56_base->can_hibernate) {
+ regcache_cache_only(cs35l56_base->regmap, true);
+ dev_dbg(cs35l56_base->dev, "Suspended: no hibernate");
+
+ return 0;
+ }
+
+ /*
+ * 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_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ dev_dbg(cs35l56_base->dev, "Suspended: hibernate");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend_common, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56_base->init_done)
+ return 0;
+
+ if (!cs35l56_base->can_hibernate)
+ goto out_sync;
+
+ /* Must be done before releasing cache-only */
+ if (!is_soundwire)
+ cs35l56_issue_wake_event(cs35l56_base);
+
+out_sync:
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ /* BOOT_DONE will be 1 if the amp reset */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val);
+ if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+ dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n");
+ regcache_mark_dirty(cs35l56_base->regmap);
+ }
+
+ regcache_sync(cs35l56_base->regmap);
+
+ dev_dbg(cs35l56_base->dev, "Resumed");
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, "SND_SOC_CS35L56_SHARED");
+
+static 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 },
+};
+
+void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ cs_dsp->num = 1;
+ cs_dsp->type = WMFW_HALO;
+ cs_dsp->rev = 0;
+ cs_dsp->dev = cs35l56_base->dev;
+ cs_dsp->regmap = cs35l56_base->regmap;
+ cs_dsp->base = CS35L56_DSP1_CORE_BASE;
+ cs_dsp->base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+ cs_dsp->mem = cs35l56_dsp1_regions;
+ cs_dsp->num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+ cs_dsp->no_core_startstop = true;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, "SND_SOC_CS35L56_SHARED");
+
+struct cs35l56_pte {
+ u8 x;
+ u8 wafer_id;
+ u8 pte[2];
+ u8 lot[3];
+ u8 y;
+ u8 unused[3];
+ u8 dvs;
+} __packed;
+static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0);
+
+static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+ struct cs35l56_pte pte;
+ u64 unique_id;
+ int ret;
+
+ ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret);
+ return ret;
+ }
+
+ unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16);
+ unique_id <<= 32;
+ unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
+ ((u32)pte.dvs << 24);
+
+ *uid = unique_id;
+
+ return 0;
+}
+
+static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+ u32 tmp[2];
+ int ret;
+
+ ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret);
+ return ret;
+ }
+
+ *uid = tmp[1];
+ *uid <<= 32;
+ *uid |= tmp[0];
+
+ return 0;
+}
+
+/* Firmware calibration controls */
+const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, "SND_SOC_CS35L56_SHARED");
+
+static const struct cirrus_amp_cal_controls cs35l63_calibration_controls = {
+ .alg_id = 0xbf210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
+{
+ u64 silicon_uid = 0;
+ int ret;
+
+ /* Driver can't apply calibration to a secured part, so skip */
+ if (cs35l56_base->secured)
+ return 0;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ case 0x63:
+ ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", silicon_uid);
+
+ ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid,
+ cs35l56_base->cal_index,
+ &cs35l56_base->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
+ return ret;
+
+ cs35l56_base->cal_data_valid = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
+ bool *fw_missing, unsigned int *fw_version)
+{
+ unsigned int prot_status;
+ int ret;
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts, &prot_status);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
+ return ret;
+ }
+
+ *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->fw_ver, fw_version);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ __be32 pid, sid, tid;
+ unsigned int alg_id;
+ int ret;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ alg_id = 0x9f212;
+ break;
+ default:
+ alg_id = 0xbf212;
+ break;
+ }
+
+ scoped_guard(mutex, &cs_dsp->pwr_lock) {
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &pid, sizeof(pid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &sid, sizeof(sid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &tid, sizeof(tid));
+ }
+
+ if (ret)
+ dev_warn(cs35l56_base->dev, "Can't read tuning IDs");
+ else
+ dev_info(cs35l56_base->dev, "Tuning PID: %#x, SID: %#x, TID: %#x\n",
+ be32_to_cpu(pid), be32_to_cpu(sid), be32_to_cpu(tid));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_log_tuning, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured, fw_ver;
+ bool fw_missing;
+
+ /*
+ * When the system is not using a reset_gpio ensure the device is
+ * awake, otherwise the device has just been released from reset and
+ * the driver must wait for the control port to become usable.
+ */
+ if (!cs35l56_base->reset_gpio)
+ cs35l56_issue_wake_event(cs35l56_base);
+ else
+ cs35l56_wait_control_port_ready();
+
+ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+ cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+ cs35l56_set_fw_reg_table(cs35l56_base);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Device ID failed\n");
+ return ret;
+ }
+ devid &= CS35L56_DEVID_MASK;
+
+ switch (devid) {
+ case 0x35A54:
+ case 0x35A56:
+ case 0x35A57:
+ cs35l56_base->calibration_controls = &cs35l56_calibration_controls;
+ break;
+ case 0x35A630:
+ cs35l56_base->calibration_controls = &cs35l63_calibration_controls;
+ devid = devid >> 4;
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
+ return -ENODEV;
+ }
+
+ cs35l56_base->type = devid & 0xFF;
+
+ /* Silicon is now identified and booted so exit cache-only */
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get Secure status failed\n");
+ return ret;
+ }
+
+ /* When any bus is restricted treat the device as secured */
+ if (secured & CS35L56_RESTRICTED_MASK)
+ cs35l56_base->secured = true;
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get OTP ID failed\n");
+ return ret;
+ }
+
+ ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
+ if (ret)
+ return ret;
+
+ dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
+ cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
+ fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
+
+ /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1,
+ CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+ 0);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8,
+ CS35L56_TEMP_ERR_EINT1_MASK,
+ 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
+{
+ struct gpio_descs *descs;
+ u32 speaker_id;
+ int i, ret;
+
+ /* Check for vendor-specific speaker ID method */
+ ret = cs_amp_get_vendor_spkid(cs35l56_base->dev);
+ if (ret >= 0) {
+ dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret);
+ return ret;
+ } else if (ret != -ENOENT) {
+ dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret);
+ return ret;
+ }
+
+ /* Attempt to read the speaker type from a device property */
+ ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id);
+ if (!ret) {
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ return speaker_id;
+ }
+
+ /* Read the speaker type qualifier from the motherboard GPIOs */
+ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN);
+ if (!descs) {
+ return -ENOENT;
+ } else if (IS_ERR(descs)) {
+ ret = PTR_ERR(descs);
+ return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
+ }
+
+ speaker_id = 0;
+ for (i = 0; i < descs->ndescs; i++) {
+ ret = gpiod_get_value_cansleep(descs->desc[i]);
+ if (ret < 0) {
+ dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i);
+ goto err;
+ }
+
+ speaker_id |= (ret << i);
+ }
+
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ ret = speaker_id;
+err:
+ gpiod_put_array(descs);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "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");
+
+const 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_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const 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_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, "SND_SOC_CS35L56_SHARED");
+
+const 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_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_base = 0x8000,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_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 = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_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");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644
index 000000000000..9bc9b7c98390
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -0,0 +1,95 @@
+// 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->base.type = 0x56;
+
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
+ }
+
+ cs35l56->base.dev = &spi->dev;
+ cs35l56->base.can_hibernate = true;
+ ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(&cs35l56->base, 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);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
+ { "CSC355C", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
+#endif
+
+static struct spi_driver cs35l56_spi_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&cs35l56_pm_ops_i2c_spi),
+ .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match),
+ },
+ .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..2c1edbd636ef
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.c
@@ -0,0 +1,1564 @@
+// 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/array_size.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/property.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/cs-amp-lib.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 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,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 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 const struct snd_kcontrol_new cs35l63_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L63_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L63_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->base.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->base.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->base.regmap,
+ cs35l56->base.fw_reg->transducer_actual_ps,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
+ return ret;
+ case SND_SOC_DAPM_POST_PMD:
+ return cs35l56_mbox_send(&cs35l56->base, 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->base.dev, "%s: %d\n", __func__, event);
+
+ return wm_adsp_event(w, kcontrol, event);
+}
+
+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->base.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->base.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->base.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->base.dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->base.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->base.regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
+ CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
+
+ return 0;
+}
+
+static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask)
+{
+ unsigned int channel_shift;
+ int bit_num;
+
+ /* 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;
+ }
+
+ return 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->base.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->base.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->base.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
+
+ /* Default unused slots to 63 */
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1,
+ cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask));
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5,
+ cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask));
+
+ dev_dbg(cs35l56->base.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->base.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->base.dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->base.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->base.regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_RX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL5,
+ CS35L56_ASP_RX_WL_MASK, asp_wl);
+ } else {
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_TX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->base.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->base.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->base.dev, "%s: rate %d\n", __func__, params_rate(params));
+
+ if (!cs35l56->base.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)
+{
+ 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,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ },
+ {
+ .name = "cs35l56-sdw1c",
+ .id = 2,
+ .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_write_cal(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ if (cs35l56->base.secured || !cs35l56->base.cal_data_valid)
+ return -ENODATA;
+
+ ret = wm_adsp_run(&cs35l56->dsp);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
+ cs35l56->base.calibration_controls,
+ &cs35l56->base.cal_data);
+
+ wm_adsp_stop(&cs35l56->dsp);
+
+ if (ret == 0)
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+ return ret;
+}
+
+static int cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56,
+ bool load_firmware)
+{
+ int ret;
+
+ /*
+ * Abort the first load if it didn't find the suffixed bins and
+ * we have an alternate fallback suffix.
+ */
+ cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix);
+
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) {
+ cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix;
+ cs35l56->fallback_fw_suffix = NULL;
+ cs35l56->dsp.bin_mandatory = false;
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ }
+
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, true);
+ if (ret)
+ return;
+
+ cs35l56_write_cal(cs35l56);
+
+ /* Always REINIT after applying patch or coefficients */
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+}
+
+static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
+{
+ int ret;
+
+ /*
+ * 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;
+ flush_work(&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);
+ flush_work(&cs35l56->sdw_irq_work);
+ }
+
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ /*
+ * Use wm_adsp to load and apply the firmware patch and coefficient files,
+ * but only if firmware is missing. If firmware is already patched just
+ * power-up wm_adsp without downloading firmware.
+ */
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing);
+ if (ret)
+ goto err;
+
+ mutex_lock(&cs35l56->base.irq_lock);
+
+ reinit_completion(&cs35l56->init_completion);
+
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
+
+ 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->base.dev, "%s: init_completion timed out (SDW)\n",
+ __func__);
+ goto err_unlock;
+ }
+ } else if (cs35l56_init(cs35l56)) {
+ goto err_unlock;
+ }
+
+ regmap_clear_bits(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->prot_sts,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+
+ if (cs35l56_write_cal(cs35l56) == 0)
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+
+err_unlock:
+ mutex_unlock(&cs35l56->base.irq_lock);
+err:
+ /* 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 void cs35l56_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ dsp_work);
+ unsigned int firmware_version;
+ bool firmware_missing;
+ int ret;
+
+ if (!cs35l56->base.init_done)
+ return;
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
+ if (ret)
+ goto err;
+
+ /* Populate fw file qualifier with the revision and security state */
+ kfree(cs35l56->dsp.fwf_name);
+ if (firmware_missing) {
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
+ } else {
+ /* Firmware files must match the running firmware version */
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
+ "%02x%s-%06x-dsp1",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ firmware_version);
+ }
+
+ if (!cs35l56->dsp.fwf_name)
+ goto err;
+
+ dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
+ cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
+
+ /*
+ * The firmware cannot be patched if it is already running from
+ * patch RAM. In this case the firmware files are versioned to
+ * match the running firmware version and will only contain
+ * tunings. We do not need to shutdown the firmware to apply
+ * tunings so can use the lower cost reinit sequence instead.
+ */
+ if (!firmware_missing)
+ cs35l56_reinit_patch(cs35l56);
+ else
+ cs35l56_patch(cs35l56, firmware_missing);
+
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp);
+err:
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+{
+ if (cs35l56->dsp.fwf_suffix)
+ return 0;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
+
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new name as an alternate.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ }
+
+ return 0;
+}
+
+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;
+ unsigned short vendor, device;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
+ if (!cs35l56->dsp.system_name &&
+ (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0) {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x-spkid%d",
+ vendor, device,
+ cs35l56->speaker_id);
+ } else {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x",
+ vendor, device);
+ }
+ if (!cs35l56->dsp.system_name)
+ return -ENOMEM;
+ }
+
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
+ return -ENODEV;
+ }
+
+ cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
+ if (!cs35l56->dsp.part)
+ return -ENOMEM;
+
+ cs35l56->component = component;
+ ret = cs35l56_set_fw_suffix(cs35l56);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs35l56->dsp, component);
+
+ debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done);
+ debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
+ debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
+
+
+ switch (cs35l56->base.type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = snd_soc_add_component_controls(component, cs35l56_controls,
+ ARRAY_SIZE(cs35l56_controls));
+ break;
+ case 0x63:
+ ret = snd_soc_add_component_controls(component, cs35l63_controls,
+ ARRAY_SIZE(cs35l63_controls));
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
+
+ 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);
+
+ if (cs35l56->dsp.cs_dsp.booted)
+ wm_adsp_power_down(&cs35l56->dsp);
+
+ wm_adsp2_component_remove(&cs35l56->dsp, component);
+
+ kfree(cs35l56->dsp.part);
+ cs35l56->dsp.part = NULL;
+
+ kfree(cs35l56->dsp.fwf_name);
+ cs35l56->dsp.fwf_name = NULL;
+
+ cs35l56->component = NULL;
+}
+
+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),
+
+ .set_bias_level = cs35l56_set_bias_level,
+
+ .suspend_bias_off = 1, /* see cs35l56_system_resume() */
+};
+
+static int __maybe_unused cs35l56_runtime_suspend_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ return cs35l56_runtime_resume_common(&cs35l56->base, false);
+}
+
+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->base.irq)
+ disable_irq(cs35l56->base.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->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.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->base.irq)
+ enable_irq(cs35l56->base.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->base.irq)
+ disable_irq(cs35l56->base.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->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.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->base.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");
+
+ /*
+ * We might have done a hard reset or the CS35L56 was power-cycled
+ * so wait for control port to be ready.
+ */
+ cs35l56_wait_control_port_ready();
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.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->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
+ if (ret < 1)
+ return ret;
+
+ cs35l56->base.fw_patched = false;
+ wm_adsp_power_down(&cs35l56->dsp);
+ 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_control_add_nop(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl)
+{
+ return 0;
+}
+
+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;
+ cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp);
+
+ /*
+ * dsp->part is filled in later as it is based on the DEVID. In a
+ * SoundWire system that cannot be read until enumeration has occurred
+ * and the device has attached.
+ */
+ dsp->fw = 12;
+ dsp->wmfw_optional = true;
+
+ /*
+ * None of the firmware controls need to be exported so add a no-op
+ * callback that suppresses creating an ALSA control.
+ */
+ dsp->control_add = &cs35l56_control_add_nop;
+
+ dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name);
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0) {
+ dev_err(cs35l56->base.dev, "wm_halo_init failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
+{
+ struct device *dev = cs35l56->base.dev;
+ const char *prop;
+ int ret;
+
+ ret = device_property_read_string(dev, "cirrus,firmware-uid", &prop);
+ /* If bad sw node property, return 0 and fallback to legacy firmware path */
+ if (ret < 0)
+ return 0;
+
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0)
+ cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
+ prop, cs35l56->speaker_id);
+ else
+ cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
+
+ if (cs35l56->dsp.system_name == NULL)
+ return -ENOMEM;
+
+ dev_dbg(dev, "Firmware UID: %s\n", cs35l56->dsp.system_name);
+
+ return 0;
+}
+
+/*
+ * Some SoundWire laptops have a spk-id-gpios property but it points to
+ * the wrong ACPI Device node so can't be used to get the GPIO. Try to
+ * find the SDCA node containing the GpioIo resource and add a GPIO
+ * mapping to it.
+ */
+static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = {
+ { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 },
+ { }
+};
+
+static void cs35l56_acpi_dev_release_driver_gpios(void *adev)
+{
+ acpi_dev_remove_driver_gpios(adev);
+}
+
+static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *af01_fwnode;
+ const union acpi_object *obj;
+ struct gpio_desc *desc;
+ int ret;
+
+ /* Find the SDCA node containing the GpioIo */
+ af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01");
+ if (!af01_fwnode) {
+ dev_dbg(cs35l56->base.dev, "No AF01 node\n");
+ return -ENOENT;
+ }
+
+ ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev),
+ "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* The broken properties we can handle are a 4-element package (one GPIO) */
+ if (obj->package.count != 4) {
+ dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
+ obj->package.count);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* Add a GPIO mapping if it doesn't already have one */
+ if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) {
+ struct acpi_device *adev = to_acpi_device_node(af01_fwnode);
+
+ /*
+ * Can't use devm_acpi_dev_add_driver_gpios() because the
+ * mapping isn't being added to the node pointed to by
+ * ACPI_COMPANION().
+ */
+ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return dev_err_probe(cs35l56->base.dev, ret,
+ "Failed to add gpio mapping to AF01\n");
+ }
+
+ ret = devm_add_action_or_reset(cs35l56->base.dev,
+ cs35l56_acpi_dev_release_driver_gpios,
+ adev);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
+ }
+
+ desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL);
+ if (IS_ERR(desc)) {
+ fwnode_handle_put(af01_fwnode);
+ ret = PTR_ERR(desc);
+ return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
+ }
+
+ ret = gpiod_get_value_cansleep(desc);
+ gpiod_put(desc);
+
+ if (ret < 0) {
+ fwnode_handle_put(af01_fwnode);
+ dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n");
+ return ret;
+ }
+
+ fwnode_handle_put(af01_fwnode);
+
+ dev_info(cs35l56->base.dev, "Got spk-id from AF01\n");
+
+ return ret;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ init_completion(&cs35l56->init_completion);
+ mutex_init(&cs35l56->base.irq_lock);
+ cs35l56->base.cal_index = -1;
+ cs35l56->speaker_id = -ENOENT;
+
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
+
+ cs35l56_fill_supply_names(cs35l56->supplies);
+ ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies),
+ cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n");
+
+ /* Reset could be controlled by the BIOS or shared by multiple amps */
+ cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.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->base.dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n");
+
+ if (cs35l56->base.reset_gpio) {
+ /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ }
+
+ ret = cs35l56_get_speaker_id(&cs35l56->base);
+ if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT))
+ ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56);
+
+ if ((ret < 0) && (ret != -ENOENT))
+ goto err;
+
+ cs35l56->speaker_id = ret;
+
+ ret = cs35l56_get_firmware_uid(cs35l56);
+ if (ret != 0)
+ goto err;
+
+ ret = cs35l56_dsp_init(cs35l56);
+ if (ret < 0) {
+ dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_component(cs35l56->base.dev,
+ &soc_component_dev_cs35l56,
+ cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
+ if (ret < 0) {
+ dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l56->base.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;
+
+ /*
+ * 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->base.init_done)
+ return 0;
+
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
+
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_get_calibration(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ if (!cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
+ if (cs35l56->sdw_peripheral) {
+ /* Keep alive while we wait for re-enumeration */
+ pm_runtime_get_noresume(cs35l56->base.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->base.init_done)
+ pm_runtime_put_noidle(cs35l56->base.dev);
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n");
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ return ret;
+
+ /* Registers could be dirty after soft reset or SoundWire enumeration */
+ regcache_sync(cs35l56->base.regmap);
+
+ /* Set ASP1 DOUT to high-impedance when it is not transmitting audio data. */
+ ret = regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK);
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n");
+
+ cs35l56->base.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->base.init_done = false;
+
+ /*
+ * WAKE IRQs unmask if CS35L56 hibernates so free the handler to
+ * prevent it racing with remove().
+ */
+ if (cs35l56->base.irq)
+ devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base);
+
+ destroy_workqueue(cs35l56->dsp_wq);
+
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_suspend(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove, "SND_SOC_CS35L56_CORE");
+
+#if IS_ENABLED(CONFIG_SND_SOC_CS35L56_I2C) || IS_ENABLED(CONFIG_SND_SOC_CS35L56_SPI)
+EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = {
+ SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend_i2c_spi, 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)
+};
+#endif
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+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..40a1800a4585
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.h
@@ -0,0 +1,71 @@
+/* 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_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 cs35l56_base base;
+ struct work_struct dsp_work;
+ struct workqueue_struct *dsp_wq;
+ struct snd_soc_component *component;
+ struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
+ struct sdw_slave *sdw_peripheral;
+ const char *fallback_fw_suffix;
+ struct work_struct sdw_irq_work;
+ bool sdw_irq_no_unmask;
+ bool soft_resetting;
+ bool sdw_attached;
+ struct completion init_completion;
+
+ int speaker_id;
+ u32 rx_mask;
+ u32 tx_mask;
+ u8 asp_slot_width;
+ u8 asp_slot_count;
+ bool tdm_mode;
+ bool sysclk_set;
+ u8 sdw_link_num;
+ u8 sdw_unique_id;
+};
+
+extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+
+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_base *cs35l56_base, 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/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c
new file mode 100644
index 000000000000..aa629ef53db4
--- /dev/null
+++ b/sound/soc/codecs/cs40l50-codec.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS40L50 Advanced Haptic Driver with waveform memory,
+// integrated DSP, and closed-loop algorithms
+//
+// Copyright 2024 Cirrus Logic, Inc.
+//
+// Author: James Ogletree <james.ogletree@cirrus.com>
+
+#include <linux/bitfield.h>
+#include <linux/mfd/cs40l50.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define CS40L50_REFCLK_INPUT 0x2C04
+#define CS40L50_ASP_CONTROL2 0x4808
+#define CS40L50_ASP_DATA_CONTROL5 0x4840
+
+/* PLL Config */
+#define CS40L50_PLL_REFCLK_BCLK 0x0
+#define CS40L50_PLL_REFCLK_MCLK 0x5
+#define CS40L50_PLL_REEFCLK_MCLK_CFG 0x00
+#define CS40L50_PLL_REFCLK_LOOP_MASK BIT(11)
+#define CS40L50_PLL_REFCLK_OPEN_LOOP 1
+#define CS40L50_PLL_REFCLK_CLOSED_LOOP 0
+#define CS40L50_PLL_REFCLK_LOOP_SHIFT 11
+#define CS40L50_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS40L50_PLL_REFCLK_FREQ_SHIFT 5
+#define CS40L50_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+#define CS40L50_BCLK_RATIO_DEFAULT 32
+
+/* ASP Config */
+#define CS40L50_ASP_RX_WIDTH_SHIFT 24
+#define CS40L50_ASP_RX_WIDTH_MASK GENMASK(31, 24)
+#define CS40L50_ASP_RX_WL_MASK GENMASK(5, 0)
+#define CS40L50_ASP_FSYNC_INV_MASK BIT(2)
+#define CS40L50_ASP_BCLK_INV_MASK BIT(6)
+#define CS40L50_ASP_FMT_MASK GENMASK(10, 8)
+#define CS40L50_ASP_FMT_I2S 0x2
+
+struct cs40l50_pll_config {
+ unsigned int freq;
+ unsigned int cfg;
+};
+
+struct cs40l50_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int daifmt;
+ unsigned int bclk_ratio;
+ unsigned int rate;
+};
+
+static const struct cs40l50_pll_config cs40l50_pll_cfg[] = {
+ { 32768, 0x00 },
+ { 1536000, 0x1B },
+ { 3072000, 0x21 },
+ { 6144000, 0x28 },
+ { 9600000, 0x30 },
+ { 12288000, 0x33 },
+};
+
+static int cs40l50_get_clk_config(const unsigned int freq, unsigned int *cfg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs40l50_pll_cfg); i++) {
+ if (cs40l50_pll_cfg[i].freq == freq) {
+ *cfg = cs40l50_pll_cfg[i].cfg;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int cs40l50_swap_ext_clk(struct cs40l50_codec *codec, const unsigned int clk_src)
+{
+ unsigned int cfg;
+ int ret;
+
+ switch (clk_src) {
+ case CS40L50_PLL_REFCLK_BCLK:
+ ret = cs40l50_get_clk_config(codec->bclk_ratio * codec->rate, &cfg);
+ if (ret)
+ return ret;
+ break;
+ case CS40L50_PLL_REFCLK_MCLK:
+ cfg = CS40L50_PLL_REEFCLK_MCLK_CFG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_OPEN_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_FREQ_MASK |
+ CS40L50_PLL_REFCLK_SEL_MASK,
+ (cfg << CS40L50_PLL_REFCLK_FREQ_SHIFT) | clk_src);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_CLOSED_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+}
+
+static int cs40l50_clk_en(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 cs40l50_codec *codec = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_STOP_PLAYBACK);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_START_I2S);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_BCLK);
+ if (ret)
+ return ret;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_MCLK);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs40l50_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("ASP PLL", 0, SND_SOC_NOPM, 0, 0, cs40l50_clk_en,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route cs40l50_dapm_routes[] = {
+ { "ASP Playback", NULL, "ASP PLL" },
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+
+ { "OUT", NULL, "ASPRX1" },
+ { "OUT", NULL, "ASPRX2" },
+};
+
+static int cs40l50_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(codec_dai->component);
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ codec->daifmt = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ codec->daifmt = CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK | CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codec->daifmt |= FIELD_PREP(CS40L50_ASP_FMT_MASK, CS40L50_ASP_FMT_I2S);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs40l50_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_rx_wl = params_width(params);
+ int ret;
+
+ codec->rate = params_rate(params);
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_ASP_DATA_CONTROL5,
+ CS40L50_ASP_RX_WL_MASK, asp_rx_wl);
+ if (ret)
+ return ret;
+
+ codec->daifmt |= (asp_rx_wl << CS40L50_ASP_RX_WIDTH_SHIFT);
+
+ return regmap_update_bits(codec->regmap, CS40L50_ASP_CONTROL2,
+ CS40L50_ASP_FSYNC_INV_MASK |
+ CS40L50_ASP_BCLK_INV_MASK |
+ CS40L50_ASP_FMT_MASK |
+ CS40L50_ASP_RX_WIDTH_MASK, codec->daifmt);
+}
+
+static int cs40l50_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+
+ codec->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs40l50_dai_ops = {
+ .set_fmt = cs40l50_set_dai_fmt,
+ .set_bclk_ratio = cs40l50_set_dai_bclk_ratio,
+ .hw_params = cs40l50_hw_params,
+};
+
+static struct snd_soc_dai_driver cs40l50_dai[] = {
+ {
+ .name = "cs40l50-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &cs40l50_dai_ops,
+ },
+};
+
+static int cs40l50_codec_probe(struct snd_soc_component *component)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(component);
+
+ codec->bclk_ratio = CS40L50_BCLK_RATIO_DEFAULT;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cs40l50 = {
+ .probe = cs40l50_codec_probe,
+ .dapm_widgets = cs40l50_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs40l50_dapm_widgets),
+ .dapm_routes = cs40l50_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs40l50_dapm_routes),
+};
+
+static int cs40l50_codec_driver_probe(struct platform_device *pdev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
+ struct cs40l50_codec *codec;
+
+ codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->regmap = cs40l50->regmap;
+ codec->dev = &pdev->dev;
+
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cs40l50,
+ cs40l50_dai, ARRAY_SIZE(cs40l50_dai));
+}
+
+static const struct platform_device_id cs40l50_id[] = {
+ { "cs40l50-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs40l50_id);
+
+static struct platform_driver cs40l50_codec_driver = {
+ .probe = cs40l50_codec_driver_probe,
+ .id_table = cs40l50_id,
+ .driver = {
+ .name = "cs40l50-codec",
+ },
+};
+module_platform_driver(cs40l50_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS40L50 driver");
+MODULE_AUTHOR("James Ogletree <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
new file mode 100644
index 000000000000..dda7f5b4f2fb
--- /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_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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_MAPLE,
+ .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 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 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 = {
+ 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 = pm_ptr(&cs4234_pm),
+ .of_match_table = cs4234_of_match,
+ },
+ .probe = 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
index fd966bb851cb..3f759c13d6d1 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * 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>
@@ -60,7 +56,7 @@ static const struct reg_default cs4265_reg_defaults[] = {
static bool cs4265_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS4265_CHIP_ID ... CS4265_SPDIF_CTL2:
+ case CS4265_CHIP_ID ... CS4265_MAX_REGISTER:
return true;
default:
return false;
@@ -157,8 +153,7 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = {
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", 0,
- 1, 1, 0),
+ 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),
};
@@ -190,7 +185,7 @@ static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
&loopback_ctl),
- SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1,
&spdif_switch),
SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
&dac_switch),
@@ -221,10 +216,11 @@ static const struct snd_soc_dapm_route cs4265_audio_map[] = {
{"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 Mux", "MIC", "MICL"},
- {"ADC Mux", "MIC", "MICR"},
{"ADC", NULL, "ADC Mux"},
{"DOUT", NULL, "ADC"},
{"DAI1 Capture", NULL, "DOUT"},
@@ -322,12 +318,12 @@ static int cs4265_get_clk_index(int mclk, int rate)
static int cs4265_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 cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid clk_id %d\n", clk_id);
+ dev_err(component->dev, "Invalid clk_id %d\n", clk_id);
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
@@ -337,24 +333,24 @@ static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
}
}
cs4265->sysclk = 0;
- dev_err(codec->dev, "Invalid freq parameter %d\n", freq);
+ 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_codec *codec = codec_dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
CS4265_ADC_MASTER);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
0);
break;
@@ -381,22 +377,22 @@ static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
-static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
CS4265_DAC_CTL_MUTE);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
CS4265_SPDIF_CTL2_MUTE);
} else {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
0);
}
@@ -407,8 +403,8 @@ 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_codec *codec = dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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 &&
@@ -418,45 +414,45 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
if (index >= 0) {
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
- snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
+ snd_soc_component_update_bits(component, CS4265_MCLK_FREQ,
CS4265_MCLK_FREQ_MASK,
clk_map_table[index].mclkdiv << 4);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ 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_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ 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_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (2 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (2 << 6));
} else {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (3 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (3 << 6));
}
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, 0);
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, 0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, 0);
break;
@@ -466,23 +462,23 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs4265_set_bias_level(struct snd_soc_codec *codec,
+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_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN, 0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
@@ -496,13 +492,15 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
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_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,
- .digital_mute = cs4265_digital_mute,
+ .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[] = {
@@ -544,17 +542,17 @@ static struct snd_soc_dai_driver cs4265_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_cs4265 = {
- .set_bias_level = cs4265_set_bias_level,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -566,14 +564,13 @@ static const struct regmap_config cs4265_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
.readable_reg = cs4265_readable_register,
.volatile_reg = cs4265_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs4265_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4265_private *cs4265;
- int ret = 0;
+ int ret;
unsigned int devid = 0;
unsigned int reg;
@@ -602,12 +599,17 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
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 Device ID (%X). Expected %X\n",
- devid, CS4265_CHIP_ID);
+ "CS4265 Part Number ID: 0x%x Expected: 0x%x\n",
+ devid >> 4, CS4265_CHIP_ID_VAL >> 4);
return ret;
}
dev_info(&i2c_client->dev,
@@ -616,16 +618,17 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_cs4265, cs4265_dai,
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_cs4265, cs4265_dai,
ARRAY_SIZE(cs4265_dai));
- return ret;
}
-static int cs4265_i2c_remove(struct i2c_client *client)
+static void cs4265_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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[] = {
@@ -635,7 +638,7 @@ static const struct of_device_id cs4265_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4265_of_match);
static const struct i2c_device_id cs4265_id[] = {
- { "cs4265", 0 },
+ { "cs4265" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4265_id);
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h
index 0a80a8dcec67..8bc28c2bf99e 100644
--- a/sound/soc/codecs/cs4265.h
+++ b/sound/soc/codecs/cs4265.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS4265_H__
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 84f86745c30e..9f9dc8b017a3 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -21,6 +21,7 @@
* - Power management is supported
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,21 +30,11 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.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 */
@@ -137,6 +128,9 @@ 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[] = {
@@ -219,7 +213,7 @@ 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 1;
+ return true;
return reg == CS4270_CHIPID;
}
@@ -254,8 +248,8 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
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;
@@ -277,8 +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);
+ 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) {
@@ -287,21 +281,21 @@ 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");
+ dev_err(component->dev, "invalid dai format\n");
return -EINVAL;
}
/* set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs4270->slave_mode = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs4270->slave_mode = 0;
break;
default:
/* all other modes are unsupported by the hardware */
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -326,8 +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_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 ret;
unsigned int i;
unsigned int rate;
@@ -346,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;
@@ -361,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) {
@@ -380,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;
}
@@ -397,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;
@@ -418,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);
}
/**
@@ -438,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_soc_kcontrol_codec(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];
@@ -468,7 +463,8 @@ 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 = {
@@ -496,14 +492,14 @@ 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);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int ret;
/* Disable auto-mute. This feature appears to be buggy. In some
@@ -511,9 +507,9 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
- ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
+ 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;
}
@@ -522,10 +518,10 @@ 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.
*/
- ret = snd_soc_update_bits(codec, CS4270_TRANS,
+ ret = snd_soc_component_update_bits(component, CS4270_TRANS,
CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -537,17 +533,15 @@ static int cs4270_probe(struct snd_soc_codec *codec)
/**
* 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);
-
- return 0;
};
#ifdef CONFIG_PM
@@ -561,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)
+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;
@@ -580,9 +574,9 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec)
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 cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
@@ -598,10 +592,10 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
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
@@ -611,20 +605,20 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
/*
* ASoC codec driver structure
*/
-static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
- .probe = cs4270_probe,
- .remove = cs4270_remove,
- .suspend = cs4270_soc_suspend,
- .resume = cs4270_soc_resume,
-
- .component_driver = {
- .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),
- },
+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,
};
/*
@@ -642,24 +636,36 @@ static const struct regmap_config cs4270_regmap = {
.max_register = CS4270_LASTREG,
.reg_defaults = cs4270_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .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 device_node *np = i2c_client->dev.of_node;
struct cs4270_private *cs4270;
unsigned int val;
int ret, i;
@@ -679,21 +685,22 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
if (ret < 0)
return ret;
- /* See if we have a way to bring the codec out of reset */
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&i2c_client->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "cs4270 reset");
- if (ret < 0)
- return ret;
- }
+ /* 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);
@@ -718,28 +725,16 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, cs4270);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_device_cs4270, &cs4270_dai, 1);
+ 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);
- return 0;
-}
-
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static const struct i2c_device_id cs4270_id[] = {
- {"cs4270", 0},
+ {"cs4270"},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index dcb3223d7d8f..1d210b969173 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 I2C audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -20,27 +11,19 @@
#include <sound/soc.h>
#include "cs4271.h"
-static int cs4271_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4271_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
config = cs4271_regmap_config;
config.reg_bits = 8;
- config.val_bits = 8;
return cs4271_probe(&client->dev,
devm_regmap_init_i2c(client, &config));
}
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id cs4271_i2c_id[] = {
- { "cs4271", 0 },
+ { "cs4271" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
@@ -51,7 +34,6 @@ static struct i2c_driver cs4271_i2c_driver = {
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_i2c_probe,
- .remove = cs4271_i2c_remove,
.id_table = cs4271_i2c_id,
};
module_i2c_driver(cs4271_i2c_driver);
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
index 1ff5f520196a..4feb80436bd9 100644
--- a/sound/soc/codecs/cs4271-spi.c
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 SPI audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -26,26 +17,18 @@ static int cs4271_spi_probe(struct spi_device *spi)
config = cs4271_regmap_config;
config.reg_bits = 16;
- config.val_bits = 8;
config.read_flag_mask = 0x21;
config.write_flag_mask = 0x20;
return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int cs4271_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
- .remove = cs4271_spi_remove,
};
module_spi_driver(cs4271_spi_driver);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 49a80627af12..6a3cca3d26c7 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* 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.
@@ -22,10 +13,8 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.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>
@@ -170,9 +159,7 @@ struct cs4271_private {
/* 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;
+ struct gpio_desc *reset;
/* enable soft reset workaround */
bool enable_soft_reset;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
@@ -206,8 +193,8 @@ static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
static int cs4271_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 cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
cs4271->mclk = freq;
return 0;
@@ -216,21 +203,21 @@ static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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 = 0;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs4271->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- cs4271->master = 1;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs4271->master = true;
val |= CS4271_MODE1_MASTER;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -250,7 +237,7 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
return ret;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -263,9 +250,9 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int cs4271_deemph[] = {0, 44100, 48000, 32000};
-static int cs4271_set_deemph(struct snd_soc_codec *codec)
+static int cs4271_set_deemph(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
int val = CS4271_DACCTL_DEM_DIS;
@@ -289,8 +276,8 @@ static int cs4271_set_deemph(struct snd_soc_codec *codec)
static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -299,11 +286,11 @@ static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ return cs4271_set_deemph(component);
}
struct cs4271_clk_cfg {
@@ -343,14 +330,14 @@ static struct cs4271_clk_cfg cs4271_clk_tab[] = {
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
};
-#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+#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_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
unsigned int ratio, val;
@@ -365,9 +352,9 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
*/
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- !dai->capture_active) ||
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) ||
(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- !dai->playback_active)) {
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK))) {
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN,
CS4271_MODE2_PDN);
@@ -392,14 +379,14 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
val = CS4271_MODE1_MODE_4X;
ratio = cs4271->mclk / cs4271->rate;
- for (i = 0; i < CS4171_NR_RATIOS; i++)
+ 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 == CS4171_NR_RATIOS) {
- dev_err(codec->dev, "Invalid sample rate\n");
+ if (i == CS4271_NR_RATIOS) {
+ dev_err(component->dev, "Invalid sample rate\n");
return -EINVAL;
}
@@ -410,13 +397,13 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return cs4271_set_deemph(codec);
+ return cs4271_set_deemph(component);
}
static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -490,28 +477,26 @@ static struct snd_soc_dai_driver cs4271_dai = {
.formats = CS4271_PCM_FORMATS,
},
.ops = &cs4271_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int cs4271_reset(struct snd_soc_codec *codec)
+static int cs4271_reset(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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);
- }
+ gpiod_direction_output(cs4271->reset, 1);
+ mdelay(1);
+ gpiod_set_value(cs4271->reset, 0);
+ mdelay(1);
return 0;
}
#ifdef CONFIG_PM
-static int cs4271_soc_suspend(struct snd_soc_codec *codec)
+static int cs4271_soc_suspend(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
/* Set power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
@@ -525,20 +510,20 @@ static int cs4271_soc_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int cs4271_soc_resume(struct snd_soc_codec *codec)
+static int cs4271_soc_resume(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
/* Do a proper reset after power up */
- cs4271_reset(codec);
+ cs4271_reset(component);
/* Restore codec state */
ret = regcache_sync(cs4271->regmap);
@@ -567,29 +552,22 @@ MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
EXPORT_SYMBOL_GPL(cs4271_dt_ids);
#endif
-static int cs4271_codec_probe(struct snd_soc_codec *codec)
+static int cs4271_component_probe(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+ 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;
+ bool amutec_eq_bmutec;
-#ifdef CONFIG_OF
- if (of_match_device(cs4271_dt_ids, codec->dev)) {
- if (of_get_property(codec->dev->of_node,
- "cirrus,amutec-eq-bmutec", NULL))
- amutec_eq_bmutec = true;
-
- if (of_get_property(codec->dev->of_node,
- "cirrus,enable-soft-reset", NULL))
- cs4271->enable_soft_reset = true;
- }
-#endif
+ amutec_eq_bmutec = of_property_read_bool(component->dev->of_node,
+ "cirrus,amutec-eq-bmutec");
+ cs4271->enable_soft_reset = of_property_read_bool(component->dev->of_node,
+ "cirrus,enable-soft-reset");
ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
cs4271->supplies);
if (ret < 0) {
- dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
@@ -599,7 +577,7 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
}
/* Reset codec */
- cs4271_reset(codec);
+ cs4271_reset(component);
ret = regcache_sync(cs4271->regmap);
if (ret < 0)
@@ -625,40 +603,36 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs4271_codec_remove(struct snd_soc_codec *codec)
+static void cs4271_component_remove(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ /* Set codec to the reset state */
+ gpiod_set_value(cs4271->reset, 1);
regcache_mark_dirty(cs4271->regmap);
regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
-
- return 0;
};
-static const struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
- .probe = cs4271_codec_probe,
- .remove = cs4271_codec_remove,
+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,
-
- .component_driver = {
- .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),
- },
+ .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;
@@ -666,19 +640,11 @@ static int cs4271_common_probe(struct device *dev,
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;
- }
+ cs4271->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(cs4271->reset))
+ return dev_err_probe(dev, PTR_ERR(cs4271->reset),
+ "error retrieving RESET GPIO\n");
+ gpiod_set_consumer_name(cs4271->reset, "CS4271 Reset");
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
cs4271->supplies[i].supply = supply_names[i];
@@ -700,8 +666,8 @@ const struct regmap_config cs4271_regmap_config = {
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
+ .cache_type = REGCACHE_FLAT,
+ .val_bits = 8,
.volatile_reg = cs4271_volatile_reg,
};
EXPORT_SYMBOL_GPL(cs4271_regmap_config);
@@ -721,8 +687,8 @@ int cs4271_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, cs4271);
cs4271->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
- 1);
+ return devm_snd_soc_register_component(dev, &soc_component_dev_cs4271,
+ &cs4271_dai, 1);
}
EXPORT_SYMBOL_GPL(cs4271_probe);
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
new file mode 100644
index 000000000000..98b6718ccabf
--- /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 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 = {
+ 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"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l42_id);
+
+static struct i2c_driver cs42l42_i2c_driver = {
+ .driver = {
+ .name = "cs42l42",
+ .pm = pm_ptr(&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 = 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..f837c7eff10b
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -0,0 +1,625 @@
+// 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/gpio/consumer.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");
+
+ /*
+ * The SoundWire core can report stale ATTACH notifications
+ * if we hard-reset CS42L42 in probe() but it had already been
+ * enumerated. Reject the ATTACH if we haven't yet seen an
+ * UNATTACH report for the device being in reset.
+ */
+ if (cs42l42->sdw_waiting_first_unattach)
+ break;
+
+ /*
+ * 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");
+
+ if (cs42l42->sdw_waiting_first_unattach) {
+ /*
+ * SoundWire core has seen that CS42L42 is not on
+ * the bus so release RESET and wait for ATTACH.
+ */
+ cs42l42->sdw_waiting_first_unattach = false;
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ }
+
+ 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 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 cs42l42_soft_reboot_seq[] = {
+ REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e),
+};
+
+static int 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume)
+ 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 = pm_ptr(&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
index a2324a0e72ee..78bb093fa0cc 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l42.c -- CS42L42 ALSA SoC audio driver
*
@@ -6,29 +7,21 @@
* Author: James Schulman <james.schulman@cirrus.com>
* Author: Brian Austin <brian.austin@cirrus.com>
* Author: Michael White <michael.white@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
+#include <linux/types.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/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.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -39,13 +32,22 @@
#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_STATUS, 0x02 },
{ 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 },
@@ -55,15 +57,12 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_RSENSE_CTL1, 0x40 },
{ CS42L42_RSENSE_CTL2, 0x00 },
{ CS42L42_OSC_SWITCH, 0x00 },
- { CS42L42_OSC_SWITCH_STATUS, 0x05 },
{ CS42L42_RSENSE_CTL3, 0x1B },
{ CS42L42_TSENSE_CTL, 0x1B },
{ CS42L42_TSRS_INT_DISABLE, 0x00 },
- { CS42L42_TRSENSE_STATUS, 0x00 },
{ CS42L42_HSDET_CTL1, 0x77 },
{ CS42L42_HSDET_CTL2, 0x00 },
{ CS42L42_HS_SWITCH_CTL, 0xF3 },
- { CS42L42_HS_DET_STATUS, 0x00 },
{ CS42L42_HS_CLAMP_DISABLE, 0x00 },
{ CS42L42_MCLK_SRC_SEL, 0x00 },
{ CS42L42_SPDIF_CLK_CFG, 0x00 },
@@ -77,25 +76,13 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_IN_ASRC_CLK, 0x00 },
{ CS42L42_OUT_ASRC_CLK, 0x00 },
{ CS42L42_PLL_DIV_CFG1, 0x00 },
- { CS42L42_ADC_OVFL_STATUS, 0x00 },
- { CS42L42_MIXER_STATUS, 0x00 },
- { CS42L42_SRC_STATUS, 0x00 },
- { CS42L42_ASP_RX_STATUS, 0x00 },
- { CS42L42_ASP_TX_STATUS, 0x00 },
- { CS42L42_CODEC_STATUS, 0x00 },
- { CS42L42_DET_INT_STATUS1, 0x00 },
- { CS42L42_DET_INT_STATUS2, 0x00 },
- { CS42L42_SRCPL_INT_STATUS, 0x00 },
- { CS42L42_VPMON_STATUS, 0x00 },
- { CS42L42_PLL_LOCK_STATUS, 0x00 },
- { CS42L42_TSRS_PLUG_STATUS, 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, 0xFF },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
{ CS42L42_VPMON_INT_MASK, 0x01 },
{ CS42L42_PLL_LOCK_INT_MASK, 0x01 },
{ CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
@@ -107,8 +94,6 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_PLL_CTL3, 0x10 },
{ CS42L42_PLL_CAL_RATIO, 0x80 },
{ CS42L42_PLL_CTL4, 0x03 },
- { CS42L42_LOAD_DET_RCSTAT, 0x00 },
- { CS42L42_LOAD_DET_DONE, 0x00 },
{ CS42L42_LOAD_DET_EN, 0x00 },
{ CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
{ CS42L42_WAKE_CTL, 0xC0 },
@@ -117,8 +102,6 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_MISC_DET_CTL, 0x03 },
{ CS42L42_MIC_DET_CTL1, 0x1F },
{ CS42L42_MIC_DET_CTL2, 0x2F },
- { CS42L42_DET_STATUS1, 0x00 },
- { CS42L42_DET_STATUS2, 0x00 },
{ CS42L42_DET_INT1_MASK, 0xE0 },
{ CS42L42_DET_INT2_MASK, 0xFF },
{ CS42L42_HS_BIAS_CTL, 0xC2 },
@@ -132,7 +115,7 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_MIXER_CHA_VOL, 0x3F },
{ CS42L42_MIXER_ADC_VOL, 0x3F },
{ CS42L42_MIXER_CHB_VOL, 0x3F },
- { CS42L42_EQ_COEF_IN0, 0x22 },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
{ CS42L42_EQ_COEF_IN1, 0x00 },
{ CS42L42_EQ_COEF_IN2, 0x00 },
{ CS42L42_EQ_COEF_IN3, 0x00 },
@@ -184,10 +167,9 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
{ CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
{ CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
- { CS42L42_SUB_REVID, 0x03 },
};
-static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
+bool cs42l42_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L42_PAGE_REGISTER:
@@ -201,6 +183,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
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:
@@ -308,6 +291,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
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:
@@ -345,14 +329,16 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
return false;
}
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_readable_register, "SND_SOC_CS42L42_CORE");
-static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
+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:
@@ -371,13 +357,15 @@ static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
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");
-static const struct regmap_range_cfg cs42l42_page_range = {
+const struct regmap_range_cfg cs42l42_page_range = {
.name = "Pages",
.range_min = 0,
.range_max = CS42L42_MAX_REGISTER,
@@ -387,8 +375,9 @@ static const struct regmap_range_cfg cs42l42_page_range = {
.window_start = 0,
.window_len = 256,
};
+EXPORT_SYMBOL_NS_GPL(cs42l42_page_range, "SND_SOC_CS42L42_CORE");
-static const struct regmap_config cs42l42_regmap = {
+const struct regmap_config cs42l42_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -401,11 +390,37 @@ static const struct regmap_config cs42l42_regmap = {
.max_register = CS42L42_MAX_REGISTER,
.reg_defaults = cs42l42_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .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, -9600, 100, false);
-static DECLARE_TLV_DB_SCALE(mixer_tlv, -6200, 100, false);
+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"
@@ -424,34 +439,23 @@ 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 char * const cs42l42_wnf05_freq_text[] = {
- "280Hz", "315Hz", "350Hz", "385Hz",
- "420Hz", "455Hz", "490Hz", "525Hz"
-};
-
-static SOC_ENUM_SINGLE_DECL(cs42l42_wnf05_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
- CS42L42_ADC_WNF_CF_SHIFT,
- cs42l42_wnf05_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, false),
+ 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_SX_TLV("ADC Volume", CS42L42_ADC_VOLUME,
- CS42L42_ADC_VOL_SHIFT, 0xA0, 0x6C, adc_tlv),
+ 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),
- SOC_ENUM("WNF 05dB Freq", cs42l42_wnf05_freq_enum),
/* DAC Volume and Filter Controls */
SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1,
@@ -462,125 +466,158 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
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,
- 0x3e, 1, mixer_tlv)
+ 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_hpdrv_evt(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- if (event & SND_SOC_DAPM_POST_PMU) {
- /* Enable the channels */
- snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN,
- CS42L42_ASP_RX0_CH_EN_MASK,
- (CS42L42_ASP_RX0_CH1_EN |
- CS42L42_ASP_RX0_CH2_EN) <<
- CS42L42_ASP_RX0_CH_EN_SHIFT);
-
- /* Power up */
- snd_soc_update_bits(codec, CS42L42_PWR_CTL1,
- CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
- CS42L42_HP_PDN_MASK, 0);
- } else if (event & SND_SOC_DAPM_PRE_PMD) {
- /* Disable the channels */
- snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN,
- CS42L42_ASP_RX0_CH_EN_MASK, 0);
-
- /* Power down */
- snd_soc_update_bits(codec, CS42L42_PWR_CTL1,
- CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
- CS42L42_HP_PDN_MASK,
- CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
- CS42L42_HP_PDN_MASK);
- } else {
- dev_err(codec->dev, "Invalid event 0x%x\n", 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_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG,
- CS42L42_ASP_SCLK_EN_SHIFT, false),
- SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0,
- 0, NULL, 0, cs42l42_hpdrv_evt,
- SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD)
+ 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[] = {
- {"SDIN", NULL, "Playback"},
- {"HPDRV", NULL, "SDIN"},
- {"HP", NULL, "HPDRV"}
+ /* 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_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
+static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
{
- struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
- int ret;
+ struct cs42l42_private *cs42l42 = 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- regcache_cache_only(cs42l42->regmap, false);
- regcache_sync(cs42l42->regmap);
- ret = regulator_bulk_enable(
- ARRAY_SIZE(cs42l42->supplies),
- cs42l42->supplies);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to enable regulators: %d\n",
- ret);
- return ret;
- }
- }
- break;
- case SND_SOC_BIAS_OFF:
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l42->irq_lock);
+ cs42l42->jack = jk;
- regcache_cache_only(cs42l42->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
- cs42l42->supplies);
- break;
+ 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;
}
-static int cs42l42_codec_probe(struct snd_soc_codec *codec)
-{
- struct cs42l42_private *cs42l42 =
- (struct cs42l42_private *)snd_soc_codec_get_drvdata(codec);
-
- cs42l42->codec = codec;
-
- return 0;
-}
-
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l42 = {
- .probe = cs42l42_codec_probe,
- .set_bias_level = cs42l42_set_bias_level,
- .ignore_pmdown_time = true,
-
- .component_driver = {
- .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),
+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,
+ },
+};
- .controls = cs42l42_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l42_snd_controls),
+/* 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_div;
u8 mclk_src_sel;
u8 sclk_prediv;
u8 pll_div_int;
@@ -589,6 +626,7 @@ struct cs42l42_pll_params {
u8 pll_divout;
u32 mclk_int;
u8 pll_cal_ratio;
+ u8 n;
};
/*
@@ -596,171 +634,109 @@ struct cs42l42_pll_params {
* Table 4-5 from the Datasheet
*/
static const struct cs42l42_pll_params pll_ratio_table[] = {
- { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125 },
- { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
- { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
- { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
- { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96 },
- { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94 },
- { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
- { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
- { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
- { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0 },
- { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0 },
- { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0 },
- { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0 },
- { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0 },
- { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0 }
+ { 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},
+ { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 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},
+ { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1},
+ { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 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},
+ { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 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}
};
-static int cs42l42_pll_config(struct snd_soc_codec *codec)
+int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk,
+ unsigned int sample_rate)
{
- struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
int i;
- u32 fsync;
+
+ /* 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++) {
- if (pll_ratio_table[i].sclk == cs42l42->sclk) {
+ /* 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_update_bits(codec, CS42L42_MCLK_CTL,
+ 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);
- /* Set the MCLK src (PLL or SCLK) and the divide
- * ratio
- */
- snd_soc_update_bits(codec, CS42L42_MCLK_SRC_SEL,
- CS42L42_MCLK_SRC_SEL_MASK |
- CS42L42_MCLKDIV_MASK,
- (pll_ratio_table[i].mclk_src_sel
- << CS42L42_MCLK_SRC_SEL_SHIFT) |
- (pll_ratio_table[i].mclk_div <<
- CS42L42_MCLKDIV_SHIFT));
- /* Set up the LRCLK */
- fsync = cs42l42->sclk / cs42l42->srate;
- if (((fsync * cs42l42->srate) != cs42l42->sclk)
- || ((fsync % 2) != 0)) {
- dev_err(codec->dev,
- "Unsupported sclk %d/sample rate %d\n",
- cs42l42->sclk,
- cs42l42->srate);
- return -EINVAL;
- }
- /* Set the LRCLK period */
- snd_soc_update_bits(codec,
- CS42L42_FSYNC_P_LOWER,
- CS42L42_FSYNC_PERIOD_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PERIOD_SHIFT);
- snd_soc_update_bits(codec,
- 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_update_bits(codec,
- CS42L42_FSYNC_PW_LOWER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
- snd_soc_update_bits(codec,
- CS42L42_FSYNC_PW_UPPER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC1_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
- snd_soc_update_bits(codec,
- CS42L42_ASP_FRM_CFG,
- CS42L42_ASP_5050_MASK,
- CS42L42_ASP_5050_MASK);
- /* Set the frame delay to 1.0 SCLK clocks */
- snd_soc_update_bits(codec, CS42L42_ASP_FRM_CFG,
- CS42L42_ASP_FSD_MASK,
- CS42L42_ASP_FSD_1_0 <<
- CS42L42_ASP_FSD_SHIFT);
- /* Set the sample rates (96k or lower) */
- snd_soc_update_bits(codec, CS42L42_FS_RATE_EN,
- CS42L42_FS_EN_MASK,
- (CS42L42_FS_EN_IASRC_96K |
- CS42L42_FS_EN_OASRC_96K) <<
- CS42L42_FS_EN_SHIFT);
- /* Set the input/output internal MCLK clock ~12 MHz */
- snd_soc_update_bits(codec, CS42L42_IN_ASRC_CLK,
- CS42L42_CLK_IASRC_SEL_MASK,
- CS42L42_CLK_IASRC_SEL_12 <<
- CS42L42_CLK_IASRC_SEL_SHIFT);
- snd_soc_update_bits(codec,
- CS42L42_OUT_ASRC_CLK,
- CS42L42_CLK_OASRC_SEL_MASK,
- CS42L42_CLK_OASRC_SEL_12 <<
- CS42L42_CLK_OASRC_SEL_SHIFT);
- /* channel 1 on low LRCLK, 32 bit */
- snd_soc_update_bits(codec,
- CS42L42_ASP_RX_DAI0_CH1_AP_RES,
- CS42L42_ASP_RX_CH_AP_MASK |
- CS42L42_ASP_RX_CH_RES_MASK,
- (CS42L42_ASP_RX_CH_AP_LOW <<
- CS42L42_ASP_RX_CH_AP_SHIFT) |
- (CS42L42_ASP_RX_CH_RES_32 <<
- CS42L42_ASP_RX_CH_RES_SHIFT));
- /* Channel 2 on high LRCLK, 32 bit */
- snd_soc_update_bits(codec,
- CS42L42_ASP_RX_DAI0_CH2_AP_RES,
- CS42L42_ASP_RX_CH_AP_MASK |
- CS42L42_ASP_RX_CH_RES_MASK,
- (CS42L42_ASP_RX_CH_AP_HI <<
- CS42L42_ASP_RX_CH_AP_SHIFT) |
- (CS42L42_ASP_RX_CH_RES_32 <<
- CS42L42_ASP_RX_CH_RES_SHIFT));
if (pll_ratio_table[i].mclk_src_sel == 0) {
/* Pass the clock straight through */
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
CS42L42_PLL_CTL1,
CS42L42_PLL_START_MASK, 0);
} else {
/* Configure PLL per table 4-5 */
- snd_soc_update_bits(codec,
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ snd_soc_component_update_bits(component,
CS42L42_PLL_CTL3,
CS42L42_PLL_DIVOUT_MASK,
- pll_ratio_table[i].pll_divout
+ (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n)
<< CS42L42_PLL_DIVOUT_SHIFT);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
CS42L42_PLL_CAL_RATIO,
CS42L42_PLL_CAL_RATIO_MASK,
pll_ratio_table[i].pll_cal_ratio
@@ -772,18 +748,93 @@ static int cs42l42_pll_config(struct snd_soc_codec *codec)
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_codec *codec = codec_dai->codec;
+ 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:
+ case SND_SOC_DAIFMT_CBC_CFP:
asp_cfg_val |= CS42L42_ASP_MASTER_MODE <<
CS42L42_ASP_MODE_SHIFT;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
asp_cfg_val |= CS42L42_ASP_SLAVE_MODE <<
CS42L42_ASP_MODE_SHIFT;
break;
@@ -794,7 +845,18 @@ static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_LEFT_J:
+ /*
+ * 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;
@@ -803,145 +865,406 @@ static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* 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_POL_INV <<
- CS42L42_ASP_LCPOL_IN_SHIFT;
+ 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:
- asp_cfg_val |= CS42L42_ASP_POL_INV <<
- CS42L42_ASP_SCPOL_IN_DAC_SHIFT;
break;
case SND_SOC_DAIFMT_IB_IF:
- asp_cfg_val |= CS42L42_ASP_POL_INV <<
- CS42L42_ASP_LCPOL_IN_SHIFT;
- asp_cfg_val |= CS42L42_ASP_POL_INV <<
- CS42L42_ASP_SCPOL_IN_DAC_SHIFT;
+ asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT;
break;
}
- snd_soc_update_bits(codec, CS42L42_ASP_CLK_CFG,
- CS42L42_ASP_MODE_MASK |
- CS42L42_ASP_SCPOL_IN_DAC_MASK |
- CS42L42_ASP_LCPOL_IN_MASK, asp_cfg_val);
+ 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_codec *codec = dai->codec;
- struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
- int retval;
+ 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;
- cs42l42->srate = params_rate(params);
- cs42l42->swidth = params_width(params);
+ ret = cs42l42_asp_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
- retval = cs42l42_pll_config(codec);
+ cs42l42_src_config(component, sample_rate);
- return retval;
+ return 0;
}
static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ int i;
- cs42l42->sclk = freq;
+ 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;
}
-static int cs42l42_digital_mute(struct snd_soc_dai *dai, int mute)
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int regval;
- u8 fullScaleVol;
+ int ret;
if (mute) {
- /* Mark SCLK as not present to turn on the internal
- * oscillator.
- */
- snd_soc_update_bits(codec, CS42L42_OSC_SWITCH,
- CS42L42_SCLK_PRESENT_MASK, 0);
-
- snd_soc_update_bits(codec, CS42L42_PLL_CTL1,
- CS42L42_PLL_START_MASK,
- 0 << CS42L42_PLL_START_SHIFT);
-
/* Mute the headphone */
- snd_soc_update_bits(codec, CS42L42_HP_CTL,
- CS42L42_HP_ANA_AMUTE_MASK |
- CS42L42_HP_ANA_BMUTE_MASK,
- CS42L42_HP_ANA_AMUTE_MASK |
- CS42L42_HP_ANA_BMUTE_MASK);
- } else {
- snd_soc_update_bits(codec, CS42L42_PLL_CTL1,
- CS42L42_PLL_START_MASK,
- 1 << CS42L42_PLL_START_SHIFT);
- /* Read the headphone load */
- regval = snd_soc_read(codec, CS42L42_LOAD_DET_RCSTAT);
- if (((regval & CS42L42_RLA_STAT_MASK) >>
- CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) {
- fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK;
- } else {
- fullScaleVol = 0;
+ 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);
+ }
- /* Un-mute the headphone, set the full scale volume flag */
- snd_soc_update_bits(codec, CS42L42_HP_CTL,
- CS42L42_HP_ANA_AMUTE_MASK |
- CS42L42_HP_ANA_BMUTE_MASK |
- CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol);
-
- /* Mark SCLK as present, turn off internal oscillator */
- snd_soc_update_bits(codec, CS42L42_OSC_SWITCH,
- CS42L42_SCLK_PRESENT_MASK,
- CS42L42_SCLK_PRESENT_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_S18_3LE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
-
+#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,
- .digital_mute = cs42l42_digital_mute
+ .set_bclk_ratio = cs42l42_set_bclk_ratio,
+ .mute_stream = cs42l42_mute_stream,
};
-static struct snd_soc_dai_driver cs42l42_dai = {
+struct snd_soc_dai_driver cs42l42_dai = {
.name = "cs42l42",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L42_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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,
@@ -950,6 +1273,10 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
(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,
@@ -962,11 +1289,15 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
(0 << CS42L42_HSBIAS_REF_SHIFT) |
(3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- /* Read and save the hs detection result */
- regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
-
- cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
- CS42L42_HSDET_TYPE_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) ||
@@ -1001,7 +1332,7 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
CS42L42_AUTO_HSBIAS_HIZ_MASK |
CS42L42_TIP_SENSE_EN_MASK |
CS42L42_HSBIAS_SENSE_TRIP_MASK,
- (1 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (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));
@@ -1009,10 +1340,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Turn on level detect circuitry */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(3 << CS42L42_HSBIAS_CTL_SHIFT) |
(0 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1039,10 +1368,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Make sure button detect and HS bias circuits are off */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
}
@@ -1063,12 +1390,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Unmask tip sense interrupts */
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));
}
@@ -1078,22 +1401,16 @@ 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_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) |
(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_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1137,10 +1454,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
/* Power up HS bias to 2.7V */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(3 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1187,10 +1502,8 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
/* Ground HS bias */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1219,7 +1532,7 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
(3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
}
-static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
+static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
{
int bias_level;
unsigned int detect_status;
@@ -1262,16 +1575,23 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
switch (bias_level) {
case 1: /* Function C button press */
- dev_dbg(cs42l42->codec->dev, "Function C button press\n");
+ bias_level = SND_JACK_BTN_2;
+ dev_dbg(cs42l42->dev, "Function C button press\n");
break;
case 2: /* Function B button press */
- dev_dbg(cs42l42->codec->dev, "Function B button press\n");
+ bias_level = SND_JACK_BTN_1;
+ dev_dbg(cs42l42->dev, "Function B button press\n");
break;
case 3: /* Function D button press */
- dev_dbg(cs42l42->codec->dev, "Function D button press\n");
+ bias_level = SND_JACK_BTN_3;
+ dev_dbg(cs42l42->dev, "Function D button press\n");
break;
case 4: /* Function A button press */
- dev_dbg(cs42l42->codec->dev, "Function A button press\n");
+ bias_level = SND_JACK_BTN_0;
+ dev_dbg(cs42l42->dev, "Function A button press\n");
+ break;
+ default:
+ bias_level = 0;
break;
}
@@ -1302,6 +1622,8 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
(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 {
@@ -1337,16 +1659,23 @@ static const struct cs42l42_irq_params irq_params_table[] = {
CS42L42_TSRS_PLUG_VAL_MASK}
};
-static irqreturn_t cs42l42_irq_thread(int irq, void *data)
+irqreturn_t cs42l42_irq_thread(int irq, void *data)
{
struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
- struct snd_soc_codec *codec = cs42l42->codec;
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,
@@ -1368,13 +1697,32 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
CS42L42_M_DETECT_FT_MASK |
CS42L42_M_HSBIAS_HIZ_MASK);
- /* Check auto-detect status */
+ /*
+ * 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);
- dev_dbg(codec->dev,
- "Auto detect done (%d)\n",
- cs42l42->hs_type);
+ 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);
}
}
@@ -1392,35 +1740,46 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
cs42l42->plug_state = CS42L42_TS_UNPLUG;
cs42l42_cancel_hs_type_detect(cs42l42);
- dev_dbg(codec->dev,
- "Unplug event\n");
+
+ 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:
- if (cs42l42->plug_state != CS42L42_TS_TRANS)
- cs42l42->plug_state = CS42L42_TS_TRANS;
+ cs42l42->plug_state = CS42L42_TS_TRANS;
}
}
/* Check button detect status */
- if ((~masks[7]) & irq_params_table[7].mask) {
+ 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(codec->dev,
- "Button released\n");
- } else if (current_button_status &
- CS42L42_M_DETECT_FT_MASK) {
- cs42l42_handle_button_press(cs42l42);
+ 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_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)
{
@@ -1531,6 +1890,13 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
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 |
@@ -1548,12 +1914,15 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
(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) |
- (0 << CS42L42_TIP_SENSE_INV_SHIFT) |
+ (!cs42l42->ts_inv << CS42L42_TIP_SENSE_INV_SHIFT) |
(2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT));
/* Save the initial status of the tip sense */
@@ -1572,17 +1941,15 @@ static const unsigned int threshold_defaults[] = {
CS42L42_HS_DET_LEVEL_1
};
-static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
+static int cs42l42_handle_device_data(struct device *dev,
struct cs42l42_private *cs42l42)
{
- struct device_node *np = i2c_client->dev.of_node;
unsigned int val;
- unsigned int thresholds[CS42L42_NUM_BIASES];
+ u32 thresholds[CS42L42_NUM_BIASES];
int ret;
int i;
- ret = of_property_read_u32(np, "cirrus,ts-inv", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-inv", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_INV_EN:
@@ -1590,7 +1957,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_inv = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-inv DT value %d\n",
val);
cs42l42->ts_inv = CS42L42_TS_INV_DIS;
@@ -1599,12 +1966,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_inv = CS42L42_TS_INV_DIS;
}
- regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
- CS42L42_TS_INV_MASK,
- (cs42l42->ts_inv << CS42L42_TS_INV_SHIFT));
-
- ret = of_property_read_u32(np, "cirrus,ts-dbnc-rise", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_DBNCE_0:
@@ -1618,7 +1980,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_dbnc_rise = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-dbnc-rise DT value %d\n",
val);
cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
@@ -1632,8 +1994,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(cs42l42->ts_dbnc_rise <<
CS42L42_TS_RISE_DBNCE_TIME_SHIFT));
- ret = of_property_read_u32(np, "cirrus,ts-dbnc-fall", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-fall", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_DBNCE_0:
@@ -1647,7 +2008,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_dbnc_fall = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-dbnc-fall DT value %d\n",
val);
cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
@@ -1661,14 +2022,12 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(cs42l42->ts_dbnc_fall <<
CS42L42_TS_FALL_DBNCE_TIME_SHIFT));
- ret = of_property_read_u32(np, "cirrus,btn-det-init-dbnce", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,btn-det-init-dbnce", &val);
if (!ret) {
- if ((val >= CS42L42_BTN_DET_INIT_DBNCE_MIN) &&
- (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX))
+ if (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX)
cs42l42->btn_det_init_dbnce = val;
else {
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,btn-det-init-dbnce DT value %d\n",
val);
cs42l42->btn_det_init_dbnce =
@@ -1679,15 +2038,13 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
}
- ret = of_property_read_u32(np, "cirrus,btn-det-event-dbnce", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,btn-det-event-dbnce", &val);
if (!ret) {
- if ((val >= CS42L42_BTN_DET_EVENT_DBNCE_MIN) &&
- (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX))
+ if (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX)
cs42l42->btn_det_event_dbnce = val;
else {
- dev_err(&i2c_client->dev,
- "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
+ 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;
}
@@ -1696,20 +2053,17 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
}
- ret = of_property_read_u32_array(np, "cirrus,bias-lvls",
- (u32 *)thresholds, CS42L42_NUM_BIASES);
-
+ 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_MIN) &&
- (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX))
+ if (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX)
cs42l42->bias_thresholds[i] = thresholds[i];
else {
- dev_err(&i2c_client->dev,
- "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
+ dev_err(dev,
+ "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
thresholds[i]);
- cs42l42->bias_thresholds[i] =
- threshold_defaults[i];
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
}
}
} else {
@@ -1717,8 +2071,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->bias_thresholds[i] = threshold_defaults[i];
}
- ret = of_property_read_u32(np, "cirrus,hs-bias-ramp-rate", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,hs-bias-ramp-rate", &val);
if (!ret) {
switch (val) {
case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL:
@@ -1738,7 +2091,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,hs-bias-ramp-rate DT value %d\n",
val);
cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
@@ -1754,39 +2107,192 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(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;
}
-static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+/* 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;
- int ret, i;
- unsigned int devid = 0;
+ 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);
+
+ 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]);
- cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private),
- GFP_KERNEL);
- if (!cs42l42)
- return -ENOMEM;
+ /* 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;
- i2c_set_clientdata(i2c_client, cs42l42);
+}
+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;
- cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
- if (IS_ERR(cs42l42->regmap)) {
- ret = PTR_ERR(cs42l42->regmap);
- dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", 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(&i2c_client->dev,
+ ret = devm_regulator_bulk_get(cs42l42->dev,
ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
if (ret != 0) {
- dev_err(&i2c_client->dev,
+ dev_err(cs42l42->dev,
"Failed to request supplies: %d\n", ret);
return ret;
}
@@ -1794,60 +2300,107 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
if (ret != 0) {
- dev_err(&i2c_client->dev,
+ dev_err(cs42l42->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
/* Reset the Device */
- cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ cs42l42->reset_gpio = devm_gpiod_get_optional(cs42l42->dev,
"reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs42l42->reset_gpio))
- return PTR_ERR(cs42l42->reset_gpio);
+ if (IS_ERR(cs42l42->reset_gpio)) {
+ ret = PTR_ERR(cs42l42->reset_gpio);
+ goto err_disable_noreset;
+ }
if (cs42l42->reset_gpio) {
- dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
- gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ dev_dbg(cs42l42->dev, "Found reset GPIO\n");
+
+ /*
+ * ACPI can override the default GPIO state we requested
+ * so ensure that we start with RESET low.
+ */
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+
+ /* Ensure minimum reset pulse width */
+ usleep_range(10, 500);
+
+ /*
+ * On SoundWire keep the chip in reset until we get an UNATTACH
+ * notification from the SoundWire core. This acts as a
+ * synchronization point to reject stale ATTACH notifications
+ * if the chip was already enumerated before we reset it.
+ */
+ if (cs42l42->sdw_peripheral)
+ cs42l42->sdw_waiting_first_unattach = true;
+ else
+ 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;
+ }
}
- mdelay(3);
- /* Request IRQ */
- ret = devm_request_threaded_irq(&i2c_client->dev,
- i2c_client->irq,
- NULL, cs42l42_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs42l42", cs42l42);
+ /* 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;
- if (ret != 0)
- dev_err(&i2c_client->dev,
- "Failed to request IRQ: %d\n", ret);
+ return 0;
- /* initialize codec */
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
+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);
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_probe, "SND_SOC_CS42L42_CORE");
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+int cs42l42_init(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+ int devid, ret;
- if (devid != CS42L42_CHIP_ID) {
+ /* 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(&i2c_client->dev,
- "CS42L42 Device ID (%X). Expected %X\n",
- devid, CS42L42_CHIP_ID);
- return ret;
+ 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(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ dev_err(cs42l42->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
}
- dev_info(&i2c_client->dev,
- "Cirrus Logic CS42L42, Revision: %02X\n", reg & 0xFF);
+ 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,
@@ -1866,118 +2419,77 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
(1 << CS42L42_ADC_PDN_SHIFT) |
(0 << CS42L42_PDN_ALL_SHIFT));
- if (i2c_client->dev.of_node) {
- ret = cs42l42_handle_device_data(i2c_client, cs42l42);
- if (ret != 0)
- return ret;
+ 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);
- /* Register codec for machine driver */
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l42, &cs42l42_dai, 1);
- if (ret < 0)
- goto err_disable;
return 0;
-err_disable:
- regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
- cs42l42->supplies);
- return ret;
-}
-
-static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
-{
- struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
+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);
- snd_soc_unregister_codec(&i2c_client->dev);
-
- /* Hold down reset */
- gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int cs42l42_runtime_suspend(struct device *dev)
-{
- struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
-
- regcache_cache_only(cs42l42->regmap, true);
- regcache_mark_dirty(cs42l42->regmap);
+err_disable:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
- /* Hold down reset */
gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
-
- /* remove power */
regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
-
- return 0;
+ return ret;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_init, "SND_SOC_CS42L42_CORE");
-static int cs42l42_runtime_resume(struct device *dev)
+void cs42l42_common_remove(struct cs42l42_private *cs42l42)
{
- struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
- int ret;
-
- /* Enable power */
- 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;
+ 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, 1);
-
- regcache_cache_only(cs42l42->regmap, false);
- regcache_sync(cs42l42->regmap);
-
- return 0;
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
}
-#endif
-
-static const struct dev_pm_ops cs42l42_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs42l42_runtime_suspend, cs42l42_runtime_resume,
- NULL)
-};
-
-static const struct of_device_id cs42l42_of_match[] = {
- { .compatible = "cirrus,cs42l42", },
- {},
-};
-MODULE_DEVICE_TABLE(of, cs42l42_of_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_runtime_pm,
- .of_match_table = cs42l42_of_match,
- },
- .id_table = cs42l42_id,
- .probe = cs42l42_i2c_probe,
- .remove = cs42l42_i2c_remove,
-};
-
-module_i2c_driver(cs42l42_i2c_driver);
+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
index d87a0a5322d5..3d85ebc59489 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -1,766 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l42.h -- CS42L42 ALSA SoC audio driver header
*
- * Copyright 2016 Cirrus Logic, Inc.
+ * 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>
- *
- * 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 __CS42L42_H__
#define __CS42L42_H__
-#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */
-#define CS42L42_WIN_START 0x00
-#define CS42L42_WIN_LEN 0x100
-#define CS42L42_RANGE_MIN 0x00
-#define CS42L42_RANGE_MAX 0x7F
-
-#define CS42L42_PAGE_10 0x1000
-#define CS42L42_PAGE_11 0x1100
-#define CS42L42_PAGE_12 0x1200
-#define CS42L42_PAGE_13 0x1300
-#define CS42L42_PAGE_15 0x1500
-#define CS42L42_PAGE_19 0x1900
-#define CS42L42_PAGE_1B 0x1B00
-#define CS42L42_PAGE_1C 0x1C00
-#define CS42L42_PAGE_1D 0x1D00
-#define CS42L42_PAGE_1F 0x1F00
-#define CS42L42_PAGE_20 0x2000
-#define CS42L42_PAGE_21 0x2100
-#define CS42L42_PAGE_23 0x2300
-#define CS42L42_PAGE_24 0x2400
-#define CS42L42_PAGE_25 0x2500
-#define CS42L42_PAGE_26 0x2600
-#define CS42L42_PAGE_28 0x2800
-#define CS42L42_PAGE_29 0x2900
-#define CS42L42_PAGE_2A 0x2A00
-#define CS42L42_PAGE_30 0x3000
-
-#define CS42L42_CHIP_ID 0x42A42
-
-/* Page 0x10 Global Registers */
-#define CS42L42_DEVID_AB (CS42L42_PAGE_10 + 0x01)
-#define CS42L42_DEVID_CD (CS42L42_PAGE_10 + 0x02)
-#define CS42L42_DEVID_E (CS42L42_PAGE_10 + 0x03)
-#define CS42L42_FABID (CS42L42_PAGE_10 + 0x04)
-#define CS42L42_REVID (CS42L42_PAGE_10 + 0x05)
-#define CS42L42_FRZ_CTL (CS42L42_PAGE_10 + 0x06)
-
-#define CS42L42_SRC_CTL (CS42L42_PAGE_10 + 0x07)
-#define CS42L42_SRC_BYPASS_DAC_SHIFT 1
-#define CS42L42_SRC_BYPASS_DAC_MASK (1 << CS42L42_SRC_BYPASS_DAC_SHIFT)
-
-#define CS42L42_MCLK_STATUS (CS42L42_PAGE_10 + 0x08)
-
-#define CS42L42_MCLK_CTL (CS42L42_PAGE_10 + 0x09)
-#define CS42L42_INTERNAL_FS_SHIFT 1
-#define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT)
-
-#define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A)
-#define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E)
-#define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F)
-#define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10)
-
-/* Page 0x11 Power and Headset Detect Registers */
-#define CS42L42_PWR_CTL1 (CS42L42_PAGE_11 + 0x01)
-#define CS42L42_ASP_DAO_PDN_SHIFT 7
-#define CS42L42_ASP_DAO_PDN_MASK (1 << CS42L42_ASP_DAO_PDN_SHIFT)
-#define CS42L42_ASP_DAI_PDN_SHIFT 6
-#define CS42L42_ASP_DAI_PDN_MASK (1 << CS42L42_ASP_DAI_PDN_SHIFT)
-#define CS42L42_MIXER_PDN_SHIFT 5
-#define CS42L42_MIXER_PDN_MASK (1 << CS42L42_MIXER_PDN_SHIFT)
-#define CS42L42_EQ_PDN_SHIFT 4
-#define CS42L42_EQ_PDN_MASK (1 << CS42L42_EQ_PDN_SHIFT)
-#define CS42L42_HP_PDN_SHIFT 3
-#define CS42L42_HP_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT)
-#define CS42L42_ADC_PDN_SHIFT 2
-#define CS42L42_ADC_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT)
-#define CS42L42_PDN_ALL_SHIFT 0
-#define CS42L42_PDN_ALL_MASK (1 << CS42L42_PDN_ALL_SHIFT)
-
-#define CS42L42_PWR_CTL2 (CS42L42_PAGE_11 + 0x02)
-#define CS42L42_ADC_SRC_PDNB_SHIFT 0
-#define CS42L42_ADC_SRC_PDNB_MASK (1 << CS42L42_ADC_SRC_PDNB_SHIFT)
-#define CS42L42_DAC_SRC_PDNB_SHIFT 1
-#define CS42L42_DAC_SRC_PDNB_MASK (1 << CS42L42_DAC_SRC_PDNB_SHIFT)
-#define CS42L42_ASP_DAI1_PDN_SHIFT 2
-#define CS42L42_ASP_DAI1_PDN_MASK (1 << CS42L42_ASP_DAI1_PDN_SHIFT)
-#define CS42L42_SRC_PDN_OVERRIDE_SHIFT 3
-#define CS42L42_SRC_PDN_OVERRIDE_MASK (1 << CS42L42_SRC_PDN_OVERRIDE_SHIFT)
-#define CS42L42_DISCHARGE_FILT_SHIFT 4
-#define CS42L42_DISCHARGE_FILT_MASK (1 << CS42L42_DISCHARGE_FILT_SHIFT)
-
-#define CS42L42_PWR_CTL3 (CS42L42_PAGE_11 + 0x03)
-#define CS42L42_RING_SENSE_PDNB_SHIFT 1
-#define CS42L42_RING_SENSE_PDNB_MASK (1 << \
- CS42L42_RING_SENSE_PDNB_SHIFT)
-#define CS42L42_VPMON_PDNB_SHIFT 2
-#define CS42L42_VPMON_PDNB_MASK (1 << \
- CS42L42_VPMON_PDNB_SHIFT)
-#define CS42L42_SW_CLK_STP_STAT_SEL_SHIFT 5
-#define CS42L42_SW_CLK_STP_STAT_SEL_MASK (3 << \
- CS42L42_SW_CLK_STP_STAT_SEL_SHIFT)
-
-#define CS42L42_RSENSE_CTL1 (CS42L42_PAGE_11 + 0x04)
-#define CS42L42_RS_TRIM_R_SHIFT 0
-#define CS42L42_RS_TRIM_R_MASK (1 << \
- CS42L42_RS_TRIM_R_SHIFT)
-#define CS42L42_RS_TRIM_T_SHIFT 1
-#define CS42L42_RS_TRIM_T_MASK (1 << \
- CS42L42_RS_TRIM_T_SHIFT)
-#define CS42L42_HPREF_RS_SHIFT 2
-#define CS42L42_HPREF_RS_MASK (1 << \
- CS42L42_HPREF_RS_SHIFT)
-#define CS42L42_HSBIAS_FILT_REF_RS_SHIFT 3
-#define CS42L42_HSBIAS_FILT_REF_RS_MASK (1 << \
- CS42L42_HSBIAS_FILT_REF_RS_SHIFT)
-#define CS42L42_RING_SENSE_PU_HIZ_SHIFT 6
-#define CS42L42_RING_SENSE_PU_HIZ_MASK (1 << \
- CS42L42_RING_SENSE_PU_HIZ_SHIFT)
-
-#define CS42L42_RSENSE_CTL2 (CS42L42_PAGE_11 + 0x05)
-#define CS42L42_TS_RS_GATE_SHIFT 7
-#define CS42L42_TS_RS_GATE_MAS (1 << CS42L42_TS_RS_GATE_SHIFT)
-
-#define CS42L42_OSC_SWITCH (CS42L42_PAGE_11 + 0x07)
-#define CS42L42_SCLK_PRESENT_SHIFT 0
-#define CS42L42_SCLK_PRESENT_MASK (1 << CS42L42_SCLK_PRESENT_SHIFT)
-
-#define CS42L42_OSC_SWITCH_STATUS (CS42L42_PAGE_11 + 0x09)
-#define CS42L42_OSC_SW_SEL_STAT_SHIFT 0
-#define CS42L42_OSC_SW_SEL_STAT_MASK (3 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
-#define CS42L42_OSC_PDNB_STAT_SHIFT 2
-#define CS42L42_OSC_PDNB_STAT_MASK (1 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
-
-#define CS42L42_RSENSE_CTL3 (CS42L42_PAGE_11 + 0x12)
-#define CS42L42_RS_RISE_DBNCE_TIME_SHIFT 0
-#define CS42L42_RS_RISE_DBNCE_TIME_MASK (7 << \
- CS42L42_RS_RISE_DBNCE_TIME_SHIFT)
-#define CS42L42_RS_FALL_DBNCE_TIME_SHIFT 3
-#define CS42L42_RS_FALL_DBNCE_TIME_MASK (7 << \
- CS42L42_RS_FALL_DBNCE_TIME_SHIFT)
-#define CS42L42_RS_PU_EN_SHIFT 6
-#define CS42L42_RS_PU_EN_MASK (1 << \
- CS42L42_RS_PU_EN_SHIFT)
-#define CS42L42_RS_INV_SHIFT 7
-#define CS42L42_RS_INV_MASK (1 << \
- CS42L42_RS_INV_SHIFT)
-
-#define CS42L42_TSENSE_CTL (CS42L42_PAGE_11 + 0x13)
-#define CS42L42_TS_RISE_DBNCE_TIME_SHIFT 0
-#define CS42L42_TS_RISE_DBNCE_TIME_MASK (7 << \
- CS42L42_TS_RISE_DBNCE_TIME_SHIFT)
-#define CS42L42_TS_FALL_DBNCE_TIME_SHIFT 3
-#define CS42L42_TS_FALL_DBNCE_TIME_MASK (7 << \
- CS42L42_TS_FALL_DBNCE_TIME_SHIFT)
-#define CS42L42_TS_INV_SHIFT 7
-#define CS42L42_TS_INV_MASK (1 << \
- CS42L42_TS_INV_SHIFT)
-
-#define CS42L42_TSRS_INT_DISABLE (CS42L42_PAGE_11 + 0x14)
-#define CS42L42_D_RS_PLUG_DBNC_SHIFT 0
-#define CS42L42_D_RS_PLUG_DBNC_MASK (1 << CS42L42_D_RS_PLUG_DBNC_SHIFT)
-#define CS42L42_D_RS_UNPLUG_DBNC_SHIFT 1
-#define CS42L42_D_RS_UNPLUG_DBNC_MASK (1 << CS42L42_D_RS_UNPLUG_DBNC_SHIFT)
-#define CS42L42_D_TS_PLUG_DBNC_SHIFT 2
-#define CS42L42_D_TS_PLUG_DBNC_MASK (1 << CS42L42_D_TS_PLUG_DBNC_SHIFT)
-#define CS42L42_D_TS_UNPLUG_DBNC_SHIFT 3
-#define CS42L42_D_TS_UNPLUG_DBNC_MASK (1 << CS42L42_D_TS_UNPLUG_DBNC_SHIFT)
-
-#define CS42L42_TRSENSE_STATUS (CS42L42_PAGE_11 + 0x15)
-#define CS42L42_RS_PLUG_DBNC_SHIFT 0
-#define CS42L42_RS_PLUG_DBNC_MASK (1 << CS42L42_RS_PLUG_DBNC_SHIFT)
-#define CS42L42_RS_UNPLUG_DBNC_SHIFT 1
-#define CS42L42_RS_UNPLUG_DBNC_MASK (1 << CS42L42_RS_UNPLUG_DBNC_SHIFT)
-#define CS42L42_TS_PLUG_DBNC_SHIFT 2
-#define CS42L42_TS_PLUG_DBNC_MASK (1 << CS42L42_TS_PLUG_DBNC_SHIFT)
-#define CS42L42_TS_UNPLUG_DBNC_SHIFT 3
-#define CS42L42_TS_UNPLUG_DBNC_MASK (1 << CS42L42_TS_UNPLUG_DBNC_SHIFT)
-
-#define CS42L42_HSDET_CTL1 (CS42L42_PAGE_11 + 0x1F)
-#define CS42L42_HSDET_COMP1_LVL_SHIFT 0
-#define CS42L42_HSDET_COMP1_LVL_MASK (15 << CS42L42_HSDET_COMP1_LVL_SHIFT)
-#define CS42L42_HSDET_COMP2_LVL_SHIFT 4
-#define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT)
-
-#define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20)
-#define CS42L42_HSDET_AUTO_TIME_SHIFT 0
-#define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)
-#define CS42L42_HSBIAS_REF_SHIFT 3
-#define CS42L42_HSBIAS_REF_MASK (1 << CS42L42_HSBIAS_REF_SHIFT)
-#define CS42L42_HSDET_SET_SHIFT 4
-#define CS42L42_HSDET_SET_MASK (3 << CS42L42_HSDET_SET_SHIFT)
-#define CS42L42_HSDET_CTRL_SHIFT 6
-#define CS42L42_HSDET_CTRL_MASK (3 << CS42L42_HSDET_CTRL_SHIFT)
-
-#define CS42L42_HS_SWITCH_CTL (CS42L42_PAGE_11 + 0x21)
-#define CS42L42_SW_GNDHS_HS4_SHIFT 0
-#define CS42L42_SW_GNDHS_HS4_MASK (1 << CS42L42_SW_GNDHS_HS4_SHIFT)
-#define CS42L42_SW_GNDHS_HS3_SHIFT 1
-#define CS42L42_SW_GNDHS_HS3_MASK (1 << CS42L42_SW_GNDHS_HS3_SHIFT)
-#define CS42L42_SW_HSB_HS4_SHIFT 2
-#define CS42L42_SW_HSB_HS4_MASK (1 << CS42L42_SW_HSB_HS4_SHIFT)
-#define CS42L42_SW_HSB_HS3_SHIFT 3
-#define CS42L42_SW_HSB_HS3_MASK (1 << CS42L42_SW_HSB_HS3_SHIFT)
-#define CS42L42_SW_HSB_FILT_HS4_SHIFT 4
-#define CS42L42_SW_HSB_FILT_HS4_MASK (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT)
-#define CS42L42_SW_HSB_FILT_HS3_SHIFT 5
-#define CS42L42_SW_HSB_FILT_HS3_MASK (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT)
-#define CS42L42_SW_REF_HS4_SHIFT 6
-#define CS42L42_SW_REF_HS4_MASK (1 << CS42L42_SW_REF_HS4_SHIFT)
-#define CS42L42_SW_REF_HS3_SHIFT 7
-#define CS42L42_SW_REF_HS3_MASK (1 << CS42L42_SW_REF_HS3_SHIFT)
-
-#define CS42L42_HS_DET_STATUS (CS42L42_PAGE_11 + 0x24)
-#define CS42L42_HSDET_TYPE_SHIFT 0
-#define CS42L42_HSDET_TYPE_MASK (3 << CS42L42_HSDET_TYPE_SHIFT)
-#define CS42L42_HSDET_COMP1_OUT_SHIFT 6
-#define CS42L42_HSDET_COMP1_OUT_MASK (1 << CS42L42_HSDET_COMP1_OUT_SHIFT)
-#define CS42L42_HSDET_COMP2_OUT_SHIFT 7
-#define CS42L42_HSDET_COMP2_OUT_MASK (1 << CS42L42_HSDET_COMP2_OUT_SHIFT)
-#define CS42L42_PLUG_CTIA 0
-#define CS42L42_PLUG_OMTP 1
-#define CS42L42_PLUG_HEADPHONE 2
-#define CS42L42_PLUG_INVALID 3
-
-#define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29)
-#define CS42L42_HS_CLAMP_DISABLE_SHIFT 0
-#define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)
-
-/* Page 0x12 Clocking Registers */
-#define CS42L42_MCLK_SRC_SEL (CS42L42_PAGE_12 + 0x01)
-#define CS42L42_MCLKDIV_SHIFT 1
-#define CS42L42_MCLKDIV_MASK (1 << CS42L42_MCLKDIV_SHIFT)
-#define CS42L42_MCLK_SRC_SEL_SHIFT 0
-#define CS42L42_MCLK_SRC_SEL_MASK (1 << CS42L42_MCLK_SRC_SEL_SHIFT)
-
-#define CS42L42_SPDIF_CLK_CFG (CS42L42_PAGE_12 + 0x02)
-#define CS42L42_FSYNC_PW_LOWER (CS42L42_PAGE_12 + 0x03)
-
-#define CS42L42_FSYNC_PW_UPPER (CS42L42_PAGE_12 + 0x04)
-#define CS42L42_FSYNC_PULSE_WIDTH_SHIFT 0
-#define CS42L42_FSYNC_PULSE_WIDTH_MASK (0xff << \
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT)
-
-#define CS42L42_FSYNC_P_LOWER (CS42L42_PAGE_12 + 0x05)
-
-#define CS42L42_FSYNC_P_UPPER (CS42L42_PAGE_12 + 0x06)
-#define CS42L42_FSYNC_PERIOD_SHIFT 0
-#define CS42L42_FSYNC_PERIOD_MASK (0xff << CS42L42_FSYNC_PERIOD_SHIFT)
-
-#define CS42L42_ASP_CLK_CFG (CS42L42_PAGE_12 + 0x07)
-#define CS42L42_ASP_SCLK_EN_SHIFT 5
-#define CS42L42_ASP_SCLK_EN_MASK (1 << CS42L42_ASP_SCLK_EN_SHIFT)
-#define CS42L42_ASP_MASTER_MODE 0x01
-#define CS42L42_ASP_SLAVE_MODE 0x00
-#define CS42L42_ASP_MODE_SHIFT 4
-#define CS42L42_ASP_MODE_MASK (1 << CS42L42_ASP_MODE_SHIFT)
-#define CS42L42_ASP_SCPOL_IN_DAC_SHIFT 2
-#define CS42L42_ASP_SCPOL_IN_DAC_MASK (1 << CS42L42_ASP_SCPOL_IN_DAC_SHIFT)
-#define CS42L42_ASP_LCPOL_IN_SHIFT 0
-#define CS42L42_ASP_LCPOL_IN_MASK (1 << CS42L42_ASP_LCPOL_IN_SHIFT)
-#define CS42L42_ASP_POL_INV 1
-
-#define CS42L42_ASP_FRM_CFG (CS42L42_PAGE_12 + 0x08)
-#define CS42L42_ASP_STP_SHIFT 4
-#define CS42L42_ASP_STP_MASK (1 << CS42L42_ASP_STP_SHIFT)
-#define CS42L42_ASP_5050_SHIFT 3
-#define CS42L42_ASP_5050_MASK (1 << CS42L42_ASP_5050_SHIFT)
-#define CS42L42_ASP_FSD_SHIFT 0
-#define CS42L42_ASP_FSD_MASK (7 << CS42L42_ASP_FSD_SHIFT)
-#define CS42L42_ASP_FSD_0_5 1
-#define CS42L42_ASP_FSD_1_0 2
-#define CS42L42_ASP_FSD_1_5 3
-#define CS42L42_ASP_FSD_2_0 4
-
-#define CS42L42_FS_RATE_EN (CS42L42_PAGE_12 + 0x09)
-#define CS42L42_FS_EN_SHIFT 0
-#define CS42L42_FS_EN_MASK (0xf << CS42L42_FS_EN_SHIFT)
-#define CS42L42_FS_EN_IASRC_96K 0x1
-#define CS42L42_FS_EN_OASRC_96K 0x2
-
-#define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A)
-#define CS42L42_CLK_IASRC_SEL_SHIFT 0
-#define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT)
-#define CS42L42_CLK_IASRC_SEL_12 1
-
-#define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B)
-#define CS42L42_CLK_OASRC_SEL_SHIFT 0
-#define CS42L42_CLK_OASRC_SEL_MASK (1 << CS42L42_CLK_OASRC_SEL_SHIFT)
-#define CS42L42_CLK_OASRC_SEL_12 1
-
-#define CS42L42_PLL_DIV_CFG1 (CS42L42_PAGE_12 + 0x0C)
-#define CS42L42_SCLK_PREDIV_SHIFT 0
-#define CS42L42_SCLK_PREDIV_MASK (3 << CS42L42_SCLK_PREDIV_SHIFT)
-
-/* Page 0x13 Interrupt Registers */
-/* Interrupts */
-#define CS42L42_ADC_OVFL_STATUS (CS42L42_PAGE_13 + 0x01)
-#define CS42L42_MIXER_STATUS (CS42L42_PAGE_13 + 0x02)
-#define CS42L42_SRC_STATUS (CS42L42_PAGE_13 + 0x03)
-#define CS42L42_ASP_RX_STATUS (CS42L42_PAGE_13 + 0x04)
-#define CS42L42_ASP_TX_STATUS (CS42L42_PAGE_13 + 0x05)
-#define CS42L42_CODEC_STATUS (CS42L42_PAGE_13 + 0x08)
-#define CS42L42_DET_INT_STATUS1 (CS42L42_PAGE_13 + 0x09)
-#define CS42L42_DET_INT_STATUS2 (CS42L42_PAGE_13 + 0x0A)
-#define CS42L42_SRCPL_INT_STATUS (CS42L42_PAGE_13 + 0x0B)
-#define CS42L42_VPMON_STATUS (CS42L42_PAGE_13 + 0x0D)
-#define CS42L42_PLL_LOCK_STATUS (CS42L42_PAGE_13 + 0x0E)
-#define CS42L42_TSRS_PLUG_STATUS (CS42L42_PAGE_13 + 0x0F)
-/* Masks */
-#define CS42L42_ADC_OVFL_INT_MASK (CS42L42_PAGE_13 + 0x16)
-#define CS42L42_ADC_OVFL_SHIFT 0
-#define CS42L42_ADC_OVFL_MASK (1 << CS42L42_ADC_OVFL_SHIFT)
-#define CS42L42_ADC_OVFL_VAL_MASK CS42L42_ADC_OVFL_MASK
-
-#define CS42L42_MIXER_INT_MASK (CS42L42_PAGE_13 + 0x17)
-#define CS42L42_MIX_CHB_OVFL_SHIFT 0
-#define CS42L42_MIX_CHB_OVFL_MASK (1 << CS42L42_MIX_CHB_OVFL_SHIFT)
-#define CS42L42_MIX_CHA_OVFL_SHIFT 1
-#define CS42L42_MIX_CHA_OVFL_MASK (1 << CS42L42_MIX_CHA_OVFL_SHIFT)
-#define CS42L42_EQ_OVFL_SHIFT 2
-#define CS42L42_EQ_OVFL_MASK (1 << CS42L42_EQ_OVFL_SHIFT)
-#define CS42L42_EQ_BIQUAD_OVFL_SHIFT 3
-#define CS42L42_EQ_BIQUAD_OVFL_MASK (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT)
-#define CS42L42_MIXER_VAL_MASK (CS42L42_MIX_CHB_OVFL_MASK | \
- CS42L42_MIX_CHA_OVFL_MASK | \
- CS42L42_EQ_OVFL_MASK | \
- CS42L42_EQ_BIQUAD_OVFL_MASK)
-
-#define CS42L42_SRC_INT_MASK (CS42L42_PAGE_13 + 0x18)
-#define CS42L42_SRC_ILK_SHIFT 0
-#define CS42L42_SRC_ILK_MASK (1 << CS42L42_SRC_ILK_SHIFT)
-#define CS42L42_SRC_OLK_SHIFT 1
-#define CS42L42_SRC_OLK_MASK (1 << CS42L42_SRC_OLK_SHIFT)
-#define CS42L42_SRC_IUNLK_SHIFT 2
-#define CS42L42_SRC_IUNLK_MASK (1 << CS42L42_SRC_IUNLK_SHIFT)
-#define CS42L42_SRC_OUNLK_SHIFT 3
-#define CS42L42_SRC_OUNLK_MASK (1 << CS42L42_SRC_OUNLK_SHIFT)
-#define CS42L42_SRC_VAL_MASK (CS42L42_SRC_ILK_MASK | \
- CS42L42_SRC_OLK_MASK | \
- CS42L42_SRC_IUNLK_MASK | \
- CS42L42_SRC_OUNLK_MASK)
-
-#define CS42L42_ASP_RX_INT_MASK (CS42L42_PAGE_13 + 0x19)
-#define CS42L42_ASPRX_NOLRCK_SHIFT 0
-#define CS42L42_ASPRX_NOLRCK_MASK (1 << CS42L42_ASPRX_NOLRCK_SHIFT)
-#define CS42L42_ASPRX_EARLY_SHIFT 1
-#define CS42L42_ASPRX_EARLY_MASK (1 << CS42L42_ASPRX_EARLY_SHIFT)
-#define CS42L42_ASPRX_LATE_SHIFT 2
-#define CS42L42_ASPRX_LATE_MASK (1 << CS42L42_ASPRX_LATE_SHIFT)
-#define CS42L42_ASPRX_ERROR_SHIFT 3
-#define CS42L42_ASPRX_ERROR_MASK (1 << CS42L42_ASPRX_ERROR_SHIFT)
-#define CS42L42_ASPRX_OVLD_SHIFT 4
-#define CS42L42_ASPRX_OVLD_MASK (1 << CS42L42_ASPRX_OVLD_SHIFT)
-#define CS42L42_ASP_RX_VAL_MASK (CS42L42_ASPRX_NOLRCK_MASK | \
- CS42L42_ASPRX_EARLY_MASK | \
- CS42L42_ASPRX_LATE_MASK | \
- CS42L42_ASPRX_ERROR_MASK | \
- CS42L42_ASPRX_OVLD_MASK)
-
-#define CS42L42_ASP_TX_INT_MASK (CS42L42_PAGE_13 + 0x1A)
-#define CS42L42_ASPTX_NOLRCK_SHIFT 0
-#define CS42L42_ASPTX_NOLRCK_MASK (1 << CS42L42_ASPTX_NOLRCK_SHIFT)
-#define CS42L42_ASPTX_EARLY_SHIFT 1
-#define CS42L42_ASPTX_EARLY_MASK (1 << CS42L42_ASPTX_EARLY_SHIFT)
-#define CS42L42_ASPTX_LATE_SHIFT 2
-#define CS42L42_ASPTX_LATE_MASK (1 << CS42L42_ASPTX_LATE_SHIFT)
-#define CS42L42_ASPTX_SMERROR_SHIFT 3
-#define CS42L42_ASPTX_SMERROR_MASK (1 << CS42L42_ASPTX_SMERROR_SHIFT)
-#define CS42L42_ASP_TX_VAL_MASK (CS42L42_ASPTX_NOLRCK_MASK | \
- CS42L42_ASPTX_EARLY_MASK | \
- CS42L42_ASPTX_LATE_MASK | \
- CS42L42_ASPTX_SMERROR_MASK)
-
-#define CS42L42_CODEC_INT_MASK (CS42L42_PAGE_13 + 0x1B)
-#define CS42L42_PDN_DONE_SHIFT 0
-#define CS42L42_PDN_DONE_MASK (1 << CS42L42_PDN_DONE_SHIFT)
-#define CS42L42_HSDET_AUTO_DONE_SHIFT 1
-#define CS42L42_HSDET_AUTO_DONE_MASK (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)
-#define CS42L42_CODEC_VAL_MASK (CS42L42_PDN_DONE_MASK | \
- CS42L42_HSDET_AUTO_DONE_MASK)
-
-#define CS42L42_SRCPL_INT_MASK (CS42L42_PAGE_13 + 0x1C)
-#define CS42L42_SRCPL_ADC_LK_SHIFT 0
-#define CS42L42_SRCPL_ADC_LK_MASK (1 << CS42L42_SRCPL_ADC_LK_SHIFT)
-#define CS42L42_SRCPL_DAC_LK_SHIFT 2
-#define CS42L42_SRCPL_DAC_LK_MASK (1 << CS42L42_SRCPL_DAC_LK_SHIFT)
-#define CS42L42_SRCPL_ADC_UNLK_SHIFT 5
-#define CS42L42_SRCPL_ADC_UNLK_MASK (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT)
-#define CS42L42_SRCPL_DAC_UNLK_SHIFT 6
-#define CS42L42_SRCPL_DAC_UNLK_MASK (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT)
-#define CS42L42_SRCPL_VAL_MASK (CS42L42_SRCPL_ADC_LK_MASK | \
- CS42L42_SRCPL_DAC_LK_MASK | \
- CS42L42_SRCPL_ADC_UNLK_MASK | \
- CS42L42_SRCPL_DAC_UNLK_MASK)
-
-#define CS42L42_VPMON_INT_MASK (CS42L42_PAGE_13 + 0x1E)
-#define CS42L42_VPMON_SHIFT 0
-#define CS42L42_VPMON_MASK (1 << CS42L42_VPMON_SHIFT)
-#define CS42L42_VPMON_VAL_MASK CS42L42_VPMON_MASK
-
-#define CS42L42_PLL_LOCK_INT_MASK (CS42L42_PAGE_13 + 0x1F)
-#define CS42L42_PLL_LOCK_SHIFT 0
-#define CS42L42_PLL_LOCK_MASK (1 << CS42L42_PLL_LOCK_SHIFT)
-#define CS42L42_PLL_LOCK_VAL_MASK CS42L42_PLL_LOCK_MASK
-
-#define CS42L42_TSRS_PLUG_INT_MASK (CS42L42_PAGE_13 + 0x20)
-#define CS42L42_RS_PLUG_SHIFT 0
-#define CS42L42_RS_PLUG_MASK (1 << CS42L42_RS_PLUG_SHIFT)
-#define CS42L42_RS_UNPLUG_SHIFT 1
-#define CS42L42_RS_UNPLUG_MASK (1 << CS42L42_RS_UNPLUG_SHIFT)
-#define CS42L42_TS_PLUG_SHIFT 2
-#define CS42L42_TS_PLUG_MASK (1 << CS42L42_TS_PLUG_SHIFT)
-#define CS42L42_TS_UNPLUG_SHIFT 3
-#define CS42L42_TS_UNPLUG_MASK (1 << CS42L42_TS_UNPLUG_SHIFT)
-#define CS42L42_TSRS_PLUG_VAL_MASK (CS42L42_RS_PLUG_MASK | \
- CS42L42_RS_UNPLUG_MASK | \
- CS42L42_TS_PLUG_MASK | \
- CS42L42_TS_UNPLUG_MASK)
-#define CS42L42_TS_PLUG 3
-#define CS42L42_TS_UNPLUG 0
-#define CS42L42_TS_TRANS 1
-
-/* Page 0x15 Fractional-N PLL Registers */
-#define CS42L42_PLL_CTL1 (CS42L42_PAGE_15 + 0x01)
-#define CS42L42_PLL_START_SHIFT 0
-#define CS42L42_PLL_START_MASK (1 << CS42L42_PLL_START_SHIFT)
-
-#define CS42L42_PLL_DIV_FRAC0 (CS42L42_PAGE_15 + 0x02)
-#define CS42L42_PLL_DIV_FRAC_SHIFT 0
-#define CS42L42_PLL_DIV_FRAC_MASK (0xff << CS42L42_PLL_DIV_FRAC_SHIFT)
-
-#define CS42L42_PLL_DIV_FRAC1 (CS42L42_PAGE_15 + 0x03)
-#define CS42L42_PLL_DIV_FRAC2 (CS42L42_PAGE_15 + 0x04)
-
-#define CS42L42_PLL_DIV_INT (CS42L42_PAGE_15 + 0x05)
-#define CS42L42_PLL_DIV_INT_SHIFT 0
-#define CS42L42_PLL_DIV_INT_MASK (0xff << CS42L42_PLL_DIV_INT_SHIFT)
-
-#define CS42L42_PLL_CTL3 (CS42L42_PAGE_15 + 0x08)
-#define CS42L42_PLL_DIVOUT_SHIFT 0
-#define CS42L42_PLL_DIVOUT_MASK (0xff << CS42L42_PLL_DIVOUT_SHIFT)
-
-#define CS42L42_PLL_CAL_RATIO (CS42L42_PAGE_15 + 0x0A)
-#define CS42L42_PLL_CAL_RATIO_SHIFT 0
-#define CS42L42_PLL_CAL_RATIO_MASK (0xff << CS42L42_PLL_CAL_RATIO_SHIFT)
-
-#define CS42L42_PLL_CTL4 (CS42L42_PAGE_15 + 0x1B)
-#define CS42L42_PLL_MODE_SHIFT 0
-#define CS42L42_PLL_MODE_MASK (3 << CS42L42_PLL_MODE_SHIFT)
-
-/* Page 0x19 HP Load Detect Registers */
-#define CS42L42_LOAD_DET_RCSTAT (CS42L42_PAGE_19 + 0x25)
-#define CS42L42_RLA_STAT_SHIFT 0
-#define CS42L42_RLA_STAT_MASK (3 << CS42L42_RLA_STAT_SHIFT)
-#define CS42L42_RLA_STAT_15_OHM 0
-
-#define CS42L42_LOAD_DET_DONE (CS42L42_PAGE_19 + 0x26)
-#define CS42L42_HPLOAD_DET_DONE_SHIFT 0
-#define CS42L42_HPLOAD_DET_DONE_MASK (1 << CS42L42_HPLOAD_DET_DONE_SHIFT)
-
-#define CS42L42_LOAD_DET_EN (CS42L42_PAGE_19 + 0x27)
-#define CS42L42_HP_LD_EN_SHIFT 0
-#define CS42L42_HP_LD_EN_MASK (1 << CS42L42_HP_LD_EN_SHIFT)
-
-/* Page 0x1B Headset Interface Registers */
-#define CS42L42_HSBIAS_SC_AUTOCTL (CS42L42_PAGE_1B + 0x70)
-#define CS42L42_HSBIAS_SENSE_TRIP_SHIFT 0
-#define CS42L42_HSBIAS_SENSE_TRIP_MASK (7 << \
- CS42L42_HSBIAS_SENSE_TRIP_SHIFT)
-#define CS42L42_TIP_SENSE_EN_SHIFT 5
-#define CS42L42_TIP_SENSE_EN_MASK (1 << \
- CS42L42_TIP_SENSE_EN_SHIFT)
-#define CS42L42_AUTO_HSBIAS_HIZ_SHIFT 6
-#define CS42L42_AUTO_HSBIAS_HIZ_MASK (1 << \
- CS42L42_AUTO_HSBIAS_HIZ_SHIFT)
-#define CS42L42_HSBIAS_SENSE_EN_SHIFT 7
-#define CS42L42_HSBIAS_SENSE_EN_MASK (1 << \
- CS42L42_HSBIAS_SENSE_EN_SHIFT)
-
-#define CS42L42_WAKE_CTL (CS42L42_PAGE_1B + 0x71)
-#define CS42L42_WAKEB_CLEAR_SHIFT 0
-#define CS42L42_WAKEB_CLEAR_MASK (1 << CS42L42_WAKEB_CLEAR_SHIFT)
-#define CS42L42_WAKEB_MODE_SHIFT 5
-#define CS42L42_WAKEB_MODE_MASK (1 << CS42L42_WAKEB_MODE_SHIFT)
-#define CS42L42_M_HP_WAKE_SHIFT 6
-#define CS42L42_M_HP_WAKE_MASK (1 << CS42L42_M_HP_WAKE_SHIFT)
-#define CS42L42_M_MIC_WAKE_SHIFT 7
-#define CS42L42_M_MIC_WAKE_MASK (1 << CS42L42_M_MIC_WAKE_SHIFT)
-
-#define CS42L42_ADC_DISABLE_MUTE (CS42L42_PAGE_1B + 0x72)
-#define CS42L42_ADC_DISABLE_S0_MUTE_SHIFT 7
-#define CS42L42_ADC_DISABLE_S0_MUTE_MASK (1 << \
- CS42L42_ADC_DISABLE_S0_MUTE_SHIFT)
-
-#define CS42L42_TIPSENSE_CTL (CS42L42_PAGE_1B + 0x73)
-#define CS42L42_TIP_SENSE_DEBOUNCE_SHIFT 0
-#define CS42L42_TIP_SENSE_DEBOUNCE_MASK (3 << \
- CS42L42_TIP_SENSE_DEBOUNCE_SHIFT)
-#define CS42L42_TIP_SENSE_INV_SHIFT 5
-#define CS42L42_TIP_SENSE_INV_MASK (1 << \
- CS42L42_TIP_SENSE_INV_SHIFT)
-#define CS42L42_TIP_SENSE_CTRL_SHIFT 6
-#define CS42L42_TIP_SENSE_CTRL_MASK (3 << \
- CS42L42_TIP_SENSE_CTRL_SHIFT)
-
-#define CS42L42_MISC_DET_CTL (CS42L42_PAGE_1B + 0x74)
-#define CS42L42_PDN_MIC_LVL_DET_SHIFT 0
-#define CS42L42_PDN_MIC_LVL_DET_MASK (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)
-#define CS42L42_HSBIAS_CTL_SHIFT 1
-#define CS42L42_HSBIAS_CTL_MASK (3 << CS42L42_HSBIAS_CTL_SHIFT)
-#define CS42L42_DETECT_MODE_SHIFT 3
-#define CS42L42_DETECT_MODE_MASK (3 << CS42L42_DETECT_MODE_SHIFT)
-
-#define CS42L42_MIC_DET_CTL1 (CS42L42_PAGE_1B + 0x75)
-#define CS42L42_HS_DET_LEVEL_SHIFT 0
-#define CS42L42_HS_DET_LEVEL_MASK (0x3F << CS42L42_HS_DET_LEVEL_SHIFT)
-#define CS42L42_EVENT_STAT_SEL_SHIFT 6
-#define CS42L42_EVENT_STAT_SEL_MASK (1 << CS42L42_EVENT_STAT_SEL_SHIFT)
-#define CS42L42_LATCH_TO_VP_SHIFT 7
-#define CS42L42_LATCH_TO_VP_MASK (1 << CS42L42_LATCH_TO_VP_SHIFT)
-
-#define CS42L42_MIC_DET_CTL2 (CS42L42_PAGE_1B + 0x76)
-#define CS42L42_DEBOUNCE_TIME_SHIFT 5
-#define CS42L42_DEBOUNCE_TIME_MASK (0x07 << CS42L42_DEBOUNCE_TIME_SHIFT)
-
-#define CS42L42_DET_STATUS1 (CS42L42_PAGE_1B + 0x77)
-#define CS42L42_HSBIAS_HIZ_MODE_SHIFT 6
-#define CS42L42_HSBIAS_HIZ_MODE_MASK (1 << CS42L42_HSBIAS_HIZ_MODE_SHIFT)
-#define CS42L42_TIP_SENSE_SHIFT 7
-#define CS42L42_TIP_SENSE_MASK (1 << CS42L42_TIP_SENSE_SHIFT)
-
-#define CS42L42_DET_STATUS2 (CS42L42_PAGE_1B + 0x78)
-#define CS42L42_SHORT_TRUE_SHIFT 0
-#define CS42L42_SHORT_TRUE_MASK (1 << CS42L42_SHORT_TRUE_SHIFT)
-#define CS42L42_HS_TRUE_SHIFT 1
-#define CS42L42_HS_TRUE_MASK (1 << CS42L42_HS_TRUE_SHIFT)
-
-#define CS42L42_DET_INT1_MASK (CS42L42_PAGE_1B + 0x79)
-#define CS42L42_TIP_SENSE_UNPLUG_SHIFT 5
-#define CS42L42_TIP_SENSE_UNPLUG_MASK (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT)
-#define CS42L42_TIP_SENSE_PLUG_SHIFT 6
-#define CS42L42_TIP_SENSE_PLUG_MASK (1 << CS42L42_TIP_SENSE_PLUG_SHIFT)
-#define CS42L42_HSBIAS_SENSE_SHIFT 7
-#define CS42L42_HSBIAS_SENSE_MASK (1 << CS42L42_HSBIAS_SENSE_SHIFT)
-#define CS42L42_DET_INT_VAL1_MASK (CS42L42_TIP_SENSE_UNPLUG_MASK | \
- CS42L42_TIP_SENSE_PLUG_MASK | \
- CS42L42_HSBIAS_SENSE_MASK)
-
-#define CS42L42_DET_INT2_MASK (CS42L42_PAGE_1B + 0x7A)
-#define CS42L42_M_SHORT_DET_SHIFT 0
-#define CS42L42_M_SHORT_DET_MASK (1 << \
- CS42L42_M_SHORT_DET_SHIFT)
-#define CS42L42_M_SHORT_RLS_SHIFT 1
-#define CS42L42_M_SHORT_RLS_MASK (1 << \
- CS42L42_M_SHORT_RLS_SHIFT)
-#define CS42L42_M_HSBIAS_HIZ_SHIFT 2
-#define CS42L42_M_HSBIAS_HIZ_MASK (1 << \
- CS42L42_M_HSBIAS_HIZ_SHIFT)
-#define CS42L42_M_DETECT_FT_SHIFT 6
-#define CS42L42_M_DETECT_FT_MASK (1 << \
- CS42L42_M_DETECT_FT_SHIFT)
-#define CS42L42_M_DETECT_TF_SHIFT 7
-#define CS42L42_M_DETECT_TF_MASK (1 << \
- CS42L42_M_DETECT_TF_SHIFT)
-#define CS42L42_DET_INT_VAL2_MASK (CS42L42_M_SHORT_DET_MASK | \
- CS42L42_M_SHORT_RLS_MASK | \
- CS42L42_M_HSBIAS_HIZ_MASK | \
- CS42L42_M_DETECT_FT_MASK | \
- CS42L42_M_DETECT_TF_MASK)
-
-/* Page 0x1C Headset Bias Registers */
-#define CS42L42_HS_BIAS_CTL (CS42L42_PAGE_1C + 0x03)
-#define CS42L42_HSBIAS_RAMP_SHIFT 0
-#define CS42L42_HSBIAS_RAMP_MASK (3 << CS42L42_HSBIAS_RAMP_SHIFT)
-#define CS42L42_HSBIAS_PD_SHIFT 4
-#define CS42L42_HSBIAS_PD_MASK (1 << CS42L42_HSBIAS_PD_SHIFT)
-#define CS42L42_HSBIAS_CAPLESS_SHIFT 7
-#define CS42L42_HSBIAS_CAPLESS_MASK (1 << CS42L42_HSBIAS_CAPLESS_SHIFT)
-
-/* Page 0x1D ADC Registers */
-#define CS42L42_ADC_CTL (CS42L42_PAGE_1D + 0x01)
-#define CS42L42_ADC_NOTCH_DIS_SHIFT 5
-#define CS42L42_ADC_FORCE_WEAK_VCM_SHIFT 4
-#define CS42L42_ADC_INV_SHIFT 2
-#define CS42L42_ADC_DIG_BOOST_SHIFT 0
-
-#define CS42L42_ADC_VOLUME (CS42L42_PAGE_1D + 0x03)
-#define CS42L42_ADC_VOL_SHIFT 0
-
-#define CS42L42_ADC_WNF_HPF_CTL (CS42L42_PAGE_1D + 0x04)
-#define CS42L42_ADC_WNF_CF_SHIFT 4
-#define CS42L42_ADC_WNF_EN_SHIFT 3
-#define CS42L42_ADC_HPF_CF_SHIFT 1
-#define CS42L42_ADC_HPF_EN_SHIFT 0
-
-/* Page 0x1F DAC Registers */
-#define CS42L42_DAC_CTL1 (CS42L42_PAGE_1F + 0x01)
-#define CS42L42_DACB_INV_SHIFT 1
-#define CS42L42_DACA_INV_SHIFT 0
-
-#define CS42L42_DAC_CTL2 (CS42L42_PAGE_1F + 0x06)
-#define CS42L42_HPOUT_PULLDOWN_SHIFT 4
-#define CS42L42_HPOUT_PULLDOWN_MASK (15 << CS42L42_HPOUT_PULLDOWN_SHIFT)
-#define CS42L42_HPOUT_LOAD_SHIFT 3
-#define CS42L42_HPOUT_LOAD_MASK (1 << CS42L42_HPOUT_LOAD_SHIFT)
-#define CS42L42_HPOUT_CLAMP_SHIFT 2
-#define CS42L42_HPOUT_CLAMP_MASK (1 << CS42L42_HPOUT_CLAMP_SHIFT)
-#define CS42L42_DAC_HPF_EN_SHIFT 1
-#define CS42L42_DAC_HPF_EN_MASK (1 << CS42L42_DAC_HPF_EN_SHIFT)
-#define CS42L42_DAC_MON_EN_SHIFT 0
-#define CS42L42_DAC_MON_EN_MASK (1 << CS42L42_DAC_MON_EN_SHIFT)
-
-/* Page 0x20 HP CTL Registers */
-#define CS42L42_HP_CTL (CS42L42_PAGE_20 + 0x01)
-#define CS42L42_HP_ANA_BMUTE_SHIFT 3
-#define CS42L42_HP_ANA_BMUTE_MASK (1 << CS42L42_HP_ANA_BMUTE_SHIFT)
-#define CS42L42_HP_ANA_AMUTE_SHIFT 2
-#define CS42L42_HP_ANA_AMUTE_MASK (1 << CS42L42_HP_ANA_AMUTE_SHIFT)
-#define CS42L42_HP_FULL_SCALE_VOL_SHIFT 1
-#define CS42L42_HP_FULL_SCALE_VOL_MASK (1 << CS42L42_HP_FULL_SCALE_VOL_SHIFT)
-
-/* Page 0x21 Class H Registers */
-#define CS42L42_CLASSH_CTL (CS42L42_PAGE_21 + 0x01)
-
-/* Page 0x23 Mixer Volume Registers */
-#define CS42L42_MIXER_CHA_VOL (CS42L42_PAGE_23 + 0x01)
-#define CS42L42_MIXER_ADC_VOL (CS42L42_PAGE_23 + 0x02)
-
-#define CS42L42_MIXER_CHB_VOL (CS42L42_PAGE_23 + 0x03)
-#define CS42L42_MIXER_CH_VOL_SHIFT 0
-#define CS42L42_MIXER_CH_VOL_MASK (0x3f << CS42L42_MIXER_CH_VOL_SHIFT)
-
-/* Page 0x24 EQ Registers */
-#define CS42L42_EQ_COEF_IN0 (CS42L42_PAGE_24 + 0x01)
-#define CS42L42_EQ_COEF_IN1 (CS42L42_PAGE_24 + 0x02)
-#define CS42L42_EQ_COEF_IN2 (CS42L42_PAGE_24 + 0x03)
-#define CS42L42_EQ_COEF_IN3 (CS42L42_PAGE_24 + 0x04)
-#define CS42L42_EQ_COEF_RW (CS42L42_PAGE_24 + 0x06)
-#define CS42L42_EQ_COEF_OUT0 (CS42L42_PAGE_24 + 0x07)
-#define CS42L42_EQ_COEF_OUT1 (CS42L42_PAGE_24 + 0x08)
-#define CS42L42_EQ_COEF_OUT2 (CS42L42_PAGE_24 + 0x09)
-#define CS42L42_EQ_COEF_OUT3 (CS42L42_PAGE_24 + 0x0A)
-#define CS42L42_EQ_INIT_STAT (CS42L42_PAGE_24 + 0x0B)
-#define CS42L42_EQ_START_FILT (CS42L42_PAGE_24 + 0x0C)
-#define CS42L42_EQ_MUTE_CTL (CS42L42_PAGE_24 + 0x0E)
-
-/* Page 0x25 Audio Port Registers */
-#define CS42L42_SP_RX_CH_SEL (CS42L42_PAGE_25 + 0x01)
-
-#define CS42L42_SP_RX_ISOC_CTL (CS42L42_PAGE_25 + 0x02)
-#define CS42L42_SP_RX_RSYNC_SHIFT 6
-#define CS42L42_SP_RX_RSYNC_MASK (1 << CS42L42_SP_RX_RSYNC_SHIFT)
-#define CS42L42_SP_RX_NSB_POS_SHIFT 3
-#define CS42L42_SP_RX_NSB_POS_MASK (7 << CS42L42_SP_RX_NSB_POS_SHIFT)
-#define CS42L42_SP_RX_NFS_NSBB_SHIFT 2
-#define CS42L42_SP_RX_NFS_NSBB_MASK (1 << CS42L42_SP_RX_NFS_NSBB_SHIFT)
-#define CS42L42_SP_RX_ISOC_MODE_SHIFT 0
-#define CS42L42_SP_RX_ISOC_MODE_MASK (3 << CS42L42_SP_RX_ISOC_MODE_SHIFT)
-
-#define CS42L42_SP_RX_FS (CS42L42_PAGE_25 + 0x03)
-#define CS42l42_SPDIF_CH_SEL (CS42L42_PAGE_25 + 0x04)
-#define CS42L42_SP_TX_ISOC_CTL (CS42L42_PAGE_25 + 0x05)
-#define CS42L42_SP_TX_FS (CS42L42_PAGE_25 + 0x06)
-#define CS42L42_SPDIF_SW_CTL1 (CS42L42_PAGE_25 + 0x07)
-
-/* Page 0x26 SRC Registers */
-#define CS42L42_SRC_SDIN_FS (CS42L42_PAGE_26 + 0x01)
-#define CS42L42_SRC_SDIN_FS_SHIFT 0
-#define CS42L42_SRC_SDIN_FS_MASK (0x1f << CS42L42_SRC_SDIN_FS_SHIFT)
-
-#define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09)
-
-/* Page 0x28 S/PDIF Registers */
-#define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01)
-#define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02)
-#define CS42L42_SPDIF_CTL3 (CS42L42_PAGE_28 + 0x03)
-#define CS42L42_SPDIF_CTL4 (CS42L42_PAGE_28 + 0x04)
-
-/* Page 0x29 Serial Port TX Registers */
-#define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01)
-#define CS42L42_ASP_TX_CH_EN (CS42L42_PAGE_29 + 0x02)
-#define CS42L42_ASP_TX_CH_AP_RES (CS42L42_PAGE_29 + 0x03)
-#define CS42L42_ASP_TX_CH1_BIT_MSB (CS42L42_PAGE_29 + 0x04)
-#define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05)
-#define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06)
-#define CS42L42_ASP_TX_CH2_BIT_MSB (CS42L42_PAGE_29 + 0x0A)
-#define CS42L42_ASP_TX_CH2_BIT_LSB (CS42L42_PAGE_29 + 0x0B)
-
-/* Page 0x2A Serial Port RX Registers */
-#define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01)
-#define CS42L42_ASP_RX0_CH_EN_SHIFT 2
-#define CS42L42_ASP_RX0_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT)
-#define CS42L42_ASP_RX0_CH1_EN 1
-#define CS42L42_ASP_RX0_CH2_EN 2
-#define CS42L42_ASP_RX0_CH3_EN 4
-#define CS42L42_ASP_RX0_CH4_EN 8
-
-#define CS42L42_ASP_RX_DAI0_CH1_AP_RES (CS42L42_PAGE_2A + 0x02)
-#define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03)
-#define CS42L42_ASP_RX_DAI0_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x04)
-#define CS42L42_ASP_RX_DAI0_CH2_AP_RES (CS42L42_PAGE_2A + 0x05)
-#define CS42L42_ASP_RX_DAI0_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x06)
-#define CS42L42_ASP_RX_DAI0_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x07)
-#define CS42L42_ASP_RX_DAI0_CH3_AP_RES (CS42L42_PAGE_2A + 0x08)
-#define CS42L42_ASP_RX_DAI0_CH3_BIT_MSB (CS42L42_PAGE_2A + 0x09)
-#define CS42L42_ASP_RX_DAI0_CH3_BIT_LSB (CS42L42_PAGE_2A + 0x0A)
-#define CS42L42_ASP_RX_DAI0_CH4_AP_RES (CS42L42_PAGE_2A + 0x0B)
-#define CS42L42_ASP_RX_DAI0_CH4_BIT_MSB (CS42L42_PAGE_2A + 0x0C)
-#define CS42L42_ASP_RX_DAI0_CH4_BIT_LSB (CS42L42_PAGE_2A + 0x0D)
-#define CS42L42_ASP_RX_DAI1_CH1_AP_RES (CS42L42_PAGE_2A + 0x0E)
-#define CS42L42_ASP_RX_DAI1_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x0F)
-#define CS42L42_ASP_RX_DAI1_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x10)
-#define CS42L42_ASP_RX_DAI1_CH2_AP_RES (CS42L42_PAGE_2A + 0x11)
-#define CS42L42_ASP_RX_DAI1_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x12)
-#define CS42L42_ASP_RX_DAI1_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x13)
-
-#define CS42L42_ASP_RX_CH_AP_SHIFT 6
-#define CS42L42_ASP_RX_CH_AP_MASK (1 << CS42L42_ASP_RX_CH_AP_SHIFT)
-#define CS42L42_ASP_RX_CH_AP_LOW 0
-#define CS42L42_ASP_RX_CH_AP_HI 1
-#define CS42L42_ASP_RX_CH_RES_SHIFT 0
-#define CS42L42_ASP_RX_CH_RES_MASK (3 << CS42L42_ASP_RX_CH_RES_SHIFT)
-#define CS42L42_ASP_RX_CH_RES_32 3
-#define CS42L42_ASP_RX_CH_RES_16 1
-#define CS42L42_ASP_RX_CH_BIT_ST_SHIFT 0
-#define CS42L42_ASP_RX_CH_BIT_ST_MASK (0xff << CS42L42_ASP_RX_CH_BIT_ST_SHIFT)
-
-/* Page 0x30 ID Registers */
-#define CS42L42_SUB_REVID (CS42L42_PAGE_30 + 0x14)
-#define CS42L42_MAX_REGISTER (CS42L42_PAGE_30 + 0x14)
-
-/* Defines for fracturing values spread across multiple registers */
-#define CS42L42_FRAC0_VAL(val) ((val) & 0x0000ff)
-#define CS42L42_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8)
-#define CS42L42_FRAC2_VAL(val) (((val) & 0xff0000) >> 16)
-
-#define CS42L42_NUM_SUPPLIES 5
-
-static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
- "VA",
- "VP",
- "VCP",
- "VD_FILT",
- "VL",
-};
+#include <dt-bindings/sound/cs42l42.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.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 snd_soc_codec *codec;
+ 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 srate;
- u32 swidth;
+ u32 sample_rate;
+ u32 bclk_ratio;
u8 plug_state;
u8 hs_type;
u8 ts_inv;
@@ -771,6 +49,34 @@ struct cs42l42_private {
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 sdw_waiting_first_unattach;
+ 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/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
new file mode 100644
index 000000000000..867e23d4fb8d
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver jack handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/build_bug.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-jack.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+static const unsigned int cs42l43_accdet_us[] = {
+ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000,
+};
+
+static const unsigned int cs42l43_accdet_db_ms[] = {
+ 0, 125, 250, 500, 750, 1000, 1250, 1500,
+};
+
+static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 };
+
+static const unsigned int cs42l43_accdet_bias_sense[] = {
+ 14, 24, 43, 52, 61, 71, 90, 99, 0,
+};
+
+static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop,
+ unsigned int defval, unsigned int *val,
+ const unsigned int *values, const int nvalues)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int i, ret;
+
+ ret = device_property_read_u32(cs42l43->dev, prop, &defval);
+ if (ret != -EINVAL && ret < 0) {
+ dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret);
+ return ret;
+ }
+
+ if (val)
+ *val = defval;
+
+ for (i = 0; i < nvalues; i++)
+ if (defval == values[i])
+ return i;
+
+ dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval);
+ return -EINVAL;
+}
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ /* This tip sense invert is always set, HW wants an inverted signal */
+ unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK;
+ unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT;
+ unsigned int autocontrol = 0, pdncntl = 0;
+ int ret;
+
+ dev_dbg(priv->dev, "Configure accessory detect\n");
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ priv->jack_hp = jack;
+
+ if (!jack)
+ goto done;
+
+ ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms");
+ if (ret != -EINVAL) {
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+
+ if (ret > CS42L43_N_BUTTONS) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n");
+ goto error;
+ }
+
+ ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
+ priv->buttons, ret);
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+ } else {
+ priv->buttons[0] = 70;
+ priv->buttons[1] = 185;
+ priv->buttons[2] = 355;
+ priv->buttons[3] = 735;
+ }
+
+ ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us,
+ cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT;
+
+ priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low");
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170,
+ &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms,
+ ARRAY_SIZE(cs42l43_accdet_ramp_ms));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 14,
+ &priv->bias_sense_ua, cs42l43_accdet_bias_sense,
+ ARRAY_SIZE(cs42l43_accdet_bias_sense));
+ if (ret < 0)
+ goto error;
+
+ if (priv->bias_sense_ua)
+ autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT;
+
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute"))
+ autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK;
+
+ ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms",
+ &priv->tip_debounce_ms);
+ if (ret < 0 && ret != -EINVAL) {
+ dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret);
+ goto error;
+ }
+
+ /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT;
+ else
+ autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500,
+ &priv->tip_fall_db_ms, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500,
+ &priv->tip_rise_db_ms, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) {
+ unsigned int ring_deb = 0;
+
+ priv->use_ring_sense = true;
+
+ /* HW wants an inverted signal, so invert the invert */
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert"))
+ ring_deb |= CS42L43_RINGSENSE_INV_MASK;
+
+ if (!device_property_read_bool(cs42l43->dev,
+ "cirrus,ring-disable-pullup"))
+ ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT;
+ pdncntl |= CS42L43_RING_SENSE_EN_MASK;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL,
+ CS42L43_RINGSENSE_INV_MASK |
+ CS42L43_RINGSENSE_PULLUP_PDNB_MASK |
+ CS42L43_RINGSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_RINGSENSE_RISING_DB_TIME_MASK,
+ ring_deb);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL,
+ CS42L43_TIPSENSE_INV_MASK |
+ CS42L43_TIPSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK |
+ CS42L43_AUTO_HSDET_TIME_MASK, hs2);
+
+done:
+ ret = 0;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK |
+ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL,
+ CS42L43_RING_SENSE_EN_MASK, pdncntl);
+
+ dev_dbg(priv->dev, "Successfully configured accessory detect\n");
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ if (!type_detect) {
+ if (priv->bias_low)
+ val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ if (priv->bias_sense_ua)
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, val);
+
+ msleep(priv->bias_ramp_ms);
+}
+
+static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+
+ if (priv->bias_sense_ua) {
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ }
+}
+
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ queue_delayed_work(system_wq, &priv->bias_sense_timeout,
+ msecs_to_jiffies(1000));
+
+ return IRQ_HANDLED;
+}
+
+#define CS42L43_JACK_PRESENT 0x3
+#define CS42L43_JACK_ABSENT 0x0
+
+#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
+#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
+#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
+#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN)
+#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL)
+#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+static inline bool cs42l43_jack_present(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ return sts == CS42L43_JACK_PRESENT;
+}
+
+static void cs42l43_start_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start button detect\n");
+
+ priv->button_detect_running = true;
+
+ if (priv->bias_low)
+ val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK, val);
+}
+
+static void cs42l43_stop_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop button detect\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK,
+ CS42L43_MIC_LVL_DET_DISABLE_MASK);
+
+ priv->button_detect_running = false;
+}
+
+#define CS42L43_BUTTON_COMB_US 11000
+#define CS42L43_BUTTON_COMB_MAX 512
+#define CS42L43_BUTTON_ROUT 2210
+
+irqreturn_t cs42l43_button_press(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct cs42l43 *cs42l43 = priv->core;
+ irqreturn_t iret = IRQ_NONE;
+ unsigned int buttons = 0;
+ unsigned int val = 0;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button press: %d\n", ret);
+ return iret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!priv->button_detect_running) {
+ dev_dbg(priv->dev, "Spurious button press IRQ\n");
+ goto error;
+ }
+
+ // Wait for 2 full cycles of comb filter to ensure good reading
+ usleep_range(2 * CS42L43_BUTTON_COMB_US, 2 * CS42L43_BUTTON_COMB_US + 50);
+
+ regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val);
+
+ /* Bail if jack removed, the button is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv)) {
+ dev_dbg(priv->dev, "Button ignored due to removal\n");
+ goto error;
+ }
+
+ if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) {
+ dev_dbg(priv->dev, "Button ignored due to bias sense\n");
+ goto error;
+ }
+
+ val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT;
+ val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20);
+ if (val)
+ val = (CS42L43_BUTTON_ROUT << 20) / val;
+ else
+ val = UINT_MAX;
+
+ for (i = 0; i < CS42L43_N_BUTTONS; i++) {
+ if (val < priv->buttons[i]) {
+ buttons = SND_JACK_BTN_0 >> i;
+ dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val);
+ break;
+ }
+ }
+
+ if (!buttons)
+ dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val);
+
+ snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS);
+
+ iret = IRQ_HANDLED;
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+irqreturn_t cs42l43_button_release(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ irqreturn_t iret = IRQ_NONE;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button release: %d\n", ret);
+ return iret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (priv->button_detect_running) {
+ dev_dbg(priv->dev, "Button release IRQ\n");
+
+ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS);
+
+ iret = IRQ_HANDLED;
+ } else {
+ dev_dbg(priv->dev, "Spurious button release IRQ\n");
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+void cs42l43_bias_sense_timeout(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ bias_sense_timeout.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (cs42l43_jack_present(priv) && priv->button_detect_running) {
+ dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n");
+
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+static void cs42l43_start_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Start load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component));
+
+ priv->load_detect_running = true;
+
+ if (priv->hp_ena && !priv->hp_ilimited) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_shutdown);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ time_left = wait_for_completion_timeout(&priv->hp_shutdown,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP power down timed out\n");
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK,
+ CS42L43_HPLOAD_DET_EN_MASK);
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component));
+}
+
+static void cs42l43_stop_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK,
+ 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2,
+ CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK,
+ priv->adc_ena);
+
+ if (priv->hp_ena && !priv->hp_ilimited) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP restore timed out\n");
+ }
+
+ priv->load_detect_running = false;
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component));
+}
+
+static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0;
+ unsigned long time_left;
+
+ reinit_completion(&priv->load_detect);
+
+ cs42l43_start_load_detect(priv);
+ time_left = wait_for_completion_timeout(&priv->load_detect,
+ msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS));
+ cs42l43_stop_load_detect(priv);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val);
+
+ dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val);
+
+ /* Bail if jack removed, the load is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ if (mic) {
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+
+ return CS42L43_JACK_HEADSET;
+ }
+
+ switch (val & CS42L43_AMP3_RES_DET_MASK) {
+ case 0x0: // low impedance
+ case 0x1: // high impedance
+ return CS42L43_JACK_HEADPHONE;
+ case 0x2: // lineout
+ case 0x3: // Open circuit
+ return CS42L43_JACK_LINEOUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200;
+ unsigned int type = 0xff;
+ unsigned long time_left;
+
+ reinit_completion(&priv->type_detect);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK);
+
+ cs42l43_start_hs_bias(priv, true);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT);
+
+ time_left = wait_for_completion_timeout(&priv->type_detect,
+ msecs_to_jiffies(timeout_ms));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT);
+ cs42l43_stop_hs_bias(priv);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK, 0);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type);
+
+ dev_dbg(priv->dev, "Type detect: 0x%x\n", type);
+
+ /* Bail if jack removed, the type is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ switch (type & CS42L43_HSDET_TYPE_STS_MASK) {
+ case 0x0: // CTIA
+ case 0x1: // OMTP
+ return cs42l43_run_load_detect(priv, true);
+ case 0x2: // 3-pole
+ return cs42l43_run_load_detect(priv, false);
+ case 0x3: // Open-circuit
+ return CS42L43_JACK_EXTENSION;
+ default:
+ return -EINVAL;
+ }
+}
+
+void cs42l43_clear_jack(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ cs42l43_stop_button_detect(priv);
+ cs42l43_stop_hs_bias(priv);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_JACK_STEREO_CONFIG_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
+ 0x2 << CS42L43_HSDET_MODE_SHIFT);
+}
+
+void cs42l43_tip_sense_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ tip_sense_work.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+ unsigned int tip, ring;
+ int ret, report;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts);
+
+ tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+ ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ if (tip == CS42L43_JACK_PRESENT) {
+ if (cs42l43->sdw && !priv->jack_present) {
+ priv->jack_present = true;
+ pm_runtime_get(priv->dev);
+ }
+
+ if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) {
+ report = CS42L43_JACK_OPTICAL;
+ } else {
+ report = cs42l43_run_type_detect(priv);
+ if (report < 0) {
+ dev_err(priv->dev, "Jack detect failed: %d\n", report);
+ goto error;
+ }
+ }
+
+ snd_soc_jack_report(priv->jack_hp, report, report);
+ } else {
+ priv->jack_override = 0;
+
+ cs42l43_clear_jack(priv);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ if (cs42l43->sdw && priv->jack_present) {
+ pm_runtime_put(priv->dev);
+ priv->jack_present = false;
+ }
+ }
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ priv->suspend_jack_debounce = false;
+
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_tip_sense(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ unsigned int db_delay = priv->tip_debounce_ms;
+
+ cancel_delayed_work(&priv->bias_sense_timeout);
+ cancel_delayed_work(&priv->tip_sense_work);
+
+ // Ensure delay after suspend is long enough to avoid false detection
+ if (priv->suspend_jack_debounce)
+ db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms;
+
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work,
+ msecs_to_jiffies(db_delay));
+
+ return IRQ_HANDLED;
+}
+
+enum cs42l43_raw_jack {
+ CS42L43_JACK_RAW_CTIA = 0,
+ CS42L43_JACK_RAW_OMTP,
+ CS42L43_JACK_RAW_HEADPHONE,
+ CS42L43_JACK_RAW_LINE_OUT,
+ CS42L43_JACK_RAW_LINE_IN,
+ CS42L43_JACK_RAW_MICROPHONE,
+ CS42L43_JACK_RAW_OPTICAL,
+};
+
+#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSGND_HS3_SEL_MASK | \
+ CS42L43_HSGND_HS4_SEL_MASK)
+
+static const struct cs42l43_jack_override_mode {
+ unsigned int hsdet_mode;
+ unsigned int mic_ctrl;
+ unsigned int clamp_ctrl;
+ int report;
+} cs42l43_jack_override_modes[] = {
+ [CS42L43_JACK_RAW_CTIA] = {
+ .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_OMTP] = {
+ .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_HEADPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADPHONE,
+ },
+ [CS42L43_JACK_RAW_LINE_OUT] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_LINEOUT,
+ },
+ [CS42L43_JACK_RAW_LINE_IN] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_MICROPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
+ CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_OPTICAL] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_OPTICAL,
+ },
+};
+
+static const char * const cs42l43_jack_text[] = {
+ "None", "CTIA", "OMTP", "Headphone", "Line-Out",
+ "Line-In", "Microphone", "Optical",
+};
+
+static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) ==
+ ARRAY_SIZE(cs42l43_jack_text) - 1);
+
+SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text);
+
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->jack_lock);
+ ucontrol->value.integer.value[0] = priv->jack_override;
+ mutex_unlock(&priv->jack_lock);
+
+ return 0;
+}
+
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int override = ucontrol->value.integer.value[0];
+
+ if (override >= e->items)
+ return -EINVAL;
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!cs42l43_jack_present(priv)) {
+ mutex_unlock(&priv->jack_lock);
+ return -EBUSY;
+ }
+
+ if (override == priv->jack_override) {
+ mutex_unlock(&priv->jack_lock);
+ return 0;
+ }
+
+ priv->jack_override = override;
+
+ cs42l43_clear_jack(priv);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ if (!override) {
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
+ } else {
+ override--;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK |
+ CS42L43_HSDET_MANUAL_MODE_MASK |
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ cs42l43_jack_override_modes[override].hsdet_mode);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK |
+ CS42L43_JACK_STEREO_CONFIG_MASK,
+ cs42l43_jack_override_modes[override].mic_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ cs42l43_jack_override_modes[override].clamp_ctrl);
+
+ switch (override) {
+ case CS42L43_JACK_RAW_CTIA:
+ case CS42L43_JACK_RAW_OMTP:
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+ break;
+ case CS42L43_JACK_RAW_LINE_IN:
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ break;
+ case CS42L43_JACK_RAW_MICROPHONE:
+ cs42l43_start_hs_bias(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(priv->jack_hp,
+ cs42l43_jack_override_modes[override].report,
+ cs42l43_jack_override_modes[override].report);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ return 1;
+}
diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c
new file mode 100644
index 000000000000..336e88a7a987
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-sdw.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver SoundWire handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/errno.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);
+ pconfig.num = dai->id;
+
+ ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);
+ if (ret) {
+ dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, "SND_SOC_CS42L43");
+
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sdw, sdw_stream);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, "SND_SOC_CS42L43");
+
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, "SND_SOC_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
new file mode 100644
index 000000000000..b61df09f20cf
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.c
@@ -0,0 +1,2476 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/bitmap.h>
+#include <linux/gcd.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/cs42l43.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs42l43.h"
+
+#define CS42L43_DECL_MUX(name, reg) \
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \
+ 0, CS42L43_MIXER_SRC_MASK, \
+ cs42l43_mixer_texts, cs42l43_mixer_values); \
+static const struct snd_kcontrol_new cs42l43_##name##_mux = \
+ SOC_DAPM_ENUM("Route", cs42l43_##name##_enum)
+
+#define CS42L43_DECL_MIXER(name, reg) \
+ CS42L43_DECL_MUX(name##_in1, reg); \
+ CS42L43_DECL_MUX(name##_in2, reg + 0x4); \
+ CS42L43_DECL_MUX(name##_in3, reg + 0x8); \
+ CS42L43_DECL_MUX(name##_in4, reg + 0xC)
+
+#define CS42L43_DAPM_MUX(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux)
+
+#define CS42L43_DAPM_MIXER(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS42L43_BASE_ROUTES(name_str) \
+ { name_str, "Tone Generator 1", "Tone 1" }, \
+ { name_str, "Tone Generator 2", "Tone 2" }, \
+ { name_str, "Decimator 1", "Decimator 1" }, \
+ { name_str, "Decimator 2", "Decimator 2" }, \
+ { name_str, "Decimator 3", "Decimator 3" }, \
+ { name_str, "Decimator 4", "Decimator 4" }, \
+ { name_str, "ASPRX1", "ASPRX1" }, \
+ { name_str, "ASPRX2", "ASPRX2" }, \
+ { name_str, "ASPRX3", "ASPRX3" }, \
+ { name_str, "ASPRX4", "ASPRX4" }, \
+ { name_str, "ASPRX5", "ASPRX5" }, \
+ { name_str, "ASPRX6", "ASPRX6" }, \
+ { name_str, "DP5RX1", "DP5RX1" }, \
+ { name_str, "DP5RX2", "DP5RX2" }, \
+ { name_str, "DP6RX1", "DP6RX1" }, \
+ { name_str, "DP6RX2", "DP6RX2" }, \
+ { name_str, "DP7RX1", "DP7RX1" }, \
+ { name_str, "DP7RX2", "DP7RX2" }, \
+ { name_str, "ASRC INT1", "ASRC_INT1" }, \
+ { name_str, "ASRC INT2", "ASRC_INT2" }, \
+ { name_str, "ASRC INT3", "ASRC_INT3" }, \
+ { name_str, "ASRC INT4", "ASRC_INT4" }, \
+ { name_str, "ASRC DEC1", "ASRC_DEC1" }, \
+ { name_str, "ASRC DEC2", "ASRC_DEC2" }, \
+ { name_str, "ASRC DEC3", "ASRC_DEC3" }, \
+ { name_str, "ASRC DEC4", "ASRC_DEC4" }, \
+ { name_str, "ISRC1 INT1", "ISRC1INT1" }, \
+ { name_str, "ISRC1 INT2", "ISRC1INT2" }, \
+ { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \
+ { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \
+ { name_str, "ISRC2 INT1", "ISRC2INT1" }, \
+ { name_str, "ISRC2 INT2", "ISRC2INT2" }, \
+ { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \
+ { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \
+ { name_str, "EQ1", "EQ" }, \
+ { name_str, "EQ2", "EQ" }
+
+#define CS42L43_MUX_ROUTES(name_str, widget) \
+ { widget, NULL, name_str " Input" }, \
+ { name_str " Input", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input")
+
+#define CS42L43_MIXER_ROUTES(name_str, widget) \
+ { name_str " Mixer", NULL, name_str " Input 1" }, \
+ { name_str " Mixer", NULL, name_str " Input 2" }, \
+ { name_str " Mixer", NULL, name_str " Input 3" }, \
+ { name_str " Mixer", NULL, name_str " Input 4" }, \
+ { widget, NULL, name_str " Mixer" }, \
+ { name_str " Mixer", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input 1"), \
+ CS42L43_BASE_ROUTES(name_str " Input 2"), \
+ CS42L43_BASE_ROUTES(name_str " Input 3"), \
+ CS42L43_BASE_ROUTES(name_str " Input 4")
+
+#define CS42L43_MIXER_VOLUMES(name_str, base) \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv)
+
+#define CS42L43_IRQ_ERROR(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_err(priv->dev, "Error " #name " IRQ\n"); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_ERROR(pll_lost_lock)
+CS42L43_IRQ_ERROR(spkr_clock_stop)
+CS42L43_IRQ_ERROR(spkl_clock_stop)
+CS42L43_IRQ_ERROR(spkr_brown_out)
+CS42L43_IRQ_ERROR(spkl_brown_out)
+CS42L43_IRQ_ERROR(spkr_therm_shutdown)
+CS42L43_IRQ_ERROR(spkl_therm_shutdown)
+CS42L43_IRQ_ERROR(spkr_therm_warm)
+CS42L43_IRQ_ERROR(spkl_therm_warm)
+CS42L43_IRQ_ERROR(spkr_sc_detect)
+CS42L43_IRQ_ERROR(spkl_sc_detect)
+
+static void cs42l43_hp_ilimit_clear_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ hp_ilimit_clear_work.work);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ priv->hp_ilimit_count--;
+
+ if (priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t cs42l43_hp_ilimit(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "headphone ilimit IRQ\n");
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) {
+ if (!priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ priv->hp_ilimit_count++;
+ snd_soc_dapm_mutex_unlock(dapm);
+ return IRQ_HANDLED;
+ }
+
+ dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n",
+ CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ priv->hp_ilimited = true;
+
+ // No need to wait for disable, as just disabling for a period of time
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ena && !priv->load_detect_running) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "ilimit HP restore timed out\n");
+ }
+
+ priv->hp_ilimited = false;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return IRQ_HANDLED;
+}
+
+#define CS42L43_IRQ_COMPLETE(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_dbg(priv->dev, #name " completed\n"); \
+ complete(&priv->name); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_COMPLETE(pll_ready)
+CS42L43_IRQ_COMPLETE(hp_startup)
+CS42L43_IRQ_COMPLETE(hp_shutdown)
+CS42L43_IRQ_COMPLETE(type_detect)
+CS42L43_IRQ_COMPLETE(spkr_shutdown)
+CS42L43_IRQ_COMPLETE(spkl_shutdown)
+CS42L43_IRQ_COMPLETE(spkr_startup)
+CS42L43_IRQ_COMPLETE(spkl_startup)
+CS42L43_IRQ_COMPLETE(load_detect)
+
+static irqreturn_t cs42l43_mic_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_component *component = priv->component;
+ int i;
+
+ dev_dbg(priv->dev, "Microphone shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 1; i < ARRAY_SIZE(priv->kctl); i++) {
+ if (!priv->kctl[i])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[i]->id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs42l43_spk_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_component *component = priv->component;
+
+ dev_dbg(priv->dev, "Speaker shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ if (!priv->kctl[0])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[0]->id);
+
+ return IRQ_HANDLED;
+}
+
+static const unsigned int cs42l43_sample_rates[] = {
+ 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000,
+};
+
+#define CS42L43_CONSUMER_RATE_MASK 0xFF
+#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer
+
+static const struct snd_pcm_hw_constraint_list cs42l43_constraint = {
+ .count = ARRAY_SIZE(cs42l43_sample_rates),
+ .list = cs42l43_sample_rates,
+};
+
+static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = !dai->id || !!regmap_test_bits(cs42l43->regmap,
+ CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+
+ if (provider)
+ priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK;
+ else
+ priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &priv->constraint);
+}
+
+static int cs42l43_convert_sample_rate(unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0x11;
+ case 16000:
+ return 0x12;
+ case 24000:
+ return 0x02;
+ case 32000:
+ return 0x13;
+ case 44100:
+ return 0x0B;
+ case 48000:
+ return 0x03;
+ case 96000:
+ return 0x04;
+ case 192000:
+ return 0x05;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = cs42l43_convert_sample_rate(params_rate(params));
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret);
+ return ret;
+ }
+
+ //FIXME: For now lets just set sample rate 1, this needs expanded in the future
+ regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1,
+ CS42L43_SAMPLE_RATE_MASK, ret);
+
+ return 0;
+}
+
+static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK);
+ int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ int n_chans = params_channels(params);
+ int data_width = params_width(params);
+ int n_slots = n_chans;
+ int slot_width = data_width;
+ int frame, bclk_target, i;
+ unsigned int reg;
+ int *slots;
+
+ if (priv->n_slots) {
+ n_slots = priv->n_slots;
+ slot_width = priv->slot_width;
+ }
+
+ if (!dsp_mode && (n_slots & 0x1)) {
+ dev_dbg(priv->dev, "Forcing balanced channels on ASP\n");
+ n_slots++;
+ }
+
+ frame = n_slots * slot_width;
+ bclk_target = params_rate(params) * frame;
+
+ if (provider) {
+ unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK);
+ int n = bclk_target / gcd_nm;
+ int m = CS42L43_INTERNAL_SYSCLK / gcd_nm;
+
+ if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) ||
+ m > CS42L43_ASP_BCLK_M_MASK) {
+ dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target);
+ return -EINVAL;
+ }
+
+ dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n",
+ n, m, bclk_target, n_slots, slot_width);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1,
+ CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK,
+ n << CS42L43_ASP_BCLK_N_SHIFT |
+ m << CS42L43_ASP_BCLK_M_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1,
+ CS42L43_ASP_FSYNC_M_MASK, frame);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4,
+ CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK,
+ frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ reg = CS42L43_ASP_TX_CH1_CTRL;
+ slots = priv->tx_slots;
+ } else {
+ reg = CS42L43_ASP_RX_CH1_CTRL;
+ slots = priv->rx_slots;
+ }
+
+ for (i = 0; i < n_chans; i++, reg += 4) {
+ int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK);
+ int slot_pos;
+
+ if (dsp_mode)
+ slot_pos = slots[i] * slot_width;
+ else
+ slot_pos = (slots[i] / 2) * slot_width;
+
+ dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n",
+ i, slots[i], slot_pos, slot_phase);
+
+ regmap_update_bits(cs42l43->regmap, reg,
+ CS42L43_ASP_CH_WIDTH_MASK |
+ CS42L43_ASP_CH_SLOT_MASK |
+ CS42L43_ASP_CH_SLOT_PHASE_MASK,
+ ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) |
+ (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) |
+ slot_phase);
+ }
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+}
+
+static int cs42l43_asp_set_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);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ struct snd_soc_dapm_route routes[] = {
+ { "BCLK", NULL, "FSYNC" },
+ };
+ unsigned int asp_ctrl = 0;
+ unsigned int data_ctrl = 0;
+ unsigned int fsync_ctrl = 0;
+ unsigned int clk_config = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_B:
+ asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK;
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ if (provider)
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!provider)
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+ clk_config |= CS42L43_ASP_MASTER_MODE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported ASP mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK;
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK,
+ asp_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL,
+ CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK |
+ CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK,
+ data_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK |
+ CS42L43_ASP_BCLK_INV_MASK,
+ clk_config);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3,
+ CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK,
+ fsync_ctrl);
+
+ return 0;
+}
+
+static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask,
+ int *slots, unsigned int nslots)
+{
+ int i = 0;
+ int slot;
+
+ for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) {
+ if (i == nslots) {
+ dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n",
+ mask);
+ return;
+ }
+
+ slots[i++] = slot;
+ }
+
+}
+
+static int cs42l43_asp_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ priv->n_slots = slots;
+ priv->slot_width = slot_width;
+
+ if (!slots) {
+ tx_mask = CS42L43_DEFAULT_SLOTS;
+ rx_mask = CS42L43_DEFAULT_SLOTS;
+ }
+
+ cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ return 0;
+}
+
+static int cs42l43_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ static const char * const controls[] = {
+ "Speaker Digital Switch",
+ "Decimator 1 Switch",
+ "Decimator 2 Switch",
+ "Decimator 3 Switch",
+ "Decimator 4 Switch",
+ };
+ int i;
+
+ static_assert(ARRAY_SIZE(controls) == ARRAY_SIZE(priv->kctl));
+
+ for (i = 0; i < ARRAY_SIZE(controls); i++) {
+ if (priv->kctl[i])
+ continue;
+
+ priv->kctl[i] = snd_soc_component_get_kcontrol(component, controls[i]);
+ }
+
+ return 0;
+}
+
+static int cs42l43_dai_remove(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->kctl); i++)
+ priv->kctl[i] = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l43_asp_ops = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .startup = cs42l43_startup,
+ .hw_params = cs42l43_asp_hw_params,
+ .set_fmt = cs42l43_asp_set_fmt,
+ .set_tdm_slot = cs42l43_asp_set_tdm_slot,
+};
+
+static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = cs42l43_sdw_add_peripheral(substream, params, dai);
+ if (ret)
+ return ret;
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+}
+
+static const struct snd_soc_dai_ops cs42l43_sdw_ops = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .startup = cs42l43_startup,
+ .set_stream = cs42l43_sdw_set_stream,
+ .hw_params = cs42l43_sdw_hw_params,
+ .hw_free = cs42l43_sdw_remove_peripheral,
+};
+
+#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver cs42l43_dais[] = {
+ {
+ .name = "cs42l43-asp",
+ .ops = &cs42l43_asp_ops,
+ .symmetric_rate = 1,
+ .capture = {
+ .stream_name = "ASP Capture",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp1",
+ .id = 1,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp2",
+ .id = 2,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp3",
+ .id = 3,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp4",
+ .id = 4,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp5",
+ .id = 5,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP5 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp6",
+ .id = 6,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP6 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp7",
+ .id = 7,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP7 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0);
+
+static const char * const cs42l43_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" };
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1,
+ CS42L43_ADC_AIN_SEL_SHIFT,
+ cs42l43_adc1_input_text);
+
+static const struct snd_kcontrol_new cs42l43_adc1_input_ctl =
+ SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input);
+
+static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text);
+
+static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = {
+ SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode),
+ SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode),
+};
+
+static const char * const cs42l43_pdm_clk_text[] = {
+ "3.072MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0);
+static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0);
+
+static const char * const cs42l43_wnf_corner_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+
+static const char * const cs42l43_hpf_corner_text[] = {
+ "3Hz", "12Hz", "48Hz", "96Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1);
+
+static const char * const cs42l43_headphone_ramp_text[] = {
+ "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44",
+ "48", "66", "72",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_RAMP_SHIFT,
+ cs42l43_headphone_ramp_text);
+
+static const char * const cs42l43_tone_freq_text[] = {
+ "1kHz", "2kHz", "4kHz", "6kHz", "8kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static const char * const cs42l43_mixer_texts[] = {
+ "None",
+ "Tone Generator 1", "Tone Generator 2",
+ "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4",
+ "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6",
+ "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2",
+ "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4",
+ "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4",
+ "ISRC1 INT1", "ISRC1 INT2",
+ "ISRC1 DEC1", "ISRC1 DEC2",
+ "ISRC2 INT1", "ISRC2 INT2",
+ "ISRC2 DEC1", "ISRC2 DEC2",
+ "EQ1", "EQ2",
+};
+
+static const unsigned int cs42l43_mixer_values[] = {
+ 0x00, // None
+ 0x04, 0x05, // Tone Generator 1, 2
+ 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2
+ 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4
+ 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4
+ 0x50, 0x51, // ISRC1 INT1, 2
+ 0x52, 0x53, // ISRC1 DEC1, 2
+ 0x54, 0x55, // ISRC2 INT1, 2
+ 0x56, 0x57, // ISRC2 DEC1, 2
+ 0x58, 0x59, // EQ1, 2
+};
+
+CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT);
+CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT);
+CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT);
+CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT);
+CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT);
+CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT);
+
+CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT);
+CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT);
+CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT);
+CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT);
+CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT);
+CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT);
+CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT);
+CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT);
+CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT);
+CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT);
+
+CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1);
+CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1);
+CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1);
+CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1);
+CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1);
+CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1);
+CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1);
+CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1);
+
+CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1);
+CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1);
+CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1);
+CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1);
+CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1);
+CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1);
+
+CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1);
+CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1);
+
+CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1);
+CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1);
+CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1);
+CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1);
+
+static int cs42l43_dapm_get_volsw(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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_volsw(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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_get_enum(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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_enum(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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs));
+
+ return 0;
+}
+
+static int cs42l43_eq_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ mutex_lock(&priv->spk_vu_lock);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, 0);
+
+ mutex_unlock(&priv->spk_vu_lock);
+}
+
+static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must
+ * be cached for the non-volatiles, so drop it from the cache here so
+ * we force a read.
+ */
+ ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL,
+ CS42L43_SHUTTER_CONTROL);
+ if (ret) {
+ dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret);
+ goto error;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter status: %d\n", ret);
+ goto error;
+ }
+
+ ret = !(val & BIT(shift));
+
+ dev_dbg(priv->dev, "%s shutter is %s\n",
+ BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker",
+ ret ? "open" : "closed");
+
+error:
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static int cs42l43_decim_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT);
+ if (ret > 0)
+ ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ return ret;
+}
+
+static int cs42l43_spk_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT);
+ if (ret > 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ return ret;
+}
+
+static int cs42l43_spk_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret > 0)
+ cs42l43_spk_vu_sync(priv);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs42l43_controls[] = {
+ SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum,
+ cs42l43_jack_get, cs42l43_jack_put),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2,
+ CS42L43_ADC_PGA_GAIN_SHIFT,
+ 0xF, 4, cs42l43_adc_tlv),
+
+ SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0),
+ SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0),
+ SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk),
+ SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk),
+
+ SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner),
+ SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner),
+ SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner),
+ SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner),
+
+ SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner),
+ SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner),
+ SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner),
+ SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner),
+
+ SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+
+ SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+
+ SOC_DOUBLE_R_EXT("Speaker Digital Switch",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_MUTE_SHIFT, 1, 1,
+ cs42l43_spk_get, cs42l43_spk_put),
+
+ SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_VOL_SHIFT,
+ 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put,
+ cs42l43_speaker_tlv),
+
+ SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up),
+ SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down),
+
+ CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1),
+
+ SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL,
+ CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT,
+ 0x11B, 229, cs42l43_headphone_tlv),
+
+ SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1,
+ CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0),
+ SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0),
+ SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp),
+
+ CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1),
+
+ SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq),
+ SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq),
+
+ SOC_DOUBLE_EXT("EQ Switch",
+ CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT,
+ CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1,
+ cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw),
+
+ SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS,
+ cs42l43_eq_get, cs42l43_eq_put),
+
+ CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1),
+};
+
+static int cs42l43_eq_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int i, ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK);
+
+ for (i = 0; i < CS42L43_N_EQ_COEFFS; i++)
+ regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0,
+ priv->eq_coeffs[i]);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, 0);
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0,
+ val, (val & CS42L43_INITIALIZE_DONE_MASK),
+ 2000, 10000);
+ if (ret)
+ dev_err(priv->dev, "Failed to start EQs: %d\n", ret);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0);
+ return ret;
+ default:
+ return 0;
+ }
+}
+
+struct cs42l43_pll_config {
+ unsigned int freq;
+
+ unsigned int div;
+ unsigned int mode;
+ unsigned int cal;
+};
+
+static const struct cs42l43_pll_config cs42l43_pll_configs[] = {
+ { 2400000, 0x50000000, 0x1, 0xA4 },
+ { 3000000, 0x40000000, 0x1, 0x83 },
+ { 3072000, 0x40000000, 0x3, 0x80 },
+};
+
+static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src,
+ unsigned int freq)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == src && priv->refclk_freq == freq)
+ return 0;
+
+ if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) {
+ dev_err(priv->dev, "PLL active, can't change configuration\n");
+ return -EBUSY;
+ }
+
+ switch (src) {
+ case CS42L43_SYSCLK_MCLK:
+ case CS42L43_SYSCLK_SDW:
+ dev_dbg(priv->dev, "Source PLL from %s at %uHz\n",
+ src ? "SoundWire" : "MCLK", freq);
+
+ priv->refclk_src = src;
+ priv->refclk_freq = freq;
+
+ return 0;
+ default:
+ dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src);
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_enable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence enable_seq[] = {
+ { CS42L43_OSC_DIV_SEL, 0x0, },
+ { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+ const struct cs42l43_pll_config *config = NULL;
+ unsigned int div = 0;
+ unsigned int freq = priv->refclk_freq;
+ unsigned long time_left;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW) {
+ if (!freq)
+ freq = cs42l43->sdw_freq;
+ else if (!cs42l43->sdw_freq)
+ cs42l43->sdw_freq = freq;
+ }
+
+ dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq);
+
+ div = fls(freq) -
+ fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq);
+ freq >>= div;
+
+ if (div <= CS42L43_PLL_REFCLK_DIV_MASK) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) {
+ if (freq == cs42l43_pll_configs[i].freq) {
+ config = &cs42l43_pll_configs[i];
+ break;
+ }
+ }
+ }
+
+ if (!config) {
+ dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK,
+ div << CS42L43_PLL_REFCLK_DIV_SHIFT |
+ priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT);
+ regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_MODE_BYPASS_500_MASK |
+ CS42L43_PLL_MODE_BYPASS_1029_MASK,
+ config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO,
+ CS42L43_PLL_CAL_RATIO_MASK, config->cal);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK);
+
+ reinit_completion(&priv->pll_ready);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK);
+
+ time_left = wait_for_completion_timeout(&priv->pll_ready,
+ msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS));
+ if (!time_left) {
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ dev_err(priv->dev, "Timeout out waiting for PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW)
+ cs42l43->sdw_pll_active = true;
+
+ dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left));
+
+ /*
+ * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL,
+ * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole
+ * change over happens under the regmap lock to prevent any reads.
+ */
+ regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq));
+
+ return 0;
+}
+
+static int cs42l43_disable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence disable_seq[] = {
+ { CS42L43_MCLK_SRC_SEL, 0x0, 5, },
+ { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Disabling PLL\n");
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq));
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ cs42l43->sdw_pll_active = false;
+
+ return 0;
+}
+
+static int cs42l43_pll_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret);
+ break;
+ }
+ }
+
+ ret = cs42l43_enable_pll(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = cs42l43_disable_pll(priv);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK)
+ clk_disable_unprepare(priv->mclk);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd,
+ int event, int timeout_ms)
+{
+ unsigned long time_left;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ reinit_completion(pmu);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ reinit_completion(pmd);
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms));
+ break;
+ default:
+ return 0;
+ }
+
+ if (!time_left)
+ return -ETIMEDOUT;
+ else
+ return 0;
+}
+
+static int cs42l43_spkr_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkr_startup,
+ &priv->spkr_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_spkl_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkl_startup,
+ &priv->spkl_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_ena &= ~mask;
+ priv->hp_ena |= val;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ if (!priv->load_detect_running && !priv->hp_ilimited)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ mask, val);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->load_detect_running || priv->hp_ilimited)
+ break;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_mic_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int reg, ramp, mute;
+ unsigned int *val;
+ int ret;
+
+ switch (w->shift) {
+ case CS42L43_ADC1_EN_SHIFT:
+ case CS42L43_PDM1_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM1_VD_RAMP_MASK;
+ mute = CS42L43_DECIM1_MUTE_MASK;
+ val = &priv->decim_cache[0];
+ break;
+ case CS42L43_ADC2_EN_SHIFT:
+ case CS42L43_PDM1_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM2_VD_RAMP_MASK;
+ mute = CS42L43_DECIM2_MUTE_MASK;
+ val = &priv->decim_cache[1];
+ break;
+ case CS42L43_PDM2_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM3_VD_RAMP_MASK;
+ mute = CS42L43_DECIM3_MUTE_MASK;
+ val = &priv->decim_cache[2];
+ break;
+ case CS42L43_PDM2_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM4_VD_RAMP_MASK;
+ mute = CS42L43_DECIM4_MUTE_MASK;
+ val = &priv->decim_cache[3];
+ break;
+ default:
+ dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regmap_read(cs42l43->regmap, reg, val);
+ if (ret) {
+ dev_err(priv->dev,
+ "Failed to cache decimator settings: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ ret = cs42l43_mic_ev(w, kcontrol, event);
+ if (ret)
+ return ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->adc_ena &= ~mask;
+ priv->adc_ena |= val;
+
+ if (!priv->load_detect_running)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ mask, val);
+ fallthrough;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs42l43_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("ADC1_IN1_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN1_N"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_N"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_P"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_N"),
+
+ SND_SOC_DAPM_INPUT("PDM1_DIN"),
+ SND_SOC_DAPM_INPUT("PDM2_DIN"),
+
+ SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl),
+
+ SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[0]),
+ SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[1]),
+
+ SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0),
+
+ SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"),
+
+ SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPDIF_TX"),
+
+ SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0,
+ cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("AMP3_OUT"),
+ SND_SOC_DAPM_OUTPUT("AMP4_OUT"),
+
+ SND_SOC_DAPM_SIGGEN("Tone"),
+ SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+
+ SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT,
+ 0, NULL, 0, cs42l43_eq_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT,
+ 0, NULL, 0),
+ CS42L43_DAPM_MUX("ASPTX1", asptx1),
+ CS42L43_DAPM_MUX("ASPTX2", asptx2),
+ CS42L43_DAPM_MUX("ASPTX3", asptx3),
+ CS42L43_DAPM_MUX("ASPTX4", asptx4),
+ CS42L43_DAPM_MUX("ASPTX5", asptx5),
+ CS42L43_DAPM_MUX("ASPTX6", asptx6),
+
+ CS42L43_DAPM_MUX("DP1TX1", dp1tx1),
+ CS42L43_DAPM_MUX("DP1TX2", dp1tx2),
+ CS42L43_DAPM_MUX("DP1TX3", dp1tx3),
+ CS42L43_DAPM_MUX("DP1TX4", dp1tx4),
+ CS42L43_DAPM_MUX("DP2TX1", dp2tx1),
+ CS42L43_DAPM_MUX("DP2TX2", dp2tx2),
+ CS42L43_DAPM_MUX("DP3TX1", dp3tx1),
+ CS42L43_DAPM_MUX("DP3TX2", dp3tx2),
+ CS42L43_DAPM_MUX("DP4TX1", dp4tx1),
+ CS42L43_DAPM_MUX("DP4TX2", dp4tx2),
+
+ CS42L43_DAPM_MUX("ASRC INT1", asrcint1),
+ CS42L43_DAPM_MUX("ASRC INT2", asrcint2),
+ CS42L43_DAPM_MUX("ASRC INT3", asrcint3),
+ CS42L43_DAPM_MUX("ASRC INT4", asrcint4),
+ CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1),
+ CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2),
+ CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3),
+ CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4),
+
+ CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1),
+ CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2),
+ CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1),
+ CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2),
+ CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1),
+ CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2),
+ CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1),
+ CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2),
+
+ CS42L43_DAPM_MUX("SPDIF1", spdif1),
+ CS42L43_DAPM_MUX("SPDIF2", spdif2),
+
+ CS42L43_DAPM_MIXER("EQ1", eq1),
+ CS42L43_DAPM_MIXER("EQ2", eq2),
+
+ CS42L43_DAPM_MIXER("Speaker L", amp1),
+ CS42L43_DAPM_MIXER("Speaker R", amp2),
+
+ CS42L43_DAPM_MIXER("Headphone L", amp3),
+ CS42L43_DAPM_MIXER("Headphone R", amp4),
+};
+
+static const struct snd_soc_dapm_route cs42l43_routes[] = {
+ { "ADC1_IN1_P", NULL, "PLL" },
+ { "ADC1_IN1_N", NULL, "PLL" },
+ { "ADC1_IN2_P", NULL, "PLL" },
+ { "ADC1_IN2_N", NULL, "PLL" },
+ { "ADC2_IN_P", NULL, "PLL" },
+ { "ADC2_IN_N", NULL, "PLL" },
+ { "PDM1_DIN", NULL, "PLL" },
+ { "PDM2_DIN", NULL, "PLL" },
+ { "AMP1_OUT_P", NULL, "PLL" },
+ { "AMP1_OUT_N", NULL, "PLL" },
+ { "AMP2_OUT_P", NULL, "PLL" },
+ { "AMP2_OUT_N", NULL, "PLL" },
+ { "SPDIF_TX", NULL, "PLL" },
+ { "HP", NULL, "PLL" },
+ { "AMP3_OUT", NULL, "PLL" },
+ { "AMP4_OUT", NULL, "PLL" },
+ { "Tone 1", NULL, "PLL" },
+ { "Tone 2", NULL, "PLL" },
+ { "ASP Playback", NULL, "PLL" },
+ { "ASP Capture", NULL, "PLL" },
+ { "DP1 Capture", NULL, "PLL" },
+ { "DP2 Capture", NULL, "PLL" },
+ { "DP3 Capture", NULL, "PLL" },
+ { "DP4 Capture", NULL, "PLL" },
+ { "DP5 Playback", NULL, "PLL" },
+ { "DP6 Playback", NULL, "PLL" },
+ { "DP7 Playback", NULL, "PLL" },
+
+ { "ADC1 Input", "IN1", "ADC1_IN1_P" },
+ { "ADC1 Input", "IN1", "ADC1_IN1_N" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_P" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_N" },
+
+ { "ADC1", NULL, "ADC1 Input" },
+ { "ADC2", NULL, "ADC2_IN_P" },
+ { "ADC2", NULL, "ADC2_IN_N" },
+
+ { "PDM1L", NULL, "PDM1_DIN" },
+ { "PDM1R", NULL, "PDM1_DIN" },
+ { "PDM2L", NULL, "PDM2_DIN" },
+ { "PDM2R", NULL, "PDM2_DIN" },
+
+ { "Decimator 1 Mode", "PDM", "PDM1L" },
+ { "Decimator 1 Mode", "ADC", "ADC1" },
+ { "Decimator 2 Mode", "PDM", "PDM1R" },
+ { "Decimator 2 Mode", "ADC", "ADC2" },
+
+ { "Decimator 1", NULL, "Decimator 1 Mode" },
+ { "Decimator 2", NULL, "Decimator 2 Mode" },
+ { "Decimator 3", NULL, "PDM2L" },
+ { "Decimator 4", NULL, "PDM2R" },
+
+ { "ASP Capture", NULL, "ASPTX1" },
+ { "ASP Capture", NULL, "ASPTX2" },
+ { "ASP Capture", NULL, "ASPTX3" },
+ { "ASP Capture", NULL, "ASPTX4" },
+ { "ASP Capture", NULL, "ASPTX5" },
+ { "ASP Capture", NULL, "ASPTX6" },
+ { "ASPTX1", NULL, "BCLK" },
+ { "ASPTX2", NULL, "BCLK" },
+ { "ASPTX3", NULL, "BCLK" },
+ { "ASPTX4", NULL, "BCLK" },
+ { "ASPTX5", NULL, "BCLK" },
+ { "ASPTX6", NULL, "BCLK" },
+
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+ { "ASPRX3", NULL, "ASP Playback" },
+ { "ASPRX4", NULL, "ASP Playback" },
+ { "ASPRX5", NULL, "ASP Playback" },
+ { "ASPRX6", NULL, "ASP Playback" },
+ { "ASPRX1", NULL, "BCLK" },
+ { "ASPRX2", NULL, "BCLK" },
+ { "ASPRX3", NULL, "BCLK" },
+ { "ASPRX4", NULL, "BCLK" },
+ { "ASPRX5", NULL, "BCLK" },
+ { "ASPRX6", NULL, "BCLK" },
+
+ { "DP1 Capture", NULL, "DP1TX1" },
+ { "DP1 Capture", NULL, "DP1TX2" },
+ { "DP1 Capture", NULL, "DP1TX3" },
+ { "DP1 Capture", NULL, "DP1TX4" },
+
+ { "DP2 Capture", NULL, "DP2TX1" },
+ { "DP2 Capture", NULL, "DP2TX2" },
+
+ { "DP3 Capture", NULL, "DP3TX1" },
+ { "DP3 Capture", NULL, "DP3TX2" },
+
+ { "DP4 Capture", NULL, "DP4TX1" },
+ { "DP4 Capture", NULL, "DP4TX2" },
+
+ { "DP5RX1", NULL, "DP5 Playback" },
+ { "DP5RX2", NULL, "DP5 Playback" },
+
+ { "DP6RX1", NULL, "DP6 Playback" },
+ { "DP6RX2", NULL, "DP6 Playback" },
+
+ { "DP7RX1", NULL, "DP7 Playback" },
+ { "DP7RX2", NULL, "DP7 Playback" },
+
+ { "AMP1", NULL, "vdd-amp" },
+ { "AMP2", NULL, "vdd-amp" },
+
+ { "AMP1_OUT_P", NULL, "AMP1" },
+ { "AMP1_OUT_N", NULL, "AMP1" },
+ { "AMP2_OUT_P", NULL, "AMP2" },
+ { "AMP2_OUT_N", NULL, "AMP2" },
+
+ { "SPDIF_TX", NULL, "SPDIF" },
+
+ { "AMP3_OUT", NULL, "HP" },
+ { "AMP4_OUT", NULL, "HP" },
+
+ { "Tone 1", NULL, "Tone" },
+ { "Tone 1", NULL, "Tone Generator" },
+ { "Tone 2", NULL, "Tone" },
+ { "Tone 2", NULL, "Tone Generator" },
+
+ { "ISRC1INT2", NULL, "ISRC1" },
+ { "ISRC1INT1", NULL, "ISRC1" },
+ { "ISRC1DEC2", NULL, "ISRC1" },
+ { "ISRC1DEC1", NULL, "ISRC1" },
+
+ { "ISRC2INT2", NULL, "ISRC2" },
+ { "ISRC2INT1", NULL, "ISRC2" },
+ { "ISRC2DEC2", NULL, "ISRC2" },
+ { "ISRC2DEC1", NULL, "ISRC2" },
+
+ { "ASRC_INT1", NULL, "ASRC_INT" },
+ { "ASRC_INT2", NULL, "ASRC_INT" },
+ { "ASRC_INT3", NULL, "ASRC_INT" },
+ { "ASRC_INT4", NULL, "ASRC_INT" },
+ { "ASRC_DEC1", NULL, "ASRC_DEC" },
+ { "ASRC_DEC2", NULL, "ASRC_DEC" },
+ { "ASRC_DEC3", NULL, "ASRC_DEC" },
+ { "ASRC_DEC4", NULL, "ASRC_DEC" },
+
+ { "EQ", NULL, "EQ Clock" },
+
+ CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"),
+ CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"),
+ CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"),
+ CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"),
+ CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"),
+ CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"),
+
+ CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"),
+ CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"),
+ CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"),
+ CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"),
+ CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"),
+ CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"),
+ CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"),
+ CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"),
+ CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"),
+ CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"),
+
+ CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"),
+ CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"),
+ CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"),
+ CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"),
+ CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"),
+ CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"),
+ CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"),
+ CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"),
+
+ CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"),
+ CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"),
+
+ CS42L43_MIXER_ROUTES("EQ1", "EQ"),
+ CS42L43_MIXER_ROUTES("EQ2", "EQ"),
+
+ CS42L43_MIXER_ROUTES("Speaker L", "AMP1"),
+ CS42L43_MIXER_ROUTES("Speaker R", "AMP2"),
+
+ CS42L43_MIXER_ROUTES("Headphone L", "HP"),
+ CS42L43_MIXER_ROUTES("Headphone R", "HP"),
+};
+
+static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int src, unsigned int freq, int dir)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+ ret = cs42l43_set_pll(priv, src, freq);
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_component_probe(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ snd_soc_component_init_regmap(component, cs42l43->regmap);
+
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ priv->component = component;
+ priv->constraint = cs42l43_constraint;
+
+ return 0;
+}
+
+static void cs42l43_component_remove(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ cs42l43_set_jack(priv->component, NULL, NULL);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ priv->component = NULL;
+}
+
+static const struct snd_soc_component_driver cs42l43_component_drv = {
+ .name = "cs42l43-codec",
+
+ .probe = cs42l43_component_probe,
+ .remove = cs42l43_component_remove,
+ .set_sysclk = cs42l43_set_sysclk,
+ .set_jack = cs42l43_set_jack,
+
+ .endianness = 1,
+
+ .controls = cs42l43_controls,
+ .num_controls = ARRAY_SIZE(cs42l43_controls),
+ .dapm_widgets = cs42l43_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets),
+ .dapm_routes = cs42l43_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l43_routes),
+};
+
+struct cs42l43_irq {
+ unsigned int irq;
+ const char *name;
+ irq_handler_t handler;
+};
+
+static const struct cs42l43_irq cs42l43_irqs[] = {
+ { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock },
+ { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready },
+ { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup },
+ { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown },
+ { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect },
+ { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense },
+ { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense },
+ { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press },
+ { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release },
+ { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp },
+ { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop },
+ { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop },
+ { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out },
+ { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out },
+ { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown },
+ { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown },
+ { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup },
+ { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup },
+ { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown },
+ { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown },
+ { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm },
+ { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm },
+ { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect },
+ { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect },
+ { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit },
+ { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect },
+};
+
+static int cs42l43_request_irq(struct cs42l43_codec *priv,
+ const char * const name, unsigned int irq,
+ irq_handler_t handler, unsigned long flags)
+{
+ int ret;
+
+ ret = irq_create_mapping(priv->dom, irq);
+ if (ret < 0)
+ return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name);
+
+ dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name);
+
+ ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler,
+ IRQF_ONESHOT | flags, name, priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name);
+
+ return 0;
+}
+
+static void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ disable_irq(ret);
+}
+
+static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ enable_irq(ret);
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter,
+ const char * const open_name, unsigned int *open_irq,
+ const char * const close_name, unsigned int *close_irq,
+ irq_handler_t handler)
+{
+ int ret;
+
+ switch (shutter) {
+ case 0x1:
+ dev_warn(priv->dev, "Manual shutters, notifications not available\n");
+ return 0;
+ case 0x2:
+ *open_irq = CS42L43_GPIO1_RISE;
+ *close_irq = CS42L43_GPIO1_FALL;
+ break;
+ case 0x4:
+ *open_irq = CS42L43_GPIO2_RISE;
+ *close_irq = CS42L43_GPIO2_FALL;
+ break;
+ case 0x8:
+ *open_irq = CS42L43_GPIO3_RISE;
+ *close_irq = CS42L43_GPIO3_FALL;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = cs42l43_request_irq(priv, close_name, *close_irq, handler, IRQF_SHARED);
+ if (ret)
+ return ret;
+
+ return cs42l43_request_irq(priv, open_name, *open_irq, handler, IRQF_SHARED);
+}
+
+static int cs42l43_codec_probe(struct platform_device *pdev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
+ struct cs42l43_codec *priv;
+ unsigned int val;
+ int i, ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->core = cs42l43;
+
+ priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+ if (!priv->dom)
+ return -EPROBE_DEFER;
+
+ platform_set_drvdata(pdev, priv);
+
+ mutex_init(&priv->jack_lock);
+ mutex_init(&priv->spk_vu_lock);
+
+ init_completion(&priv->hp_startup);
+ init_completion(&priv->hp_shutdown);
+ init_completion(&priv->spkr_shutdown);
+ init_completion(&priv->spkl_shutdown);
+ init_completion(&priv->spkr_startup);
+ init_completion(&priv->spkl_startup);
+ init_completion(&priv->pll_ready);
+ init_completion(&priv->type_detect);
+ init_completion(&priv->load_detect);
+
+ INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work);
+ INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout);
+ INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work);
+
+ pm_runtime_set_autosuspend_delay(priv->dev, 100);
+ pm_runtime_use_autosuspend(priv->dev);
+ pm_runtime_set_active(priv->dev);
+ pm_runtime_get_noresume(priv->dev);
+
+ ret = devm_pm_runtime_enable(priv->dev);
+ if (ret)
+ goto err_pm;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
+ ret = cs42l43_request_irq(priv, cs42l43_irqs[i].name,
+ cs42l43_irqs[i].irq,
+ cs42l43_irqs[i].handler, 0);
+ if (ret)
+ goto err_pm;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter source: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+ "mic shutter open", &priv->shutter_irqs[0],
+ "mic shutter close", &priv->shutter_irqs[1],
+ cs42l43_mic_shutter);
+ if (ret)
+ goto err_pm;
+
+ ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+ CS42L43_SPK_SHUTTER_CFG_SHIFT,
+ "spk shutter open", &priv->shutter_irqs[2],
+ "spk shutter close", &priv->shutter_irqs[3],
+ cs42l43_spk_shutter);
+ if (ret)
+ goto err_pm;
+
+ // Don't use devm as we need to get against the MFD device
+ priv->mclk = clk_get_optional(cs42l43->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ ret = PTR_ERR(priv->mclk);
+ dev_err_probe(priv->dev, ret, "Failed to get mclk\n");
+ goto err_pm;
+ }
+
+ ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv,
+ cs42l43_dais, ARRAY_SIZE(cs42l43_dais));
+ if (ret) {
+ dev_err_probe(priv->dev, ret, "Failed to register component\n");
+ goto err_clk;
+ }
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return 0;
+
+err_clk:
+ clk_put(priv->mclk);
+err_pm:
+ pm_runtime_put_sync(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_codec_remove(struct platform_device *pdev)
+{
+ struct cs42l43_codec *priv = platform_get_drvdata(pdev);
+
+ clk_put(priv->mclk);
+}
+
+static int cs42l43_codec_runtime_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+
+ dev_dbg(priv->dev, "Runtime resume\n");
+
+ // Toggle the speaker volume update incase the speaker volume was synced
+ cs42l43_spk_vu_sync(priv);
+
+ return 0;
+}
+
+static int cs42l43_codec_suspend(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(priv->dev, "System suspend\n");
+
+ priv->suspend_jack_debounce = true;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_disable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_disable_irq(priv, priv->shutter_irqs[i]);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ cs42l43_clear_jack(priv);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_enable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_enable_irq(priv, priv->shutter_irqs[i]);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l43_codec_pm_ops = {
+ RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
+};
+
+static const struct platform_device_id cs42l43_codec_id_table[] = {
+ { "cs42l43-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table);
+
+static struct platform_driver cs42l43_codec_driver = {
+ .driver = {
+ .name = "cs42l43-codec",
+ .pm = pm_ptr(&cs42l43_codec_pm_ops),
+ },
+
+ .probe = cs42l43_codec_probe,
+ .remove = cs42l43_codec_remove,
+ .id_table = cs42l43_codec_id_table,
+};
+module_platform_driver(cs42l43_codec_driver);
+
+MODULE_IMPORT_NS("SND_SOC_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 CODEC Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
new file mode 100644
index 000000000000..b2fa2cd1d99f
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS42L43 CODEC driver internal data
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS42L43_ASOC_INT_H
+#define CS42L43_ASOC_INT_H
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+
+#define CS42L43_INTERNAL_SYSCLK 24576000
+#define CS42L43_DEFAULT_SLOTS 0x3F
+
+#define CS42L43_PLL_TIMEOUT_MS 200
+#define CS42L43_SPK_TIMEOUT_MS 100
+#define CS42L43_HP_TIMEOUT_MS 2000
+#define CS42L43_LOAD_TIMEOUT_MS 1000
+
+#define CS42L43_HP_ILIMIT_BACKOFF_MS 1000
+#define CS42L43_HP_ILIMIT_DECAY_MS 300
+#define CS42L43_HP_ILIMIT_MAX_COUNT 4
+
+#define CS42L43_ASP_MAX_CHANNELS 6
+#define CS42L43_N_EQ_COEFFS 15
+
+#define CS42L43_N_BUTTONS 6
+
+struct clk;
+struct device;
+
+struct snd_soc_component;
+struct snd_soc_jack;
+
+struct cs42l43;
+
+struct cs42l43_codec {
+ struct device *dev;
+ struct cs42l43 *core;
+ struct snd_soc_component *component;
+ struct irq_domain *dom;
+ unsigned int shutter_irqs[4];
+
+ struct clk *mclk;
+
+ int n_slots;
+ int slot_width;
+ int tx_slots[CS42L43_ASP_MAX_CHANNELS];
+ int rx_slots[CS42L43_ASP_MAX_CHANNELS];
+ struct snd_pcm_hw_constraint_list constraint;
+
+ u32 eq_coeffs[CS42L43_N_EQ_COEFFS];
+
+ unsigned int refclk_src;
+ unsigned int refclk_freq;
+ struct completion pll_ready;
+
+ unsigned int decim_cache[4];
+ unsigned int adc_ena;
+ unsigned int hp_ena;
+
+ struct completion hp_startup;
+ struct completion hp_shutdown;
+ struct completion spkr_shutdown;
+ struct completion spkl_shutdown;
+ struct completion spkr_startup;
+ struct completion spkl_startup;
+ // Lock to ensure speaker VU updates don't clash
+ struct mutex spk_vu_lock;
+
+ // Lock for all jack detect operations
+ struct mutex jack_lock;
+ struct snd_soc_jack *jack_hp;
+
+ bool use_ring_sense;
+ unsigned int tip_debounce_ms;
+ unsigned int tip_fall_db_ms;
+ unsigned int tip_rise_db_ms;
+ unsigned int bias_low;
+ unsigned int bias_sense_ua;
+ unsigned int bias_ramp_ms;
+ unsigned int detect_us;
+ unsigned int buttons[CS42L43_N_BUTTONS];
+
+ struct delayed_work tip_sense_work;
+ struct delayed_work bias_sense_timeout;
+ struct completion type_detect;
+ struct completion load_detect;
+
+ bool load_detect_running;
+ bool button_detect_running;
+ bool jack_present;
+ int jack_override;
+ bool suspend_jack_debounce;
+
+ struct delayed_work hp_ilimit_clear_work;
+ bool hp_ilimited;
+ int hp_ilimit_count;
+
+ struct snd_kcontrol *kctl[5];
+};
+
+#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction);
+
+#else
+
+static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EINVAL;
+}
+
+#define cs42l43_sdw_remove_peripheral NULL
+#define cs42l43_sdw_set_stream NULL
+
+#endif
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d);
+void cs42l43_bias_sense_timeout(struct work_struct *work);
+void cs42l43_clear_jack(struct cs42l43_codec *priv);
+void cs42l43_tip_sense_work(struct work_struct *work);
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data);
+irqreturn_t cs42l43_button_press(int irq, void *data);
+irqreturn_t cs42l43_button_release(int irq, void *data);
+irqreturn_t cs42l43_tip_sense(int irq, void *data);
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+extern const struct soc_enum cs42l43_jack_enum;
+
+#endif /* CS42L43_ASOC_INT_H */
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index 9bad478474fa..ba7e237619f2 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * 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/i2c.h>
@@ -17,35 +13,41 @@
#include "cs42l51.h"
-static struct i2c_device_id cs42l51_i2c_id[] = {
- {"cs42l51", 0},
- {}
+static const struct i2c_device_id cs42l51_i2c_id[] = {
+ { "cs42l51" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
-static int cs42l51_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct of_device_id cs42l51_of_match[] = {
+ { .compatible = "cirrus,cs42l51", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config config;
config = cs42l51_regmap;
- config.val_bits = 8;
- config.reg_bits = 8;
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
}
-static int cs42l51_i2c_remove(struct i2c_client *i2c)
+static void cs42l51_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
+ cs42l51_remove(&i2c->dev);
}
+static const struct dev_pm_ops cs42l51_pm_ops = {
+ 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 = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index f8072f1897d4..8083a339dc7b 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,20 +8,12 @@
*
* 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/slab.h>
#include <sound/core.h>
@@ -29,7 +22,9 @@
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -39,23 +34,31 @@ enum master_slave_mode {
MODE_MASTER,
};
+static const char * const cs42l51_supply_names[] = {
+ "VL",
+ "VD",
+ "VA",
+ "VAHP",
+};
+
struct cs42l51_private {
unsigned int mclk;
+ struct clk *mclk_handle;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
+ 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)
+#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_soc_kcontrol_codec(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:
@@ -82,7 +85,7 @@ 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_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned char val;
switch (ucontrol->value.enumerated.item[0]) {
@@ -98,7 +101,7 @@ 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;
}
@@ -109,12 +112,16 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 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 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[] = {
@@ -131,12 +138,20 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
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",
@@ -153,17 +168,17 @@ 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)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
+ 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_update_bits(codec, CS42L51_POWER_CTL1,
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, 0);
break;
}
@@ -193,7 +208,8 @@ 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,
@@ -204,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"),
@@ -237,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"},
@@ -263,8 +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);
+ 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:
@@ -273,19 +317,19 @@ 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");
+ dev_err(component->dev, "invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l51->func = MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -323,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;
@@ -337,19 +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_codec *codec = dai->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);
@@ -370,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));
@@ -385,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);
@@ -418,51 +486,59 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
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 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 = {
@@ -484,9 +560,17 @@ static struct snd_soc_dai_driver cs42l51_dai = {
.ops = &cs42l51_dai_ops,
};
-static int cs42l51_codec_probe(struct snd_soc_codec *codec)
+static int cs42l51_component_probe(struct snd_soc_component *component)
{
int ret, reg;
+ struct snd_soc_dapm_context *dapm;
+ struct cs42l51_private *cs42l51;
+
+ cs42l51 = snd_soc_component_get_drvdata(component);
+ dapm = snd_soc_component_get_dapm(component);
+
+ if (cs42l51->mclk_handle)
+ snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
/*
* DAC configuration
@@ -497,29 +581,129 @@ static int cs42l51_codec_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;
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
- .probe = cs42l51_codec_probe,
-
- .component_driver = {
- .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),
- },
+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 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,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42l51_regmap);
@@ -527,7 +711,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
- int ret;
+ int ret, i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -538,6 +722,41 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
dev_set_drvdata(dev, cs42l51);
+ cs42l51->regmap = regmap;
+
+ cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK");
+ if (IS_ERR(cs42l51->mclk_handle))
+ return PTR_ERR(cs42l51->mclk_handle);
+
+ 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)) {
+ ret = PTR_ERR(cs42l51->reset_gpio);
+ goto error;
+ }
+
+ 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 = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
@@ -555,19 +774,57 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
val & CS42L51_CHIP_REV_MASK);
- ret = snd_soc_register_codec(dev,
- &soc_codec_device_cs42l51, &cs42l51_dai, 1);
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_device_cs42l51, &cs42l51_dai, 1);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
error:
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-const struct of_device_id cs42l51_of_match[] = {
- { .compatible = "cirrus,cs42l51", },
- { }
-};
-MODULE_DEVICE_TABLE(of, cs42l51_of_match);
-EXPORT_SYMBOL_GPL(cs42l51_of_match);
+void cs42l51_remove(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+ int ret;
+
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+
+ 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));
+
+}
+EXPORT_SYMBOL_GPL(cs42l51_remove);
+
+int cs42l51_suspend(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, true);
+ regcache_mark_dirty(cs42l51->regmap);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs42l51_suspend);
+
+int cs42l51_resume(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, false);
+
+ return regcache_sync(cs42l51->regmap);
+}
+EXPORT_SYMBOL_GPL(cs42l51_resume);
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 0ca805492ac4..125703ede113 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -1,19 +1,10 @@
+/* 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
@@ -22,7 +13,9 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
-extern const struct of_device_id cs42l51_of_match[];
+void cs42l51_remove(struct device *dev);
+int __maybe_unused cs42l51_suspend(struct device *dev);
+int __maybe_unused cs42l51_resume(struct device *dev);
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 9731e5dff291..a9ffba62aaf8 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l52.c -- CS42L52 ALSA SoC audio driver
*
@@ -5,34 +6,28 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#include <linux/module.h>
-#include <linux/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/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
#include <sound/core.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/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l52.h>
#include "cs42l52.h"
struct sp_config {
@@ -40,9 +35,27 @@ struct sp_config {
u32 srate;
};
+struct cs42l52_platform_data {
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* MICA mode selection Differential or Single-ended */
+ bool mica_diff_cfg;
+
+ /* MICB mode selection Differential or Single-ended */
+ bool micb_diff_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg73 */
+ unsigned int chgfreq;
+
+ /* Reset GPIO */
+ struct gpio_desc *reset_gpio;
+};
+
struct cs42l52_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct sp_config config;
struct cs42l52_platform_data pdata;
@@ -141,7 +154,9 @@ 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(mix_tlv, -50, 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);
@@ -355,7 +370,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
- CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pga_tlv),
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pass_tlv),
SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
@@ -368,7 +383,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
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, ipd_tlv),
+ 0, 0x19, 0x7F, mix_tlv),
SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
@@ -473,17 +488,17 @@ static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
SOC_ENUM("MICB Select", micb_enum),
};
-static int cs42l52_add_mic_controls(struct snd_soc_codec *codec)
+static int cs42l52_add_mic_controls(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_controls(codec, cs42l52_mica_controls,
+ snd_soc_add_component_controls(component, cs42l52_mica_controls,
ARRAY_SIZE(cs42l52_mica_controls));
if (!pdata->micb_diff_cfg)
- snd_soc_add_codec_controls(codec, cs42l52_micb_controls,
+ snd_soc_add_component_controls(component, cs42l52_micb_controls,
ARRAY_SIZE(cs42l52_micb_controls));
return 0;
@@ -716,13 +731,13 @@ static int cs42l52_get_clk(int mclk, int rate)
static int cs42l52_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 cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid freq parameter\n");
+ dev_err(component->dev, "Invalid freq parameter\n");
return -EINVAL;
}
return 0;
@@ -730,15 +745,15 @@ static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = CS42L52_IFACE_CTL1_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = CS42L52_IFACE_CTL1_SLAVE;
break;
default:
@@ -783,21 +798,21 @@ static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
cs42l52->config.format = iface;
- snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+ snd_soc_component_write(component, CS42L52_IFACE_CTL1, cs42l52->config.format);
return 0;
}
-static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l52_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute)
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_MUTE);
else
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_UNMUTE);
@@ -808,8 +823,8 @@ 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_codec *codec = dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
u32 clk = 0;
int index;
@@ -823,36 +838,36 @@ static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
(clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
clk_map_table[index].mclkdiv2;
- snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+ snd_soc_component_write(component, CS42L52_CLK_CTL, clk);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ dev_err(component->dev, "can't get correct mclk\n");
return -EINVAL;
}
return 0;
}
-static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l52_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L52_PWRCTL1,
+ snd_soc_component_update_bits(component, CS42L52_PWRCTL1,
CS42L52_PWRCTL1_PDN_CODEC, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l52->regmap, false);
regcache_sync(cs42l52->regmap);
}
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
regcache_cache_only(cs42l52->regmap, true);
break;
}
@@ -869,9 +884,10 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops cs42l52_ops = {
.hw_params = cs42l52_pcm_hw_params,
- .digital_mute = cs42l52_digital_mute,
+ .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 = {
@@ -902,8 +918,8 @@ static void cs42l52_beep_work(struct work_struct *work)
{
struct cs42l52_private *cs42l52 =
container_of(work, struct cs42l52_private, beep_work);
- struct snd_soc_codec *codec = cs42l52->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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;
@@ -915,18 +931,18 @@ static void cs42l52_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ 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(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_FREQ,
CS42L52_BEEP_RATE_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -938,15 +954,16 @@ static void cs42l52_beep_work(struct work_struct *work)
static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ 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:
@@ -959,9 +976,8 @@ static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l52_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+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;
@@ -976,16 +992,16 @@ static ssize_t cs42l52_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l52_init_beep(struct snd_soc_codec *codec)
+static void cs42l52_init_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l52->beep = devm_input_allocate_device(codec->dev);
+ cs42l52->beep = devm_input_allocate_device(component->dev);
if (!cs42l52->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -993,49 +1009,49 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
cs42l52->beep_rate = 0;
cs42l52->beep->name = "CS42L52 Beep Generator";
- cs42l52->beep->phys = dev_name(codec->dev);
+ 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 = codec->dev;
- input_set_drvdata(cs42l52->beep, codec);
+ 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(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l52_free_beep(struct snd_soc_codec *codec)
+static void cs42l52_free_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l52->beep_work);
cs42l52->beep = NULL;
- snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL,
CS42L52_BEEP_EN_MASK, 0);
}
-static int cs42l52_probe(struct snd_soc_codec *codec)
+static int cs42l52_probe(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
regcache_cache_only(cs42l52->regmap, true);
- cs42l52_add_mic_controls(codec);
+ cs42l52_add_mic_controls(component);
- cs42l52_init_beep(codec);
+ cs42l52_init_beep(component);
cs42l52->sysclk = CS42L52_DEFAULT_CLK;
cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
@@ -1043,27 +1059,25 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs42l52_remove(struct snd_soc_codec *codec)
+static void cs42l52_remove(struct snd_soc_component *component)
{
- cs42l52_free_beep(codec);
-
- return 0;
+ cs42l52_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
- .probe = cs42l52_probe,
- .remove = cs42l52_remove,
- .set_bias_level = cs42l52_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 */
@@ -1087,16 +1101,15 @@ static const struct regmap_config cs42l52_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
.readable_reg = cs42l52_readable_register,
.volatile_reg = cs42l52_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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);
+ struct cs42l52_platform_data *pdata;
int ret;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int reg;
u32 val32;
@@ -1111,50 +1124,43 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
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;
+
+ 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 = devm_gpiod_get_optional(&i2c_client->dev,
+ "cirrus,reset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L52 /RST");
}
+ 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);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
}
i2c_set_clientdata(i2c_client, cs42l52);
@@ -1166,6 +1172,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
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;
@@ -1202,17 +1213,8 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
CS42L52_IFACE_CTL2_BIAS_LVL,
cs42l52->pdata.micbias_lvl);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l52, &cs42l52_dai, 1);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static int cs42l52_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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[] = {
@@ -1223,7 +1225,7 @@ MODULE_DEVICE_TABLE(of, cs42l52_of_match);
static const struct i2c_device_id cs42l52_id[] = {
- { "cs42l52", 0 },
+ { "cs42l52" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l52_id);
@@ -1234,8 +1236,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
- .probe = cs42l52_i2c_probe,
- .remove = cs42l52_i2c_remove,
+ .probe = cs42l52_i2c_probe,
};
module_i2c_driver(cs42l52_i2c_driver);
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
index ac445993e6bf..e485670f9a6f 100644
--- a/sound/soc/codecs/cs42l52.h
+++ b/sound/soc/codecs/cs42l52.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l52.h -- CS42L52 ALSA SoC audio driver
*
@@ -5,11 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.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 __CS42L52_H__
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index fd7b8d32c2b2..98fa812bc07b 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1,42 +1,70 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.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/initval.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
+
+struct cs42l56_platform_data {
+ /* GPIO for Reset */
+ struct gpio_desc *gpio_nreset;
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1a_ref_cfg;
+
+ /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2a_ref_cfg;
+
+ /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1b_ref_cfg;
+
+ /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2b_ref_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg62 */
+ unsigned int chgfreq;
+
+ /* HighPass Filter Right Channel Corner Frequency */
+ unsigned int hpfb_freq;
+
+ /* HighPass Filter Left Channel Corner Frequency */
+ unsigned int hpfa_freq;
+
+ /* Adaptive Power Control for LO/HP */
+ unsigned int adaptive_pwr;
+};
+
static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
"VA",
"VCP",
@@ -45,7 +73,7 @@ static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
struct cs42l56_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct cs42l56_platform_data pdata;
struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
@@ -203,14 +231,6 @@ 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 adc_swap_text[] = {
- "None", "A+B/2", "A-B/2", "Swap"
-};
-
-static const struct soc_enum adc_swap_enum =
- SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3,
- ARRAY_SIZE(adc_swap_text), adc_swap_text);
-
static const char * const pgaa_mux_text[] = {
"AIN1A", "AIN2A", "AIN3A"};
@@ -403,9 +423,9 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
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, 0x84, 0x48, hl_tlv),
+ CS42L56_HPB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
- CS42L56_LOB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+ CS42L56_LOB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
0, 0x00, 1, tone_tlv),
@@ -726,8 +746,8 @@ static int cs42l56_get_mclk_ratio(int mclk, int rate)
static int cs42l56_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 cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
switch (freq) {
case CS42L56_MCLK_5P6448MHZ:
@@ -753,10 +773,10 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
}
cs42l56->mclk = freq;
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_PREDIV_MASK,
cs42l56->mclk_prediv);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIV2_MASK,
cs42l56->mclk_div2);
@@ -765,14 +785,14 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l56->iface = CS42L56_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l56->iface = CS42L56_SLAVE_MODE;
break;
default:
@@ -803,22 +823,22 @@ static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MS_MODE_MASK, cs42l56->iface);
- snd_soc_update_bits(codec, CS42L56_SERIAL_FMT,
+ snd_soc_component_update_bits(component, CS42L56_SERIAL_FMT,
CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
return 0;
}
-static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l56_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
/* Hit the DSP Mixer first */
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -827,21 +847,21 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_MUTE_ALL);
/* Mute ADC's */
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ 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_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
} else {
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -850,18 +870,18 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
}
return 0;
@@ -871,39 +891,39 @@ 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_codec *codec = dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L56_CLKCTL_2,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_2,
CS42L56_CLK_RATIO_MASK, ratio);
} else {
- dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n");
+ dev_err(component->dev, "unsupported mclk/sclk/lrclk ratio\n");
return -EINVAL;
}
return 0;
}
-static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l56_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIS_MASK, 0);
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ 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),
@@ -915,13 +935,13 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_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),
@@ -941,9 +961,10 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops cs42l56_ops = {
.hw_params = cs42l56_pcm_hw_params,
- .digital_mute = cs42l56_digital_mute,
+ .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 = {
@@ -974,8 +995,8 @@ static void cs42l56_beep_work(struct work_struct *work)
{
struct cs42l56_private *cs42l56 =
container_of(work, struct cs42l56_private, beep_work);
- struct snd_soc_codec *codec = cs42l56->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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;
@@ -987,18 +1008,18 @@ static void cs42l56_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ 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(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_FREQ_ONTIME,
CS42L56_BEEP_FREQ_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -1010,15 +1031,16 @@ static void cs42l56_beep_work(struct work_struct *work)
static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ 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:
@@ -1031,9 +1053,8 @@ static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l56_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+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;
@@ -1048,16 +1069,16 @@ static ssize_t cs42l56_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l56_init_beep(struct snd_soc_codec *codec)
+static void cs42l56_init_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l56->beep = devm_input_allocate_device(codec->dev);
+ cs42l56->beep = devm_input_allocate_device(component->dev);
if (!cs42l56->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -1065,68 +1086,66 @@ static void cs42l56_init_beep(struct snd_soc_codec *codec)
cs42l56->beep_rate = 0;
cs42l56->beep->name = "CS42L56 Beep Generator";
- cs42l56->beep->phys = dev_name(codec->dev);
+ 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 = codec->dev;
- input_set_drvdata(cs42l56->beep, codec);
+ 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(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l56_free_beep(struct snd_soc_codec *codec)
+static void cs42l56_free_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l56->beep_work);
cs42l56->beep = NULL;
- snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG,
CS42L56_BEEP_EN_MASK, 0);
}
-static int cs42l56_probe(struct snd_soc_codec *codec)
+static int cs42l56_probe(struct snd_soc_component *component)
{
- cs42l56_init_beep(codec);
+ cs42l56_init_beep(component);
return 0;
}
-static int cs42l56_remove(struct snd_soc_codec *codec)
+static void cs42l56_remove(struct snd_soc_component *component)
{
- cs42l56_free_beep(codec);
-
- return 0;
+ cs42l56_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
- .probe = cs42l56_probe,
- .remove = cs42l56_remove,
- .set_bias_level = cs42l56_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -1138,7 +1157,7 @@ static const struct regmap_config cs42l56_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
.readable_reg = cs42l56_readable_register,
.volatile_reg = cs42l56_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
@@ -1174,19 +1193,22 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
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);
+ pdata->gpio_nreset = devm_gpiod_get_optional(&i2c_client->dev, "cirrus,gpio-nreset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->gpio_nreset))
+ return PTR_ERR(pdata->gpio_nreset);
+
+ gpiod_set_consumer_name(pdata->gpio_nreset, "CS42L56 /RST");
return 0;
}
-static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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 = 0;
+ unsigned int devid;
unsigned int alpha_rev, metal_rev;
unsigned int reg;
@@ -1202,37 +1224,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- if (pdata) {
- cs42l56->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- if (i2c_client->dev.of_node) {
- ret = cs42l56_handle_of_data(i2c_client,
- &cs42l56->pdata);
- if (ret != 0)
- return ret;
- }
- cs42l56->pdata = *pdata;
+ 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);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
}
-
i2c_set_clientdata(i2c_client, cs42l56);
for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
@@ -1256,11 +1258,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
}
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;
@@ -1315,10 +1323,10 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
CS42L56_ADAPT_PWR_MASK,
cs42l56->pdata.adaptive_pwr);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l56, &cs42l56_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l56, &cs42l56_dai, 1);
if (ret < 0)
- return ret;
+ goto err_enable;
return 0;
@@ -1328,14 +1336,12 @@ err_enable:
return ret;
}
-static int cs42l56_i2c_remove(struct i2c_client *client)
+static void cs42l56_i2c_remove(struct i2c_client *client)
{
struct cs42l56_private *cs42l56 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
cs42l56->supplies);
- return 0;
}
static const struct of_device_id cs42l56_of_match[] = {
@@ -1346,7 +1352,7 @@ MODULE_DEVICE_TABLE(of, cs42l56_of_match);
static const struct i2c_device_id cs42l56_id[] = {
- { "cs42l56", 0 },
+ { "cs42l56" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l56_id);
diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h
index 5025ec9be9b2..62a8c3cb1a01 100644
--- a/sound/soc/codecs/cs42l56.h
+++ b/sound/soc/codecs/cs42l56.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * 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 __CS42L56_H__
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index aebaa97490b6..535a867f9f2a 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l73.c -- CS42L73 ALSA Soc Audio driver
*
@@ -5,33 +6,36 @@
*
* Authors: Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>
* Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.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/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#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/initval.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 "cirrus_legacy.h"
#include "cs42l73.h"
+struct cs42l73_platform_data {
+ /* RST GPIO */
+ struct gpio_desc *reset_gpio;
+ unsigned int chgfreq;
+ int jack_detection;
+ unsigned int mclk_freq;
+};
+
struct sp_config {
u8 spc, mmcc, spfs;
u32 srate;
@@ -277,12 +281,6 @@ static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
CS42L73_MIXERCTL, 4,
cs42l73_spo_mixer_text);
-static const struct snd_kcontrol_new vsp_output_mux =
- SOC_DAPM_ENUM("Route", vsp_output_mux_enum);
-
-static const struct snd_kcontrol_new xsp_output_mux =
- SOC_DAPM_ENUM("Route", xsp_output_mux_enum);
-
static const struct snd_kcontrol_new hp_amp_ctl =
SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1);
@@ -490,8 +488,8 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -506,8 +504,8 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -524,8 +522,8 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -884,8 +882,8 @@ static int cs42l73_get_mclk_coeff(int mclk, int srate)
static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
int mclkx_coeff;
u32 mclk = 0;
@@ -899,14 +897,14 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx /
cs42l73_mclkx_coeffs[mclkx_coeff].ratio;
- dev_dbg(codec->dev, "MCLK%u %u <-> internal MCLK %u\n",
+ 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_write(codec, CS42L73_DMMCC, dmmcc);
+ snd_soc_component_write(component, CS42L73_DMMCC, dmmcc);
priv->sysclk = mclkx_coeff;
priv->mclk = mclk;
@@ -917,8 +915,8 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case CS42L73_CLKID_MCLK1:
@@ -930,7 +928,7 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
}
if ((cs42l73_set_mclk(dai, freq)) < 0) {
- dev_err(codec->dev, "Unable to set MCLK for dai %s\n",
+ dev_err(component->dev, "Unable to set MCLK for dai %s\n",
dai->name);
return -EINVAL;
}
@@ -942,21 +940,21 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, CS42L73_SPC(id));
- mmcc = snd_soc_read(codec, CS42L73_MMCC(id));
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
mmcc |= CS42L73_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mmcc &= ~CS42L73_MS_MASTER;
break;
@@ -974,12 +972,12 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
if (mmcc & CS42L73_MS_MASTER) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format in slave mode only\n");
return -EINVAL;
}
if (id == CS42L73_ASP) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format is not supported on ASP port\n");
return -EINVAL;
}
@@ -1029,7 +1027,7 @@ static unsigned int cs42l73_get_xspfs_coeff(u32 rate)
return 0; /* 0 = Don't know */
}
-static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
+static void cs42l73_update_asrc(struct snd_soc_component *component, int id, int srate)
{
u8 spfs = 0;
@@ -1038,13 +1036,13 @@ static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
switch (id) {
case CS42L73_XSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0x0f, spfs);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0x0f, spfs);
break;
case CS42L73_ASP:
- snd_soc_update_bits(codec, CS42L73_ASPC, 0x3c, spfs << 2);
+ snd_soc_component_update_bits(component, CS42L73_ASPC, 0x3c, spfs << 2);
break;
case CS42L73_VSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0xf0, spfs << 4);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0xf0, spfs << 4);
break;
default:
break;
@@ -1055,8 +1053,8 @@ 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_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -1070,7 +1068,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
if (mclk_coeff < 0)
return -EINVAL;
- dev_dbg(codec->dev,
+ 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);
@@ -1091,38 +1089,38 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
/* Update ASRCs */
priv->config[id].srate = srate;
- snd_soc_write(codec, CS42L73_SPC(id), priv->config[id].spc);
- snd_soc_write(codec, CS42L73_MMCC(id), priv->config[id].mmcc);
+ 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(codec, id, srate);
+ cs42l73_update_asrc(component, id, srate);
return 0;
}
-static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l73_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 0);
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 0);
+ 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l73->regmap, false);
regcache_sync(cs42l73->regmap);
}
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
if (cs42l73->shutdwn_delay > 0) {
mdelay(cs42l73->shutdwn_delay);
cs42l73->shutdwn_delay = 0;
@@ -1131,7 +1129,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
* down.
*/
}
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
break;
}
return 0;
@@ -1139,10 +1137,10 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int id = dai->id;
- return snd_soc_update_bits(codec, CS42L73_SPC(id), CS42L73_SP_3ST,
+ return snd_soc_component_update_bits(component, CS42L73_SPC(id), CS42L73_SP_3ST,
tristate << 7);
}
@@ -1191,7 +1189,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-asp",
@@ -1211,7 +1209,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-vsp",
@@ -1231,17 +1229,17 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs42l73_probe(struct snd_soc_codec *codec)
+static int cs42l73_probe(struct snd_soc_component *component)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
/* Set Charge Pump Frequency */
if (cs42l73->pdata.chgfreq)
- snd_soc_update_bits(codec, CS42L73_CPFCHC,
+ snd_soc_component_update_bits(component, CS42L73_CPFCHC,
CS42L73_CHARGEPUMP_MASK,
cs42l73->pdata.chgfreq << 4);
@@ -1252,19 +1250,19 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
- .probe = cs42l73_probe,
- .set_bias_level = cs42l73_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -1276,16 +1274,17 @@ static const struct regmap_config cs42l73_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults),
.volatile_reg = cs42l73_volatile_register,
.readable_reg = cs42l73_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
- unsigned int devid = 0;
+ struct cs42l73_platform_data *pdata;
+ int ret, devid;
unsigned int reg;
u32 val32;
@@ -1300,79 +1299,66 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
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;
+ 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 = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L73 /RST");
+ 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);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
}
/* initialize codec */
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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);
- return ret;
+ goto err_reset;
}
ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
"Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l73, cs42l73_dai,
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
if (ret < 0)
- return ret;
- return 0;
-}
+ goto err_reset;
-static int cs42l73_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+
+ return ret;
}
static const struct of_device_id cs42l73_of_match[] = {
@@ -1382,7 +1368,7 @@ static const struct of_device_id cs42l73_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l73_of_match);
static const struct i2c_device_id cs42l73_id[] = {
- {"cs42l73", 0},
+ {"cs42l73"},
{}
};
@@ -1395,7 +1381,6 @@ static struct i2c_driver cs42l73_i2c_driver = {
},
.id_table = cs42l73_id,
.probe = cs42l73_i2c_probe,
- .remove = cs42l73_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h
index 45746186a678..e43a35576111 100644
--- a/sound/soc/codecs/cs42l73.h
+++ b/sound/soc/codecs/cs42l73.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS42L73 codec driver
*
@@ -5,21 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#ifndef __CS42L73_H__
diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c
new file mode 100644
index 000000000000..53a7fe1ab3dd
--- /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_MAPLE,
+
+ .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 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 = {
+ 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 = pm_ptr(&cs42l83_i2c_pm_ops),
+ .of_match_table = of_match_ptr(cs42l83_of_match),
+ },
+ .probe = 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/cs42l84.c b/sound/soc/codecs/cs42l84.c
new file mode 100644
index 000000000000..88cf3c03986e
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l84.c -- CS42L84 ALSA SoC audio driver
+ *
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42{.c,.h}
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#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/acpi.h>
+#include <linux/platform_device.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/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 "cs42l84.h"
+#include "cirrus_legacy.h"
+
+struct cs42l84_private {
+ struct regmap *regmap;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct snd_soc_jack *jack;
+ struct mutex irq_lock;
+ u8 tip_state;
+ u8 ring_state;
+ int pll_config;
+ int bclk;
+ u8 pll_mclk_f;
+ u32 srate;
+ u8 stream_use;
+ int hs_type;
+};
+
+static bool cs42l84_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L84_DEVID ... CS42L84_DEVID+5:
+ case CS42L84_TSRS_PLUG_INT_STATUS:
+ case CS42L84_PLL_LOCK_STATUS:
+ case CS42L84_TSRS_PLUG_STATUS:
+ case CS42L84_HS_DET_STATUS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs42l84_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .volatile_reg = cs42l84_volatile_register,
+
+ .max_register = 0x73fe,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret, ret2, updated = 0;
+
+ vola = val->value.integer.value[0] + mc->min;
+ volb = val->value.integer.value[1] + mc->min;
+
+ if (vola < mc->min || vola > mc->max || volb < mc->min || volb > mc->max)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE,
+ CS42L84_FRZ_CTL_ENGAGE);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_LSB,
+ 0xff, vola & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_MSB,
+ 0xff, (vola >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_LSB,
+ 0xff, volb & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_MSB,
+ 0xff, (volb >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ ret |= updated;
+
+bail:
+ ret2 = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE, 0);
+ if (ret2 < 0 && ret >= 0)
+ ret = ret2;
+
+ return ret;
+}
+
+static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ vola = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ vola |= (ret & 1) << 8;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ volb = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ volb |= (ret & 1) << 8;
+
+ if (vola & BIT(8))
+ vola |= ~((int)(BIT(8) - 1));
+ if (volb & BIT(8))
+ volb |= ~((int)(BIT(8) - 1));
+
+ val->value.integer.value[0] = vola - mc->min;
+ val->value.integer.value[1] = volb - mc->min;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -12800, 50, true);
+static const DECLARE_TLV_DB_SCALE(cs42l84_adc_tlv, -1200, 50, false);
+static const DECLARE_TLV_DB_SCALE(cs42l84_pre_tlv, 0, 1000, false);
+
+static const struct snd_kcontrol_new cs42l84_snd_controls[] = {
+ SOC_DOUBLE_R_S_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB,
+ CS42L84_DAC_CHB_VOL_LSB, 0, -256, 24, 8, 0,
+ cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv),
+ SOC_SINGLE_TLV("ADC Preamp Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0, cs42l84_pre_tlv),
+ SOC_SINGLE_TLV("ADC PGA Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 24, 0, cs42l84_adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("WNF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_CF_SHIFT, 3, 0),
+ SOC_SINGLE("ADC HPF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("HPF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_CF_SHIFT, 3, 0),
+};
+
+static const char * const cs42l84_mux_text[] = {
+ "Blank", "ADC", "ASP RX CH1", "ASP RX CH2",
+};
+
+static const unsigned int cs42l84_mux_values[] = {
+ 0b0000, 0b0111, 0b1101, 0b1110,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_daca_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACA_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_dacb_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACB_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_sdout1_mux_enum,
+ CS42L84_BUS_ASP_TX_SRC, CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static const struct snd_kcontrol_new cs42l84_daca_mux_ctrl =
+ SOC_DAPM_ENUM("DACA Select", cs42l84_daca_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_dacb_mux_ctrl =
+ SOC_DAPM_ENUM("DACB Select", cs42l84_dacb_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_sdout1_mux_ctrl =
+ SOC_DAPM_ENUM("SDOUT1 Select", cs42l84_sdout1_mux_enum);
+
+static const struct snd_soc_dapm_widget cs42l84_dapm_widgets[] = {
+ /* Playback Path */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_DAC("DAC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_DAC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("DACA Select", SND_SOC_NOPM, 0, 0, &cs42l84_daca_mux_ctrl),
+ SND_SOC_DAPM_MUX("DACB Select", SND_SOC_NOPM, 0, 0, &cs42l84_dacb_mux_ctrl),
+ SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH1_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH2_SHIFT, 0),
+
+ /* Capture Path */
+ SND_SOC_DAPM_INPUT("HS"),
+ SND_SOC_DAPM_ADC("ADC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ADC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("SDOUT1 Select", SND_SOC_NOPM, 0, 0, &cs42l84_sdout1_mux_ctrl),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L84_ASP_TX_EN, CS42L84_ASP_TX_EN_CH1_SHIFT, 0),
+
+ /* Playback/Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("BUS", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_BUS_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASP", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ASP_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BCLK", CS42L84_ASP_CTL, CS42L84_ASP_CTL_BCLK_EN_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route cs42l84_audio_map[] = {
+ /* Playback Path */
+ {"HP", NULL, "DAC"},
+ {"DAC", NULL, "DACA Select"},
+ {"DAC", NULL, "DACB Select"},
+ {"DACA Select", "ASP RX CH1", "SDIN1"},
+ {"DACA Select", "ASP RX CH2", "SDIN2"},
+ {"DACB Select", "ASP RX CH1", "SDIN1"},
+ {"DACB Select", "ASP RX CH2", "SDIN2"},
+ {"SDIN1", NULL, "Playback"},
+ {"SDIN2", NULL, "Playback"},
+
+ {"ADC", NULL, "HS"},
+ {"SDOUT1 Select", "ADC", "ADC"},
+ {"SDOUT1", NULL, "SDOUT1 Select"},
+ {"Capture", NULL, "SDOUT1"},
+
+ /* Playback Requirements */
+ {"DAC", NULL, "BUS"},
+ {"SDIN1", NULL, "ASP"},
+ {"SDIN2", NULL, "ASP"},
+ {"SDIN1", NULL, "BCLK"},
+ {"SDIN2", NULL, "BCLK"},
+
+ /* Capture Requirements */
+ {"SDOUT1", NULL, "BUS"},
+ {"SDOUT1", NULL, "ASP"},
+ {"SDOUT1", NULL, "BCLK"},
+};
+
+static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l84->irq_lock);
+ cs42l84->jack = jk;
+ snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET);
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return 0;
+}
+
+static int cs42l84_component_probe(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, CS42L84_ASP_CTL,
+ CS42L84_ASP_CTL_TDM_MODE, 0);
+ snd_soc_component_update_bits(component, CS42L84_HP_VOL_CTL,
+ CS42L84_HP_VOL_CTL_SOFT | CS42L84_HP_VOL_CTL_ZERO_CROSS,
+ CS42L84_HP_VOL_CTL_ZERO_CROSS);
+
+ /* TDM settings */
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ /* Routing defaults */
+ snd_soc_component_write(component, CS42L84_BUS_DAC_SRC,
+ 0b1101 << CS42L84_BUS_DAC_SRC_DACA_SHIFT |
+ 0b1110 << CS42L84_BUS_DAC_SRC_DACB_SHIFT);
+ snd_soc_component_write(component, CS42L84_BUS_ASP_TX_SRC,
+ 0b0111 << CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l84 = {
+ .set_jack = cs42l84_set_jack,
+ .probe = cs42l84_component_probe,
+ .controls = cs42l84_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l84_snd_controls),
+ .dapm_widgets = cs42l84_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l84_dapm_widgets),
+ .dapm_routes = cs42l84_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l84_audio_map),
+ .endianness = 1,
+};
+
+struct cs42l84_pll_params {
+ u32 bclk;
+ u8 mclk_src_sel;
+ u8 bclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+};
+
+/*
+ * Common PLL Settings for given BCLK
+ */
+static const struct cs42l84_pll_params pll_ratio_table[] = {
+ { 3072000, 1, 0, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 6144000, 1, 1, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000},
+ { 24576000, 1, 3, 0x40, 0x000000, 0x03, 0x10, 12288000},
+};
+
+static int cs42l84_pll_config(struct snd_soc_component *component)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+ u32 clk;
+ u32 fsync;
+
+ clk = cs42l84->bclk;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l84->stream_use) {
+ if (pll_ratio_table[cs42l84->pll_config].bclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == clk) {
+ cs42l84->pll_config = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(pll_ratio_table))
+ return -EINVAL;
+
+ /* Set up the LRCLK */
+ fsync = clk / cs42l84->srate;
+ if (((fsync * cs42l84->srate) != clk)
+ || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported bclk %d/sample rate %d\n",
+ clk, cs42l84->srate);
+ return -EINVAL;
+ }
+
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL2,
+ CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO, fsync & 0x7f));
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL3,
+ CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, fsync >> 7));
+
+ /* Save what the MCLK will be */
+ switch (pll_ratio_table[i].mclk_int) {
+ case 12000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12MHZ;
+ break;
+ case 12288000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12_288KHZ;
+ break;
+ case 24000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24MHZ;
+ break;
+ case 24576000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24_576KHZ;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, 0);
+
+ if (pll_ratio_table[i].mclk_src_sel) {
+ /* Configure PLL */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL3, CS42L84_CCM_CTL3_REFCLK_DIV,
+ FIELD_PREP(CS42L84_CCM_CTL3_REFCLK_DIV, pll_ratio_table[i].bclk_prediv));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_INT,
+ pll_ratio_table[i].pll_div_int);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC0,
+ pll_ratio_table[i].pll_div_frac);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC1,
+ pll_ratio_table[i].pll_div_frac >> 8);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC2,
+ pll_ratio_table[i].pll_div_frac >> 16);
+ snd_soc_component_update_bits(component,
+ CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_MODE,
+ FIELD_PREP(CS42L84_PLL_CTL1_MODE, pll_ratio_table[i].pll_mode));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIVOUT,
+ pll_ratio_table[i].pll_divout);
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l84_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 cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u32 ccm_samp_rate;
+
+ cs42l84->srate = params_rate(params);
+
+ ret = cs42l84_pll_config(component);
+ if (ret)
+ return ret;
+
+ switch (params_rate(params)) {
+ case 44100:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_44K1HZ;
+ break;
+ case 48000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_48KHZ;
+ break;
+ case 88200:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_88K2HZ;
+ break;
+ case 96000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_96KHZ;
+ break;
+ case 176400:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_176K4HZ;
+ break;
+ case 192000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, CS42L84_CCM_SAMP_RATE, ccm_samp_rate);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (freq == 0) {
+ cs42l84->bclk = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == freq) {
+ cs42l84->bclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(component->dev, "BCLK %u not supported\n", freq);
+
+ return -EINVAL;
+}
+
+static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = 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, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE, 0);
+ cs42l84->stream_use &= ~(1 << stream);
+ if (!cs42l84->stream_use) {
+ /* Must disconnect PLL before stopping it */
+ snd_soc_component_write(component, CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_RCO);
+
+ usleep_range(150, 300);
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN, 0);
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN, 0);
+ }
+ } else {
+ if (!cs42l84->stream_use) {
+ /* SCLK must be running before codec unmute.
+ *
+ * Note carried over from CS42L42:
+ *
+ * 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.
+ */
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN,
+ CS42L84_CCM_CTL4_REFCLK_EN);
+
+ if (pll_ratio_table[cs42l84->pll_config].mclk_src_sel) {
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN,
+ CS42L84_PLL_CTL1_EN);
+ /* TODO: should we be doing something with divout here? */
+
+ ret = regmap_read_poll_timeout(cs42l84->regmap,
+ CS42L84_PLL_LOCK_STATUS,
+ regval,
+ (regval & CS42L84_PLL_LOCK_STATUS_LOCKED),
+ CS42L84_PLL_LOCK_POLL_US,
+ CS42L84_PLL_LOCK_TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+
+ if (regval & CS42L84_PLL_LOCK_STATUS_ERROR)
+ dev_warn(component->dev, "PLL lock error\n");
+
+ /* PLL must be running to drive glitchless switch logic */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_PLL)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ } else {
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_BCLK)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ }
+ }
+ cs42l84->stream_use |= 1 << stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ /* Un-mute the headphone */
+ snd_soc_component_update_bits(component, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE,
+ CS42L84_DAC_CTL1_UNMUTE);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l84_ops = {
+ .hw_params = cs42l84_pcm_hw_params,
+ .set_fmt = cs42l84_set_dai_fmt,
+ .set_sysclk = cs42l84_set_sysclk,
+ .mute_stream = cs42l84_mute_stream,
+};
+
+#define CS42L84_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver cs42l84_dai = {
+ .name = "cs42l84",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &cs42l84_ops,
+};
+
+struct cs42l84_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l84_irq_params irq_params_table[] = {
+ {CS42L84_TSRS_PLUG_INT_STATUS, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_TSRS_PLUG_VAL_MASK}
+};
+
+static void cs42l84_detect_hs(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Power up HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 3) | /* 2.7 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Power up level detection circuitry */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, 0);
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ /* Connect HSBIAS in CTIA wiring */
+ /* TODO: Should likely be subject of detection */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 0));
+
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 3));
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ regmap_read(cs42l84->regmap, CS42L84_HS_DET_STATUS2, &reg);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET);
+
+ switch (reg & 0b11) {
+ case 0b11: /* shorted */
+ case 0b00: /* open */
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1)); /* 0.0 V */
+ break;
+ }
+
+ switch (reg & 0b11) {
+ case 0b10: /* load */
+ dev_dbg(cs42l84->dev, "Detected mic\n");
+ cs42l84->hs_type = SND_JACK_HEADSET;
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ break;
+
+ case 0b00: /* open */
+ dev_dbg(cs42l84->dev, "Detected open circuit on HS4\n");
+ fallthrough;
+ case 0b11: /* shorted */
+ default:
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ cs42l84->hs_type = SND_JACK_HEADPHONE;
+ dev_dbg(cs42l84->dev, "Detected bare headphone (no mic)\n");
+ break;
+ }
+}
+
+static void cs42l84_revert_hs(struct cs42l84_private *cs42l84)
+{
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1) | /* 0.0 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Disconnect HSBIAS */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2));
+}
+
+static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84,
+ unsigned int val)
+{
+ regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG |
+ CS42L84_TS_PLUG | CS42L84_TS_UNPLUG,
+ val);
+}
+
+static irqreturn_t cs42l84_irq_thread(int irq, void *data)
+{
+ struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data;
+ unsigned int stickies[1];
+ unsigned int masks[1];
+ unsigned int reg;
+ u8 current_tip_state;
+ u8 current_ring_state;
+ int i;
+
+ mutex_lock(&cs42l84->irq_lock);
+ /* Read sticky registers to clear interrupt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l84->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l84->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* When handling plug sene IRQs, we only care about EITHER tip OR ring.
+ * Ring is useless on remove, and is only useful on insert for
+ * detecting if the plug state has changed AFTER we have handled the
+ * tip sense IRQ, e.g. if the plug was not fully seated within the tip
+ * sense debounce time.
+ */
+
+ if ((~masks[0]) & irq_params_table[0].mask) {
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ if (current_tip_state != cs42l84->tip_state) {
+ cs42l84->tip_state = current_tip_state;
+ switch (current_tip_state) {
+ case CS42L84_PLUG:
+ dev_dbg(cs42l84->dev, "Plug event\n");
+
+ cs42l84_detect_hs(cs42l84);
+
+ /*
+ * Check the tip sense status again, and possibly invalidate
+ * the detection result
+ *
+ * Thanks to debounce, this should reliably indicate if the tip
+ * was disconnected at any point during the detection procedure.
+ */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+ if (current_tip_state != CS42L84_PLUG) {
+ dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n");
+ cs42l84->tip_state = CS42L84_UNPLUG;
+ cs42l84_revert_hs(cs42l84);
+ }
+
+ /* Unmask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, 0);
+ break;
+ case CS42L84_UNPLUG:
+ cs42l84->ring_state = CS42L84_UNPLUG;
+ dev_dbg(cs42l84->dev, "Unplug event\n");
+
+ cs42l84_revert_hs(cs42l84);
+ cs42l84->hs_type = 0;
+ snd_soc_jack_report(cs42l84->jack, 0,
+ SND_JACK_HEADSET);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+ break;
+ default:
+ cs42l84->ring_state = CS42L84_TRANS;
+ break;
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+ }
+
+ /* Tip state didn't change, we must've got a ring sense IRQ */
+ current_ring_state = (((char) reg) &
+ (CS42L84_RS_PLUG | CS42L84_RS_UNPLUG)) >>
+ CS42L84_RS_PLUG_SHIFT;
+
+ if (current_ring_state != cs42l84->ring_state) {
+ cs42l84->ring_state = current_ring_state;
+ if (current_ring_state == CS42L84_PLUG)
+ cs42l84_detect_hs(cs42l84);
+ }
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Set up plug detection */
+ regmap_update_bits(cs42l84->regmap, CS42L84_MIC_DET_CTL4,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP);
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL2,
+ CS42L84_TIP_SENSE_CTL2_MODE,
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL2_MODE, CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET));
+ regmap_update_bits(cs42l84->regmap, CS42L84_RING_SENSE_CTL,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ CS42L84_RING_SENSE_CTL_RISETIME | CS42L84_RING_SENSE_CTL_FALLTIME,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_125MS) |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL,
+ CS42L84_TIP_SENSE_CTL_INV |
+ CS42L84_TIP_SENSE_CTL_RISETIME | CS42L84_TIP_SENSE_CTL_FALLTIME,
+ CS42L84_TIP_SENSE_CTL_INV |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_500MS) |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_MSM_BLOCK_EN3,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE);
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ cs42l84->tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ /* Set mic-detection threshold */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MIC_DET_CTL1, CS42L84_MIC_DET_CTL1_HS_DET_LEVEL,
+ FIELD_PREP(CS42L84_MIC_DET_CTL1_HS_DET_LEVEL, 0x2c)); /* ~1.9 V */
+
+ /* Disconnect HSBIAS (initially) */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET | CS42L84_HS_DET_CTL2_CTL,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2) |
+ FIELD_PREP(CS42L84_HS_DET_CTL2_CTL, 0));
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_CLAMP_DISABLE, 1, 1);
+
+}
+
+static int cs42l84_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84;
+ int ret, devid;
+ unsigned int reg;
+
+ cs42l84 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l84_private),
+ GFP_KERNEL);
+ if (!cs42l84)
+ return -ENOMEM;
+
+ cs42l84->dev = &i2c_client->dev;
+ i2c_set_clientdata(i2c_client, cs42l84);
+ mutex_init(&cs42l84->irq_lock);
+
+ cs42l84->regmap = devm_regmap_init_i2c(i2c_client, &cs42l84_regmap);
+ if (IS_ERR(cs42l84->regmap)) {
+ ret = PTR_ERR(cs42l84->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l84->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l84->reset_gpio)) {
+ ret = PTR_ERR(cs42l84->reset_gpio);
+ goto err_disable_noreset;
+ }
+
+ if (cs42l84->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 1);
+ }
+ usleep_range(CS42L84_BOOT_TIME_US, CS42L84_BOOT_TIME_US * 2);
+
+ /* Request IRQ if one was specified */
+ if (i2c_client->irq) {
+ ret = request_threaded_irq(i2c_client->irq,
+ NULL, cs42l84_irq_thread,
+ IRQF_ONESHOT,
+ "cs42l84", cs42l84);
+ if (ret == -EPROBE_DEFER) {
+ goto err_disable_noirq;
+ } else if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request IRQ: %d\n", ret);
+ goto err_disable_noirq;
+ }
+ }
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l84->regmap, CS42L84_DEVID);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != CS42L84_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS42L84 Device ID (%X). Expected %X\n",
+ devid, CS42L84_CHIP_ID);
+ ret = -EINVAL;
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs42l84->regmap, CS42L84_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS42L84, Revision: %02X\n", reg & 0xFF);
+
+ /* Setup plug detection */
+ cs42l84_setup_plug_detect(cs42l84);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+
+ /* Register codec for machine driver */
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l84, &cs42l84_dai, 1);
+ if (ret < 0)
+ goto err_shutdown;
+
+ return 0;
+
+err_shutdown:
+ /* Nothing to do */
+
+err_disable:
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+err_disable_noreset:
+ return ret;
+}
+
+static void cs42l84_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84 = i2c_get_clientdata(i2c_client);
+
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+}
+
+static const struct of_device_id cs42l84_of_match[] = {
+ { .compatible = "cirrus,cs42l84", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l84_of_match);
+
+static const struct i2c_device_id cs42l84_id[] = {
+ { "cs42l84" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l84_id);
+
+static struct i2c_driver cs42l84_i2c_driver = {
+ .driver = {
+ .name = "cs42l84",
+ .of_match_table = cs42l84_of_match,
+ },
+ .id_table = cs42l84_id,
+ .probe = cs42l84_i2c_probe,
+ .remove = cs42l84_i2c_remove,
+};
+
+module_i2c_driver(cs42l84_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L84 driver");
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_AUTHOR("James Calligeros <jcalligeros99@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h
new file mode 100644
index 000000000000..dbf778a902b9
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42.h
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+
+#ifndef __CS42L84_H__
+#define __CS42L84_H__
+
+#include <linux/bits.h>
+
+#define CS42L84_CHIP_ID 0x42a84
+
+#define CS42L84_DEVID 0x0000
+#define CS42L84_REVID 0x73fe
+#define CS42L84_FRZ_CTL 0x0006
+#define CS42L84_FRZ_CTL_ENGAGE BIT(0)
+
+#define CS42L84_TSRS_PLUG_INT_STATUS 0x0400
+#define CS42L84_TSRS_PLUG_INT_MASK 0x0418
+#define CS42L84_RS_PLUG_SHIFT 0
+#define CS42L84_RS_PLUG BIT(0)
+#define CS42L84_RS_UNPLUG BIT(1)
+#define CS42L84_TS_PLUG_SHIFT 2
+#define CS42L84_TS_PLUG BIT(2)
+#define CS42L84_TS_UNPLUG BIT(3)
+#define CS42L84_TSRS_PLUG_VAL_MASK GENMASK(3, 0)
+#define CS42L84_PLL_LOCK_STATUS 0x040e // probably bit 0x10
+#define CS42L84_PLL_LOCK_STATUS_LOCKED BIT(4)
+#define CS42L84_PLL_LOCK_STATUS_ERROR BIT(5)
+
+#define CS42L84_PLUG 3
+#define CS42L84_UNPLUG 0
+#define CS42L84_TRANS 1
+
+#define CS42L84_CCM_CTL1 0x0600
+#define CS42L84_CCM_CTL1_MCLK_SRC GENMASK(1, 0)
+#define CS42L84_CCM_CTL1_MCLK_SRC_RCO 0
+#define CS42L84_CCM_CTL1_MCLK_SRC_MCLK 1
+#define CS42L84_CCM_CTL1_MCLK_SRC_BCLK 2
+#define CS42L84_CCM_CTL1_MCLK_SRC_PLL 3
+#define CS42L84_CCM_CTL1_MCLK_FREQ GENMASK(3, 2)
+#define CS42L84_CCM_CTL1_MCLK_F_12MHZ 0b00
+#define CS42L84_CCM_CTL1_MCLK_F_24MHZ 0b01
+#define CS42L84_CCM_CTL1_MCLK_F_12_288KHZ 0b10
+#define CS42L84_CCM_CTL1_MCLK_F_24_576KHZ 0b11
+#define CS42L84_CCM_CTL1_RCO \
+ (FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_RCO) \
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, CS42L84_CCM_CTL1_MCLK_F_12MHZ))
+
+#define CS42L84_CCM_SAMP_RATE 0x0601
+#define CS42L84_CCM_SAMP_RATE_RATE_48KHZ 4
+#define CS42L84_CCM_SAMP_RATE_RATE_96KHZ 5
+#define CS42L84_CCM_SAMP_RATE_RATE_192KHZ 6
+#define CS42L84_CCM_SAMP_RATE_RATE_44K1HZ 12
+#define CS42L84_CCM_SAMP_RATE_RATE_88K2HZ 13
+#define CS42L84_CCM_SAMP_RATE_RATE_176K4HZ 14
+#define CS42L84_CCM_CTL3 0x0602
+#define CS42L84_CCM_CTL3_REFCLK_DIV GENMASK(2, 1)
+#define CS42L84_CCM_CTL4 0x0603
+#define CS42L84_CCM_CTL4_REFCLK_EN BIT(0)
+
+#define CS42L84_CCM_ASP_CLK_CTRL 0x0608
+
+#define CS42L84_PLL_CTL1 0x0800
+#define CS42L84_PLL_CTL1_EN BIT(0)
+#define CS42L84_PLL_CTL1_MODE GENMASK(2, 1)
+#define CS42L84_PLL_DIV_FRAC0 0x0804
+#define CS42L84_PLL_DIV_FRAC1 0x0805
+#define CS42L84_PLL_DIV_FRAC2 0x0806
+#define CS42L84_PLL_DIV_INT 0x0807
+#define CS42L84_PLL_DIVOUT 0x0808
+
+#define CS42L84_RING_SENSE_CTL 0x1282
+#define CS42L84_RING_SENSE_CTL_INV BIT(7)
+#define CS42L84_RING_SENSE_CTL_UNK1 BIT(6)
+#define CS42L84_RING_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_RING_SENSE_CTL_RISETIME GENMASK(2, 0)
+#define CS42L84_TIP_SENSE_CTL 0x1283
+#define CS42L84_TIP_SENSE_CTL_INV BIT(7)
+#define CS42L84_TIP_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_TIP_SENSE_CTL_RISETIME GENMASK(2, 0)
+
+#define CS42L84_TSRS_PLUG_STATUS 0x1288
+
+#define CS42L84_TIP_SENSE_CTL2 0x1473
+#define CS42L84_TIP_SENSE_CTL2_MODE GENMASK(7, 6)
+#define CS42L84_TIP_SENSE_CTL2_MODE_DISABLED 0b00
+#define CS42L84_TIP_SENSE_CTL2_MODE_DIG_INPUT 0b01
+#define CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET 0b11
+#define CS42L84_TIP_SENSE_CTL2_INV BIT(5)
+
+#define CS42L84_MISC_DET_CTL 0x1474
+#define CS42L84_MISC_DET_CTL_DETECT_MODE GENMASK(4, 3)
+#define CS42L84_MISC_DET_CTL_HSBIAS_CTL GENMASK(2, 1)
+#define CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET BIT(0)
+
+#define CS42L84_MIC_DET_CTL1 0x1475
+#define CS42L84_MIC_DET_CTL1_HS_DET_LEVEL GENMASK(5, 0)
+
+#define CS42L84_MIC_DET_CTL4 0x1477
+#define CS42L84_MIC_DET_CTL4_LATCH_TO_VP BIT(1)
+
+#define CS42L84_HS_DET_STATUS2 0x147d
+
+#define CS42L84_MSM_BLOCK_EN1 0x1800
+#define CS42L84_MSM_BLOCK_EN2 0x1801
+#define CS42L84_MSM_BLOCK_EN2_ASP_SHIFT 6
+#define CS42L84_MSM_BLOCK_EN2_BUS_SHIFT 5
+#define CS42L84_MSM_BLOCK_EN2_DAC_SHIFT 4
+#define CS42L84_MSM_BLOCK_EN2_ADC_SHIFT 3
+#define CS42L84_MSM_BLOCK_EN3 0x1802
+#define CS42L84_MSM_BLOCK_EN3_TR_SENSE BIT(3)
+
+#define CS42L84_HS_DET_CTL2 0x1811
+#define CS42L84_HS_DET_CTL2_CTL GENMASK(7, 6)
+#define CS42L84_HS_DET_CTL2_SET GENMASK(5, 4)
+#define CS42L84_HS_DET_CTL2_REF BIT(3)
+#define CS42L84_HS_DET_CTL2_AUTO_TIME GENMASK(1, 0)
+
+#define CS42L84_HS_SWITCH_CTL 0x1812
+#define CS42L84_HS_SWITCH_CTL_REF_HS3 BIT(7)
+#define CS42L84_HS_SWITCH_CTL_REF_HS4 BIT(6)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 BIT(5)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 BIT(4)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS3 BIT(3)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS4 BIT(2)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS3 BIT(1)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS4 BIT(0)
+
+#define CS42L84_HS_CLAMP_DISABLE 0x1813
+
+#define CS42L84_ADC_CTL1 0x2000
+#define CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT 6
+#define CS42L84_ADC_CTL1_PGA_GAIN_SHIFT 0
+#define CS42L84_ADC_CTL4 0x2003
+#define CS42L84_ADC_CTL4_WNF_CF_SHIFT 4
+#define CS42L84_ADC_CTL4_WNF_EN_SHIFT 3
+#define CS42L84_ADC_CTL4_HPF_CF_SHIFT 1
+#define CS42L84_ADC_CTL4_HPF_EN_SHIFT 0
+
+#define CS42L84_DAC_CTL1 0x3000
+#define CS42L84_DAC_CTL1_UNMUTE BIT(0)
+//#define CS42L84_DAC_CTL1_DACB_INV_SHIFT 1
+//#define CS42L84_DAC_CTL1_DACA_INV_SHIFT 0
+#define CS42L84_DAC_CTL2 0x3001
+
+#define CS42L84_DAC_CHA_VOL_LSB 0x3004
+#define CS42L84_DAC_CHA_VOL_MSB 0x3005
+#define CS42L84_DAC_CHB_VOL_LSB 0x3006
+#define CS42L84_DAC_CHB_VOL_MSB 0x3007
+#define CS42L84_HP_VOL_CTL 0x3020
+#define CS42L84_HP_VOL_CTL_ZERO_CROSS BIT(1)
+#define CS42L84_HP_VOL_CTL_SOFT BIT(0)
+
+#define CS42L84_SRC_ASP_RX_CH1 0b1101
+#define CS42L84_SRC_ASP_RX_CH2 0b1110
+
+#define CS42L84_BUS_ASP_TX_SRC 0x4000
+#define CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT 0
+#define CS42L84_BUS_DAC_SRC 0x4001
+#define CS42L84_BUS_DAC_SRC_DACA_SHIFT 0
+#define CS42L84_BUS_DAC_SRC_DACB_SHIFT 4
+
+#define CS42L84_ASP_CTL 0x5000
+#define CS42L84_ASP_CTL_BCLK_EN_SHIFT 1
+#define CS42L84_ASP_CTL_TDM_MODE BIT(2)
+#define CS42L84_ASP_FSYNC_CTL2 0x5010
+#define CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO GENMASK(7, 1)
+#define CS42L84_ASP_FSYNC_CTL3 0x5011
+#define CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI GENMASK(4, 0)
+#define CS42L84_ASP_DATA_CTL 0x5018
+
+#define CS42L84_ASP_RX_EN 0x5020
+#define CS42L84_ASP_RX_EN_CH1_SHIFT 0
+#define CS42L84_ASP_RX_EN_CH2_SHIFT 1
+#define CS42L84_ASP_TX_EN 0x5024
+#define CS42L84_ASP_TX_EN_CH1_SHIFT 0
+
+#define CS42L84_ASP_RX_CH1_CTL1 0x5028
+#define CS42L84_ASP_RX_CH1_CTL2 0x5029
+#define CS42L84_ASP_RX_CH1_WIDTH 0x502a
+#define CS42L84_ASP_RX_CH2_CTL1 0x502c
+#define CS42L84_ASP_RX_CH2_CTL2 0x502d
+#define CS42L84_ASP_RX_CH2_WIDTH 0x502e
+
+#define CS42L84_ASP_RX_CHx_CTL1_EDGE BIT(0)
+#define CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB GENMASK(7, 1)
+#define CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB GENMASK(2, 0)
+
+#define CS42L84_ASP_TX_CH1_CTL1 0x5068
+#define CS42L84_ASP_TX_CH1_CTL2 0x5069
+#define CS42L84_ASP_TX_CH1_WIDTH 0x506a
+#define CS42L84_ASP_TX_CH2_CTL1 0x506c
+#define CS42L84_ASP_TX_CH2_CTL2 0x506d
+#define CS42L84_ASP_TX_CH2_WIDTH 0x506e
+
+#define CS42L84_DEBOUNCE_TIME_125MS 0b001
+#define CS42L84_DEBOUNCE_TIME_500MS 0b011
+
+#define CS42L84_BOOT_TIME_US 3000
+#define CS42L84_CLOCK_SWITCH_DELAY_US 150
+#define CS42L84_PLL_LOCK_POLL_US 250
+#define CS42L84_PLL_LOCK_TIMEOUT_US 1250
+
+#endif /* __CS42L84_H__ */
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 800c1d549347..039b3ecb3b9b 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -12,16 +12,24 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "cs42xx8.h"
-static int cs42xx8_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
{
- int ret = cs42xx8_probe(&i2c->dev,
- devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
+ int ret;
+ struct cs42xx8_driver_data *drvdata;
+
+ drvdata = (struct cs42xx8_driver_data *)i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(&i2c->dev, -EINVAL,
+ "failed to find driver data\n");
+
+ ret = cs42xx8_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config), drvdata);
if (ret)
return ret;
@@ -31,15 +39,19 @@ static int cs42xx8_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int cs42xx8_i2c_remove(struct i2c_client *i2c)
+static void cs42xx8_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
pm_runtime_disable(&i2c->dev);
-
- return 0;
}
-static struct i2c_device_id cs42xx8_i2c_id[] = {
+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},
{}
@@ -49,7 +61,7 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
static struct i2c_driver cs42xx8_i2c_driver = {
.driver = {
.name = "cs42xx8",
- .pm = &cs42xx8_pm,
+ .pm = pm_ptr(&cs42xx8_pm),
.of_match_table = cs42xx8_of_match,
},
.probe = cs42xx8_i2c_probe,
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index c1785bd4ff19..6a925f3f7137 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -13,7 +13,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
@@ -45,6 +45,8 @@ struct cs42xx8_priv {
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 */
@@ -174,28 +176,34 @@ static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
};
struct cs42xx8_ratios {
- unsigned int ratio;
- unsigned char speed;
- unsigned char mclk;
+ 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[] = {
- { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
- { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
- { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
- { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
- { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
- { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
- { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
- { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
- { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
+ { 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_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
cs42xx8->sysclk = freq;
@@ -205,8 +213,8 @@ static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
u32 val;
/* Set DAI format */
@@ -224,7 +232,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
break;
default:
- dev_err(codec->dev, "unsupported dai format\n");
+ dev_err(component->dev, "unsupported dai format\n");
return -EINVAL;
}
@@ -234,14 +242,14 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* Set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42xx8->slave_mode = true;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42xx8->slave_mode = false;
break;
default:
- dev_err(codec->dev, "unsupported master/slave mode\n");
+ dev_err(component->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
@@ -252,41 +260,111 @@ static int cs42xx8_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 cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ 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 = cs42xx8->sysclk / params_rate(params);
- u32 i, fm, val, mask;
+ 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++) {
- if (cs42xx8_ratios[i].ratio == ratio)
+ /* 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(codec->dev, "unsupported sysclk ratio\n");
+ dev_err(component->dev, "unsupported sysclk ratio\n");
return -EINVAL;
}
- mask = CS42XX8_FUNCMOD_MFREQ_MASK;
- val = cs42xx8_ratios[i].mclk;
+ cs42xx8->rate[tx] = params_rate(params);
- fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
+ 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) | val);
+ CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val);
return 0;
}
-static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -300,7 +378,9 @@ 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,
- .digital_mute = cs42xx8_digital_mute,
+ .hw_free = cs42xx8_hw_free,
+ .mute_stream = cs42xx8_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42xx8_dai = {
@@ -378,18 +458,18 @@ const struct regmap_config cs42xx8_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
-static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
+static int cs42xx8_component_probe(struct snd_soc_component *component)
{
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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_codec_controls(codec, cs42xx8_adc3_snd_controls,
+ 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));
@@ -406,18 +486,16 @@ static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver cs42xx8_driver = {
- .probe = cs42xx8_codec_probe,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -432,17 +510,8 @@ const struct cs42xx8_driver_data cs42888_data = {
};
EXPORT_SYMBOL_GPL(cs42888_data);
-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);
-EXPORT_SYMBOL_GPL(cs42xx8_of_match);
-
-int cs42xx8_probe(struct device *dev, struct regmap *regmap)
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata)
{
- const struct of_device_id *of_id;
struct cs42xx8_priv *cs42xx8;
int ret, val, i;
@@ -456,17 +525,18 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
if (cs42xx8 == NULL)
return -ENOMEM;
- cs42xx8->regmap = regmap;
dev_set_drvdata(dev, cs42xx8);
- of_id = of_match_device(cs42xx8_of_match, dev);
- if (of_id)
- cs42xx8->drvdata = of_id->data;
+ cs42xx8->regmap = regmap;
+
+ cs42xx8->drvdata = drvdata;
- if (!cs42xx8->drvdata) {
- dev_err(dev, "failed to find driver data\n");
- return -EINVAL;
- }
+ 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)) {
@@ -520,9 +590,9 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
/* Each adc supports stereo input */
cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
- ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
if (ret) {
- dev_err(dev, "failed to register codec:%d\n", ret);
+ dev_err(dev, "failed to register component:%d\n", ret);
goto err_enable;
}
@@ -536,7 +606,6 @@ err_enable:
}
EXPORT_SYMBOL_GPL(cs42xx8_probe);
-#ifdef CONFIG_PM
static int cs42xx8_runtime_resume(struct device *dev)
{
struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
@@ -548,6 +617,8 @@ static int cs42xx8_runtime_resume(struct device *dev)
return ret;
}
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
+
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
if (ret) {
@@ -559,6 +630,7 @@ static int cs42xx8_runtime_resume(struct device *dev)
msleep(5);
regcache_cache_only(cs42xx8->regmap, false);
+ regcache_mark_dirty(cs42xx8->regmap);
ret = regcache_sync(cs42xx8->regmap);
if (ret) {
@@ -586,16 +658,17 @@ static int cs42xx8_runtime_suspend(struct device *dev)
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_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(cs42xx8_pm) = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ 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.");
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
index d36c61b6df74..342389e8b1a8 100644
--- a/sound/soc/codecs/cs42xx8.h
+++ b/sound/soc/codecs/cs42xx8.h
@@ -22,8 +22,7 @@ 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;
-extern const struct of_device_id cs42xx8_of_match[];
-int cs42xx8_probe(struct device *dev, struct regmap *regmap);
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata);
/* CS42888 register map */
#define CS42XX8_CHIPID 0x01 /* Chip ID */
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 5ba0edc19df4..d9b3d73c8388 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -1,25 +1,21 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/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/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,16 +25,15 @@
#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},
@@ -236,12 +231,12 @@ static const struct cs43130_pll_params *cs43130_get_pll_table(
return NULL;
}
-static int cs43130_pll_config(struct snd_soc_codec *codec)
+static int cs43130_pll_config(struct snd_soc_component *component)
{
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
const struct cs43130_pll_params *pll_entry;
- dev_dbg(codec->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n",
+ dev_dbg(cs43130->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);
@@ -286,11 +281,11 @@ static int cs43130_pll_config(struct snd_soc_codec *codec)
return 0;
}
-static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+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_codec_get_drvdata(codec);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
switch (freq_in) {
case 9600000:
@@ -306,7 +301,7 @@ static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
cs43130->mclk = freq_in;
break;
default:
- dev_err(codec->dev,
+ dev_err(cs43130->dev,
"unsupported pll input reference clock:%d\n", freq_in);
return -EINVAL;
}
@@ -319,21 +314,49 @@ static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
cs43130->mclk_int = freq_out;
break;
default:
- dev_err(codec->dev,
+ dev_err(cs43130->dev,
"unsupported pll output ref clock: %u\n", freq_out);
return -EINVAL;
}
- ret = cs43130_pll_config(codec);
- dev_dbg(codec->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
+ ret = cs43130_pll_config(component);
+ dev_dbg(cs43130->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
return ret;
}
-static int cs43130_change_clksrc(struct snd_soc_codec *codec,
+static int cs43130_wait_for_completion(struct cs43130_private *cs43130, struct completion *to_poll,
+ int time)
+{
+ int stickies, offset, flag, ret;
+
+ if (cs43130->has_irq_line) {
+ ret = wait_for_completion_timeout(to_poll, msecs_to_jiffies(time));
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return 0; // Discard number of jiffies left till timeout and return success
+ }
+
+ if (to_poll == &cs43130->xtal_rdy) {
+ offset = 0;
+ flag = CS43130_XTAL_RDY_INT;
+ } else if (to_poll == &cs43130->pll_rdy) {
+ offset = 0;
+ flag = CS43130_PLL_RDY_INT;
+ } else {
+ return -EINVAL;
+ }
+
+ return regmap_read_poll_timeout(cs43130->regmap, CS43130_INT_STATUS_1 + offset,
+ stickies, (stickies & flag),
+ 1000, time * 1000);
+}
+
+static int cs43130_change_clksrc(struct snd_soc_component *component,
enum cs43130_mclk_src_sel src)
{
int ret;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
int mclk_int_decoded;
if (src == cs43130->mclk_int_src) {
@@ -349,7 +372,7 @@ static int cs43130_change_clksrc(struct snd_soc_codec *codec,
mclk_int_decoded = CS43130_MCLK_24P5;
break;
default:
- dev_err(codec->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
return -EINVAL;
}
@@ -367,14 +390,13 @@ static int cs43130_change_clksrc(struct snd_soc_codec *codec,
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));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 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(codec->dev, "Timeout waiting for XTAL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
}
}
@@ -403,14 +425,13 @@ static int cs43130_change_clksrc(struct snd_soc_codec *codec,
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));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 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(codec->dev, "Timeout waiting for XTAL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
}
}
@@ -419,14 +440,13 @@ static int cs43130_change_clksrc(struct snd_soc_codec *codec,
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));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->pll_rdy, 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(codec->dev, "Timeout waiting for PLL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for PLL_READY interrupt: %d\n", ret);
+ return ret;
}
regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
@@ -456,7 +476,7 @@ static int cs43130_change_clksrc(struct snd_soc_codec *codec,
1 << CS43130_PDN_PLL_SHIFT);
break;
default:
- dev_err(codec->dev, "Invalid MCLK source value\n");
+ dev_err(cs43130->dev, "Invalid MCLK source value\n");
return -EINVAL;
}
@@ -581,7 +601,7 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
break;
case SND_SOC_DAIFMT_LEFT_J:
hi_size = bitwidth_sclk;
- frm_delay = 2;
+ frm_delay = 0;
frm_phase = 1;
break;
case SND_SOC_DAIFMT_DSP_A:
@@ -598,11 +618,32 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
return -EINVAL;
}
+ switch (cs43130->dais[dai_id].dai_invert) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sclk_edge = 1;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sclk_edge = 0;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sclk_edge = 1;
+ lrck_edge = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sclk_edge = 0;
+ lrck_edge = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
switch (cs43130->dais[dai_id].dai_mode) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
dai_mode_val = 0;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
dai_mode_val = 1;
break;
default:
@@ -610,8 +651,6 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
}
frm_size = bitwidth_sclk * params_channels(params);
- sclk_edge = 1;
- lrck_edge = 0;
loc_ch1 = 0;
loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
@@ -714,30 +753,30 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
case CS43130_ASP_PCM_DAI:
case CS43130_ASP_DOP_DAI:
regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
- (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+ (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->den & CS43130_SP_M_MSB_DATA_MASK) >>
+ (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->num & CS43130_SP_N_LSB_DATA_MASK) >>
+ (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->num & CS43130_SP_N_MSB_DATA_MASK) >>
+ (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->den & CS43130_SP_M_LSB_DATA_MASK) >>
+ (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->den & CS43130_SP_M_MSB_DATA_MASK) >>
+ (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->num & CS43130_SP_N_LSB_DATA_MASK) >>
+ (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->num & CS43130_SP_N_MSB_DATA_MASK) >>
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
CS43130_SP_N_MSB_DATA_SHIFT);
break;
default:
@@ -774,8 +813,8 @@ 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_codec *codec = dai->codec;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
unsigned int required_clk;
u8 dsd_speed;
@@ -787,11 +826,11 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
else
required_clk = CS43130_MCLK_24M;
- cs43130_set_pll(codec, 0, 0, cs43130->mclk, required_clk);
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
if (cs43130->pll_bypass)
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_EXT);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
else
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
}
cs43130->clk_req++;
@@ -807,12 +846,12 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
dsd_speed = 1;
break;
default:
- dev_err(codec->dev, "Rate(%u) not supported\n",
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
params_rate(params));
return -EINVAL;
}
- if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM)
+ if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG,
CS43130_DSD_MASTER, CS43130_DSD_MASTER);
else
@@ -833,8 +872,8 @@ static int cs43130_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -850,11 +889,11 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
else
required_clk = CS43130_MCLK_24M;
- cs43130_set_pll(codec, 0, 0, cs43130->mclk, required_clk);
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
if (cs43130->pll_bypass)
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_EXT);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
else
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
}
cs43130->clk_req++;
@@ -878,7 +917,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
dsd_speed = 1;
break;
default:
- dev_err(codec->dev, "Rate(%u) not supported\n",
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
params_rate(params));
return -EINVAL;
}
@@ -895,7 +934,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val);
break;
default:
- dev_err(codec->dev, "Invalid DAI (%d)\n", dai->id);
+ dev_err(cs43130->dev, "Invalid DAI (%d)\n", dai->id);
return -EINVAL;
}
@@ -912,28 +951,28 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
break;
}
- if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM)
+ if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
/* 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(codec->dev, "SCLK freq is not set\n");
+ dev_err(cs43130->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(codec->dev, "Format not supported: SCLK freq is too low\n");
+ dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n");
return -EINVAL;
}
- dev_dbg(codec->dev,
+ dev_dbg(cs43130->dev,
"sclk = %u, fs = %d, bitwidth_dai = %u\n",
sclk, params_rate(params), bitwidth_dai);
- dev_dbg(codec->dev,
+ dev_dbg(cs43130->dev,
"bitwidth_sclk = %u, num_ch = %u\n",
bitwidth_sclk, params_channels(params));
@@ -946,14 +985,14 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
static int cs43130_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, CS43130_MCLK_SRC_RCO);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
cs43130_pcm_dsd_mix(false, cs43130->regmap);
}
mutex_unlock(&cs43130->clk_mutex);
@@ -1023,8 +1062,8 @@ static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol,
{
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1144,8 +1183,8 @@ static const struct reg_sequence unmute_seq[] = {
static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1192,7 +1231,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1201,8 +1240,8 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
static int cs43130_pcm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1249,7 +1288,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1270,8 +1309,8 @@ static const struct reg_sequence dac_postpmd_seq[] = {
static int cs43130_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1325,7 +1364,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(codec->dev, "Invalid DAC event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid DAC event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1350,8 +1389,8 @@ static const struct reg_sequence hpin_postpmu_seq[] = {
static int cs43130_hpin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1363,13 +1402,21 @@ static int cs43130_hpin_event(struct snd_soc_dapm_widget *w,
ARRAY_SIZE(hpin_postpmu_seq));
break;
default:
- dev_err(codec->dev, "Invalid HPIN event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid HPIN event = 0x%x\n", event);
return -EINVAL;
}
return 0;
}
-static const struct snd_soc_dapm_widget digital_hp_widgets[] = {
+static const char * const bypass_mux_text[] = {
+ "Internal",
+ "Alternative",
+};
+static SOC_ENUM_SINGLE_DECL(bypass_enum, SND_SOC_NOPM, 0, bypass_mux_text);
+static const struct snd_kcontrol_new bypass_ctrl = SOC_DAPM_ENUM("Switch", bypass_enum);
+
+static const struct snd_soc_dapm_widget hp_widgets[] = {
+ SND_SOC_DAPM_MUX("Bypass Switch", SND_SOC_NOPM, 0, 0, &bypass_ctrl),
SND_SOC_DAPM_OUTPUT("HPOUTA"),
SND_SOC_DAPM_OUTPUT("HPOUTB"),
@@ -1400,19 +1447,16 @@ static const struct snd_soc_dapm_widget digital_hp_widgets[] = {
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[] = {
+/* Some devices have some extra analog widgets */
+#define NUM_ANALOG_WIDGETS 1
+
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[] = {
+static const struct snd_soc_dapm_route hp_routes[] = {
{"ASPIN PCM", NULL, "ASP PCM Playback"},
{"ASPIN DoP", NULL, "ASP DoP Playback"},
{"XSPIN DoP", NULL, "XSP DoP Playback"},
@@ -1422,18 +1466,15 @@ static const struct snd_soc_dapm_route digital_hp_routes[] = {
{"DSD", NULL, "XSPIN DSD"},
{"HiFi DAC", NULL, "ASPIN PCM"},
{"HiFi DAC", NULL, "DSD"},
- {"HPOUTA", NULL, "HiFi DAC"},
- {"HPOUTB", NULL, "HiFi DAC"},
-};
+ {"Bypass Switch", "Internal", "HiFi DAC"},
+ {"HPOUTA", NULL, "Bypass Switch"},
+ {"HPOUTB", NULL, "Bypass Switch"},
-static const struct snd_soc_dapm_route analog_hp_routes[] = {
- {"HPOUTA", NULL, "Analog Playback"},
- {"HPOUTB", NULL, "Analog Playback"},
+/* Some devices have some extra analog routes */
+#define NUM_ANALOG_ROUTES 1
+ {"Bypass Switch", "Alternative", "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
@@ -1471,18 +1512,37 @@ static int cs43130_dop_startup(struct snd_pcm_substream *substream,
static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ default:
+ dev_err(cs43130->dev, "unsupported mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_NF;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_IF;
break;
default:
- dev_err(codec->dev, "unsupported mode\n");
+ dev_err(cs43130->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
@@ -1500,12 +1560,12 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B;
break;
default:
- dev_err(codec->dev,
+ dev_err(cs43130->dev,
"unsupported audio format\n");
return -EINVAL;
}
- dev_dbg(codec->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n",
+ dev_dbg(cs43130->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);
@@ -1515,22 +1575,22 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
break;
default:
- dev_err(codec->dev, "Unsupported DAI format.\n");
+ dev_err(cs43130->dev, "Unsupported DAI format.\n");
return -EINVAL;
}
- dev_dbg(codec->dev, "dai_mode = 0x%x\n",
+ dev_dbg(cs43130->dev, "dai_mode = 0x%x\n",
cs43130->dais[codec_dai->id].dai_mode);
return 0;
@@ -1539,11 +1599,11 @@ static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int cs43130_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
+ dev_dbg(cs43130->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
cs43130->dais[codec_dai->id].sclk);
return 0;
@@ -1584,7 +1644,7 @@ static struct snd_soc_dai_driver cs43130_dai[] = {
.formats = CS43130_PCM_FORMATS,
},
.ops = &cs43130_pcm_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs43130-asp-dop",
@@ -1597,7 +1657,7 @@ static struct snd_soc_dai_driver cs43130_dai[] = {
.formats = CS43130_DOP_FORMATS,
},
.ops = &cs43130_dop_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs43130-xsp-dop",
@@ -1610,7 +1670,7 @@ static struct snd_soc_dai_driver cs43130_dai[] = {
.formats = CS43130_DOP_FORMATS,
},
.ops = &cs43130_dop_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs43130-xsp-dsd",
@@ -1627,13 +1687,13 @@ static struct snd_soc_dai_driver cs43130_dai[] = {
};
-static int cs43130_codec_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ dev_dbg(cs43130->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
clk_id, source, freq, dir);
switch (freq) {
@@ -1642,14 +1702,14 @@ static int cs43130_codec_set_sysclk(struct snd_soc_codec *codec,
cs43130->mclk = freq;
break;
default:
- dev_err(codec->dev, "Invalid MCLK INT freq: %u\n", freq);
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", freq);
return -EINVAL;
}
if (source == CS43130_MCLK_SRC_EXT) {
cs43130->pll_bypass = true;
} else {
- dev_err(codec->dev, "Invalid MCLK source\n");
+ dev_err(cs43130->dev, "Invalid MCLK source\n");
return -EINVAL;
}
@@ -1668,25 +1728,24 @@ static int cs43130_show_dc(struct device *dev, char *buf, u8 ch)
struct cs43130_private *cs43130 = i2c_get_clientdata(client);
if (!cs43130->hpload_done)
- return scnprintf(buf, PAGE_SIZE, "NO_HPLOAD\n");
+ return sysfs_emit(buf, "NO_HPLOAD\n");
else
- return scnprintf(buf, PAGE_SIZE, "%u\n",
- cs43130->hpload_dc[ch]);
+ return sysfs_emit(buf, "%u\n", cs43130->hpload_dc[ch]);
}
-static ssize_t cs43130_show_dc_l(struct device *dev,
- struct device_attribute *attr, char *buf)
+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 cs43130_show_dc_r(struct device *dev,
- struct device_attribute *attr, char *buf)
+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] = {
+static const u16 cs43130_ac_freq[CS43130_AC_FREQ] = {
24,
43,
93,
@@ -1707,8 +1766,8 @@ static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
if (cs43130->hpload_done && cs43130->ac_meas) {
for (i = 0; i < ARRAY_SIZE(cs43130_ac_freq); i++) {
- tmp = scnprintf(buf + j, PAGE_SIZE - j, "%u\n",
- cs43130->hpload_ac[i][ch]);
+ tmp = sysfs_emit_at(buf, j, "%u\n",
+ cs43130->hpload_ac[i][ch]);
if (!tmp)
break;
@@ -1717,28 +1776,36 @@ static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
return j;
} else {
- return scnprintf(buf, PAGE_SIZE, "NO_HPLOAD\n");
+ return sysfs_emit(buf, "NO_HPLOAD\n");
}
}
-static ssize_t cs43130_show_ac_l(struct device *dev,
- struct device_attribute *attr, char *buf)
+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 cs43130_show_ac_r(struct device *dev,
- struct device_attribute *attr, char *buf)
+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(hpload_dc_l, S_IRUGO, cs43130_show_dc_l, NULL);
-static DEVICE_ATTR(hpload_dc_r, S_IRUGO, cs43130_show_dc_r, NULL);
-static DEVICE_ATTR(hpload_ac_l, S_IRUGO, cs43130_show_ac_l, NULL);
-static DEVICE_ATTR(hpload_ac_r, S_IRUGO, cs43130_show_ac_r, NULL);
+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[] = {
+static const 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},
@@ -1753,7 +1820,7 @@ static struct reg_sequence hp_en_cal_seq[] = {
{CS43130_HP_LOAD_1, 0x80},
};
-static struct reg_sequence hp_en_cal_seq2[] = {
+static const 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},
@@ -1761,7 +1828,7 @@ static struct reg_sequence hp_en_cal_seq2[] = {
{CS43130_HP_LOAD_1, 0x80},
};
-static struct reg_sequence hp_dis_cal_seq[] = {
+static const struct reg_sequence hp_dis_cal_seq[] = {
{CS43130_HP_LOAD_1, 0x80},
{CS43130_DXD1, 0x99},
{CS43130_DXD12, 0},
@@ -1769,12 +1836,12 @@ static struct reg_sequence hp_dis_cal_seq[] = {
{CS43130_HP_LOAD_1, 0},
};
-static struct reg_sequence hp_dis_cal_seq2[] = {
+static const 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[] = {
+static const struct reg_sequence hp_dc_ch_l_seq[] = {
{CS43130_DXD1, 0x99},
{CS43130_DXD19, 0x0A},
{CS43130_DXD17, 0x93},
@@ -1784,12 +1851,12 @@ static struct reg_sequence hp_dc_ch_l_seq[] = {
{CS43130_HP_LOAD_1, 0x81},
};
-static struct reg_sequence hp_dc_ch_l_seq2[] = {
+static const 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[] = {
+static const struct reg_sequence hp_dc_ch_r_seq[] = {
{CS43130_DXD1, 0x99},
{CS43130_DXD19, 0x8A},
{CS43130_DXD17, 0x15},
@@ -1799,12 +1866,12 @@ static struct reg_sequence hp_dc_ch_r_seq[] = {
{CS43130_HP_LOAD_1, 0x91},
};
-static struct reg_sequence hp_dc_ch_r_seq2[] = {
+static const 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[] = {
+static const struct reg_sequence hp_ac_ch_l_seq[] = {
{CS43130_DXD1, 0x99},
{CS43130_DXD19, 0x0A},
{CS43130_DXD17, 0x93},
@@ -1814,12 +1881,12 @@ static struct reg_sequence hp_ac_ch_l_seq[] = {
{CS43130_HP_LOAD_1, 0x82},
};
-static struct reg_sequence hp_ac_ch_l_seq2[] = {
+static const 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[] = {
+static const struct reg_sequence hp_ac_ch_r_seq[] = {
{CS43130_DXD1, 0x99},
{CS43130_DXD19, 0x8A},
{CS43130_DXD17, 0x15},
@@ -1829,24 +1896,24 @@ static struct reg_sequence hp_ac_ch_r_seq[] = {
{CS43130_HP_LOAD_1, 0x92},
};
-static struct reg_sequence hp_ac_ch_r_seq2[] = {
+static const 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[] = {
+static const 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;
+ const struct reg_sequence *seq;
+ int size;
+ unsigned int msk;
};
-static struct reg_sequences hpload_seq1[] = {
+static const struct reg_sequences hpload_seq1[] = {
{
.seq = hp_en_cal_seq,
.size = ARRAY_SIZE(hp_en_cal_seq),
@@ -1884,7 +1951,7 @@ static struct reg_sequences hpload_seq1[] = {
},
};
-static struct reg_sequences hpload_seq2[] = {
+static const struct reg_sequences hpload_seq2[] = {
{
.seq = hp_en_cal_seq2,
.size = ARRAY_SIZE(hp_en_cal_seq2),
@@ -1929,7 +1996,6 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
unsigned int reg;
u32 addr;
u16 impedance;
- struct snd_soc_codec *codec = cs43130->codec;
switch (msk) {
case CS43130_HPLOAD_DC_INT:
@@ -1959,7 +2025,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
else
cs43130->hpload_dc[HP_RIGHT] = impedance;
- dev_dbg(codec->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
+ dev_dbg(cs43130->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
impedance);
} else {
if (left_ch)
@@ -1967,7 +2033,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
else
cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance;
- dev_dbg(codec->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
+ dev_dbg(cs43130->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
cs43130->ac_freq[ac_idx], !left_ch, impedance);
}
@@ -1975,13 +2041,12 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
}
static int cs43130_hpload_proc(struct cs43130_private *cs43130,
- struct reg_sequence *seq, int seq_size,
+ const 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_codec *codec = cs43130->codec;
reinit_completion(&cs43130->hpload_evt);
@@ -2004,17 +2069,17 @@ static int cs43130_hpload_proc(struct cs43130_private *cs43130,
msecs_to_jiffies(1000));
regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk);
if (!ret) {
- dev_err(codec->dev, "Timeout waiting for HPLOAD interrupt\n");
- return -1;
+ dev_err(cs43130->dev, "Timeout waiting for HPLOAD interrupt\n");
+ return -ETIMEDOUT;
}
- dev_dbg(codec->dev, "HP load stat: %x, INT_MASK_4: %x\n",
+ dev_dbg(cs43130->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(codec->dev, "HP load measure failed\n");
+ dev_dbg(cs43130->dev, "HP load measure failed\n");
return -1;
}
@@ -2056,11 +2121,11 @@ 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_codec *codec;
- struct reg_sequences *hpload_seq;
+ struct snd_soc_component *component;
+ const struct reg_sequences *hpload_seq;
cs43130 = container_of(wk, struct cs43130_private, work);
- codec = cs43130->codec;
+ component = cs43130->component;
if (!cs43130->mclk)
return;
@@ -2070,11 +2135,11 @@ static void cs43130_imp_meas(struct work_struct *wk)
mutex_lock(&cs43130->clk_mutex);
if (!cs43130->clk_req) {
/* clk not in use */
- cs43130_set_pll(codec, 0, 0, cs43130->mclk, CS43130_MCLK_22M);
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, CS43130_MCLK_22M);
if (cs43130->pll_bypass)
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_EXT);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
else
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
}
cs43130->clk_req++;
@@ -2125,9 +2190,9 @@ static void cs43130_imp_meas(struct work_struct *wk)
snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE,
CS43130_JACK_MASK);
- dev_dbg(codec->dev, "Set HP output control. DC threshold\n");
+ dev_dbg(cs43130->dev, "Set HP output control. DC threshold\n");
for (i = 0; i < CS43130_DC_THRESHOLD; i++)
- dev_dbg(codec->dev, "DC threshold[%d]: %u.\n", i,
+ dev_dbg(cs43130->dev, "DC threshold[%d]: %u.\n", i,
cs43130->dc_threshold[i]);
cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT],
@@ -2154,14 +2219,13 @@ exit:
cs43130->clk_req--;
/* clk not in use */
if (!cs43130->clk_req)
- cs43130_change_clksrc(codec, CS43130_MCLK_SRC_RCO);
+ 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_codec *codec = cs43130->codec;
unsigned int stickies[CS43130_NUM_INT];
unsigned int irq_occurrence = 0;
unsigned int masks[CS43130_NUM_INT];
@@ -2179,8 +2243,6 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
for (j = 0; j < 8; j++)
irq_occurrence += (stickies[i] >> j) & 1;
}
- dev_dbg(codec->dev, "number of interrupts occurred (%u)\n",
- irq_occurrence);
if (!irq_occurrence)
return IRQ_NONE;
@@ -2197,7 +2259,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(codec->dev,
+ dev_err(cs43130->dev,
"DC load has not completed before AC load (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
@@ -2206,7 +2268,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(codec->dev, "HP unplugged during measurement (%x)\n",
+ dev_err(cs43130->dev, "HP unplugged during measurement (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2214,7 +2276,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_OOR_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(codec->dev, "HP load out of range (%x)\n",
+ dev_err(cs43130->dev, "HP load out of range (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2222,7 +2284,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_AC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(codec->dev, "HP AC load measurement done (%x)\n",
+ dev_dbg(cs43130->dev, "HP AC load measurement done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2230,7 +2292,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_DC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(codec->dev, "HP DC load measurement done (%x)\n",
+ dev_dbg(cs43130->dev, "HP DC load measurement done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2238,7 +2300,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_ON_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(codec->dev, "HP load state machine on done (%x)\n",
+ dev_dbg(cs43130->dev, "HP load state machine on done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2246,19 +2308,19 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_OFF_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(codec->dev, "HP load state machine off done (%x)\n",
+ dev_dbg(cs43130->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(codec->dev, "Crystal err: clock is not running\n");
+ dev_err(cs43130->dev, "Crystal err: clock is not running\n");
return IRQ_HANDLED;
}
if (stickies[0] & CS43130_HP_UNPLUG_INT) {
- dev_dbg(codec->dev, "HP unplugged\n");
+ dev_dbg(cs43130->dev, "HP unplugged\n");
cs43130->hpload_done = false;
snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK);
return IRQ_HANDLED;
@@ -2267,7 +2329,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[0] & CS43130_HP_PLUG_INT) {
if (cs43130->dc_meas && !cs43130->hpload_done &&
!work_busy(&cs43130->work)) {
- dev_dbg(codec->dev, "HP load queue work\n");
+ dev_dbg(cs43130->dev, "HP load queue work\n");
queue_work(cs43130->wq, &cs43130->work);
}
@@ -2279,14 +2341,14 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
return IRQ_NONE;
}
-static int cs43130_probe(struct snd_soc_codec *codec)
+static int cs43130_probe(struct snd_soc_component *component)
{
int ret;
- struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_card *card = codec->component.card;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_card *card = component->card;
unsigned int reg;
- cs43130->codec = codec;
+ cs43130->component = component;
if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) {
regmap_update_bits(cs43130->regmap, CS43130_CRYSTAL_SET,
@@ -2297,31 +2359,23 @@ static int cs43130_probe(struct snd_soc_codec *codec)
}
ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
- &cs43130->jack, NULL, 0);
+ &cs43130->jack);
if (ret < 0) {
- dev_err(codec->dev, "Cannot create jack\n");
+ dev_err(cs43130->dev, "Cannot create jack\n");
return ret;
}
cs43130->hpload_done = false;
if (cs43130->dc_meas) {
- ret = device_create_file(codec->dev, &dev_attr_hpload_dc_l);
- if (ret < 0)
- return ret;
-
- ret = device_create_file(codec->dev, &dev_attr_hpload_dc_r);
- if (ret < 0)
- return ret;
-
- ret = device_create_file(codec->dev, &dev_attr_hpload_ac_l);
- if (ret < 0)
- return ret;
-
- ret = device_create_file(codec->dev, &dev_attr_hpload_ac_r);
- if (ret < 0)
+ ret = sysfs_create_groups(&cs43130->dev->kobj, hpload_groups);
+ if (ret)
return ret;
cs43130->wq = create_singlethread_workqueue("cs43130_hp");
+ if (!cs43130->wq) {
+ sysfs_remove_groups(&cs43130->dev->kobj, hpload_groups);
+ return -ENOMEM;
+ }
INIT_WORK(&cs43130->work, cs43130_imp_meas);
}
@@ -2338,14 +2392,35 @@ static int cs43130_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_cs43130 = {
+static const struct snd_soc_component_driver soc_component_dev_cs43130_digital = {
.probe = cs43130_probe,
- .component_driver = {
- .controls = cs43130_snd_controls,
- .num_controls = ARRAY_SIZE(cs43130_snd_controls),
- },
- .set_sysclk = cs43130_codec_set_sysclk,
+ .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,
+ /* Don't take into account the ending analog widgets and routes */
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets) - NUM_ANALOG_WIDGETS,
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes) - NUM_ANALOG_ROUTES,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs43130_analog = {
+ .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,
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets),
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes),
};
static const struct regmap_config cs43130_regmap = {
@@ -2359,23 +2434,23 @@ static const struct regmap_config cs43130_regmap = {
.readable_reg = cs43130_readable_register,
.precious_reg = cs43130_precious_register,
.volatile_reg = cs43130_volatile_register,
- .cache_type = REGCACHE_RBTREE,
- .use_single_rw = true, /* needed for regcache_sync */
+ .cache_type = REGCACHE_MAPLE,
+ /* needed for regcache_sync */
+ .use_single_read = true,
+ .use_single_write = true,
};
-static u16 const cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
+static const u16 cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
50,
120,
};
-static int cs43130_handle_device_data(struct i2c_client *i2c_client,
- struct cs43130_private *cs43130)
+static int cs43130_handle_device_data(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) {
+ if (device_property_read_u32(cs43130->dev, "cirrus,xtal-ibias", &val) < 0) {
/* Crystal is unused. System clock is used for external MCLK */
cs43130->xtal_ibias = CS43130_XTAL_UNUSED;
return 0;
@@ -2392,23 +2467,23 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client,
cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(cs43130->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");
+ cs43130->dc_meas = device_property_read_bool(cs43130->dev, "cirrus,dc-measure");
+ cs43130->ac_meas = device_property_read_bool(cs43130->dev, "cirrus,ac-measure");
- if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq,
- CS43130_AC_FREQ) < 0) {
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,ac-freq", cs43130->ac_freq,
+ CS43130_AC_FREQ)) {
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",
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,dc-threshold",
cs43130->dc_threshold,
- CS43130_DC_THRESHOLD) < 0) {
+ CS43130_DC_THRESHOLD)) {
for (i = 0; i < CS43130_DC_THRESHOLD; i++)
cs43130->dc_threshold[i] = cs43130_dc_threshold[i];
}
@@ -2416,19 +2491,20 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs43130_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs43130_i2c_probe(struct i2c_client *client)
{
+ const struct snd_soc_component_driver *component_driver;
struct cs43130_private *cs43130;
int ret;
- unsigned int devid = 0;
unsigned int reg;
- int i;
+ int i, devid;
cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
if (!cs43130)
return -ENOMEM;
+ cs43130->dev = &client->dev;
+
i2c_set_clientdata(client, cs43130);
cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
@@ -2437,44 +2513,46 @@ static int cs43130_i2c_probe(struct i2c_client *client,
return ret;
}
- if (client->dev.of_node) {
- ret = cs43130_handle_device_data(client, cs43130);
+ if (dev_fwnode(cs43130->dev)) {
+ ret = cs43130_handle_device_data(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,
+ ret = devm_regulator_bulk_get(cs43130->dev,
ARRAY_SIZE(cs43130->supplies),
cs43130->supplies);
if (ret != 0) {
- dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(cs43130->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);
+ dev_err(cs43130->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ cs43130->reset_gpio = devm_gpiod_get_optional(cs43130->dev,
"reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs43130->reset_gpio))
- return PTR_ERR(cs43130->reset_gpio);
+ 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);
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(cs43130->dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
switch (devid) {
case CS43130_CHIP_ID:
@@ -2483,7 +2561,7 @@ static int cs43130_i2c_probe(struct i2c_client *client,
case CS43198_CHIP_ID:
break;
default:
- dev_err(&client->dev,
+ dev_err(cs43130->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);
@@ -2494,11 +2572,11 @@ static int cs43130_i2c_probe(struct i2c_client *client,
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");
+ dev_err(cs43130->dev, "Get Revision ID failed\n");
goto err;
}
- dev_info(&client->dev,
+ dev_info(cs43130->dev,
"Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
reg & 0xFF);
@@ -2508,61 +2586,44 @@ static int cs43130_i2c_probe(struct i2c_client *client,
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);
- return ret;
+ if (!client->irq) {
+ dev_dbg(cs43130->dev, "IRQ not found, will poll instead\n");
+ cs43130->has_irq_line = 0;
+ } else {
+ ret = devm_request_threaded_irq(cs43130->dev, client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ cs43130->has_irq_line = 1;
}
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);
+ pm_runtime_set_autosuspend_delay(cs43130->dev, 100);
+ pm_runtime_use_autosuspend(cs43130->dev);
+ pm_runtime_set_active(cs43130->dev);
+ pm_runtime_enable(cs43130->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_codec_dev_cs43130.component_driver.dapm_widgets =
- all_hp_widgets;
- soc_codec_dev_cs43130.component_driver.num_dapm_widgets =
- ARRAY_SIZE(all_hp_widgets);
- soc_codec_dev_cs43130.component_driver.dapm_routes =
- all_hp_routes;
- soc_codec_dev_cs43130.component_driver.num_dapm_routes =
- ARRAY_SIZE(all_hp_routes);
+ component_driver = &soc_component_dev_cs43130_analog;
break;
case CS43198_CHIP_ID:
case CS4399_CHIP_ID:
- soc_codec_dev_cs43130.component_driver.dapm_widgets =
- digital_hp_widgets;
- soc_codec_dev_cs43130.component_driver.num_dapm_widgets =
- ARRAY_SIZE(digital_hp_widgets);
- soc_codec_dev_cs43130.component_driver.dapm_routes =
- digital_hp_routes;
- soc_codec_dev_cs43130.component_driver.num_dapm_routes =
- ARRAY_SIZE(digital_hp_routes);
+ component_driver = &soc_component_dev_cs43130_digital;
break;
}
- ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_cs43130,
+ ret = devm_snd_soc_register_component(cs43130->dev, component_driver,
cs43130_dai, ARRAY_SIZE(cs43130_dai));
if (ret < 0) {
- dev_err(&client->dev,
- "snd_soc_register_codec failed with ret = %d\n", ret);
+ dev_err(cs43130->dev,
+ "snd_soc_register_component failed with ret = %d\n", ret);
goto err;
}
@@ -2572,11 +2633,17 @@ static int cs43130_i2c_probe(struct i2c_client *client,
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 int cs43130_i2c_remove(struct i2c_client *client)
+static void cs43130_i2c_remove(struct i2c_client *client)
{
struct cs43130_private *cs43130 = i2c_get_clientdata(client);
@@ -2593,23 +2660,19 @@ static int cs43130_i2c_remove(struct i2c_client *client)
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);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_r);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_r);
}
gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
- pm_runtime_disable(&client->dev);
+ pm_runtime_disable(cs43130->dev);
regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
-
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
}
-static int __maybe_unused cs43130_runtime_suspend(struct device *dev)
+static int cs43130_runtime_suspend(struct device *dev)
{
struct cs43130_private *cs43130 = dev_get_drvdata(dev);
@@ -2628,7 +2691,7 @@ static int __maybe_unused cs43130_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused cs43130_runtime_resume(struct device *dev)
+static int cs43130_runtime_resume(struct device *dev)
{
struct cs43130_private *cs43130 = dev_get_drvdata(dev);
int ret;
@@ -2664,10 +2727,10 @@ err:
}
static const struct dev_pm_ops cs43130_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, NULL)
};
+#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id cs43130_of_match[] = {
{.compatible = "cirrus,cs43130",},
{.compatible = "cirrus,cs4399",},
@@ -2677,12 +2740,23 @@ static const struct of_device_id cs43130_of_match[] = {
};
MODULE_DEVICE_TABLE(of, cs43130_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id cs43130_acpi_match[] = {
+ { "CSC4399", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match);
+#endif
+
static const struct i2c_device_id cs43130_i2c_id[] = {
- {"cs43130", 0},
- {"cs4399", 0},
- {"cs43131", 0},
- {"cs43198", 0},
+ {"cs43130"},
+ {"cs4399"},
+ {"cs43131"},
+ {"cs43198"},
{}
};
@@ -2690,9 +2764,10 @@ 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,
+ .name = "cs43130",
+ .of_match_table = of_match_ptr(cs43130_of_match),
+ .acpi_match_table = ACPI_PTR(cs43130_acpi_match),
+ .pm = pm_ptr(&cs43130_runtime_pm),
},
.id_table = cs43130_i2c_id,
.probe = cs43130_i2c_probe,
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
index 781258418d89..dbdb5b262f1b 100644
--- a/sound/soc/codecs/cs43130.h
+++ b/sound/soc/codecs/cs43130.h
@@ -1,24 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS43130 codec driver
*
* Copyright 2017 Cirrus Logic, Inc.
*
* Author: Li Xu <li.xu@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
*/
#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
@@ -381,97 +374,96 @@ enum cs43130_dai_id {
};
struct cs43130_clk_gen {
- unsigned int mclk_int;
- int fs;
- u16 den;
- u16 num;
+ 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, 441, 10,},
- {22579200, 44100, 32, 1,},
- {22579200, 48000, 147, 5,},
- {22579200, 88200, 16, 1,},
- {22579200, 96000, 147, 10,},
- {22579200, 176400, 8, 1,},
- {22579200, 192000, 147, 20,},
- {22579200, 352800, 4, 1,},
- {22579200, 384000, 147, 40,},
- {24576000, 32000, 48, 1,},
- {24576000, 44100, 5120, 147,},
- {24576000, 48000, 32, 1,},
- {24576000, 88200, 2560, 147,},
- {24576000, 96000, 16, 1,},
- {24576000, 176400, 1280, 147,},
- {24576000, 192000, 8, 1,},
- {24576000, 352800, 640, 147,},
- {24576000, 384000, 4, 1,},
+ { 22579200, 32000, .v = { 10, 441, }, },
+ { 22579200, 44100, .v = { 1, 32, }, },
+ { 22579200, 48000, .v = { 5, 147, }, },
+ { 22579200, 88200, .v = { 1, 16, }, },
+ { 22579200, 96000, .v = { 10, 147, }, },
+ { 22579200, 176400, .v = { 1, 8, }, },
+ { 22579200, 192000, .v = { 20, 147, }, },
+ { 22579200, 352800, .v = { 1, 4, }, },
+ { 22579200, 384000, .v = { 40, 147, }, },
+ { 24576000, 32000, .v = { 1, 48, }, },
+ { 24576000, 44100, .v = { 147, 5120, }, },
+ { 24576000, 48000, .v = { 1, 32, }, },
+ { 24576000, 88200, .v = { 147, 2560, }, },
+ { 24576000, 96000, .v = { 1, 16, }, },
+ { 24576000, 176400, .v = { 147, 1280, }, },
+ { 24576000, 192000, .v = { 1, 8, }, },
+ { 24576000, 352800, .v = { 147, 640, }, },
+ { 24576000, 384000, .v = { 1, 4, }, },
};
/* frm_size = 32 */
static const struct cs43130_clk_gen cs43130_32_clk_gen[] = {
- {22579200, 32000, 441, 20,},
- {22579200, 44100, 16, 1,},
- {22579200, 48000, 147, 10,},
- {22579200, 88200, 8, 1,},
- {22579200, 96000, 147, 20,},
- {22579200, 176400, 4, 1,},
- {22579200, 192000, 147, 40,},
- {22579200, 352800, 2, 1,},
- {22579200, 384000, 147, 80,},
- {24576000, 32000, 24, 1,},
- {24576000, 44100, 2560, 147,},
- {24576000, 48000, 16, 1,},
- {24576000, 88200, 1280, 147,},
- {24576000, 96000, 8, 1,},
- {24576000, 176400, 640, 147,},
- {24576000, 192000, 4, 1,},
- {24576000, 352800, 320, 147,},
- {24576000, 384000, 2, 1,},
+ { 22579200, 32000, .v = { 20, 441, }, },
+ { 22579200, 44100, .v = { 1, 16, }, },
+ { 22579200, 48000, .v = { 10, 147, }, },
+ { 22579200, 88200, .v = { 1, 8, }, },
+ { 22579200, 96000, .v = { 20, 147, }, },
+ { 22579200, 176400, .v = { 1, 4, }, },
+ { 22579200, 192000, .v = { 40, 147, }, },
+ { 22579200, 352800, .v = { 1, 2, }, },
+ { 22579200, 384000, .v = { 80, 147, }, },
+ { 24576000, 32000, .v = { 1, 24, }, },
+ { 24576000, 44100, .v = { 147, 2560, }, },
+ { 24576000, 48000, .v = { 1, 16, }, },
+ { 24576000, 88200, .v = { 147, 1280, }, },
+ { 24576000, 96000, .v = { 1, 8, }, },
+ { 24576000, 176400, .v = { 147, 640, }, },
+ { 24576000, 192000, .v = { 1, 4, }, },
+ { 24576000, 352800, .v = { 147, 320, }, },
+ { 24576000, 384000, .v = { 1, 2, }, },
};
/* frm_size = 48 */
static const struct cs43130_clk_gen cs43130_48_clk_gen[] = {
- {22579200, 32000, 147, 100,},
- {22579200, 44100, 32, 3,},
- {22579200, 48000, 49, 5,},
- {22579200, 88200, 16, 3,},
- {22579200, 96000, 49, 10,},
- {22579200, 176400, 8, 3,},
- {22579200, 192000, 49, 20,},
- {22579200, 352800, 4, 3,},
- {22579200, 384000, 49, 40,},
- {24576000, 32000, 16, 1,},
- {24576000, 44100, 5120, 441,},
- {24576000, 48000, 32, 3,},
- {24576000, 88200, 2560, 441,},
- {24576000, 96000, 16, 3,},
- {24576000, 176400, 1280, 441,},
- {24576000, 192000, 8, 3,},
- {24576000, 352800, 640, 441,},
- {24576000, 384000, 4, 3,},
+ { 22579200, 32000, .v = { 100, 147, }, },
+ { 22579200, 44100, .v = { 3, 32, }, },
+ { 22579200, 48000, .v = { 5, 49, }, },
+ { 22579200, 88200, .v = { 3, 16, }, },
+ { 22579200, 96000, .v = { 10, 49, }, },
+ { 22579200, 176400, .v = { 3, 8, }, },
+ { 22579200, 192000, .v = { 20, 49, }, },
+ { 22579200, 352800, .v = { 3, 4, }, },
+ { 22579200, 384000, .v = { 40, 49, }, },
+ { 24576000, 32000, .v = { 1, 16, }, },
+ { 24576000, 44100, .v = { 441, 5120, }, },
+ { 24576000, 48000, .v = { 3, 32, }, },
+ { 24576000, 88200, .v = { 441, 2560, }, },
+ { 24576000, 96000, .v = { 3, 16, }, },
+ { 24576000, 176400, .v = { 441, 1280, }, },
+ { 24576000, 192000, .v = { 3, 8, }, },
+ { 24576000, 352800, .v = { 441, 640, }, },
+ { 24576000, 384000, .v = { 3, 4, }, },
};
/* frm_size = 64 */
static const struct cs43130_clk_gen cs43130_64_clk_gen[] = {
- {22579200, 32000, 441, 40,},
- {22579200, 44100, 8, 1,},
- {22579200, 48000, 147, 20,},
- {22579200, 88200, 4, 1,},
- {22579200, 96000, 147, 40,},
- {22579200, 176400, 2, 1,},
- {22579200, 192000, 147, 80,},
- {22579200, 352800, 1, 1,},
- {24576000, 32000, 12, 1,},
- {24576000, 44100, 1280, 147,},
- {24576000, 48000, 8, 1,},
- {24576000, 88200, 640, 147,},
- {24576000, 96000, 4, 1,},
- {24576000, 176400, 320, 147,},
- {24576000, 192000, 2, 1,},
- {24576000, 352800, 160, 147,},
- {24576000, 384000, 1, 1,},
+ { 22579200, 32000, .v = { 40, 441, }, },
+ { 22579200, 44100, .v = { 1, 8, }, },
+ { 22579200, 48000, .v = { 20, 147, }, },
+ { 22579200, 88200, .v = { 1, 4, }, },
+ { 22579200, 96000, .v = { 40, 147, }, },
+ { 22579200, 176400, .v = { 1, 2, }, },
+ { 22579200, 192000, .v = { 80, 147, }, },
+ { 22579200, 352800, .v = { 1, 1, }, },
+ { 24576000, 32000, .v = { 1, 12, }, },
+ { 24576000, 44100, .v = { 147, 1280, }, },
+ { 24576000, 48000, .v = { 1, 8, }, },
+ { 24576000, 88200, .v = { 147, 640, }, },
+ { 24576000, 96000, .v = { 1, 4, }, },
+ { 24576000, 176400, .v = { 147, 320, }, },
+ { 24576000, 192000, .v = { 1, 2, }, },
+ { 24576000, 352800, .v = { 147, 160, }, },
+ { 24576000, 384000, .v = { 1, 1, }, },
};
struct cs43130_bitwidth_map {
@@ -505,15 +497,18 @@ struct cs43130_dai {
unsigned int sclk;
unsigned int dai_format;
unsigned int dai_mode;
+ unsigned int dai_invert;
};
struct cs43130_private {
- struct snd_soc_codec *codec;
+ struct device *dev;
+ 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;
+ bool has_irq_line;
/* shared by both DAIs */
struct mutex clk_mutex;
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
new file mode 100644
index 000000000000..b726e22ef57d
--- /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_CBC_CFC:
+ 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" },
+ { }
+};
+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 = 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
index 0a749c79ef57..d9a9c34fffe3 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -1,26 +1,22 @@
+// 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>
- *
- * 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/mod_devicetable.h>
#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>
@@ -74,8 +70,8 @@ static bool cs4349_writeable_register(struct device *dev, unsigned int reg)
static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -97,8 +93,8 @@ 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_codec *codec = dai->codec;
- struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -126,7 +122,7 @@ static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- ret = snd_soc_update_bits(codec, CS4349_MODE, DIF_MASK,
+ ret = snd_soc_component_update_bits(component, CS4349_MODE, DIF_MASK,
MODE_FORMAT(fmt));
if (ret < 0)
return ret;
@@ -134,16 +130,16 @@ static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs4349_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs4349_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;
reg = 0;
if (mute)
reg = MUTE_AB_MASK;
- return snd_soc_update_bits(codec, CS4349_MUTE, MUTE_AB_MASK, reg);
+ return snd_soc_component_update_bits(component, CS4349_MUTE, MUTE_AB_MASK, reg);
}
static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0);
@@ -226,12 +222,9 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
{"OutputB", NULL, "HiFi DAC"},
};
-#define CS4349_PCM_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 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
@@ -239,7 +232,8 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
static const struct snd_soc_dai_ops cs4349_dai_ops = {
.hw_params = cs4349_pcm_hw_params,
.set_fmt = cs4349_set_dai_fmt,
- .digital_mute = cs4349_digital_mute,
+ .mute_stream = cs4349_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4349_dai = {
@@ -252,18 +246,19 @@ static struct snd_soc_dai_driver cs4349_dai = {
.formats = CS4349_PCM_FORMATS,
},
.ops = &cs4349_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static const struct snd_soc_codec_driver soc_codec_dev_cs4349 = {
- .component_driver = {
- .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),
- },
+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 = {
@@ -275,11 +270,10 @@ static const struct regmap_config cs4349_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults),
.readable_reg = cs4349_readable_register,
.writeable_reg = cs4349_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs4349_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4349_i2c_probe(struct i2c_client *client)
{
struct cs4349_private *cs4349;
int ret;
@@ -305,23 +299,19 @@ static int cs4349_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, cs4349);
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4349,
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_cs4349,
&cs4349_dai, 1);
}
-static int cs4349_i2c_remove(struct i2c_client *client)
+static void cs4349_i2c_remove(struct i2c_client *client)
{
struct cs4349_private *cs4349 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
-
/* Hold down reset */
gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
-
- return 0;
}
-#ifdef CONFIG_PM
static int cs4349_runtime_suspend(struct device *dev)
{
struct cs4349_private *cs4349 = dev_get_drvdata(dev);
@@ -355,11 +345,9 @@ static int cs4349_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops cs4349_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, NULL)
};
static const struct of_device_id cs4349_of_match[] = {
@@ -370,7 +358,7 @@ static const struct of_device_id cs4349_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4349_of_match);
static const struct i2c_device_id cs4349_i2c_id[] = {
- {"cs4349", 0},
+ {"cs4349"},
{}
};
@@ -380,6 +368,7 @@ static struct i2c_driver cs4349_i2c_driver = {
.driver = {
.name = "cs4349",
.of_match_table = cs4349_of_match,
+ .pm = pm_ptr(&cs4349_runtime_pm),
},
.id_table = cs4349_i2c_id,
.probe = cs4349_i2c_probe,
diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h
index d58c06a25358..bf31405f7f05 100644
--- a/sound/soc/codecs/cs4349.h
+++ b/sound/soc/codecs/cs4349.h
@@ -1,19 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS4349 codec driver
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Author: Tim Howe <Tim.Howe@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
*/
#ifndef __CS4349_H__
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
new file mode 100644
index 000000000000..29a2bcfb3048
--- /dev/null
+++ b/sound/soc/codecs/cs47l15.c
@@ -0,0 +1,1506 @@
+// 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 const struct snd_soc_dai_ops cs47l15_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+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,
+ },
+ .ops = &cs47l15_dai_ops,
+ },
+ {
+ .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(snd_soc_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",
+ snd_soc_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 = 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
index be2750680838..e2a839fae4fc 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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>
@@ -33,26 +30,28 @@
#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 wm_adsp_region cs47l24_dsp2_regions[] = {
+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 wm_adsp_region cs47l24_dsp3_regions[] = {
+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 wm_adsp_region *cs47l24_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l24_dsp_regions[] = {
cs47l24_dsp2_regions,
cs47l24_dsp3_regions,
};
@@ -60,20 +59,22 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ 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(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+ dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret);
return ret;
}
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
- return wm_adsp2_early_event(w, kcontrol, event, v);
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
}
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
@@ -194,14 +195,13 @@ SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
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),
+ 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),
+ 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),
@@ -234,6 +234,9 @@ 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);
@@ -492,77 +495,76 @@ 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),
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
@@ -929,10 +931,10 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "DSP3 Voice Trigger", "Switch", "DSP3" },
};
-static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+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_codec_get_drvdata(codec);
+ struct cs47l24_priv *cs47l24 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case CS47L24_FLL1:
@@ -955,6 +957,10 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
#define CS47L24_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 cs47l24_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l24_dai[] = {
{
.name = "cs47l24-aif1",
@@ -975,8 +981,8 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "cs47l24-aif2",
@@ -997,8 +1003,8 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "cs47l24-aif3",
@@ -1019,8 +1025,8 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "cs47l24-cpu-voicectrl",
@@ -1031,7 +1037,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l24_dai_ops,
},
{
.name = "cs47l24-dsp-voicectrl",
@@ -1052,7 +1058,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l24_dai_ops,
},
{
.name = "cs47l24-dsp-trace",
@@ -1066,21 +1072,22 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
},
};
-static int cs47l24_open(struct snd_compr_stream *stream)
+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_platform_get_drvdata(rtd->platform);
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
- if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) {
n_adsp = 2;
- } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
+ } else if (strcmp(snd_soc_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",
- rtd->codec_dai->name);
+ snd_soc_rtd_to_codec(rtd, 0)->name);
return -EINVAL;
}
@@ -1115,34 +1122,34 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int cs47l24_codec_probe(struct snd_soc_codec *codec)
+static int cs47l24_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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_codec_init_regmap(codec, arizona->regmap);
+ snd_soc_component_init_regmap(component, arizona->regmap);
- ret = arizona_init_spk(codec);
+ ret = arizona_init_spk(component);
if (ret < 0)
return ret;
- arizona_init_gpio(codec);
- arizona_init_mono(codec);
+ arizona_init_gpio(component);
+ arizona_init_mono(component);
- ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
+ ret = wm_adsp2_component_probe(&priv->core.adsp[1], component);
if (ret)
goto err_adsp2_codec_probe;
- ret = wm_adsp2_codec_probe(&priv->core.adsp[2], codec);
+ ret = wm_adsp2_component_probe(&priv->core.adsp[2], component);
if (ret)
goto err_adsp2_codec_probe;
- ret = snd_soc_add_codec_controls(codec,
- &arizona_adsp2_rate_controls[1], 2);
+ ret = snd_soc_add_component_controls(component,
+ &arizona_adsp2_rate_controls[1],
+ 2);
if (ret)
goto err_adsp2_codec_probe;
@@ -1151,22 +1158,20 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
return 0;
err_adsp2_codec_probe:
- wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
- wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
return ret;
}
-static int cs47l24_codec_remove(struct snd_soc_codec *codec)
+static void cs47l24_component_remove(struct snd_soc_component *component)
{
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
- wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
- wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
priv->core.arizona->dapm = NULL;
-
- return 0;
}
#define CS47L24_DIG_VU 0x0200
@@ -1177,37 +1182,31 @@ static unsigned int cs47l24_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_4L,
};
-static const struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
- .probe = cs47l24_codec_probe,
- .remove = cs47l24_codec_remove,
-
- .idle_bias_off = true,
-
- .set_sysclk = arizona_set_sysclk,
- .set_pll = cs47l24_set_fll,
-
- .component_driver = {
- .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),
- },
+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_compr_ops cs47l24_compr_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_platform_driver cs47l24_compr_platform = {
- .compr_ops = &cs47l24_compr_ops,
+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)
@@ -1219,7 +1218,7 @@ static int cs47l24_probe(struct platform_device *pdev)
BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI);
cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!cs47l24)
return -ENOMEM;
@@ -1238,15 +1237,15 @@ static int cs47l24_probe(struct platform_device *pdev)
for (i = 1; i <= 2; i++) {
cs47l24->core.adsp[i].part = "cs47l24";
- cs47l24->core.adsp[i].num = i + 1;
- cs47l24->core.adsp[i].type = WMFW_ADSP2;
- cs47l24->core.adsp[i].dev = arizona->dev;
- cs47l24->core.adsp[i].regmap = arizona->regmap;
+ 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].base = ARIZONA_DSP1_CONTROL_1 +
+ cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 +
(0x100 * i);
- cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1];
- cs47l24->core.adsp[i].num_mems =
+ 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]);
@@ -1289,6 +1288,12 @@ static int cs47l24_probe(struct platform_device *pdev)
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);
@@ -1298,38 +1303,31 @@ static int cs47l24_probe(struct platform_device *pdev)
if (ret < 0)
goto err_dsp_irq;
- ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
+ 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 platform: %d\n", ret);
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
goto err_spk_irqs;
}
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
- cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- goto err_platform;
- }
-
return ret;
-err_platform:
- snd_soc_unregister_platform(&pdev->dev);
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 int cs47l24_remove(struct platform_device *pdev)
+static void cs47l24_remove(struct platform_device *pdev)
{
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
struct arizona *arizona = cs47l24->core.arizona;
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
wm_adsp2_remove(&cs47l24->core.adsp[1]);
@@ -1337,9 +1335,8 @@ static int cs47l24_remove(struct platform_device *pdev)
arizona_free_spk_irqs(arizona);
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
-
- return 0;
}
static struct platform_driver cs47l24_codec_driver = {
diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h
index 77ab2b77b2e6..9fd4b41f1f3a 100644
--- a/sound/soc/codecs/cs47l24.h
+++ b/sound/soc/codecs/cs47l24.h
@@ -1,13 +1,10 @@
+/* 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>
- *
- * 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 _CS47L24_H
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
new file mode 100644
index 000000000000..85555c7a2e4b
--- /dev/null
+++ b/sound/soc/codecs/cs47l35.c
@@ -0,0 +1,1781 @@
+// 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 const struct snd_soc_dai_ops cs47l35_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+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,
+ },
+ .ops = &cs47l35_dai_ops,
+ },
+ {
+ .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,
+ },
+ .ops = &cs47l35_dai_ops,
+ },
+ {
+ .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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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..d34f4e8c26d3
--- /dev/null
+++ b/sound/soc/codecs/cs47l85.c
@@ -0,0 +1,2732 @@
+// 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 const struct snd_soc_dai_ops cs47l85_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+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,
+ },
+ .ops = &cs47l85_dai_ops,
+ },
+ {
+ .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,
+ },
+ .ops = &cs47l85_dai_ops,
+ },
+ {
+ .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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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..a9e703981f37
--- /dev/null
+++ b/sound/soc/codecs/cs47l90.c
@@ -0,0 +1,2656 @@
+// 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 const struct snd_soc_dai_ops cs47l90_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+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,
+ },
+ .ops = &cs47l90_dai_ops,
+ },
+ {
+ .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,
+ },
+ .ops = &cs47l90_dai_ops,
+ },
+ {
+ .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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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..2c355c61acd8
--- /dev/null
+++ b/sound/soc/codecs/cs47l92.c
@@ -0,0 +1,2104 @@
+// 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 const struct snd_soc_dai_ops cs47l92_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+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,
+ },
+ .ops = &cs47l92_dai_ops,
+ },
+ {
+ .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(snd_soc_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",
+ snd_soc_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 = 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/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
new file mode 100644
index 000000000000..8ff3652a010e
--- /dev/null
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Regmap tables and other data for Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/device.h>
+#include <linux/linear_range.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+
+#include "cs48l32.h"
+
+static const struct reg_sequence cs48l32_reva_patch[] = {
+ { 0x00001044, 0x0005000f },
+ { 0x00001c34, 0x000037e8 },
+ { 0x000046d8, 0x00000fe0 },
+};
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch,
+ ARRAY_SIZE(cs48l32_reva_patch));
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to apply patch\n");
+
+ return 0;
+}
+
+static const struct reg_default cs48l32_reg_default[] = {
+ { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */
+ { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */
+ { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */
+ { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */
+ { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */
+ { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */
+ { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */
+ { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */
+ { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */
+ { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */
+ { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */
+ { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */
+ { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */
+ { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */
+ { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */
+ { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */
+ { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */
+ { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */
+ { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */
+ { 0x00001400, 0x00000002 }, /* CLOCK32K */
+ { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */
+ { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */
+ { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */
+ { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */
+ { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */
+ { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */
+ { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */
+ { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */
+ { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */
+ { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */
+ { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */
+ { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */
+ { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */
+ { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */
+ { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */
+ { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */
+ { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */
+ { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */
+ { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */
+ { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */
+ { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */
+ { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */
+ { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */
+ { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */
+ { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */
+ { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */
+ { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */
+ { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */
+ { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */
+ { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */
+ { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */
+ { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */
+ { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */
+ { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */
+ { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */
+ { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */
+ { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */
+ { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */
+ { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */
+ { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */
+ { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */
+ { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */
+ { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */
+ { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */
+ { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */
+ { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */
+ { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */
+ { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */
+ { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */
+ { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */
+ { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */
+ { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */
+ { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */
+ { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */
+ { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */
+ { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */
+ { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */
+ { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */
+ { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */
+ { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */
+ { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */
+ { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */
+ { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */
+ { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */
+ { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */
+ { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */
+ { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */
+ { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */
+ { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */
+ { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */
+ { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */
+ { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */
+ { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */
+ { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */
+ { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */
+ { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */
+ { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */
+ { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */
+ { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */
+ { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */
+ { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */
+ { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */
+ { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */
+ { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */
+ { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */
+ { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */
+ { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */
+ { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */
+ { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */
+ { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */
+ { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */
+ { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */
+ { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */
+ { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */
+ { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */
+ { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */
+ { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */
+ { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */
+ { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */
+ { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */
+ { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */
+ { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */
+ { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */
+ { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */
+ { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */
+ { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */
+ { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */
+ { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */
+ { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */
+ { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */
+ { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */
+ { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */
+ { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */
+ { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */
+ { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */
+ { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */
+ { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */
+ { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */
+ { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */
+ { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */
+ { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */
+ { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */
+ { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */
+ { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */
+ { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */
+ { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */
+ { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */
+ { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */
+ { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */
+ { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */
+ { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */
+ { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */
+ { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */
+ { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */
+ { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */
+ { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */
+ { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */
+ { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */
+ { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */
+ { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */
+ { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */
+ { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */
+ { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */
+ { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */
+ { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */
+ { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */
+ { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */
+ { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */
+ { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */
+ { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */
+ { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */
+ { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */
+ { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */
+ { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */
+ { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */
+ { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */
+ { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */
+ { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */
+ { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */
+ { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */
+ { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */
+ { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */
+ { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */
+ { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */
+ { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */
+ { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */
+ { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */
+ { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */
+ { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */
+ { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */
+ { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */
+ { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */
+ { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */
+ { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */
+ { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */
+ { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */
+ { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */
+ { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */
+ { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */
+ { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */
+ { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */
+ { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */
+ { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */
+ { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */
+ { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */
+ { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */
+ { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */
+ { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */
+ { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */
+ { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */
+ { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */
+ { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */
+ { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */
+ { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */
+ { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */
+ { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */
+ { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */
+ { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */
+ { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */
+ { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */
+ { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */
+ { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */
+ { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */
+ { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */
+ { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */
+ { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */
+ { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */
+ { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */
+ { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */
+ { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */
+ { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */
+ { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */
+ { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */
+ { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */
+ { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */
+ { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */
+ { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */
+ { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */
+ { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */
+ { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */
+ { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */
+ { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */
+ { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */
+ { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */
+ { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */
+ { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */
+ { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */
+ { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */
+ { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */
+ { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */
+ { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */
+ { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */
+ { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */
+ { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */
+ { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */
+ { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */
+ { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */
+ { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */
+ { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */
+ { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */
+ { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */
+ { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */
+ { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */
+ { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */
+ { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */
+ { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */
+ { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */
+ { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */
+ { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */
+ { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */
+ { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */
+ { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */
+ { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */
+ { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */
+ { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */
+ { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */
+ { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */
+ { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */
+ { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */
+ { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */
+ { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */
+ { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */
+ { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */
+ { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */
+ { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */
+ { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */
+ { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */
+ { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */
+ { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */
+ { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */
+ { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */
+ { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */
+ { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */
+ { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */
+ { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */
+ { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */
+ { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */
+ { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */
+ { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */
+ { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */
+ { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */
+ { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */
+ { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */
+ { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */
+ { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */
+ { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */
+ { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */
+ { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */
+ { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */
+ { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */
+ { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */
+ { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */
+ { 0x0000b800, 0x00000000 }, /* US_CONTROL */
+ { 0x0000b804, 0x00002020 }, /* US1_CONTROL */
+ { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */
+ { 0x0000b814, 0x00002020 }, /* US2_CONTROL */
+ { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */
+ { 0x00018110, 0x00000700 }, /* IRQ1_MASK_1 */
+ { 0x00018114, 0x00000004 }, /* IRQ1_MASK_2 */
+ { 0x00018120, 0x03ff0000 }, /* IRQ1_MASK_5 */
+ { 0x00018124, 0x00000103 }, /* IRQ1_MASK_6 */
+ { 0x00018128, 0x003f0000 }, /* IRQ1_MASK_7 */
+ { 0x00018130, 0xff00000f }, /* IRQ1_MASK_9 */
+ { 0x00018138, 0xffff0000 }, /* IRQ1_MASK_11 */
+};
+
+static bool cs48l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1:
+ case CS48L32_OUTPUT_SYS_CLK:
+ case CS48L32_AUXPDM_CTRL:
+ case CS48L32_AUXPDM_CTRL2:
+ case CS48L32_CLOCK32K:
+ case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4:
+ case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK:
+ case CS48L32_CHARGE_PUMP1:
+ case CS48L32_LDO2_CTRL1:
+ case CS48L32_MICBIAS_CTRL1:
+ case CS48L32_MICBIAS_CTRL5:
+ case CS48L32_IRQ1_CTRL_AOD:
+ case CS48L32_INPUT_CONTROL:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_RATE_CONTROL:
+ case CS48L32_INPUT_CONTROL2:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_INPUT1_CONTROL1:
+ case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2:
+ case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2:
+ case CS48L32_INPUT2_CONTROL1:
+ case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2:
+ case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2:
+ case CS48L32_INPUT_HPF_CONTROL:
+ case CS48L32_INPUT_VOL_CONTROL:
+ case CS48L32_AUXPDM_CONTROL1:
+ case CS48L32_AUXPDM_CONTROL2:
+ case CS48L32_AUXPDM1_CONTROL1:
+ case CS48L32_AUXPDM2_CONTROL1:
+ case CS48L32_ADC1L_ANA_CONTROL1:
+ case CS48L32_ADC1R_ANA_CONTROL1:
+ case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5:
+ case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5:
+ case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4:
+ case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4:
+ case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1:
+ case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1:
+ case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1:
+ case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4:
+ case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4:
+ case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4:
+ case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4:
+ case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4:
+ case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4:
+ case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4:
+ case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4:
+ case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2:
+ case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2:
+ case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2:
+ case CS48L32_FX_SAMPLE_RATE:
+ case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2:
+ case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG:
+ case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG:
+ case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG:
+ case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG:
+ case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2:
+ case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF:
+ case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4:
+ case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4:
+ case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2:
+ case CS48L32_COMFORT_NOISE_GENERATOR:
+ case CS48L32_US_CONTROL:
+ case CS48L32_US1_CONTROL:
+ case CS48L32_US1_DET_CONTROL:
+ case CS48L32_US2_CONTROL:
+ case CS48L32_US2_DET_CONTROL:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs48l32_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_FLL1_CONTROL5:
+ case CS48L32_FLL1_CONTROL6:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * The bus bridge requires DSP packed memory registers to be accessed in
+ * aligned block multiples.
+ * Mark precious to prevent regmap debugfs causing an illegal bus transaction.
+ */
+static bool cs48l32_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs48l32_regmap = {
+ .name = "cs48l32",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .pad_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS48L32_DSP1_PMEM_LAST,
+ .readable_reg = &cs48l32_readable_register,
+ .volatile_reg = &cs48l32_volatile_register,
+ .precious_reg = &cs48l32_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs48l32_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default),
+};
+
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
+{
+ cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
+
+ return PTR_ERR_OR_ZERO(cs48l32->regmap);
+}
diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c
new file mode 100644
index 000000000000..a306af4289ad
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.c
@@ -0,0 +1,4076 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <dt-bindings/sound/cs48l32.h>
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string_choices.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs48l32.h"
+
+static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" };
+
+static const struct cs_dsp_region cs48l32_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
+ { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2800000 },
+ { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3400000 },
+};
+
+static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = {
+ { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 },
+ { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 },
+ { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 },
+};
+
+static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = {
+ CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0,
+};
+
+static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = {
+ .ext = cs48l32_dsp1_sram_ext_regs,
+ .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs),
+ .pwd = cs48l32_dsp1_sram_pwd_regs,
+ .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs),
+};
+
+static const char * const cs48l32_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "ASP1RX1",
+ "ASP1RX2",
+ "ASP1RX3",
+ "ASP1RX4",
+ "ASP1RX5",
+ "ASP1RX6",
+ "ASP1RX7",
+ "ASP1RX8",
+ "ASP2RX1",
+ "ASP2RX2",
+ "ASP2RX3",
+ "ASP2RX4",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "Ultrasonic 1",
+ "Ultrasonic 2",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP1.7",
+ "DSP1.8",
+};
+
+static unsigned int cs48l32_mixer_values[] = {
+ 0x000, /* Silence (mute) */
+ 0x004, /* Tone generator 1 */
+ 0x005, /* Tone generator 2 */
+ 0x00C, /* Noise Generator */
+ 0x010, /* IN1L signal path */
+ 0x011, /* IN1R signal path */
+ 0x012, /* IN2L signal path */
+ 0x013, /* IN2R signal path */
+ 0x020, /* ASP1 RX1 */
+ 0x021, /* ASP1 RX2 */
+ 0x022, /* ASP1 RX3 */
+ 0x023, /* ASP1 RX4 */
+ 0x024, /* ASP1 RX5 */
+ 0x025, /* ASP1 RX6 */
+ 0x026, /* ASP1 RX7 */
+ 0x027, /* ASP1 RX8 */
+ 0x030, /* ASP2 RX1 */
+ 0x031, /* ASP2 RX2 */
+ 0x032, /* ASP2 RX3 */
+ 0x033, /* ASP2 RX4 */
+ 0x098, /* ISRC1 INT1 */
+ 0x099, /* ISRC1 INT2 */
+ 0x09a, /* ISRC1 INT3 */
+ 0x09b, /* ISRC1 INT4 */
+ 0x09C, /* ISRC1 DEC1 */
+ 0x09D, /* ISRC1 DEC2 */
+ 0x09e, /* ISRC1 DEC3 */
+ 0x09f, /* ISRC1 DEC4 */
+ 0x0A0, /* ISRC2 INT1 */
+ 0x0A1, /* ISRC2 INT2 */
+ 0x0A4, /* ISRC2 DEC1 */
+ 0x0A5, /* ISRC2 DEC2 */
+ 0x0A8, /* ISRC3 INT1 */
+ 0x0A9, /* ISRC3 INT2 */
+ 0x0AC, /* ISRC3 DEC1 */
+ 0x0AD, /* ISRC3 DEC2 */
+ 0x0B8, /* EQ1 */
+ 0x0B9, /* EQ2 */
+ 0x0BA, /* EQ3 */
+ 0x0BB, /* EQ4 */
+ 0x0C0, /* DRC1 Left */
+ 0x0C1, /* DRC1 Right */
+ 0x0C2, /* DRC2 Left */
+ 0x0C3, /* DRC2 Right */
+ 0x0C8, /* LHPF1 */
+ 0x0C9, /* LHPF2 */
+ 0x0CA, /* LHPF3 */
+ 0x0CB, /* LHPF4 */
+ 0x0D8, /* Ultrasonic 1 */
+ 0x0D9, /* Ultrasonic 2 */
+ 0x100, /* DSP1 channel 1 */
+ 0x101, /* DSP1 channel 2 */
+ 0x102, /* DSP1 channel 3 */
+ 0x103, /* DSP1 channel 4 */
+ 0x104, /* DSP1 channel 5 */
+ 0x105, /* DSP1 channel 6 */
+ 0x106, /* DSP1 channel 7 */
+ 0x107, /* DSP1 channel 8 */
+};
+static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values));
+#define CS48L32_NUM_MIXER_INPUTS ARRAY_SIZE(cs48l32_mixer_values)
+
+static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0);
+
+static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(cs48l32->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out some clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val);
+ if (ret)
+ dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n",
+ __func__, ret, i);
+ }
+
+ udelay(300);
+}
+
+static const char * const cs48l32_rate_text[] = {
+ "Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4",
+};
+
+static const unsigned int cs48l32_rate_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text));
+
+static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Prevent any mixer mux changes while we do this */
+ mutex_lock(&cs48l32_codec->rate_lock);
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ mutex_unlock(&cs48l32_codec->rate_lock);
+
+ return ret;
+}
+
+static const char * const cs48l32_sample_rate_text[] = {
+ "12kHz",
+ "24kHz",
+ "48kHz",
+ "96kHz",
+ "192kHz",
+ "384kHz",
+ "768kHz",
+ "11.025kHz",
+ "22.05kHz",
+ "44.1kHz",
+ "88.2kHz",
+ "176.4kHz",
+ "352.8kHz",
+ "705.6kHz",
+ "8kHz",
+ "16kHz",
+ "32kHz",
+};
+
+static const unsigned int cs48l32_sample_rate_val[] = {
+ 0x01, /* 12kHz */
+ 0x02, /* 24kHz */
+ 0x03, /* 48kHz */
+ 0x04, /* 96kHz */
+ 0x05, /* 192kHz */
+ 0x06, /* 384kHz */
+ 0x07, /* 768kHz */
+ 0x09, /* 11.025kHz */
+ 0x0a, /* 22.05kHz */
+ 0x0b, /* 44.1kHz */
+ 0x0c, /* 88.2kHz */
+ 0x0d, /* 176.4kHz */
+ 0x0e, /* 352.8kHz */
+ 0x0f, /* 705.6kHz */
+ 0x11, /* 8kHz */
+ 0x12, /* 16kHz */
+ 0x13, /* 32kHz */
+};
+static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text));
+#define CS48L32_SAMPLE_RATE_ENUM_SIZE ARRAY_SIZE(cs48l32_sample_rate_val)
+
+static const struct soc_enum cs48l32_sample_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+};
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mux, src_val, in_type;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case CS48L32_IN1L_CONTROL1:
+ in_type = cs48l32_codec->in_type[0][mux];
+ break;
+ case CS48L32_IN1R_CONTROL1:
+ in_type = cs48l32_codec->in_type[1][mux];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_val = mux << e->shift_l;
+
+ if (in_type == CS48L32_IN_TYPE_SE)
+ src_val |= 1 << CS48L32_INx_SRC_SHIFT;
+
+ ret = snd_soc_component_update_bits(dapm->component,
+ e->reg,
+ CS48L32_INx_SRC_MASK,
+ src_val);
+ if (ret > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return ret;
+}
+
+static const char * const cs48l32_inmux_texts[] = {
+ "Analog 1", "Analog 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static const struct snd_kcontrol_new cs48l32_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+};
+
+static const char * const cs48l32_dmode_texts[] = {
+ "Analog", "Digital",
+};
+
+static int cs48l32_dmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mode;
+ int ret, result;
+
+ mode = ucontrol->value.enumerated.item[0];
+ switch (mode) {
+ case 0:
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ result = snd_soc_component_update_bits(component,
+ e->reg,
+ BIT(CS48L32_IN1_MODE_SHIFT),
+ 0);
+ if (result < 0) {
+ dev_err(component->dev, "Failed to set input mode: %d\n", result);
+ return result;
+ }
+
+ usleep_range(200, 300);
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ if (result > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL);
+
+ return result;
+ case 1:
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ default:
+ return -EINVAL;
+ }
+}
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum,
+ CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_MODE_SHIFT,
+ cs48l32_dmode_texts);
+
+static const struct snd_kcontrol_new cs48l32_dmode_mux[] = {
+ SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_dmode_put),
+};
+
+static const char * const cs48l32_in_texts[] = {
+ "IN1L", "IN1R", "IN2L", "IN2R",
+};
+static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT);
+
+static const char * const cs48l32_us_freq_texts[] = {
+ "16-24kHz", "20-28kHz",
+};
+
+static const unsigned int cs48l32_us_freq_val[] = {
+ 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_freq[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+};
+
+static const unsigned int cs48l32_us_in_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_inmux[] = {
+ SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]),
+ SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]),
+};
+
+static const char * const cs48l32_us_det_thr_texts[] = {
+ "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB",
+};
+
+static const struct soc_enum cs48l32_us_det_thr[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+};
+
+static const char * const cs48l32_us_det_num_texts[] = {
+ "1 Sample",
+ "2 Samples",
+ "4 Samples",
+ "8 Samples",
+ "16 Samples",
+ "32 Samples",
+ "64 Samples",
+ "128 Samples",
+ "256 Samples",
+ "512 Samples",
+ "1024 Samples",
+ "2048 Samples",
+ "4096 Samples",
+ "8192 Samples",
+ "16384 Samples",
+ "32768 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_num[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+};
+
+static const char * const cs48l32_us_det_hold_texts[] = {
+ "0 Samples",
+ "31 Samples",
+ "63 Samples",
+ "127 Samples",
+ "255 Samples",
+ "511 Samples",
+ "1023 Samples",
+ "2047 Samples",
+ "4095 Samples",
+ "8191 Samples",
+ "16383 Samples",
+ "32767 Samples",
+ "65535 Samples",
+ "131071 Samples",
+ "262143 Samples",
+ "524287 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_hold[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+};
+
+static const struct soc_enum cs48l32_us_output_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const char * const cs48l32_us_det_lpf_cut_texts[] = {
+ "1722Hz", "833Hz", "408Hz", "203Hz",
+};
+
+static const struct soc_enum cs48l32_us_det_lpf_cut[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+};
+
+static const char * const cs48l32_us_det_dcy_texts[] = {
+ "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms",
+};
+
+static const struct soc_enum cs48l32_us_det_dcy[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const char * const cs48l32_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VD_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VI_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static const char * const cs48l32_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum,
+ CS48L32_INPUT_HPF_CONTROL,
+ CS48L32_IN_HPF_CUT_SHIFT,
+ cs48l32_in_hpf_cut_text);
+
+static const char * const cs48l32_in_dmic_osr_text[] = {
+ "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz",
+};
+
+static const struct soc_enum cs48l32_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+};
+
+static bool cs48l32_is_input_enabled(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int input_active;
+
+ input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL);
+ switch (reg) {
+ case CS48L32_IN1L_CONTROL1:
+ return input_active & BIT(CS48L32_IN1L_EN_SHIFT);
+ case CS48L32_IN1R_CONTROL1:
+ return input_active & BIT(CS48L32_IN1R_EN_SHIFT);
+ case CS48L32_IN2L_CONTROL1:
+ return input_active & BIT(CS48L32_IN2L_EN_SHIFT);
+ case CS48L32_IN2R_CONTROL1:
+ return input_active & BIT(CS48L32_IN2R_EN_SHIFT);
+ default:
+ return false;
+ }
+}
+
+static int cs48l32_in_rate_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;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, e->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const struct soc_enum cs48l32_input_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static int cs48l32_low_power_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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, mc->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static const struct soc_enum noise_gen_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_RATE_SHIFT,
+ CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_auxpdm_freq_texts[] = {
+ "3.072MHz", "2.048MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq,
+ CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq,
+ CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static const char * const cs48l32_auxpdm_src_texts[] = {
+ "Analog", "IN1 Digital", "IN2 Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT1_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT2_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in),
+ SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in),
+};
+
+static const unsigned int cs48l32_auxpdm_analog_in_val[] = {
+ 0x0, 0x1,
+};
+
+static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]),
+ SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct soc_enum cs48l32_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_fx_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE,
+ CS48L32_FX_RATE_SHIFT,
+ CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+static const struct soc_enum cs48l32_lhpf_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+};
+
+static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ __be32 *data = (__be32 *)ucontrol->value.bytes.data;
+ s16 val = (s16)be32_to_cpu(*data);
+
+ if (abs(val) > CS48L32_LHPF_MAX_COEFF) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+
+static const char * const cs48l32_eq_mode_text[] = {
+ "Low-pass", "High-pass",
+};
+
+static const struct soc_enum cs48l32_eq_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+};
+
+static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = 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, cs48l32_codec->eq_mode[e->shift_l]);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_eq_mode_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val;
+ bool changed = false;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->eq_mode[e->shift_l] != val) {
+ cs48l32_codec->eq_mode[e->shift_l] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = ctl->max;
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int cached_rate;
+ const unsigned int rate_num = e->mask;
+ int item;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ cached_rate = cs48l32_codec->dsp_dma_rates[rate_num];
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_dsp_rate_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ const unsigned int rate_num = e->mask;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ bool changed = false;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->dsp_dma_rates[rate_num] != val) {
+ cs48l32_codec->dsp_dma_rates[rate_num] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static const struct soc_enum cs48l32_dsp_rate_enum[] = {
+ /* RX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 0,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 1,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 2,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 3,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 4,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 5,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 6,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 7,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ /* TX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 8,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 9,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 10,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 11,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 12,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 13,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 14,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 15,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+};
+
+static int cs48l32_dsp_pre_run(struct wm_adsp *dsp)
+{
+ struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp);
+ unsigned int reg;
+ const u8 *rate = cs48l32_codec->dsp_dma_rates;
+ int i;
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1;
+ for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1;
+ for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ usleep_range(300, 600);
+
+ return 0;
+}
+
+static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return;
+
+err:
+ dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+}
+
+static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ /* disable power-off */
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0x3);
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* power-up the banks in sequence */
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0x1);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+
+ ret = regmap_write(regmap, regs->pwd[i], 0x3);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+ }
+
+ return 0;
+
+err:
+ dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+ cs48l32_dsp_memory_disable(cs48l32_codec, regs);
+
+ return ret;
+}
+
+static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
+ unsigned int freqsel_reg)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ struct wm_adsp *dsp = &cs48l32_codec->dsp;
+ int ret;
+ unsigned int freq, freq_sel, freq_sts;
+
+ if (!freq_reg)
+ return -EINVAL;
+
+ ret = regmap_read(regmap, freq_reg, &freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret);
+ return ret;
+ }
+
+ if (freqsel_reg) {
+ freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT;
+
+ ret = regmap_read(regmap, freqsel_reg, &freq_sel);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret);
+ return ret;
+ }
+ freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT;
+
+ if (freq_sts != freq_sel) {
+ dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n",
+ freq_sel, freq_sts);
+ return -ETIMEDOUT;
+ }
+ }
+
+ freq &= CS48L32_DSP_CLK_FREQ_MASK;
+ freq >>= CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1);
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t cs48l32_irq(int irq, void *data)
+{
+ static const unsigned int eint1_regs[] = {
+ CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9,
+ CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7
+ };
+ u32 reg_vals[4];
+ struct cs48l32_codec *cs48l32_codec = data;
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ irqreturn_t result = IRQ_NONE;
+ unsigned int eint_pending;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals));
+
+ ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+ if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0)
+ goto out;
+
+ ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals));
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) {
+ reg_vals[i] &= ~reg_vals[i + 1];
+ regmap_write(regmap, eint1_regs[i], reg_vals[i]);
+ }
+
+ if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK)
+ wm_adsp_compr_handle_irq(&cs48l32_codec->dsp);
+
+ if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n");
+ wm_halo_bus_error(irq, &cs48l32_codec->dsp);
+ }
+
+ if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n");
+ wm_halo_wdt_expire(irq, &cs48l32_codec->dsp);
+ }
+
+ result = IRQ_HANDLED;
+
+out:
+ pm_runtime_put_autosuspend(cs48l32_codec->core.dev);
+
+ return result;
+}
+
+static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq,
+ int src, unsigned int *val)
+{
+ freq /= 15625; /* convert to 1/64ths of 1MHz */
+ *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ return 0;
+}
+
+static int cs48l32_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return CS48L32_SYSCLK_RATE_6MHZ;
+ case 11289600:
+ case 12288000:
+ return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val;
+
+ switch (source) {
+ case CS48L32_PDMCLK_SRC_IN1_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN2_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN3_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN4_PDMCLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM1_CLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM2_CLK:
+ val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT;
+ break;
+ default:
+ dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2,
+ CS48L32_PDM_FLLCLK_SRC_MASK, val);
+}
+
+static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ char *name;
+ unsigned int reg;
+ unsigned int mask = CS48L32_SYSCLK_SRC_MASK;
+ unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = CS48L32_SYSTEM_CLOCK1;
+ clk = &cs48l32_codec->sysclk;
+ clk_freq_sel = cs48l32_get_sysclk_setting(freq);
+ mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK;
+ break;
+ case CS48L32_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = CS48L32_DSP_CLOCK1;
+ clk = &cs48l32_codec->dspclk;
+ clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val);
+ mask |= CS48L32_DSP_CLK_FREQ_MASK;
+ break;
+ case CS48L32_CLK_PDM_FLLCLK:
+ return cs48l32_set_pdm_fllclk(component, source);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (freq % 6144000)
+ val |= CS48L32_SYSCLK_FRAC_MASK;
+
+ dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, &reg);
+ if (ret != 0) {
+ cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & CS48L32_FLL_EN_MASK;
+}
+
+static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int val = 0;
+ int i;
+
+ cs48l32_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(regmap, fll->sts_addr, &val);
+ if (!!(val & fll->sts_mask) == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 6 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ fsleep(20000);
+ break;
+ }
+ }
+
+ cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock");
+
+ return -ETIMEDOUT;
+}
+
+static int cs48l32_fllhj_disable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ bool change;
+
+ cs48l32_fll_dbg(fll, "Disabling FLL\n");
+
+ /*
+ * Disable lockdet, but don't set ctrl_upd update bit. 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_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL5_OFFS,
+ CS48L32_FLL_FRC_INTEG_UPD_MASK);
+ regmap_update_bits_check(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK,
+ 0,
+ &change);
+
+ cs48l32_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_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ if (change)
+ pm_runtime_put_autosuspend(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
+ unsigned int gains, num;
+
+ cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++) {
+ if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH)
+ break;
+ }
+
+ fref = fin / (1 << refdiv);
+ fout = fll->fout;
+ frac = fout % fref;
+
+ /*
+ * Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ if (fref < CS48L32_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = CS48L32_FLLHJ_LOW_GAINS;
+
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < CS48L32_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_MID_GAINS;
+ fbdiv = (frac) ? 16 : 2;
+ } else {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 3;
+ min_n = CS48L32_FLLHJ_FRAC_MIN_N;
+ max_n = CS48L32_FLLHJ_FRAC_MAX_N;
+ } else {
+ if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)
+ hp = 0;
+ else
+ hp = 1;
+
+ min_n = CS48L32_FLLHJ_INT_MIN_N;
+ max_n = CS48L32_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < min_n) {
+ cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%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;
+
+ cs48l32_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) {
+ cs48l32_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)) {
+ cs48l32_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_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_THR_MASK |
+ CS48L32_FLL_PHASEDET_MASK |
+ CS48L32_FLL_REFCLK_DIV_MASK |
+ CS48L32_FLL_N_MASK |
+ CS48L32_FLL_CTRL_UPD_MASK,
+ (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) |
+ (1 << CS48L32_FLL_PHASEDET_SHIFT) |
+ (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) |
+ (fll_n << CS48L32_FLL_N_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL3_OFFS,
+ CS48L32_FLL_LAMBDA_MASK |
+ CS48L32_FLL_THETA_MASK,
+ (lambda << CS48L32_FLL_LAMBDA_SHIFT) |
+ (theta << CS48L32_FLL_THETA_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL4_OFFS,
+ (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ CS48L32_FLL_HP_MASK |
+ CS48L32_FLL_FB_DIV_MASK,
+ (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ (hp << CS48L32_FLL_HP_SHIFT) |
+ (fbdiv << CS48L32_FLL_FB_DIV_SHIFT));
+
+ return 0;
+}
+
+static int cs48l32_fllhj_enable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ int already_enabled = cs48l32_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(cs48l32->dev);
+
+ cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ /* Apply refclk */
+ ret = cs48l32_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_REFCLK_SRC_MASK,
+ fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK);
+
+out:
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ if (!already_enabled)
+ cs48l32_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_validate(struct cs48l32_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ cs48l32_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ cs48l32_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) {
+ cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ if (fout > CS48L32_FLL_MAX_FOUT) {
+ cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n",
+ fout, CS48L32_FLL_MAX_FOUT);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = cs48l32_fllhj_enable(fll);
+ else
+ cs48l32_fllhj_disable(fll);
+
+ return ret;
+}
+
+static int cs48l32_init_fll(struct cs48l32_fll *fll)
+{
+ fll->ref_src = CS48L32_FLL_SRC_NONE;
+
+ return 0;
+}
+
+static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case CS48L32_FLL1_REFCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout);
+}
+
+static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int pin_reg, last_pin_reg, hiz_reg;
+
+ switch (dai->id) {
+ case 1:
+ pin_reg = CS48L32_GPIO3_CTRL1;
+ hiz_reg = CS48L32_ASP1_CONTROL3;
+ break;
+ case 2:
+ pin_reg = CS48L32_GPIO7_CTRL1;
+ hiz_reg = CS48L32_ASP2_CONTROL3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg)
+ regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK);
+
+ /* DOUT high-impendance when not transmitting */
+ regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK);
+
+ return 0;
+}
+
+static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val = 0U;
+ unsigned int base = dai->driver->base;
+ unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK |
+ CS48L32_ASP_BCLK_MSTR_MASK |
+ CS48L32_ASP_FSYNC_INV_MASK |
+ CS48L32_ASP_FSYNC_MSTR_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported clock direction %d\n",
+ fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val);
+
+ return 0;
+}
+
+static const struct {
+ u32 freq;
+ u32 id;
+} cs48l32_sclk_rates[] = {
+ { 128000, 12 },
+ { 176400, 13 },
+ { 192000, 14 },
+ { 256000, 15 },
+ { 352800, 16 },
+ { 384000, 17 },
+ { 512000, 18 },
+ { 705600, 19 },
+ { 768000, 21 },
+ { 1024000, 23 },
+ { 1411200, 25 },
+ { 1536000, 27 },
+ { 2048000, 29 },
+ { 2822400, 31 },
+ { 3072000, 33 },
+ { 4096000, 36 },
+ { 5644800, 38 },
+ { 6144000, 40 },
+ { 8192000, 47 },
+ { 11289600, 49 },
+ { 12288000, 51 },
+ { 22579200, 57 },
+ { 24576000, 59 },
+};
+
+#define CS48L32_48K_RATE_MASK 0x0e00fe
+#define CS48L32_44K1_RATE_MASK 0x00fe00
+#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK)
+
+static const unsigned int cs48l32_sr_vals[] = {
+ 0,
+ 12000, /* CS48L32_48K_RATE_MASK */
+ 24000, /* CS48L32_48K_RATE_MASK */
+ 48000, /* CS48L32_48K_RATE_MASK */
+ 96000, /* CS48L32_48K_RATE_MASK */
+ 192000, /* CS48L32_48K_RATE_MASK */
+ 384000, /* CS48L32_48K_RATE_MASK */
+ 768000, /* CS48L32_48K_RATE_MASK */
+ 0,
+ 11025, /* CS48L32_44K1_RATE_MASK */
+ 22050, /* CS48L32_44K1_RATE_MASK */
+ 44100, /* CS48L32_44K1_RATE_MASK */
+ 88200, /* CS48L32_44K1_RATE_MASK */
+ 176400, /* CS48L32_44K1_RATE_MASK */
+ 352800, /* CS48L32_44K1_RATE_MASK */
+ 705600, /* CS48L32_44K1_RATE_MASK */
+ 0,
+ 8000, /* CS48L32_48K_RATE_MASK */
+ 16000, /* CS48L32_48K_RATE_MASK */
+ 32000, /* CS48L32_48K_RATE_MASK */
+};
+
+static const struct snd_pcm_hw_constraint_list cs48l32_constraint = {
+ .count = ARRAY_SIZE(cs48l32_sr_vals),
+ .list = cs48l32_sr_vals,
+};
+
+static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ base_rate = cs48l32_codec->sysclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ dai_priv->constraint.mask = CS48L32_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = CS48L32_48K_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int sr_val, sr_reg, rate;
+
+ rate = params_rate(params);
+ for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++)
+ if (cs48l32_sr_vals[sr_val] == rate)
+ break;
+
+ if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ sr_reg = CS48L32_SAMPLE_RATE1;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ sr_reg = CS48L32_SAMPLE_RATE2;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ sr_reg = CS48L32_SAMPLE_RATE3;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ sr_reg = CS48L32_SAMPLE_RATE4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val);
+
+ return 0;
+}
+
+static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component,
+ unsigned int base, unsigned int sclk,
+ unsigned int slotws, unsigned int dataw)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1);
+ if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1);
+ if (dataw != (val & (CS48L32_ASP_TX_WL_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5);
+ if (dataw != (val & (CS48L32_ASP_RX_WL_MASK)))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int base = dai->driver->base;
+ int dai_id = dai->id - 1;
+ unsigned int rate = params_rate(params);
+ unsigned int dataw = snd_pcm_format_width(params_format(params));
+ unsigned int asp_state = 0;
+ int sclk, sclk_target;
+ unsigned int slotw, n_slots, n_slots_multiple, val;
+ int i, ret;
+
+ cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n",
+ params_channels(params), dataw, rate);
+ /*
+ * The following calculations hold only under the assumption that
+ * symmetric_[rates|channels|samplebits] are set to 1
+ */
+ if (cs48l32_codec->tdm_slots[dai_id]) {
+ n_slots = cs48l32_codec->tdm_slots[dai_id];
+ slotw = cs48l32_codec->tdm_width[dai_id];
+ } else {
+ n_slots = params_channels(params);
+ slotw = dataw;
+ }
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT;
+ if (val == CS48L32_ASP_FMT_I2S_MODE)
+ n_slots_multiple = 2;
+ else
+ n_slots_multiple = 1;
+
+ sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple);
+ if (sclk_target < 0) {
+ cs48l32_asp_err(dai, "Invalid parameters\n");
+ return sclk_target;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) {
+ if ((cs48l32_sclk_rates[i].freq >= sclk_target) &&
+ (cs48l32_sclk_rates[i].freq % rate == 0)) {
+ sclk = cs48l32_sclk_rates[i].id;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(cs48l32_sclk_rates)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n",
+ n_slots, dataw, slotw, sclk_target, sclk);
+
+ slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) |
+ (slotw << CS48L32_ASP_RX_WIDTH_SHIFT);
+
+ if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw))
+ return cs48l32_hw_params_rate(substream, params, dai);
+
+ /* ASP must be disabled while changing configuration */
+ asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1);
+ regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff);
+
+ ret = cs48l32_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_asp;
+
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_BCLK_FREQ_MASK,
+ sclk);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL2,
+ CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK,
+ slotw);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_DATA_CONTROL1,
+ CS48L32_ASP_TX_WL_MASK,
+ dataw);
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_DATA_CONTROL5,
+ CS48L32_ASP_RX_WL_MASK,
+ dataw);
+
+restore_asp:
+ /* Restore ASP TX/RX enable state */
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_ENABLES1,
+ 0xff00ff,
+ asp_state);
+ return ret;
+}
+
+static const char *cs48l32_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ return "SYSCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base = dai->driver->base;
+ unsigned int current_asp_rate, target_asp_rate;
+ bool change_rate_domain = false;
+ int ret;
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ cs48l32_asp_err(dai, "Can't change clock on active DAI\n");
+ return -EBUSY;
+ }
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_priv->clk = clk_id;
+ cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id));
+
+ if (base) {
+ ret = regmap_read(cs48l32_codec->core.regmap,
+ base + CS48L32_ASP_CONTROL1,
+ &current_asp_rate);
+ if (ret != 0) {
+ cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((current_asp_rate & CS48L32_ASP_RATE_MASK) !=
+ (target_asp_rate & CS48L32_ASP_RATE_MASK)) {
+ change_rate_domain = true;
+
+ mutex_lock(&cs48l32_codec->rate_lock);
+ /* Guard the rate change with SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ }
+
+ snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_RATE_MASK, target_asp_rate);
+
+ if (change_rate_domain) {
+ cs48l32_spin_sysclk(cs48l32_codec);
+ mutex_unlock(&cs48l32_codec->rate_lock);
+ }
+ }
+
+ return 0;
+}
+
+static void cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int slot, i, j = 0, shift;
+ unsigned int frame_ctls[2] = {0, 0};
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ if (i - (j * 4) >= 4) {
+ ++j;
+ if (j >= 2)
+ break;
+ }
+
+ shift = (8 * (i - j * 4));
+
+ frame_ctls[j] |= slot << shift;
+
+ mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
+ }
+
+ regmap_write(regmap, base, frame_ctls[0]);
+ regmap_write(regmap, base + 0x4, frame_ctls[1]);
+
+ if (mask)
+ cs48l32_asp_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = 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 ASPs */
+ if (dai->id > CS48L32_MAX_ASP)
+ return -EINVAL;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1,
+ tx_max_chan, tx_mask);
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5,
+ rx_max_chan, rx_mask);
+
+ cs48l32_codec->tdm_width[dai->id - 1] = slot_width;
+ cs48l32_codec->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs48l32_dai_ops = {
+ .probe = &cs48l32_asp_dai_probe,
+ .startup = &cs48l32_startup,
+ .set_fmt = &cs48l32_set_fmt,
+ .set_tdm_slot = &cs48l32_set_tdm_slot,
+ .hw_params = &cs48l32_hw_params,
+ .set_sysclk = &cs48l32_dai_set_sysclk,
+};
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ return 0;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = CS48L32_IN1L_CONTROL2;
+ else
+ reg = CS48L32_IN1R_CONTROL2;
+
+ reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(200, 300);
+
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending--;
+ snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0);
+
+ /* Uncached write-only register, no need for update_bits */
+ if (!cs48l32_codec->in_up_pending) {
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK);
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Uncached write-only register, no need for update_bits.
+ * Will fail if codec is off but that will be handled by cs48l32_in_ev
+ */
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU);
+
+ return ret;
+}
+
+static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
+{
+ s16 a = be16_to_cpu(in_a);
+ s16 b = be16_to_cpu(in_b);
+
+ if (!mode)
+ return abs(a) > CS48L32_EQ_MAX_COEFF;
+
+ if (abs(b) > CS48L32_EQ_MAX_COEFF)
+ return true;
+
+ if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_eq_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int mode = cs48l32_codec->eq_mode[w->shift];
+ unsigned int reg;
+ __be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0];
+ int ret = 0;
+
+ reg = CS48L32_EQ1_BAND1_COEFF1;
+ reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) ||
+ cs48l32_eq_filter_unstable(true, data[7], data[6]) ||
+ cs48l32_eq_filter_unstable(true, data[13], data[12]) ||
+ cs48l32_eq_filter_unstable(true, data[19], data[18]) ||
+ cs48l32_eq_filter_unstable(false, data[25], data[24])) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n");
+ ret = -EINVAL;
+ } else {
+ ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ coefficients: %d\n", ret);
+ goto out;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_EQ_CONTROL2,
+ w->mask,
+ mode << w->shift);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ mode: %d\n", ret);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs48l32_snd_controls[] = {
+SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp),
+
+CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]),
+CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]),
+
+SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]),
+SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]),
+
+SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+
+SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]),
+SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]),
+SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]),
+SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]),
+SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]),
+
+SOC_SINGLE("Ultrasonic 1 Detect LPF Switch",
+ CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+SOC_SINGLE("Ultrasonic 2 Detect LPF Switch",
+ CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+
+SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]),
+SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]),
+
+CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1),
+
+SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+
+CS48L32_EQ_COEFF_CONTROLS(EQ1),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ3),
+SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ4),
+SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1),
+
+SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+
+CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1),
+
+CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF),
+CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF),
+CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF),
+CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF),
+
+SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]),
+SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]),
+SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]),
+SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]),
+
+CS48L32_RATE_CONTROL("Sample Rate 1", 1),
+CS48L32_RATE_CONTROL("Sample Rate 2", 2),
+CS48L32_RATE_CONTROL("Sample Rate 3", 3),
+CS48L32_RATE_CONTROL("Sample Rate 4", 4),
+
+CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate),
+
+CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]),
+CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]),
+CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]),
+CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]),
+CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]),
+CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]),
+
+SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq),
+SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq),
+
+SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put),
+
+CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate),
+
+SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv),
+
+CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1),
+
+CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+
+CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0),
+CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1),
+CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2),
+CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3),
+CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4),
+CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5),
+CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6),
+CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7),
+CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8),
+CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9),
+CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10),
+CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11),
+CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12),
+CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13),
+CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14),
+CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15),
+};
+
+CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1);
+CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1);
+CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1);
+CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1);
+
+CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1);
+
+CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1);
+
+CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1);
+
+static int cs48l32_dsp_mem_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ case SND_SOC_DAPM_PRE_PMD:
+ cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0,
+ cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0),
+
+SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT,
+ 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1LN_1"),
+SND_SOC_DAPM_INPUT("IN1LN_2"),
+SND_SOC_DAPM_INPUT("IN1LP_1"),
+SND_SOC_DAPM_INPUT("IN1LP_2"),
+SND_SOC_DAPM_INPUT("IN1RN_1"),
+SND_SOC_DAPM_INPUT("IN1RN_2"),
+SND_SOC_DAPM_INPUT("IN1RP_1"),
+SND_SOC_DAPM_INPUT("IN1RP_2"),
+SND_SOC_DAPM_INPUT("IN1_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN1_PDMDATA"),
+
+SND_SOC_DAPM_INPUT("IN2_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN2_PDMDATA"),
+
+SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]),
+SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[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_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+
+SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0),
+
+SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0),
+
+SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]),
+SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[1]),
+
+SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]),
+SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in CS48L32_MIXER_INPUT_ROUTES
+ */
+SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0),
+
+SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, wm_adsp_early_event),
+
+/* end of ordered widget list */
+
+CS48L32_MIXER_WIDGETS(EQ1, "EQ1"),
+CS48L32_MIXER_WIDGETS(EQ2, "EQ2"),
+CS48L32_MIXER_WIDGETS(EQ3, "EQ3"),
+CS48L32_MIXER_WIDGETS(EQ4, "EQ4"),
+
+CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"),
+CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"),
+CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"),
+CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[1]),
+
+CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"),
+CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"),
+CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"),
+CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"),
+CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"),
+CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"),
+CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"),
+CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"),
+CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"),
+CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"),
+CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"),
+
+CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"),
+CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"),
+CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"),
+CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"),
+
+CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"),
+CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"),
+CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"),
+CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"),
+CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"),
+CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"),
+CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"),
+CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_dsp_trigger_output_mux[0]),
+
+SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+
+SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"),
+};
+
+static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = {
+ { "IN1LN_1", NULL, "SYSCLK" },
+ { "IN1LN_2", NULL, "SYSCLK" },
+ { "IN1LP_1", NULL, "SYSCLK" },
+ { "IN1LP_2", NULL, "SYSCLK" },
+ { "IN1RN_1", NULL, "SYSCLK" },
+ { "IN1RN_2", NULL, "SYSCLK" },
+ { "IN1RP_1", NULL, "SYSCLK" },
+ { "IN1RP_2", NULL, "SYSCLK" },
+
+ { "IN1_PDMCLK", NULL, "SYSCLK" },
+ { "IN1_PDMDATA", NULL, "SYSCLK" },
+ { "IN2_PDMCLK", NULL, "SYSCLK" },
+ { "IN2_PDMDATA", NULL, "SYSCLK" },
+
+ { "DSP1 Preloader", NULL, "DSP1MEM" },
+ { "DSP1", NULL, "DSP1FREQ" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Voice Ctrl DSP", NULL, "DSP1" },
+
+ { "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" },
+ { "MICBIAS1", NULL, "VOUT_MIC_REGULATED" },
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+ { "Noise Generator", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+ { "Noise Generator", NULL, "NOISE" },
+
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+ { "ASP1 Capture", NULL, "ASP1TX5" },
+ { "ASP1 Capture", NULL, "ASP1TX6" },
+ { "ASP1 Capture", NULL, "ASP1TX7" },
+ { "ASP1 Capture", NULL, "ASP1TX8" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "ASP1RX3", NULL, "ASP1 Playback" },
+ { "ASP1RX4", NULL, "ASP1 Playback" },
+ { "ASP1RX5", NULL, "ASP1 Playback" },
+ { "ASP1RX6", NULL, "ASP1 Playback" },
+ { "ASP1RX7", NULL, "ASP1 Playback" },
+ { "ASP1RX8", NULL, "ASP1 Playback" },
+
+ { "ASP2 Capture", NULL, "ASP2TX1" },
+ { "ASP2 Capture", NULL, "ASP2TX2" },
+ { "ASP2 Capture", NULL, "ASP2TX3" },
+ { "ASP2 Capture", NULL, "ASP2TX4" },
+
+ { "ASP2RX1", NULL, "ASP2 Playback" },
+ { "ASP2RX2", NULL, "ASP2 Playback" },
+ { "ASP2RX3", NULL, "ASP2 Playback" },
+ { "ASP2RX4", NULL, "ASP2 Playback" },
+
+ { "ASP1 Playback", NULL, "SYSCLK" },
+ { "ASP2 Playback", NULL, "SYSCLK" },
+
+ { "ASP1 Capture", NULL, "SYSCLK" },
+ { "ASP2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "Analog 1", "IN1LN_1" },
+ { "IN1L Mux", "Analog 2", "IN1LN_2" },
+ { "IN1L Mux", "Analog 1", "IN1LP_1" },
+ { "IN1L Mux", "Analog 2", "IN1LP_2" },
+ { "IN1R Mux", "Analog 1", "IN1RN_1" },
+ { "IN1R Mux", "Analog 2", "IN1RN_2" },
+ { "IN1R Mux", "Analog 1", "IN1RP_1" },
+ { "IN1R Mux", "Analog 2", "IN1RP_2" },
+
+ { "IN1L PGA", NULL, "IN1L Mode" },
+ { "IN1R PGA", NULL, "IN1R Mode" },
+
+ { "IN1L Mode", "Analog", "IN1L Mux" },
+ { "IN1R Mode", "Analog", "IN1R Mux" },
+
+ { "IN1L Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1L Mode", "Digital", "IN1_PDMDATA" },
+ { "IN1R Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1R Mode", "Digital", "IN1_PDMDATA" },
+
+ { "IN1L PGA", NULL, "VOUT_MIC" },
+ { "IN1R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "VOUT_MIC" },
+ { "IN2R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "IN2_PDMCLK" },
+ { "IN2R PGA", NULL, "IN2_PDMCLK" },
+ { "IN2L PGA", NULL, "IN2_PDMDATA" },
+ { "IN2R PGA", NULL, "IN2_PDMDATA" },
+
+ { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" },
+ { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" },
+
+ { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" },
+ { "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" },
+
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" },
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" },
+
+ CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"),
+ CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"),
+ CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"),
+ CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"),
+ CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"),
+ CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"),
+ CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"),
+ CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"),
+
+ CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"),
+ CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"),
+ CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"),
+ CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"),
+
+ CS48L32_MIXER_ROUTES("EQ1", "EQ1"),
+ CS48L32_MIXER_ROUTES("EQ2", "EQ2"),
+ CS48L32_MIXER_ROUTES("EQ3", "EQ3"),
+ CS48L32_MIXER_ROUTES("EQ4", "EQ4"),
+
+ CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"),
+ CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"),
+ CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"),
+ CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"),
+ CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"),
+ CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"),
+ CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" },
+ { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" },
+ { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" },
+
+ { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" },
+ { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" },
+ { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" },
+
+ { "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 cs48l32_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") &&
+ strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) {
+ dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&cs48l32_codec->dsp, stream);
+}
+
+static const struct snd_compress_ops cs48l32_compress_ops = {
+ .open = &cs48l32_compr_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_dai_ops cs48l32_compress_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs48l32_dai[] = {
+ {
+ .name = "cs48l32-asp1",
+ .id = 1,
+ .base = CS48L32_ASP1_ENABLES1,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-asp2",
+ .id = 2,
+ .base = CS48L32_ASP2_ENABLES1,
+ .playback = {
+ .stream_name = "ASP2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-cpu-trace",
+ .id = 3,
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-trace",
+ .id = 4,
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+ {
+ .name = "cs48l32-cpu-voicectrl",
+ .id = 5,
+ .capture = {
+ .stream_name = "Voice Ctrl CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-voicectrl",
+ .id = 6,
+ .capture = {
+ .stream_name = "Voice Ctrl DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+};
+
+static int cs48l32_init_inputs(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int ana_mode_l, ana_mode_r, dig_mode;
+ int i;
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ switch (cs48l32_codec->in_type[0][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ switch (cs48l32_codec->in_type[1][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n",
+ ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_l);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_r);
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT;
+
+ dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode);
+
+ regmap_update_bits(regmap,
+ CS48L32_INPUT1_CONTROL1 + (i * 0x40),
+ CS48L32_IN1_PDM_SUP_MASK, dig_mode);
+ }
+
+ return 0;
+}
+
+static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id)
+{
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id];
+
+ dai_priv->clk = CS48L32_CLK_SYSCLK_1;
+ dai_priv->constraint = cs48l32_constraint;
+
+ return 0;
+}
+
+static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode;
+ __be16 *data;
+ int i, ret;
+
+ ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1;
+
+ data = &cs48l32_codec->eq_coefficients[i][0];
+ ret = regmap_raw_read(regmap, reg + (i * 68), data,
+ CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error reading EQ coefficients: %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int cs48l32_component_probe(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap);
+
+ ret = cs48l32_init_inputs(component);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++)
+ cs48l32_init_dai(cs48l32_codec, i);
+
+ ret = cs48l32_init_eq(cs48l32_codec);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs48l32_codec->dsp, component);
+
+ /* Unmask DSP IRQs */
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ return 0;
+}
+
+static void cs48l32_component_remove(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ /* Mask DSP IRQs */
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ wm_adsp2_component_remove(&cs48l32_codec->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs48l32_soc_component_drv = {
+ .probe = &cs48l32_component_probe,
+ .remove = &cs48l32_component_remove,
+ .set_sysclk = &cs48l32_set_sysclk,
+ .set_pll = &cs48l32_set_fll,
+ .name = "cs48l32-codec",
+ .compress_ops = &cs48l32_compress_ops,
+ .controls = cs48l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs48l32_snd_controls),
+ .dapm_widgets = cs48l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets),
+ .dapm_routes = cs48l32_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec,
+ const char *propname,
+ u32 *dest,
+ int n_max)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ int ret;
+
+ ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max);
+ if (ret == -EINVAL)
+ return -ENOENT;
+
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname);
+
+ return 0;
+}
+
+static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,in-type";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS];
+ int i, in_idx, mux_way_idx, ret;
+
+ static_assert(ARRAY_SIZE(tmp) ==
+ ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0]));
+
+ ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+ if (ret < 0)
+ return;
+
+ in_idx = 0;
+ mux_way_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(tmp); ++i) {
+ switch (tmp[i]) {
+ case CS48L32_IN_TYPE_DIFF:
+ case CS48L32_IN_TYPE_SE:
+ cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, tmp[i]);
+ break;
+ }
+
+ /*
+ * Property array is [mux_way][in_channel]. Swap to
+ * [in_channel][mux_way] for convenience.
+ */
+ if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) {
+ in_idx = 0;
+ ++mux_way_idx;
+ }
+ }
+}
+
+static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,pdm-sup";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT];
+ int i;
+
+ static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup));
+
+ cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ switch (tmp[i]) {
+ case CS48L32_PDM_SUP_VOUT_MIC:
+ case CS48L32_PDM_SUP_MICBIAS1:
+ cs48l32_codec->pdm_sup[i] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, cs48l32_codec->pdm_sup[i]);
+ break;
+ }
+ }
+}
+
+static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec)
+{
+ cs48l32_prop_get_in_type(cs48l32_codec);
+ cs48l32_prop_get_pdm_sup(cs48l32_codec);
+}
+
+static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec)
+{
+ int irq = cs48l32_codec->core.irq;
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ /*
+ * Don't use devm because this must be freed before destroying the
+ * rest of the driver
+ */
+ ret = request_threaded_irq(irq, NULL, cs48l32_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs48l32", cs48l32_codec);
+ if (ret)
+ return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n");
+
+ return 0;
+}
+
+static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0);
+ static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai));
+
+ cs48l32_handle_properties(cs48l32_codec);
+
+ dsp = &cs48l32_codec->dsp;
+ dsp->part = "cs48l32";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs48l32_codec->core.dev;
+ dsp->cs_dsp.regmap = cs48l32_codec->core.regmap;
+ dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs48l32_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions);
+ dsp->pre_run = cs48l32_dsp_pre_run;
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0)
+ return ret;
+
+ cs48l32_codec->fll.codec = cs48l32_codec;
+ cs48l32_codec->fll.id = 1;
+ cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1;
+ cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6;
+ cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK;
+ cs48l32_init_fll(&cs48l32_codec->fll);
+
+ ret = cs48l32_request_interrupt(cs48l32_codec);
+ if (ret)
+ goto err_dsp;
+
+ ret = devm_snd_soc_register_component(cs48l32_codec->core.dev,
+ &cs48l32_soc_component_drv,
+ cs48l32_dai,
+ ARRAY_SIZE(cs48l32_dai));
+ if (ret < 0) {
+ dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n");
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(&cs48l32_codec->dsp);
+
+ return ret;
+}
+
+static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val,
+ ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)),
+ 1000, CS48L32_BOOT_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs48l32->dev, "BOOT_DONE timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret);
+ return ret;
+ }
+
+ if (val & BIT(CS48L32_MCU_STS_SHIFT)) {
+ dev_err(cs48l32->dev, "MCU boot failed\n");
+ return -EIO;
+ }
+
+ pm_runtime_mark_last_busy(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_soft_reset(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC);
+ if (ret != 0) {
+ dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ return 0;
+}
+
+static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio)
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0);
+}
+
+static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio) {
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1);
+ usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000);
+ }
+}
+
+static int cs48l32_runtime_resume(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret;
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret)
+ goto err;
+
+ /* Check whether registers reset during suspend */
+ regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val);
+ if (!val)
+ regcache_mark_dirty(cs48l32->regmap);
+ else
+ dev_dbg(cs48l32->dev, "Did not reset during suspend\n");
+
+ ret = regcache_sync(cs48l32->regmap);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return ret;
+}
+
+static int cs48l32_runtime_suspend(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Flag to detect if the registers reset during suspend */
+ regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1);
+
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs48l32_pm_ops = {
+ RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL)
+};
+
+static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(cs48l32->mclk1);
+ if (ret)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n");
+
+ ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1);
+ if (ret) {
+ clk_disable_unprepare(cs48l32->mclk1);
+ return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n");
+ }
+
+ return 0;
+}
+
+static int cs48l32_get_clocks(struct cs48l32 *cs48l32)
+{
+ cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1");
+ if (IS_ERR(cs48l32->mclk1))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1),
+ "Failed to get mclk1\n");
+
+ return 0;
+}
+
+static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32)
+{
+ struct gpio_desc *reset;
+
+ reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n");
+
+ /* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */
+ gpiod_set_raw_value_cansleep(reset, 0);
+
+ cs48l32->reset_gpio = reset;
+
+ return 0;
+}
+
+static int cs48l32_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cs48l32_codec *cs48l32_codec;
+ struct cs48l32 *cs48l32;
+ unsigned int hwid, rev, otp_rev;
+ int i, ret;
+
+ cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL);
+ if (!cs48l32_codec)
+ return -ENOMEM;
+
+ cs48l32 = &cs48l32_codec->core;
+ cs48l32->dev = dev;
+ cs48l32->irq = spi->irq;
+ mutex_init(&cs48l32_codec->rate_lock);
+ cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3;
+
+ dev_set_drvdata(cs48l32->dev, cs48l32_codec);
+
+ ret = cs48l32_create_regmap(spi, cs48l32);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n");
+
+ regcache_cache_only(cs48l32->regmap, true);
+
+ ret = cs48l32_get_reset_gpio(cs48l32);
+ if (ret)
+ return ret;
+
+ ret = cs48l32_get_clocks(cs48l32);
+ if (ret)
+ return ret;
+
+ static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies));
+ for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++)
+ cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies),
+ cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request core supplies\n");
+
+ cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d");
+ if (IS_ERR(cs48l32->vdd_d))
+ return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable core supplies\n");
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(dev, "Failed to enable vdd-d: %d\n", ret);
+ goto err_enable;
+ }
+
+ cs48l32_disable_hard_reset(cs48l32);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ /* If we don't have a reset GPIO use a soft reset */
+ if (!cs48l32->reset_gpio) {
+ ret = cs48l32_soft_reset(cs48l32);
+ if (ret)
+ goto err_reset;
+ }
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid);
+ if (ret) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ hwid &= CS48L32_DEVID_MASK;
+
+ switch (hwid) {
+ case CS48L32_SILICON_ID:
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev);
+ if (ret) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ goto err_reset;
+ }
+ rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK;
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev);
+ if (ret) {
+ dev_err(dev, "Failed to read OTP revision register: %d\n", ret);
+ goto err_reset;
+ }
+ otp_rev &= CS48L32_OTPID_MASK;
+
+ dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff,
+ rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev);
+
+ /* Apply hardware patch */
+ ret = cs48l32_apply_patch(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret);
+ goto err_reset;
+ }
+
+ /* BOOT_DONE interrupt is unmasked by default, so mask it */
+ ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK);
+
+ ret = cs48l32_configure_clk32k(cs48l32);
+ if (ret)
+ goto err_reset;
+
+ pm_runtime_set_active(cs48l32->dev);
+ pm_runtime_set_autosuspend_delay(cs48l32->dev, 100);
+ pm_runtime_use_autosuspend(cs48l32->dev);
+ pm_runtime_enable(cs48l32->dev);
+
+ ret = cs48l32_create_codec_component(cs48l32_codec);
+ if (ret)
+ goto err_clk32k;
+
+ return 0;
+
+err_clk32k:
+ clk_disable_unprepare(cs48l32->mclk1);
+err_reset:
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_disable(cs48l32->vdd_d);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ return ret;
+}
+
+static void cs48l32_spi_remove(struct spi_device *spi)
+{
+ struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Remove IRQ handler before destroying anything else */
+ if (cs48l32->irq >= 1)
+ free_irq(cs48l32->irq, cs48l32_codec);
+
+ pm_runtime_disable(cs48l32->dev);
+ regulator_disable(cs48l32->vdd_d);
+ clk_disable_unprepare(cs48l32->mclk1);
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ mutex_destroy(&cs48l32_codec->rate_lock);
+}
+
+static const struct of_device_id cs48l32_of_match[] = {
+ { .compatible = "cirrus,cs48l32", },
+ {},
+};
+
+static const struct spi_device_id cs48l32_spi_ids[] = {
+ { "cs48l32", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids);
+
+static struct spi_driver cs48l32_spi_driver = {
+ .driver = {
+ .name = "cs48l32",
+ .pm = pm_ptr(&cs48l32_pm_ops),
+ .of_match_table = cs48l32_of_match,
+ },
+ .probe = &cs48l32_spi_probe,
+ .remove = &cs48l32_spi_remove,
+ .id_table = cs48l32_spi_ids,
+};
+module_spi_driver(cs48l32_spi_driver);
+
+MODULE_DESCRIPTION("CS48L32 ASoC codec driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h
new file mode 100644
index 000000000000..c1b4e13feae4
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic CS48L32 audio DSP.
+ *
+ * Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+#ifndef SND_SOC_CS48L32_H
+#define SND_SOC_CS48L32_H
+
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include "wm_adsp.h"
+
+#define CS48L32_SILICON_ID 0x48a32
+
+#define CS48L32_32K_MCLK1 0
+
+#define CS48L32_SFT_RESET_MAGIC 0x5a000000
+#define CS48L32_SOFT_RESET_US 2000
+#define CS48L32_HARD_RESET_MIN_US 1000
+
+#define CS48L32_SEEN_BOOT_DONE BIT(0)
+#define CS48L32_BOOT_TIMEOUT_US 25000
+
+#define CS48L32_ASP_ENABLES1 0x00
+#define CS48L32_ASP_CONTROL1 0x04
+#define CS48L32_ASP_CONTROL2 0x08
+#define CS48L32_ASP_CONTROL3 0x0c
+#define CS48L32_ASP_FRAME_CONTROL1 0x10
+#define CS48L32_ASP_FRAME_CONTROL2 0x14
+#define CS48L32_ASP_FRAME_CONTROL5 0x20
+#define CS48L32_ASP_FRAME_CONTROL6 0x24
+#define CS48L32_ASP_DATA_CONTROL1 0x30
+#define CS48L32_ASP_DATA_CONTROL5 0x40
+#define CS48L32_SYSCLK_RATE_6MHZ 0
+#define CS48L32_SYSCLK_RATE_12MHZ 1
+#define CS48L32_SYSCLK_RATE_24MHZ 2
+#define CS48L32_SYSCLK_RATE_49MHZ 3
+#define CS48L32_SYSCLK_RATE_98MHZ 4
+#define CS48L32_FLLHJ_INT_MAX_N 1023
+#define CS48L32_FLLHJ_INT_MIN_N 1
+#define CS48L32_FLLHJ_FRAC_MAX_N 255
+#define CS48L32_FLLHJ_FRAC_MIN_N 2
+#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000
+#define CS48L32_FLLHJ_LOW_THRESH 192000
+#define CS48L32_FLLHJ_MID_THRESH 1152000
+#define CS48L32_FLLHJ_MAX_THRESH 13000000
+#define CS48L32_FLLHJ_LOW_GAINS 0x23f0
+#define CS48L32_FLLHJ_MID_GAINS 0x22f2
+#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0
+#define CS48L32_FLL_MAX_FOUT 50000000
+#define CS48L32_FLL_MAX_REFDIV 8
+#define CS48L32_FLL_CONTROL1_OFFS 0x00
+#define CS48L32_FLL_CONTROL2_OFFS 0x04
+#define CS48L32_FLL_CONTROL3_OFFS 0x08
+#define CS48L32_FLL_CONTROL4_OFFS 0x0c
+#define CS48L32_FLL_CONTROL5_OFFS 0x10
+#define CS48L32_FLL_CONTROL6_OFFS 0x14
+#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34
+#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0
+#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000
+#define CS48L32_ASP_FMT_DSP_MODE_A 0
+#define CS48L32_ASP_FMT_DSP_MODE_B 1
+#define CS48L32_ASP_FMT_I2S_MODE 2
+#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3
+#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080
+#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280
+#define CS48L32_HALO_DSP_RATE_MASK 0x1f
+
+#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0
+#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1
+#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2
+#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3
+#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8
+#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9
+
+#define CS48L32_MAX_DAI 6
+#define CS48L32_MAX_INPUT 4
+#define CS48L32_MAX_ANALOG_INPUT 2
+#define CS48L32_MAX_IN_MUX_WAYS 2
+#define CS48L32_MAX_ASP 2
+
+#define CS48L32_EQ_BLOCK_SZ 60
+#define CS48L32_N_EQ_BLOCKS 4
+
+#define CS48L32_DSP_N_RX_CHANNELS 8
+#define CS48L32_DSP_N_TX_CHANNELS 8
+
+#define CS48L32_LHPF_MAX_COEFF 4095
+#define CS48L32_EQ_MAX_COEFF 4095
+
+#define CS48L32_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv)
+
+#define CS48L32_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL( \
+ name, reg, 0, CS48L32_MIXER_SRC_MASK, \
+ cs48l32_mixer_texts, cs48l32_mixer_values)
+
+#define CS48L32_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum)
+
+#define CS48L32_MUX_ENUMS(name, base_reg) \
+ static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static CS48L32_MUX_CTL_DECL(name)
+
+#define CS48L32_MIXER_ENUMS(name, base_reg) \
+ CS48L32_MUX_ENUMS(name##_in1, base_reg); \
+ CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \
+ CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \
+ CS48L32_MUX_ENUMS(name##_in4, base_reg + 12)
+
+#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux)
+
+#define CS48L32_MIXER_WIDGETS(name, name_str) \
+ CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \
+ CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \
+ CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \
+ CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS48L32_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define CS48L32_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" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+ { name, NULL, name " Preloader" }, \
+ { name, NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader" }, \
+ CS48L32_MIXER_ROUTES(name, name "RX1"), \
+ CS48L32_MIXER_ROUTES(name, name "RX2"), \
+ CS48L32_MIXER_ROUTES(name, name "RX3"), \
+ CS48L32_MIXER_ROUTES(name, name "RX4"), \
+ CS48L32_MIXER_ROUTES(name, name "RX5"), \
+ CS48L32_MIXER_ROUTES(name, name "RX6"), \
+ CS48L32_MIXER_ROUTES(name, name "RX7"), \
+ CS48L32_MIXER_ROUTES(name, name "RX8") \
+
+#define CS48L32_DSP_ROUTES_1_8(name) \
+ { name, NULL, "DSPCLK" }, \
+ CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+
+#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1])
+
+#define CS48L32_RATE_ENUM(name, enum) \
+ SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put)
+
+#define CS48L32_DSP_RATE_CONTROL(name, num) \
+ SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \
+ cs48l32_dsp_rate_get, cs48l32_dsp_rate_put)
+
+#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \
+ .put = cs48l32_eq_coeff_put, .private_value = \
+ (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\
+ .shift = xshift, .block_base = xbase, .max = 65535 } }
+
+#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \
+ CS48L32_ ## eq ## _ ## band ## _ ## type
+
+#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_EQ_COEFF_CONTROLS(name) \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = cs48l32_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */
+#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \
+ SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \
+ event, SND_SOC_DAPM_POST_PMU)
+
+#define CS48L32_RATES SNDRV_PCM_RATE_KNOT
+
+#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS48L32_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "ASP1RX1", "ASP1RX1" }, \
+ { name, "ASP1RX2", "ASP1RX2" }, \
+ { name, "ASP1RX3", "ASP1RX3" }, \
+ { name, "ASP1RX4", "ASP1RX4" }, \
+ { name, "ASP1RX5", "ASP1RX5" }, \
+ { name, "ASP1RX6", "ASP1RX6" }, \
+ { name, "ASP1RX7", "ASP1RX7" }, \
+ { name, "ASP1RX8", "ASP1RX8" }, \
+ { name, "ASP2RX1", "ASP2RX1" }, \
+ { name, "ASP2RX2", "ASP2RX2" }, \
+ { name, "ASP2RX3", "ASP2RX3" }, \
+ { name, "ASP2RX4", "ASP2RX4" }, \
+ { 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, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { 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, "Ultrasonic 1", "Ultrasonic 1" }, \
+ { name, "Ultrasonic 2", "Ultrasonic 2" }, \
+ { 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, "DSP1.7", "DSP1" }, \
+ { name, "DSP1.8", "DSP1" }
+
+struct cs48l32_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+struct cs48l32_eq_control {
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int block_base;
+ unsigned int max;
+};
+
+struct cs48l32_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct cs48l32_dsp_power_reg_block {
+ unsigned int start;
+ unsigned int end;
+};
+
+struct cs48l32_dsp_power_regs {
+ const unsigned int *pwd;
+ unsigned int n_pwd;
+ const struct cs48l32_dsp_power_reg_block *ext;
+ unsigned int n_ext;
+};
+
+struct cs48l32;
+struct cs48l32_codec;
+struct spi_device;
+
+struct cs48l32_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct cs48l32_fll {
+ struct cs48l32_codec *codec;
+ int id;
+ unsigned int base;
+
+ unsigned int sts_addr;
+ unsigned int sts_mask;
+ unsigned int fout;
+ int ref_src;
+ unsigned int ref_freq;
+
+ struct cs48l32_fll_cfg ref_cfg;
+};
+
+struct cs48l32_codec {
+ struct wm_adsp dsp; /* must be first */
+ struct cs48l32 core;
+ int sysclk;
+ int dspclk;
+ struct cs48l32_dai_priv dai[CS48L32_MAX_DAI];
+ struct cs48l32_fll fll;
+
+ unsigned int in_up_pending;
+ unsigned int in_vu_reg;
+
+ struct mutex rate_lock;
+
+ u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS];
+
+ u8 in_type[CS48L32_MAX_ANALOG_INPUT][CS48L32_MAX_IN_MUX_WAYS];
+ u8 pdm_sup[CS48L32_MAX_ANALOG_INPUT];
+ u8 tdm_width[CS48L32_MAX_ASP];
+ u8 tdm_slots[CS48L32_MAX_ASP];
+
+ unsigned int eq_mode[CS48L32_N_EQ_BLOCKS];
+ __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2];
+
+ const struct cs48l32_dsp_power_regs *dsp_power_regs;
+};
+
+#define cs48l32_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define cs48l32_asp_err(_dai, fmt, ...) \
+ dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_warn(_dai, fmt, ...) \
+ dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32);
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32);
+int cs48l32_enable_asp1_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_enable_asp2_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_micvdd_voltage_index(u32 voltage);
+int cs48l32_micbias1_voltage_index(u32 voltage);
+
+#endif
diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c
new file mode 100644
index 000000000000..22b1a4d6b61c
--- /dev/null
+++ b/sound/soc/codecs/cs530x-i2c.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs530x.h"
+
+static const struct of_device_id cs530x_of_match[] = {
+ {
+ .compatible = "cirrus,cs5302",
+ .data = (void *)CS5302,
+ }, {
+ .compatible = "cirrus,cs5304",
+ .data = (void *)CS5304,
+ }, {
+ .compatible = "cirrus,cs5308",
+ .data = (void *)CS5308,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs530x_of_match);
+
+static const struct i2c_device_id cs530x_i2c_id[] = {
+ { "cs5302", CS5302 },
+ { "cs5304", CS5304 },
+ { "cs5308", CS5308 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id);
+
+static int cs530x_i2c_probe(struct i2c_client *client)
+{
+ struct cs530x_priv *cs530x;
+
+ cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL);
+ if (!cs530x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs530x);
+
+ cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap);
+ if (IS_ERR(cs530x->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap),
+ "Failed to allocate register map\n");
+
+ cs530x->devtype = (uintptr_t)i2c_get_match_data(client);
+ cs530x->dev = &client->dev;
+
+ return cs530x_probe(cs530x);
+}
+
+static struct i2c_driver cs530x_i2c_driver = {
+ .driver = {
+ .name = "cs530x",
+ .of_match_table = cs530x_of_match,
+ },
+ .probe = cs530x_i2c_probe,
+ .id_table = cs530x_i2c_id,
+};
+module_i2c_driver(cs530x_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS530X driver");
+MODULE_IMPORT_NS("SND_SOC_CS530X");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
new file mode 100644
index 000000000000..b9eff240b929
--- /dev/null
+++ b/sound/soc/codecs/cs530x.c
@@ -0,0 +1,971 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs530x.h"
+
+#define CS530X_MAX_ADC_CH 8
+#define CS530X_MIN_ADC_CH 2
+
+static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = {
+ "vdd-a",
+ "vdd-io",
+};
+
+static const struct reg_default cs530x_reg_defaults[] = {
+ { CS530X_CLK_CFG_0, 0x30 },
+ { CS530X_CLK_CFG_1, 0x0001 },
+ { CS530X_CHIP_ENABLE, 0 },
+ { CS530X_ASP_CFG, 0 },
+ { CS530X_SIGNAL_PATH_CFG, 0 },
+ { CS530X_IN_ENABLES, 0 },
+ { CS530X_IN_RAMP_SUM, 0x0022 },
+ { CS530X_IN_FILTER, 0 },
+ { CS530X_IN_HIZ, 0 },
+ { CS530X_IN_INV, 0 },
+ { CS530X_IN_VOL_CTRL1_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL1_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_1, 0x8000 },
+ { CS530X_PAD_FN, 0 },
+ { CS530X_PAD_LVL, 0 },
+};
+
+static bool cs530x_read_and_write_regs(unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_CLK_CFG_0:
+ case CS530X_CLK_CFG_1:
+ case CS530X_CHIP_ENABLE:
+ case CS530X_ASP_CFG:
+ case CS530X_SIGNAL_PATH_CFG:
+ case CS530X_IN_ENABLES:
+ case CS530X_IN_RAMP_SUM:
+ case CS530X_IN_FILTER:
+ case CS530X_IN_HIZ:
+ case CS530X_IN_INV:
+ case CS530X_IN_VOL_CTRL1_0:
+ case CS530X_IN_VOL_CTRL1_1:
+ case CS530X_IN_VOL_CTRL2_0:
+ case CS530X_IN_VOL_CTRL2_1:
+ case CS530X_IN_VOL_CTRL3_0:
+ case CS530X_IN_VOL_CTRL3_1:
+ case CS530X_IN_VOL_CTRL4_0:
+ case CS530X_IN_VOL_CTRL4_1:
+ case CS530X_PAD_FN:
+ case CS530X_PAD_LVL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs530x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_DEVID:
+ case CS530X_REVID:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static bool cs530x_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_SW_RESET:
+ case CS530X_IN_VOL_CTRL5:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static int cs530x_put_volsw_vu(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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret)
+ goto volsw_err;
+
+ /* Write IN_VU bit for the volume change to take effect */
+ regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU);
+
+volsw_err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0);
+
+static const char * const cs530x_in_filter_text[] = {
+ "Min Phase Slow Roll-off",
+ "Min Phase Fast Roll-off",
+ "Linear Phase Slow Roll-off",
+ "Linear Phase Fast Roll-off",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_filter_enum, CS530X_IN_FILTER,
+ CS530X_IN_FILTER_SHIFT,
+ cs530x_in_filter_text);
+
+static const char * const cs530x_in_4ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM,
+ CS530X_IN_SUM_MODE_SHIFT,
+ cs530x_in_4ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum),
+};
+
+static const char * const cs530x_in_8ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+ "Groups of 8",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM,
+ CS530X_IN_SUM_MODE_SHIFT,
+ cs530x_in_8ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum),
+};
+
+
+static const char * const cs530x_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_INC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_DEC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = {
+SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_ENUM("IN DEC Filter Select", cs530x_in_filter_enum),
+SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum),
+SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum),
+
+SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = {
+SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = {
+SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0),
+};
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs530x->adc_pairs_count++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_IN_MUTE);
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift+1) * 2), CS530X_IN_MUTE);
+
+ cs530x->adc_pairs_count--;
+ if (!cs530x->adc_pairs_count) {
+ usleep_range(1000, 1100);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_IN_VU);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_IN_MUTE);
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift+1) * 2), CS530X_IN_MUTE);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_IN_VU);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new adc12_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc34_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc56_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc78_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new in_hpf_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* General DAPM widgets for all devices */
+static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0),
+};
+
+/* ADC's Channels 1 and 2 plus generic ADC DAPM events */
+static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0),
+SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl),
+SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT,
+ 0, &in_hpf_ctrl),
+};
+
+/* ADC's Channels 3 and 4 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN3"),
+SND_SOC_DAPM_INPUT("IN4"),
+SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0),
+SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl),
+};
+
+/* ADC's Channels 5 to 8 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN5"),
+SND_SOC_DAPM_INPUT("IN6"),
+SND_SOC_DAPM_INPUT("IN7"),
+SND_SOC_DAPM_INPUT("IN8"),
+SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0),
+SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl),
+SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0),
+SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl),
+};
+
+static const struct snd_soc_dapm_route adc_ch1_2_routes[] = {
+ { "ADC1", NULL, "Global Enable" },
+ { "ADC2", NULL, "Global Enable" },
+
+ { "ADC12 Enable", "Switch", "IN1" },
+ { "ADC12 Enable", "Switch", "IN2" },
+ { "ADC1", NULL, "ADC12 Enable" },
+ { "ADC2", NULL, "ADC12 Enable" },
+ { "IN HPF", "Switch", "ADC1" },
+ { "IN HPF", "Switch", "ADC2" },
+
+ { "AIF Capture", NULL, "IN HPF" },
+ { "AIF Capture", NULL, "ADC1" },
+ { "AIF Capture", NULL, "ADC2" },
+};
+
+static const struct snd_soc_dapm_route adc_ch3_4_routes[] = {
+ { "ADC3", NULL, "Global Enable" },
+ { "ADC4", NULL, "Global Enable" },
+
+ { "ADC34 Enable", "Switch", "IN3" },
+ { "ADC34 Enable", "Switch", "IN4" },
+ { "ADC3", NULL, "ADC34 Enable" },
+ { "ADC4", NULL, "ADC34 Enable" },
+ { "IN HPF", "Switch", "ADC3" },
+ { "IN HPF", "Switch", "ADC4" },
+
+ { "AIF Capture", NULL, "ADC3" },
+ { "AIF Capture", NULL, "ADC4" },
+};
+
+static const struct snd_soc_dapm_route adc_ch5_8_routes[] = {
+ { "ADC5", NULL, "Global Enable" },
+ { "ADC6", NULL, "Global Enable" },
+ { "ADC7", NULL, "Global Enable" },
+ { "ADC8", NULL, "Global Enable" },
+
+ { "ADC56 Enable", "Switch", "IN5" },
+ { "ADC56 Enable", "Switch", "IN6" },
+ { "ADC5", NULL, "ADC56 Enable" },
+ { "ADC6", NULL, "ADC56 Enable" },
+ { "IN HPF", "Switch", "ADC5" },
+ { "IN HPF", "Switch", "ADC6" },
+
+ { "AIF Capture", NULL, "ADC5" },
+ { "AIF Capture", NULL, "ADC6" },
+
+ { "ADC78 Enable", "Switch", "IN7" },
+ { "ADC78 Enable", "Switch", "IN8" },
+ { "ADC7", NULL, "ADC78 Enable" },
+ { "ADC8", NULL, "ADC78 Enable" },
+ { "IN HPF", "Switch", "ADC7" },
+ { "IN HPF", "Switch", "ADC8" },
+
+ { "AIF Capture", NULL, "ADC7" },
+ { "AIF Capture", NULL, "ADC8" },
+};
+
+static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_1_to_2_controls,
+ ARRAY_SIZE(cs530x_in_1_to_2_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes,
+ ARRAY_SIZE(adc_ch1_2_routes));
+}
+
+static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_3_to_4_controls,
+ ARRAY_SIZE(cs530x_in_3_to_4_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes,
+ ARRAY_SIZE(adc_ch3_4_routes));
+}
+
+static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int bclk_val;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ bclk_val = CS530X_BCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ bclk_val = CS530X_BCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ bclk_val = CS530X_BCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ bclk_val = CS530X_BCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid BCLK frequency %d\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "BCLK frequency is %d\n", freq);
+
+ return regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_BCLK_FREQ_MASK, bclk_val);
+}
+
+static int cs530x_set_pll_refclk(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int refclk;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ refclk = CS530X_REFCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ refclk = CS530X_REFCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ refclk = CS530X_REFCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ refclk = CS530X_REFCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL refclk %d\n", freq);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_FREQ_MASK, refclk);
+}
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret = 0, fs = params_rate(params), bclk;
+ unsigned int fs_val;
+
+
+ switch (fs) {
+ case 32000:
+ fs_val = CS530X_FS_32K;
+ break;
+ case 44100:
+ case 48000:
+ fs_val = CS530X_FS_48K_44P1K;
+ break;
+ case 88200:
+ case 96000:
+ fs_val = CS530X_FS_96K_88P2K;
+ break;
+ case 176400:
+ case 192000:
+ fs_val = CS530X_FS_192K_176P4K;
+ break;
+ case 356800:
+ case 384000:
+ fs_val = CS530X_FS_384K_356P8K;
+ break;
+ case 705600:
+ case 768000:
+ fs_val = CS530X_FS_768K_705P6K;
+ break;
+ default:
+ dev_err(component->dev, "Invalid sample rate %d\n", fs);
+ return -EINVAL;
+ }
+
+ cs530x->fs = fs;
+ regmap_update_bits(regmap, CS530X_CLK_CFG_1,
+ CS530X_SAMPLE_RATE_MASK, fs_val);
+
+
+ if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_TDM_EN_MASK)) {
+ dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",
+ cs530x->tdm_slots, cs530x->tdm_width);
+ bclk = snd_soc_tdm_params_to_bclk(params,
+ cs530x->tdm_width,
+ cs530x->tdm_slots,
+ 1);
+ } else {
+ bclk = snd_soc_params_to_bclk(params);
+ }
+
+ if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK)) {
+ ret = cs530x_set_pll_refclk(component, bclk);
+ if (ret)
+ return ret;
+ }
+
+ return cs530x_set_bclk(component, bclk);
+}
+
+static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int asp_fmt, asp_cfg = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ asp_cfg = CS530X_ASP_PRIMARY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS530X_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS530X_ASP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ asp_fmt = CS530X_ASP_FMT_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg |= CS530X_ASP_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV,
+ asp_cfg);
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_FMT_MASK, asp_fmt);
+}
+
+static bool cs530x_check_mclk_freq(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ switch (freq) {
+ case 24576000:
+ case 22579200:
+ case 12288000:
+ case 11289600:
+ return true;
+ default:
+ dev_err(component->dev, "Invalid MCLK %d\n", freq);
+ return false;
+ }
+}
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int val;
+
+ switch (tx_mask) {
+ case CS530X_0_1_TDM_SLOT_MASK:
+ case CS530X_0_3_TDM_SLOT_MASK:
+ case CS530X_0_7_TDM_SLOT_MASK:
+ val = CS530X_0_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_2_3_TDM_SLOT_MASK:
+ val = CS530X_2_3_TDM_SLOT_VAL;
+ break;
+ case CS530X_4_5_TDM_SLOT_MASK:
+ case CS530X_4_7_TDM_SLOT_MASK:
+ val = CS530X_4_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_6_7_TDM_SLOT_MASK:
+ val = CS530X_6_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_8_9_TDM_SLOT_MASK:
+ case CS530X_8_11_TDM_SLOT_MASK:
+ case CS530X_8_15_TDM_SLOT_MASK:
+ val = CS530X_8_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_10_11_TDM_SLOT_MASK:
+ val = CS530X_10_11_TDM_SLOT_VAL;
+ break;
+ case CS530X_12_13_TDM_SLOT_MASK:
+ case CS530X_12_15_TDM_SLOT_MASK:
+ val = CS530X_12_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_14_15_TDM_SLOT_MASK:
+ val = CS530X_14_15_TDM_SLOT_VAL;
+ break;
+ default:
+ dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask);
+ return -EINVAL;
+ }
+
+ cs530x->tdm_width = slot_width;
+ cs530x->tdm_slots = slots;
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_TDM_SLOT_MASK,
+ val << CS530X_ASP_TDM_SLOT_SHIFT);
+}
+
+static const struct snd_soc_dai_ops cs530x_dai_ops = {
+ .set_fmt = cs530x_set_fmt,
+ .hw_params = cs530x_hw_params,
+ .set_tdm_slot = cs530x_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_driver cs530x_dai = {
+ .name = "cs530x-dai",
+ .capture = {
+ .stream_name = "AIF Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &cs530x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int sysclk_src;
+ int ret;
+
+ regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src);
+
+ /* Check if the source is the PLL */
+ if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0)
+ return 0;
+
+ switch (source) {
+ case CS530X_PLL_SRC_MCLK:
+ if (!cs530x_check_mclk_freq(component, freq_in))
+ return -EINVAL;
+
+ ret = cs530x_set_pll_refclk(component, freq_in);
+ if (ret)
+ return ret;
+
+ break;
+ case CS530X_PLL_SRC_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK, source);
+}
+
+static int cs530x_component_probe(struct snd_soc_component *component)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int num_widgets;
+
+ snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets,
+ ARRAY_SIZE(cs530x_gen_dapm_widgets));
+
+ switch (cs530x->devtype) {
+ case CS5302:
+ cs530x_add_12_adc_widgets(component);
+ break;
+ case CS5304:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_4ch_controls,
+ num_widgets);
+ break;
+
+ case CS5308:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_5_to_8_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_8ch_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets);
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets,
+ num_widgets);
+
+ snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes,
+ ARRAY_SIZE(adc_ch5_8_routes));
+ break;
+ default:
+ dev_err(component->dev, "Invalid device type %d\n",
+ cs530x->devtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (source) {
+ case CS530X_SYSCLK_SRC_MCLK:
+ if (freq != 24560000 && freq != 22572000) {
+ dev_err(component->dev, "Invalid MCLK source rate %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ cs530x->mclk_rate = freq;
+ break;
+ case CS530X_SYSCLK_SRC_PLL:
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_SYSCLK_SRC_MASK,
+ source << CS530X_SYSCLK_SRC_SHIFT);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs530x = {
+ .probe = cs530x_component_probe,
+ .set_sysclk = cs530x_set_sysclk,
+ .set_pll = cs530x_set_pll,
+ .endianness = 1,
+};
+
+const struct regmap_config cs530x_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = CS530X_MAX_REGISTER,
+ .readable_reg = cs530x_readable_register,
+ .writeable_reg = cs530x_writeable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs530x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
+};
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap, "SND_SOC_CS530X");
+
+static int cs530x_check_device_id(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ unsigned int dev_id, rev;
+ int ret;
+
+ ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read device ID\n");
+
+ ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read REV ID\n");
+
+ dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev);
+
+ switch (dev_id) {
+ case CS530X_2CH_ADC_DEV_ID:
+ cs530x->num_adcs = 2;
+ break;
+ case CS530X_4CH_ADC_DEV_ID:
+ cs530x->num_adcs = 4;
+ break;
+ case CS530X_8CH_ADC_DEV_ID:
+ cs530x->num_adcs = 8;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n",
+ dev_id);
+ }
+
+ return 0;
+}
+
+static int cs530x_parse_device_properties(struct cs530x_priv *cs530x)
+{
+ struct regmap *regmap = cs530x->regmap;
+ struct device *dev = cs530x->dev;
+ unsigned int val = 0;
+
+ switch (cs530x->num_adcs) {
+ case 8:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin78"))
+ val = CS530X_IN78_HIZ;
+
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin56"))
+ val |= CS530X_IN56_HIZ;
+
+ fallthrough;
+ case 4:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin34"))
+ val |= CS530X_IN34_HIZ;
+
+ fallthrough;
+ case 2:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin12"))
+ val |= CS530X_IN12_HIZ;
+
+ return regmap_set_bits(regmap, CS530X_IN_HIZ, val);
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of adcs %d\n",
+ cs530x->num_adcs);
+ }
+}
+
+int cs530x_probe(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ int ret, i;
+
+ cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai,
+ sizeof(*(cs530x->dev_dai)),
+ GFP_KERNEL);
+ if (!cs530x->dev_dai)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++)
+ cs530x->supplies[i].supply = cs530x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to request supplies");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to enable supplies");
+
+ cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs530x->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio),
+ "Reset gpio not available\n");
+ goto err_regulator;
+ }
+
+ if (cs530x->reset_gpio) {
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 0);
+ }
+
+ usleep_range(5000, 5100);
+ ret = cs530x_check_device_id(cs530x);
+ if (ret)
+ goto err_reset;
+
+ if (!cs530x->reset_gpio) {
+ ret = regmap_write(cs530x->regmap, CS530X_SW_RESET,
+ CS530X_SW_RST_VAL);
+ if (ret) {
+ dev_err_probe(dev, ret, "Soft Reset Failed\n");
+ goto err_reset;
+ }
+ }
+
+ ret = cs530x_parse_device_properties(cs530x);
+ if (ret)
+ goto err_reset;
+
+ cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_cs530x, cs530x->dev_dai, 1);
+ if (ret) {
+ dev_err_probe(dev, ret, "Can't register cs530x component\n");
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 1);
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs530x_probe, "SND_SOC_CS530X");
+
+MODULE_DESCRIPTION("CS530X CODEC Driver");
+MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h
new file mode 100644
index 000000000000..f473e33eb835
--- /dev/null
+++ b/sound/soc/codecs/cs530x.h
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS530x CODEC driver internal data
+ *
+ * Copyright (C) 2023-2024 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef _CS530X_H
+#define _CS530X_H
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+/* Devices */
+#define CS530X_2CH_ADC_DEV_ID 0x5302
+#define CS530X_4CH_ADC_DEV_ID 0x5304
+#define CS530X_8CH_ADC_DEV_ID 0x5308
+
+/* Registers */
+
+#define CS530X_DEVID 0x0000000
+#define CS530X_REVID 0x0000004
+#define CS530X_SW_RESET 0x0000022
+
+#define CS530X_CLK_CFG_0 0x0000040
+#define CS530X_CLK_CFG_1 0x0000042
+#define CS530X_CHIP_ENABLE 0x0000044
+#define CS530X_ASP_CFG 0x0000048
+#define CS530X_SIGNAL_PATH_CFG 0x0000050
+#define CS530X_IN_ENABLES 0x0000080
+#define CS530X_IN_RAMP_SUM 0x0000082
+#define CS530X_IN_FILTER 0x0000086
+#define CS530X_IN_HIZ 0x0000088
+#define CS530X_IN_INV 0x000008A
+#define CS530X_IN_VOL_CTRL1_0 0x0000090
+#define CS530X_IN_VOL_CTRL1_1 0x0000092
+#define CS530X_IN_VOL_CTRL2_0 0x0000094
+#define CS530X_IN_VOL_CTRL2_1 0x0000096
+#define CS530X_IN_VOL_CTRL3_0 0x0000098
+#define CS530X_IN_VOL_CTRL3_1 0x000009A
+#define CS530X_IN_VOL_CTRL4_0 0x000009C
+#define CS530X_IN_VOL_CTRL4_1 0x000009E
+#define CS530X_IN_VOL_CTRL5 0x00000A0
+
+#define CS530X_PAD_FN 0x0003D24
+#define CS530X_PAD_LVL 0x0003D28
+
+#define CS530X_MAX_REGISTER CS530X_PAD_LVL
+
+/* Register Fields */
+
+/* REVID */
+#define CS530X_MTLREVID GENMASK(3, 0)
+#define CS530X_AREVID GENMASK(7, 4)
+
+/* SW_RESET */
+#define CS530X_SW_RST_SHIFT 8
+#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT)
+
+/* CLK_CFG_0 */
+#define CS530X_PLL_REFCLK_SRC_MASK BIT(0)
+#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4)
+#define CS530X_SYSCLK_SRC_MASK BIT(12)
+#define CS530X_SYSCLK_SRC_SHIFT 12
+#define CS530X_REFCLK_2P822_3P072 0
+#define CS530X_REFCLK_5P6448_6P144 0x10
+#define CS530X_REFCLK_11P2896_12P288 0x20
+#define CS530X_REFCLK_24P5792_24P576 0x30
+
+/* CLK_CFG_1 */
+#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0)
+#define CS530X_FS_32K 0
+#define CS530X_FS_48K_44P1K 1
+#define CS530X_FS_96K_88P2K 2
+#define CS530X_FS_192K_176P4K 3
+#define CS530X_FS_384K_356P8K 4
+#define CS530X_FS_768K_705P6K 5
+
+/* CHIP_ENABLE */
+#define CS530X_GLOBAL_EN BIT(0)
+
+/* ASP_CFG */
+#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0)
+#define CS530X_ASP_PRIMARY BIT(5)
+#define CS530X_ASP_BCLK_INV BIT(6)
+#define CS530X_BCLK_2P822_3P072 0
+#define CS530X_BCLK_5P6448_6P144 1
+#define CS530X_BCLK_11P2896_12P288 2
+#define CS530X_BCLK_24P5792_24P576 3
+
+/* SIGNAL_PATH_CFG */
+#define CS530X_ASP_FMT_MASK GENMASK(2, 0)
+#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3)
+#define CS530X_ASP_TDM_SLOT_SHIFT 3
+#define CS530X_ASP_CH_REVERSE BIT(9)
+#define CS530X_TDM_EN_MASK BIT(2)
+#define CS530X_ASP_FMT_I2S 0
+#define CS530X_ASP_FMT_LJ 1
+#define CS530X_ASP_FMT_DSP_A 0x6
+
+/* TDM Slots */
+#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0)
+#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0)
+#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0)
+#define CS530X_0_7_TDM_SLOT_VAL 0
+
+#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2)
+#define CS530X_2_3_TDM_SLOT_VAL 1
+
+#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4)
+#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4)
+#define CS530X_4_7_TDM_SLOT_VAL 2
+
+#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6)
+#define CS530X_6_7_TDM_SLOT_VAL 3
+
+#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8)
+#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8)
+#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8)
+#define CS530X_8_15_TDM_SLOT_VAL 4
+
+#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10)
+#define CS530X_10_11_TDM_SLOT_VAL 5
+
+#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12)
+#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12)
+#define CS530X_12_15_TDM_SLOT_VAL 6
+
+#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14)
+#define CS530X_14_15_TDM_SLOT_VAL 7
+
+/* IN_RAMP_SUM */
+#define CS530X_RAMP_RATE_INC_SHIFT 0
+#define CS530X_RAMP_RATE_DEC_SHIFT 4
+#define CS530X_IN_SUM_MODE_SHIFT 13
+
+/* IN_FILTER */
+#define CS530X_IN_FILTER_SHIFT 8
+#define CS530X_IN_HPF_EN_SHIFT 12
+
+/* IN_HIZ */
+#define CS530X_IN12_HIZ BIT(0)
+#define CS530X_IN34_HIZ BIT(1)
+#define CS530X_IN56_HIZ BIT(2)
+#define CS530X_IN78_HIZ BIT(3)
+
+/* IN_INV */
+#define CS530X_IN1_INV_SHIFT 0
+#define CS530X_IN2_INV_SHIFT 1
+#define CS530X_IN3_INV_SHIFT 2
+#define CS530X_IN4_INV_SHIFT 3
+#define CS530X_IN5_INV_SHIFT 4
+#define CS530X_IN6_INV_SHIFT 5
+#define CS530X_IN7_INV_SHIFT 6
+#define CS530X_IN8_INV_SHIFT 7
+
+/* IN_VOL_CTLy_z */
+#define CS530X_IN_MUTE BIT(15)
+
+/* IN_VOL_CTL5 */
+#define CS530X_IN_VU BIT(0)
+
+/* PAD_FN */
+#define CS530X_DOUT2_FN BIT(0)
+#define CS530X_DOUT3_FN BIT(1)
+#define CS530X_DOUT4_FN BIT(2)
+#define CS530X_SPI_CS_FN BIT(3)
+#define CS530X_CONFIG2_FN BIT(6)
+#define CS530X_CONFIG3_FN BIT(7)
+#define CS530X_CONFIG4_FN BIT(8)
+#define CS530X_CONFIG5_FN BIT(9)
+
+/* PAD_LVL */
+#define CS530X_CONFIG2_LVL BIT(6)
+#define CS530X_CONFIG3_LVL BIT(7)
+#define CS530X_CONFIG4_LVL BIT(8)
+#define CS530X_CONFIG5_LVL BIT(9)
+
+/* System Clock Source */
+#define CS530X_SYSCLK_SRC_MCLK 0
+#define CS530X_SYSCLK_SRC_PLL 1
+
+/* PLL Reference Clock Source */
+#define CS530X_PLL_SRC_BCLK 0
+#define CS530X_PLL_SRC_MCLK 1
+
+#define CS530X_NUM_SUPPLIES 2
+
+enum cs530x_type {
+ CS5302,
+ CS5304,
+ CS5308,
+};
+
+/* codec private data */
+struct cs530x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct snd_soc_dai_driver *dev_dai;
+
+ enum cs530x_type devtype;
+ int num_adcs;
+ int num_dacs;
+
+ struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES];
+
+ unsigned int mclk_rate;
+
+ int tdm_width;
+ int tdm_slots;
+ int fs;
+ int adc_pairs_count;
+
+ struct gpio_desc *reset_gpio;
+};
+
+extern const struct regmap_config cs530x_regmap;
+int cs530x_probe(struct cs530x_priv *cs530x);
+
+#endif
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index c7edf2df5e36..61bf72681674 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs53l30.c -- CS53l30 ALSA Soc Audio driver
*
@@ -5,18 +6,12 @@
*
* Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
* Tim Howe <Tim.Howe@cirrus.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/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>
@@ -24,6 +19,7 @@
#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] = {
@@ -351,22 +347,22 @@ static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
- CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
- CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
- CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
- CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
- CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
- CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
- CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
- CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
};
static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
@@ -549,7 +545,7 @@ static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
int mclkx_coeff;
u32 mclk_rate;
@@ -572,14 +568,14 @@ static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
aspcfg |= CS53L30_ASP_MS;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -626,7 +622,7 @@ 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_codec_get_drvdata(dai->codec);
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
int srate = params_rate(params);
int mclk_coeff;
@@ -650,11 +646,11 @@ static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_dapm(codec);
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -670,7 +666,7 @@ static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
if (dapm->bias_level == SND_SOC_BIAS_OFF) {
ret = clk_prepare_enable(priv->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to enable MCLK: %d\n", ret);
return ret;
}
@@ -736,31 +732,13 @@ static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ 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
@@ -770,7 +748,7 @@ 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_codec_get_drvdata(dai->codec);
+ 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;
@@ -840,21 +818,21 @@ static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ 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_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000)
#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,
@@ -873,13 +851,13 @@ static struct snd_soc_dai_driver cs53l30_dai = {
.formats = CS53L30_FORMATS,
},
.ops = &cs53l30_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int cs53l30_codec_probe(struct snd_soc_codec *codec)
+static int cs53l30_component_probe(struct snd_soc_component *component)
{
- struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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,
@@ -891,22 +869,20 @@ static int cs53l30_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver cs53l30_driver = {
- .probe = cs53l30_codec_probe,
- .set_bias_level = cs53l30_set_bias_level,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
+static const struct regmap_config cs53l30_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -916,18 +892,19 @@ static struct regmap_config cs53l30_regmap = {
.volatile_reg = cs53l30_volatile_register,
.writeable_reg = cs53l30_writeable_register,
.readable_reg = cs53l30_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int cs53l30_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+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 devid = 0;
unsigned int reg;
- int ret = 0, i;
+ int ret = 0, i, devid;
u8 val;
cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
@@ -956,7 +933,7 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
GPIOD_OUT_LOW);
if (IS_ERR(cs53l30->reset_gpio)) {
ret = PTR_ERR(cs53l30->reset_gpio);
- goto error;
+ goto error_supplies;
}
gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
@@ -973,14 +950,12 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
}
/* Initialize codec */
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
- devid = reg << 12;
-
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
- devid |= reg << 4;
-
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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;
@@ -996,14 +971,10 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
}
/* Check if MCLK provided */
- cs53l30->mclk = devm_clk_get(dev, "mclk");
+ cs53l30->mclk = devm_clk_get_optional(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;
+ ret = PTR_ERR(cs53l30->mclk);
+ goto error;
}
/* Fetch the MUTE control */
@@ -1033,36 +1004,33 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
- ret = snd_soc_register_codec(dev, &cs53l30_driver, &cs53l30_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &cs53l30_driver, &cs53l30_dai, 1);
if (ret) {
- dev_err(dev, "failed to register codec: %d\n", 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 int cs53l30_i2c_remove(struct i2c_client *client)
+static void cs53l30_i2c_remove(struct i2c_client *client)
{
struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
-
/* Hold down reset */
gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
-
- return 0;
}
-#ifdef CONFIG_PM
static int cs53l30_runtime_suspend(struct device *dev)
{
struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
@@ -1101,11 +1069,9 @@ static int cs53l30_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops cs53l30_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume, NULL)
};
static const struct of_device_id cs53l30_of_match[] = {
@@ -1116,7 +1082,7 @@ static const struct of_device_id cs53l30_of_match[] = {
MODULE_DEVICE_TABLE(of, cs53l30_of_match);
static const struct i2c_device_id cs53l30_id[] = {
- { "cs53l30", 0 },
+ { "cs53l30" },
{}
};
@@ -1126,7 +1092,7 @@ static struct i2c_driver cs53l30_i2c_driver = {
.driver = {
.name = "cs53l30",
.of_match_table = cs53l30_of_match,
- .pm = &cs53l30_runtime_pm,
+ .pm = pm_ptr(&cs53l30_runtime_pm),
},
.id_table = cs53l30_id,
.probe = cs53l30_i2c_probe,
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
index 5e39da568749..071547c55719 100644
--- a/sound/soc/codecs/cs53l30.h
+++ b/sound/soc/codecs/cs53l30.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS53L30 codec driver
*
@@ -5,11 +6,6 @@
*
* Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
* Tim Howe <Tim.Howe@cirrus.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 __CS53L30_H__
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 95bb10ba80dc..9d54141a0cd1 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,11 +7,6 @@
* 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>
@@ -89,10 +85,10 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
+static unsigned int cx20442_read_reg_cache(struct snd_soc_component *component,
unsigned int reg)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
if (reg >= 1)
return -EINVAL;
@@ -153,10 +149,10 @@ 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);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int vls, vsp, old, len;
char buf[18];
@@ -193,7 +189,7 @@ 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);
+ dev_dbg(component->dev, "%s: %s\n", __func__, buf);
if (cx20442->tty->ops->write(cx20442->tty, buf, len) != len)
return -EIO;
@@ -210,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)
@@ -240,63 +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 */
cx20442->tty = NULL;
- codec->component.card->pop_time = 0;
+ 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 u8 *cp, const u8 *fp,
+ size_t 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->tty) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
cx20442->tty = tty;
- codec->component.card->pop_time = 1;
+ 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);
@@ -323,15 +311,15 @@ static struct snd_soc_dai_driver cx20442_dai = {
},
};
-static int cx20442_set_bias_level(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_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int err = 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY)
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_STANDBY)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -339,7 +327,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
err = regulator_enable(cx20442->por);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE)
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_PREPARE)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -353,7 +341,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
return err;
}
-static int cx20442_codec_probe(struct snd_soc_codec *codec)
+static int cx20442_component_probe(struct snd_soc_component *component)
{
struct cx20442_priv *cx20442;
@@ -361,21 +349,40 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
if (cx20442 == NULL)
return -ENOMEM;
- cx20442->por = regulator_get(codec->dev, "POR");
- if (IS_ERR(cx20442->por))
- dev_warn(codec->dev, "failed to get the regulator");
+ 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;
- snd_soc_codec_set_drvdata(codec, cx20442);
- codec->component.card->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;
@@ -387,36 +394,29 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec)
regulator_put(cx20442->por);
}
- snd_soc_codec_set_drvdata(codec, NULL);
+ snd_soc_component_set_drvdata(component, NULL);
kfree(cx20442);
- return 0;
}
-static const struct snd_soc_codec_driver cx20442_codec_dev = {
- .probe = cx20442_codec_probe,
- .remove = cx20442_codec_remove,
- .set_bias_level = cx20442_set_bias_level,
- .read = cx20442_read_reg_cache,
- .write = cx20442_write,
-
- .component_driver = {
- .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),
- },
+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 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 = {
@@ -424,7 +424,6 @@ static struct platform_driver cx20442_platform_driver = {
.name = "cx20442-codec",
},
.probe = cx20442_platform_probe,
- .remove = cx20442_platform_remove,
};
module_platform_driver(cx20442_platform_driver);
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..934526f8f292
--- /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);
+
+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;
+}
+
+static const struct snd_soc_dai_ops cx2072x_dai_ops2 = {
+ .probe = cx2072x_dsp_dai_probe,
+ .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,
+};
+
+#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,
+ .playback = {
+ .stream_name = "DSP Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .ops = &cx2072x_dai_ops2,
+ },
+ { /* 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 cx2072x_runtime_suspend(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(cx2072x->mclk);
+ return 0;
+}
+
+static int 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" },
+ { "cx20723" },
+ {}
+};
+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 = {
+ RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, NULL)
+ 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 = pm_ptr(&cx2072x_runtime_pm),
+ },
+ .probe = 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 1af443ccbc51..a889f05119f8 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1,19 +1,14 @@
-/*
- * 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>
@@ -331,12 +326,12 @@ static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) {
- dev_dbg(codec->dev,
+ 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;
}
@@ -354,32 +349,32 @@ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, DA7210_ADC) & DA7210_ADC_ALC_EN)
+ if (snd_soc_component_read(component, DA7210_ADC) & DA7210_ADC_ALC_EN)
goto err;
/* Check ZC for HP and AUX1 PGA */
- if ((snd_soc_read(codec, DA7210_ZERO_CROSS) &
+ 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_read(codec, DA7210_IN_GAIN);
+ 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_read(codec, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
+ if (((snd_soc_component_read(component, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
DA7210_AUX1_MIN_VOL_NS) ||
- ((snd_soc_read(codec, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
+ ((snd_soc_component_read(component, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
DA7210_AUX1_MIN_VOL_NS))
goto err;
}
@@ -760,19 +755,19 @@ static int da7210_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 da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u32 dai_cfg1;
u32 fs, sysclk;
/* set DAI source to Left and Right ADC */
- snd_soc_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 */
- snd_soc_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 & snd_soc_read(codec, DA7210_DAI_CFG1);
+ dai_cfg1 = 0xFC & snd_soc_component_read(component, DA7210_DAI_CFG1);
switch (params_width(params)) {
case 16:
@@ -791,7 +786,7 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
switch (params_rate(params)) {
case 8000:
@@ -839,17 +834,17 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
+ 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_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
if (!da7210->master) {
/* PLL slave mode, also enable SRM */
- snd_soc_update_bits(codec, DA7210_PLL,
+ snd_soc_component_update_bits(component, DA7210_PLL,
(DA7210_MCLK_SRM_EN |
DA7210_MCLK_DET_EN),
(DA7210_MCLK_SRM_EN |
@@ -857,13 +852,13 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
} else {
/* PLL bypass mode, enable PLL bypass and Auto Detection */
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_MCLK_DET_EN,
DA7210_MCLK_DET_EN);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ 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;
@@ -874,24 +869,24 @@ 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 da7210_priv *da7210 = snd_soc_codec_get_drvdata(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 & snd_soc_read(codec, DA7210_DAI_CFG1);
- dai_cfg3 = 0xfc & snd_soc_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_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
- (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7210->master = 1;
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7210->master = 0;
dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
break;
@@ -923,21 +918,21 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
*/
dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
- snd_soc_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;
}
-static int da7210_mute(struct snd_soc_dai *dai, int mute)
+static int da7210_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 mute_reg = snd_soc_read(codec, DA7210_DAC_HPF) & 0xFB;
+ struct snd_soc_component *component = dai->component;
+ u8 mute_reg = snd_soc_component_read(component, DA7210_DAC_HPF) & 0xFB;
if (mute)
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg | 0x4);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg | 0x4);
else
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg);
return 0;
}
@@ -947,8 +942,8 @@ static int da7210_mute(struct snd_soc_dai *dai, int mute)
static int da7210_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 da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -976,20 +971,22 @@ static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/**
* da7210_set_dai_pll :Configure the codec PLL
- * @param codec_dai : pointer to codec DAI
- * @param pll_id : da7210 has only one pll, so pll_id is always zero
- * @param fref : MCLK frequency, should be < 20MHz
- * @param fout : FsDM value, Refer page 44 & 45 of datasheet
- * @return int : Zero for success, negative error code for error
+ * @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_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1014,18 +1011,18 @@ static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto err;
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
/* Write PLL dividers */
- snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1);
- snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3,
+ 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_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN,
DA7210_SC_MST_EN);
return 0;
err:
@@ -1039,7 +1036,8 @@ static const struct snd_soc_dai_ops da7210_dai_ops = {
.set_fmt = da7210_set_dai_fmt,
.set_sysclk = da7210_set_dai_sysclk,
.set_pll = da7210_set_dai_pll,
- .digital_mute = da7210_mute,
+ .mute_stream = da7210_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da7210_dai = {
@@ -1061,56 +1059,56 @@ 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);
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
da7210->mclk_rate = 0; /* This will be set from set_sysclk() */
da7210->master = 0; /* This will be set from set_fmt() */
/* Enable internal regulator & bias current */
- snd_soc_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
+ snd_soc_component_write(component, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
/*
* ADC settings
*/
/* Enable Left & Right MIC PGA and Mic Bias */
- snd_soc_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
- snd_soc_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 */
- snd_soc_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
- snd_soc_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 */
- snd_soc_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 */
- snd_soc_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 */
- snd_soc_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
- snd_soc_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 */
- snd_soc_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);
/* Enable ramp mode for DAC gain update */
- snd_soc_write(codec, DA7210_SOFTMUTE, DA7210_RAMP_EN);
+ snd_soc_component_write(component, DA7210_SOFTMUTE, DA7210_RAMP_EN);
/*
* For DA7210 codec, there are two ways to enable/disable analog IOs
@@ -1138,43 +1136,43 @@ static int da7210_probe(struct snd_soc_codec *codec)
*/
/* Enable Line out amplifiers */
- snd_soc_write(codec, DA7210_OUT1_L, DA7210_OUT1_L_EN);
- snd_soc_write(codec, DA7210_OUT1_R, DA7210_OUT1_R_EN);
- snd_soc_write(codec, DA7210_OUT2, DA7210_OUT2_EN |
+ 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_write(codec, DA7210_AUX1_L, DA7210_AUX1_L_EN);
- snd_soc_write(codec, DA7210_AUX1_R, DA7210_AUX1_R_EN);
+ 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_write(codec, DA7210_AUX2, DA7210_AUX2_EN);
+ snd_soc_component_write(component, DA7210_AUX2, DA7210_AUX2_EN);
/* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
- snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+ snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
DA7210_PLL_BYP);
/* Diable PLL and bypass it */
- snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+ snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000);
/* Activate all enabled subsystem */
- snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+ snd_soc_component_write(component, DA7210_STARTUP1, DA7210_SC_MST_EN);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_da7210 = {
+static const struct snd_soc_component_driver soc_component_dev_da7210 = {
.probe = da7210_probe,
-
- .component_driver = {
- .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),
- },
+ .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 IS_ENABLED(CONFIG_I2C)
@@ -1207,8 +1205,7 @@ static const struct regmap_config da7210_regmap_config_i2c = {
.cache_type = REGCACHE_RBTREE,
};
-static int da7210_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7210_i2c_probe(struct i2c_client *i2c)
{
struct da7210_priv *da7210;
int ret;
@@ -1232,22 +1229,16 @@ static int da7210_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
if (ret < 0)
- dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int da7210_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7210_i2c_id[] = {
- { "da7210", 0 },
+ { "da7210" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
@@ -1258,7 +1249,6 @@ static struct i2c_driver da7210_i2c_driver = {
.name = "da7210",
},
.probe = da7210_i2c_probe,
- .remove = da7210_i2c_remove,
.id_table = da7210_i2c_id,
};
#endif
@@ -1325,24 +1315,17 @@ static int da7210_spi_probe(struct spi_device *spi)
if (ret != 0)
dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
return ret;
}
-static int da7210_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver da7210_spi_driver = {
.driver = {
.name = "da7210",
},
.probe = da7210_spi_probe,
- .remove = da7210_spi_remove
};
#endif
@@ -1351,6 +1334,8 @@ static int __init da7210_modinit(void)
int ret = 0;
#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);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index b2b4e90fc02a..ae89260ca215 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA7213 ALSA SoC Codec Driver
*
@@ -5,15 +6,10 @@
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
* Based on DA9055 ALSA SoC codec driver.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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/acpi.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/property.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -23,6 +19,8 @@
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/units.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -58,6 +56,7 @@ 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);
+static const DECLARE_TLV_DB_SCALE(da7213_tonegen_gain_tlv, -4500, 300, 0);
/* ADC and DAC voice mode (8kHz) high pass cutoff value */
static const char * const da7213_voice_hpf_corner_txt[] = {
@@ -89,6 +88,23 @@ static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
DA7213_AUDIO_HPF_CORNER_SHIFT,
da7213_audio_hpf_corner_txt);
+static const char * const da7213_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7213_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG1, DA7213_DTMF_REG_SHIFT,
+ DA7213_DTMF_REG_MAX, da7213_tonegen_dtmf_key_txt);
+
+static const char * const da7213_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "Sum"
+};
+
+static const struct soc_enum da7213_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG2, DA7213_SWG_SEL_SHIFT,
+ DA7213_SWG_SEL_MAX, da7213_tonegen_swg_sel_txt);
+
/* Gain ramping rate value */
static const char * const da7213_gain_ramp_rate_txt[] = {
"nominal rate * 8", "nominal rate * 16", "nominal rate / 16",
@@ -194,7 +210,65 @@ static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
* Control Functions
*/
-static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+/* Locked Kcontrol calls */
+static int da7213_volsw_locked_get(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_volsw_locked_put(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_get(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_put(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+/* ALC */
+static int da7213_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -203,17 +277,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val);
+ 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_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ 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_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -221,17 +295,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
return sum / DA7213_ALC_AVG_ITERATIONS;
}
-static void da7213_alc_calib_man(struct snd_soc_codec *codec)
+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(codec,
+ avg_left_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da7213_get_alc_data(codec,
+ avg_right_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -239,122 +313,122 @@ static void da7213_alc_calib_man(struct snd_soc_codec *codec)
offset_r = -avg_right_data;
reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_L, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_U_L, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_M_R, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
/* Enable analog/digital gain mode & offset cancellation */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_codec *codec)
+static void da7213_alc_calib_auto(struct snd_soc_component *component)
{
u8 alc_ctrl1;
/* Begin auto calibration and wait for completion */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
DA7213_ALC_AUTO_CALIB_EN);
do {
- alc_ctrl1 = snd_soc_read(codec, DA7213_ALC_CTRL1);
+ 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(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed with overflow\n");
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_codec *codec)
+static void da7213_alc_calib(struct snd_soc_component *component)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, DA7213_ADC_L_CTRL);
- adc_r_ctrl = snd_soc_read(codec, DA7213_ADC_R_CTRL);
+ 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_read(codec, DA7213_MIXIN_L_SELECT);
- mixin_r_sel = snd_soc_read(codec, DA7213_MIXIN_R_SELECT);
+ 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_read(codec, DA7213_MIC_1_CTRL);
- mic_2_ctrl = snd_soc_read(codec, DA7213_MIC_2_CTRL);
+ 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_update_bits(codec, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
/* Enable MIC paths */
- snd_soc_update_bits(codec, DA7213_MIXIN_L_SELECT,
+ 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_update_bits(codec, DA7213_MIXIN_R_SELECT,
+ 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_update_bits(codec, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
+ snd_soc_component_update_bits(component, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_MIC_2_CTRL, 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(codec);
+ da7213_alc_calib_auto(component);
else
- da7213_alc_calib_man(codec);
+ da7213_alc_calib_man(component);
/* Restore MIXIN_L/R_SELECT registers to their original states */
- snd_soc_write(codec, DA7213_MIXIN_L_SELECT, mixin_l_sel);
- snd_soc_write(codec, DA7213_MIXIN_R_SELECT, mixin_r_sel);
+ 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_write(codec, DA7213_ADC_L_CTRL, adc_l_ctrl);
- snd_soc_write(codec, DA7213_ADC_R_CTRL, adc_r_ctrl);
+ 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_write(codec, DA7213_MIC_1_CTRL, mic_1_ctrl);
- snd_soc_write(codec, DA7213_MIC_2_CTRL, mic_2_ctrl);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ da7213_alc_calib(component);
return ret;
}
@@ -362,14 +436,14 @@ static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ da7213_alc_calib(component);
da7213->alc_en = true;
}
} else {
@@ -379,6 +453,64 @@ static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
return snd_soc_put_volsw(kcontrol, ucontrol);
}
+/* ToneGen */
+static int da7213_tonegen_freq_get(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);
+ 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(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7213->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 da7213_tonegen_freq_put(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);
+ 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(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old));
+ if (ret == 0 && (val_old != val_new))
+ ret = regmap_raw_write(da7213->regmap, reg,
+ &val_new, sizeof(val_new));
+ mutex_unlock(&da7213->ctrl_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
+}
/*
* KControls
@@ -480,6 +612,37 @@ static const struct snd_kcontrol_new da7213_snd_controls[] = {
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),
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7213_TONE_GEN_CFG2,
+ DA7213_TONE_GEN_GAIN_SHIFT, DA7213_TONE_GEN_GAIN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put, da7213_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7213_tonegen_dtmf_key,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7213_TONE_GEN_CFG1,
+ DA7213_DTMF_EN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE_EXT("ToneGen Start", DA7213_TONE_GEN_CFG1,
+ DA7213_START_STOPN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7213_tonegen_swg_sel,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7213_TONE_GEN_FREQ1_L,
+ DA7213_FREQ1_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7213_TONE_GEN_FREQ2_L,
+ DA7213_FREQ2_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7213_TONE_GEN_ON_PER,
+ DA7213_BEEP_ON_PER_SHIFT, DA7213_BEEP_ON_OFF_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7213_TONE_GEN_OFF_PER,
+ DA7213_BEEP_OFF_PER_SHIFT, DA7213_BEEP_ON_OFF_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,
@@ -735,8 +898,8 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
static int da7213_dai_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -745,30 +908,30 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
/* Enable DAI clks for master mode */
if (da7213->master)
- snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE,
+ 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_update_bits(codec, DA7213_PC_COUNT,
+ 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_read(codec, DA7213_PLL_CTRL);
+ 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_write(codec, 0xF0, 0x8B);
- snd_soc_write(codec, 0xF2, 0x03);
- snd_soc_write(codec, 0xF0, 0x00);
+ 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_read(codec, DA7213_PLL_STATUS);
- if (pll_status & DA7219_PLL_SRM_LOCK) {
+ pll_status = snd_soc_component_read(component, DA7213_PLL_STATUS);
+ if (pll_status & DA7213_PLL_SRM_LOCK) {
srm_lock = true;
} else {
++i;
@@ -777,26 +940,26 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w,
} while ((i < DA7213_SRM_CHECK_RETRIES) && (!srm_lock));
if (!srm_lock)
- dev_warn(codec->dev, "SRM failed to lock\n");
+ 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_read(codec, DA7213_PLL_CTRL);
+ pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL);
if (pll_ctrl & DA7213_PLL_32K_MODE) {
- snd_soc_write(codec, 0xF0, 0x8B);
- snd_soc_write(codec, 0xF2, 0x01);
- snd_soc_write(codec, 0xF0, 0x00);
+ 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_update_bits(codec, DA7213_PC_COUNT,
+ 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_update_bits(codec, DA7213_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
DA7213_DAI_CLK_EN_MASK, 0);
return 0;
default:
@@ -811,6 +974,11 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
/*
+ * Power Supply
+ */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDMIC", 0, 0),
+
+ /*
* Input & Output
*/
@@ -936,6 +1104,9 @@ 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"},
@@ -1140,9 +1311,9 @@ static bool da7213_volatile_register(struct device *dev, unsigned int reg)
case DA7213_ALC_OFFSET_AUTO_M_R:
case DA7213_ALC_OFFSET_AUTO_U_R:
case DA7213_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1150,14 +1321,33 @@ static int da7213_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;
+ 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;
@@ -1176,58 +1366,71 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
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_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK,
- dai_ctrl);
- snd_soc_write(codec, DA7213_SR, fs);
+ 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_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7213->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7213->master = false;
break;
default:
@@ -1256,8 +1459,8 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
break;
- case SND_SOC_DAI_FORMAT_DSP_A:
- case SND_SOC_DAI_FORMAT_DSP_B:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
/* The bclk is inverted wrt ASoC conventions */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
@@ -1284,19 +1487,24 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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 */
+ case SND_SOC_DAIFMT_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 */
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
break;
default:
return -EINVAL;
@@ -1305,27 +1513,30 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default only 64 BCLK per WCLK is supported */
dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
- snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode);
- snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
+ 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_write(codec, DA7213_DAI_OFFSET, dai_offset);
+ snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset);
return 0;
}
-static int da7213_mute(struct snd_soc_dai *dai, int mute)
+static int da7213_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, 0);
}
@@ -1335,34 +1546,38 @@ static int da7213_mute(struct snd_soc_dai *dai, int mute)
#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_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
+static int da7213_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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(codec_dai->dev, "Unsupported MCLK value %d\n",
+ /* Maybe audio stream is closing. */
+ if (freq == 0)
+ return 0;
+
+ if (((freq < da7213->fin_min_rate) && (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_update_bits(codec, DA7213_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
DA7213_PLL_MCLK_SQR_EN, 0);
break;
case DA7213_CLKSRC_MCLK_SQR:
- snd_soc_update_bits(codec, DA7213_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
DA7213_PLL_MCLK_SQR_EN,
DA7213_PLL_MCLK_SQR_EN);
break;
default:
- dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ dev_err(component->dev, "Unknown clock source %d\n", clk_id);
return -EINVAL;
}
@@ -1372,7 +1587,7 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
freq = clk_round_rate(da7213->mclk, freq);
ret = clk_set_rate(da7213->mclk, freq);
if (ret) {
- dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ dev_err(component->dev, "Failed to set clock rate %d\n",
freq);
return ret;
}
@@ -1384,11 +1599,11 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */
-static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- int source, unsigned int fref, unsigned int fout)
+static int _da7213_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source,
+ unsigned int fref, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1398,7 +1613,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Workout input divider based on MCLK rate */
if (da7213->mclk_rate == 32768) {
if (!da7213->master) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"32KHz only valid if codec is clock master\n");
return -EINVAL;
}
@@ -1411,7 +1626,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
} else {
if (da7213->mclk_rate < 5000000) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PLL input clock %d below valid range\n",
da7213->mclk_rate);
return -EINVAL;
@@ -1428,7 +1643,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PLL input clock %d above valid range\n",
da7213->mclk_rate);
return -EINVAL;
@@ -1441,7 +1656,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Configure PLL */
switch (source) {
case DA7213_SYSCLK_MCLK:
- snd_soc_update_bits(codec, DA7213_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
DA7213_PLL_INDIV_MASK |
DA7213_PLL_MODE_MASK, pll_ctrl);
return 0;
@@ -1453,7 +1668,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
break;
case DA7213_SYSCLK_PLL_32KHZ:
if (da7213->mclk_rate != 32768) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"32KHz mode only valid with 32KHz MCLK\n");
return -EINVAL;
}
@@ -1462,7 +1677,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
fout = DA7213_PLL_FREQ_OUT_94310400;
break;
default:
- dev_err(codec->dev, "Invalid PLL config\n");
+ dev_err(component->dev, "Invalid PLL config\n");
return -EINVAL;
}
@@ -1474,34 +1689,61 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7213_BYTE_MASK;
/* Write PLL dividers */
- snd_soc_write(codec, DA7213_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7213_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7213_PLL_INTEGER, pll_integer);
+ 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_update_bits(codec, DA7213_PLL_CTRL,
+ 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_write(codec, 0xF0, 0x8B);
- snd_soc_write(codec, 0xF1, 0x03);
- snd_soc_write(codec, 0xF1, 0x01);
- snd_soc_write(codec, 0xF0, 0x00);
+ 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);
+}
+
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 da7213_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
/* DAI operations */
static const struct snd_soc_dai_ops da7213_dai_ops = {
.hw_params = da7213_hw_params,
.set_fmt = da7213_set_dai_fmt,
- .set_sysclk = da7213_set_dai_sysclk,
- .set_pll = da7213_set_dai_pll,
- .digital_mute = da7213_mute,
+ .mute_stream = da7213_mute,
+ .no_capture_mute = 1,
+ .auto_selectable_formats = &da7213_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver da7213_dai = {
@@ -1523,13 +1765,57 @@ static struct snd_soc_dai_driver da7213_dai = {
.formats = DA7213_FORMATS,
},
.ops = &da7213_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7213_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1537,42 +1823,50 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/* Enable MCLK for transition to ON state */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
+ 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(codec->dev,
+ 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA7213_REFERENCES,
+ 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)
+ 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_update_bits(codec, DA7213_REFERENCES,
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
break;
}
return 0;
}
+#define DA7213_FIN_MIN_RATE (5 * MEGA)
+#define DA7212_FIN_MIN_RATE (2 * MEGA)
+
#if defined(CONFIG_OF)
/* DT */
static const struct of_device_id da7213_of_match[] = {
- { .compatible = "dlg,da7213", },
+ { .compatible = "dlg,da7212", .data = (void *)DA7212_FIN_MIN_RATE },
+ { .compatible = "dlg,da7213", .data = (void *)DA7213_FIN_MIN_RATE },
{ }
};
MODULE_DEVICE_TABLE(of, da7213_of_match);
@@ -1580,15 +1874,15 @@ MODULE_DEVICE_TABLE(of, da7213_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id da7213_acpi_match[] = {
- { "DLGS7212", 0},
- { "DLGS7213", 0},
+ { "DLGS7212", DA7212_FIN_MIN_RATE },
+ { "DLGS7213", DA7213_FIN_MIN_RATE },
{ },
};
MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
#endif
static enum da7213_micbias_voltage
- da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+ da7213_of_micbias_lvl(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1600:
@@ -1600,39 +1894,39 @@ static enum da7213_micbias_voltage
case 3000:
return DA7213_MICBIAS_3_0V;
default:
- dev_warn(codec->dev, "Invalid micbias level\n");
+ 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_codec *codec, const char *str)
+ 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(codec->dev, "Invalid DMIC data select type\n");
+ 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_codec *codec, const char *str)
+ 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(codec->dev, "Invalid DMIC sample phase\n");
+ 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_codec *codec, u32 val)
+ da7213_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1500000:
@@ -1640,88 +1934,89 @@ static enum da7213_dmic_clk_rate
case 3000000:
return DA7213_DMIC_CLK_3_0MHZ;
default:
- dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+ 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_codec *codec)
+ *da7213_fw_to_pdata(struct snd_soc_component *component)
{
- struct device *dev = codec->dev;
+ struct device *dev = component->dev;
struct da7213_platform_data *pdata;
const char *fw_str;
u32 fw_val32;
- pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ 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(codec, fw_val32);
+ 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(codec, fw_val32);
+ 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(codec, 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(codec, fw_str);
+ 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(codec, fw_val32);
+ 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_codec *codec)
+static int da7213_probe(struct snd_soc_component *component)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
DA7213_PC_FREERUN_MASK);
/* Enable all Gain Ramps */
- snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
/*
@@ -1732,28 +2027,28 @@ static int da7213_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_L_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_R_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ 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(codec->dev);
+ da7213->pdata = dev_get_platdata(component->dev);
if (!da7213->pdata)
- da7213->pdata = da7213_fw_to_pdata(codec);
+ da7213->pdata = da7213_fw_to_pdata(component);
/* Set platform data values */
if (da7213->pdata) {
@@ -1779,7 +2074,7 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_MICBIAS2_LEVEL_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MICBIAS_CTRL,
DA7213_MICBIAS1_LEVEL_MASK |
DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl);
@@ -1805,60 +2100,102 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_CLK_RATE_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MIC_CONFIG,
+ 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(codec->dev, "mclk");
- if (IS_ERR(da7213->mclk)) {
- if (PTR_ERR(da7213->mclk) != -ENOENT)
- return PTR_ERR(da7213->mclk);
- else
- da7213->mclk = NULL;
- }
+ da7213->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(da7213->mclk))
+ return PTR_ERR(da7213->mclk);
+ if (da7213->mclk)
+ /* 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;
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_da7213 = {
+static const struct snd_soc_component_driver soc_component_dev_da7213 = {
.probe = da7213_probe,
.set_bias_level = da7213_set_bias_level,
-
- .component_driver = {
- .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),
- },
+ .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,
+ .max_register = DA7213_TONE_GEN_OFF_PER,
.reg_defaults = da7213_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults),
.volatile_reg = da7213_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
-static int da7213_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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 ret;
+ int i, ret;
da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL);
if (!da7213)
return -ENOMEM;
+ da7213->fin_min_rate = (uintptr_t)i2c_get_match_data(i2c);
+ if (!da7213->fin_min_rate)
+ return -EINVAL;
+
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);
@@ -1866,23 +2203,55 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7213, &da7213_dai, 1);
+ mutex_init(&da7213->ctrl_lock);
+
+ 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 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da7213 component: %d\n",
ret);
}
return ret;
}
-static int da7213_remove(struct i2c_client *client)
+static void da7213_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
+ pm_runtime_disable(&i2c->dev);
+}
+
+static int 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 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);
+ return regcache_sync(da7213->regmap);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(da7213_pm, da7213_runtime_suspend,
+ da7213_runtime_resume, NULL);
+
static const struct i2c_device_id da7213_i2c_id[] = {
- { "da7213", 0 },
+ { "da7213" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
@@ -1893,9 +2262,10 @@ static struct i2c_driver da7213_i2c_driver = {
.name = "da7213",
.of_match_table = of_match_ptr(da7213_of_match),
.acpi_match_table = ACPI_PTR(da7213_acpi_match),
+ .pm = pm_ptr(&da7213_pm),
},
.probe = da7213_i2c_probe,
- .remove = da7213_remove,
+ .remove = da7213_i2c_remove,
.id_table = da7213_i2c_id,
};
@@ -1903,4 +2273,5 @@ module_i2c_driver(da7213_i2c_driver);
MODULE_DESCRIPTION("ASoC DA7213 Codec 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/da7213.h b/sound/soc/codecs/da7213.h
index 5a78dba1dcb5..b9ab791d6b88 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -1,13 +1,11 @@
+/* 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>
- *
- * 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.
+ * Author: David Rau <David.Rau.opensource@dm.renesas.com>
*/
#ifndef _DA7213_H
@@ -15,6 +13,7 @@
#include <linux/clk.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/da7213.h>
/*
@@ -137,13 +136,24 @@
#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
#define DA7213_DAC_NG_CTRL 0xB2
+#define DA7213_TONE_GEN_CFG1 0xB4
+#define DA7213_TONE_GEN_CFG2 0xB5
+#define DA7213_TONE_GEN_CYCLES 0xB6
+#define DA7213_TONE_GEN_FREQ1_L 0xB7
+#define DA7213_TONE_GEN_FREQ1_U 0xB8
+#define DA7213_TONE_GEN_FREQ2_L 0xB9
+#define DA7213_TONE_GEN_FREQ2_U 0xBA
+#define DA7213_TONE_GEN_ON_PER 0xBB
+#define DA7213_TONE_GEN_OFF_PER 0xBC
/*
* Bit fields
*/
+#define DA7213_SWITCH_EN_MAX 0x1
+
/* DA7213_PLL_STATUS = 0x03 */
-#define DA7219_PLL_SRM_LOCK (0x1 << 1)
+#define DA7213_PLL_SRM_LOCK (0x1 << 1)
/* DA7213_SR = 0x22 */
#define DA7213_SR_8000 (0x1 << 0)
@@ -181,7 +191,9 @@
#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 */
@@ -195,6 +207,8 @@
#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 */
@@ -482,6 +496,55 @@
#define DA7213_DAC_NG_EN_SHIFT 7
#define DA7213_DAC_NG_EN_MAX 0x1
+/* DA7213_TONE_GEN_CFG1 = 0xB4 */
+#define DA7213_DTMF_REG_SHIFT 0
+#define DA7213_DTMF_REG_MASK (0xF << 0)
+#define DA7213_DTMF_REG_MAX 16
+#define DA7213_DTMF_EN_SHIFT 4
+#define DA7213_DTMF_EN_MASK (0x1 << 4)
+#define DA7213_START_STOPN_SHIFT 7
+#define DA7213_START_STOPN_MASK (0x1 << 7)
+
+/* DA7213_TONE_GEN_CFG2 = 0xB5 */
+#define DA7213_SWG_SEL_SHIFT 0
+#define DA7213_SWG_SEL_MASK (0x3 << 0)
+#define DA7213_SWG_SEL_MAX 4
+#define DA7213_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7213_TONE_GEN_GAIN_SHIFT 4
+#define DA7213_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7213_TONE_GEN_GAIN_MAX 0xF
+#define DA7213_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7213_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7213_TONE_GEN_CYCLES = 0xB6 */
+#define DA7213_BEEP_CYCLES_SHIFT 0
+#define DA7213_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7213_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7213_FREQ1_L_SHIFT 0
+#define DA7213_FREQ1_L_MASK (0xFF << 0)
+#define DA7213_FREQ_MAX 0xFFFF
+
+/* DA7213_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7213_FREQ1_U_SHIFT 0
+#define DA7213_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7213_FREQ2_L_SHIFT 0
+#define DA7213_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7213_FREQ2_U_SHIFT 0
+#define DA7213_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_ON_PER = 0xBB */
+#define DA7213_BEEP_ON_PER_SHIFT 0
+#define DA7213_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7213_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7213_TONE_GEN_OFF_PER = 0xBC */
+#define DA7213_BEEP_OFF_PER_SHIFT 0
+#define DA7213_BEEP_OFF_PER_MASK (0x3F << 0)
/*
* General defines
@@ -522,16 +585,29 @@ enum da7213_sys_clk {
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 mutex ctrl_lock;
+ struct regulator_bulk_data supplies[DA7213_NUM_SUPPLIES];
struct clk *mclk;
unsigned int mclk_rate;
+ unsigned int out_rate;
+ unsigned int fin_min_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
index 96c644a15b11..a7539e1a1893 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -1,19 +1,15 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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/clk.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
@@ -291,7 +287,7 @@ static const struct soc_enum da7218_cp_tau_delay =
*/
/* ALC */
-static void da7218_alc_calib(struct snd_soc_codec *codec)
+static void da7218_alc_calib(struct snd_soc_component *component)
{
u8 mic_1_ctrl, mic_2_ctrl;
u8 mixin_1_ctrl, mixin_2_ctrl;
@@ -302,59 +298,59 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
bool calibrated = false;
/* Save current state of MIC control registers */
- mic_1_ctrl = snd_soc_read(codec, DA7218_MIC_1_CTRL);
- mic_2_ctrl = snd_soc_read(codec, DA7218_MIC_2_CTRL);
+ 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_read(codec, DA7218_MIXIN_1_CTRL);
- mixin_2_ctrl = snd_soc_read(codec, DA7218_MIXIN_2_CTRL);
+ 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_read(codec, DA7218_IN_1L_FILTER_CTRL);
- in_1r_filt_ctrl = snd_soc_read(codec, DA7218_IN_1R_FILTER_CTRL);
- in_2l_filt_ctrl = snd_soc_read(codec, DA7218_IN_2L_FILTER_CTRL);
- in_2r_filt_ctrl = snd_soc_read(codec, DA7218_IN_2R_FILTER_CTRL);
+ 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_read(codec, DA7218_IN_1_HPF_FILTER_CTRL);
- in_2_hpf_ctrl = snd_soc_read(codec, DA7218_IN_2_HPF_FILTER_CTRL);
+ 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_update_bits(codec, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
DA7218_MIC_1_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, DA7218_MIC_2_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_update_bits(codec, DA7218_MIC_1_CTRL,
+ 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_update_bits(codec, DA7218_MIC_2_CTRL,
+ 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_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ 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_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ 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_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ 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);
@@ -364,16 +360,16 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
* rates above 32KHz the ADC signals will be stopped and will cause
* calibration to lock up.
*/
- snd_soc_update_bits(codec, DA7218_IN_1_HPF_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1_HPF_FILTER_CTRL,
DA7218_IN_1_VOICE_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7218_IN_2_HPF_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2_HPF_FILTER_CTRL,
DA7218_IN_2_VOICE_EN_MASK, 0);
/* Perform auto calibration */
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
DA7218_CALIB_AUTO_EN_MASK);
do {
- calib_ctrl = snd_soc_read(codec, DA7218_CALIB_CTRL);
+ 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,
@@ -386,51 +382,51 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
/* If auto calibration fails, disable DC offset, hybrid ALC */
if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed - %s\n",
(calibrated) ? "overflow" : "timeout");
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL,
DA7218_CALIB_OFFSET_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7218_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7218_ALC_CTRL1,
DA7218_ALC_SYNC_MODE_MASK, 0);
} else {
/* Enable DC offset cancellation */
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ 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_update_bits(codec, DA7218_ALC_CTRL1,
+ 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_write(codec, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl);
- snd_soc_write(codec, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl);
+ 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_write(codec, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl);
+ 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_write(codec, DA7218_MIXIN_1_CTRL, mixin_1_ctrl);
- snd_soc_write(codec, DA7218_MIXIN_2_CTRL, mixin_2_ctrl);
+ 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_write(codec, DA7218_MIC_1_CTRL, mic_1_ctrl);
- snd_soc_write(codec, DA7218_MIC_2_CTRL, mic_2_ctrl);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -440,7 +436,7 @@ static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol,
* make sure calibrated offsets are updated.
*/
if ((ret == 1) && (da7218->alc_en))
- da7218_alc_calib(codec);
+ da7218_alc_calib(component);
return ret;
}
@@ -450,8 +446,8 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *) kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -460,7 +456,7 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
/* Force ALC offset calibration if enabling ALC */
if ((lvalue || rvalue) && (!da7218->alc_en))
- da7218_alc_calib(codec);
+ da7218_alc_calib(component);
/* Update bits to detail which channels are enabled/disabled */
da7218->alc_en &= ~mask;
@@ -473,8 +469,8 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -497,8 +493,8 @@ static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -517,8 +513,8 @@ static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -537,15 +533,15 @@ static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
* power the path (IN_FILTER widget events). This handling avoids
* unwanted level detect events.
*/
- return snd_soc_write(codec, mixer_ctrl->reg,
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -564,8 +560,8 @@ static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol,
static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -589,8 +585,8 @@ static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -617,8 +613,8 @@ static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
}
/* Make sure at least out filter1 enabled to allow programming */
- out_filt1l = snd_soc_read(codec, DA7218_OUT_1L_FILTER_CTRL);
- snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL,
+ 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) {
@@ -628,7 +624,7 @@ static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
}
/* Restore filter to previous setting */
- snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
+ snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
return 0;
}
@@ -1349,8 +1345,8 @@ static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = {
static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -1389,7 +1385,7 @@ static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
}
/* Enable configured level detection paths */
- snd_soc_write(codec, DA7218_LVL_DET_CTRL,
+ snd_soc_component_write(component, DA7218_LVL_DET_CTRL,
(da7218->in_filt_en & da7218->mic_lvl_det_en));
return 0;
@@ -1398,8 +1394,8 @@ static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
static int da7218_dai_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1408,14 +1404,14 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
if (da7218->master)
/* Enable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7218_DAI_CLK_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_write(codec, DA7218_PLL_REFOSC_CAL,
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
DA7218_PLL_REFOSC_CAL_START_MASK);
- snd_soc_write(codec, DA7218_PLL_REFOSC_CAL,
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
DA7218_PLL_REFOSC_CAL_START_MASK |
DA7218_PLL_REFOSC_CAL_EN_MASK);
@@ -1423,7 +1419,7 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
i = 0;
success = false;
do {
- refosc_cal = snd_soc_read(codec, DA7218_PLL_REFOSC_CAL);
+ refosc_cal = snd_soc_component_read(component, DA7218_PLL_REFOSC_CAL);
if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) {
success = true;
} else {
@@ -1434,15 +1430,15 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
} while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success));
if (!success)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Reference oscillator failed calibration\n");
/* PC synchronised to DAI */
- snd_soc_write(codec, DA7218_PC_COUNT,
+ 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_read(codec, DA7218_PLL_CTRL);
+ pll_ctrl = snd_soc_component_read(component, DA7218_PLL_CTRL);
if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM)
return 0;
@@ -1450,7 +1446,7 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
i = 0;
success = false;
do {
- pll_status = snd_soc_read(codec, DA7218_PLL_STATUS);
+ pll_status = snd_soc_component_read(component, DA7218_PLL_STATUS);
if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) {
success = true;
} else {
@@ -1460,16 +1456,16 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
} while ((i < DA7218_SRM_CHECK_TRIES) && (!success));
if (!success)
- dev_warn(codec->dev, "SRM failed to lock\n");
+ dev_warn(component->dev, "SRM failed to lock\n");
return 0;
case SND_SOC_DAPM_POST_PMD:
/* PC free-running */
- snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+ snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
if (da7218->master)
/* Disable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_CLK_EN_MASK, 0);
return 0;
@@ -1481,8 +1477,8 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
static int da7218_cp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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
@@ -1493,11 +1489,11 @@ static int da7218_cp_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ 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_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
0);
return 0;
default:
@@ -1508,17 +1504,17 @@ static int da7218_cp_event(struct snd_soc_dapm_widget *w,
static int da7218_hp_pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK,
+ 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_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK, 0);
return 0;
default:
return -EINVAL;
@@ -1813,8 +1809,8 @@ static const struct snd_soc_dapm_route da7218_audio_map[] = {
static int da7218_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1828,12 +1824,12 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (clk_id) {
case DA7218_CLKSRC_MCLK_SQR:
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ 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_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_MCLK_SQR_EN_MASK, 0);
break;
default:
@@ -1859,8 +1855,8 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1869,7 +1865,7 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
if (da7218->mclk_rate < 2000000) {
- dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
da7218->mclk_rate);
return -EINVAL;
} else if (da7218->mclk_rate <= 4500000) {
@@ -1888,7 +1884,7 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
da7218->mclk_rate);
return -EINVAL;
}
@@ -1899,7 +1895,7 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
switch (source) {
case DA7218_SYSCLK_MCLK:
pll_ctrl |= DA7218_PLL_MODE_BYPASS;
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_INDIV_MASK |
DA7218_PLL_MODE_MASK, pll_ctrl);
return 0;
@@ -1910,7 +1906,7 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_ctrl |= DA7218_PLL_MODE_SRM;
break;
default:
- dev_err(codec->dev, "Invalid PLL config\n");
+ dev_err(component->dev, "Invalid PLL config\n");
return -EINVAL;
}
@@ -1922,10 +1918,10 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7218_BYTE_MASK;
/* Write PLL config & dividers */
- snd_soc_write(codec, DA7218_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7218_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7218_PLL_INTEGER, pll_integer);
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ 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);
@@ -1934,15 +1930,15 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7218->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7218->master = false;
break;
default:
@@ -2012,8 +2008,8 @@ static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default 64 BCLKs per WCLK is supported */
dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64;
- snd_soc_write(codec, DA7218_DAI_CLK_MODE, dai_clk_mode);
- snd_soc_update_bits(codec, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK,
+ 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;
@@ -2023,16 +2019,16 @@ 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_codec *codec = dai->codec;
+ 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_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ 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_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_BCLKS_PER_WCLK_MASK,
DA7218_DAI_BCLKS_PER_WCLK_64);
return 0;
@@ -2040,14 +2036,14 @@ static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
/* Check we have valid slots */
if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) {
- dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ 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(codec->dev, "Invalid slot offset, max = %d\n",
+ dev_err(component->dev, "Invalid slot offset, max = %d\n",
DA7218_2BYTE_MASK);
return -EINVAL;
}
@@ -2068,18 +2064,18 @@ static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256;
break;
default:
- dev_err(codec->dev, "Invalid frame size\n");
+ dev_err(component->dev, "Invalid frame size\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_BCLKS_PER_WCLK_MASK,
dai_bclks_per_wclk);
- snd_soc_write(codec, DA7218_DAI_OFFSET_LOWER,
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_LOWER,
(rx_mask & DA7218_BYTE_MASK));
- snd_soc_write(codec, DA7218_DAI_OFFSET_UPPER,
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_UPPER,
((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK));
- snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ 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) |
@@ -2092,7 +2088,7 @@ static int da7218_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 dai_ctrl = 0, fs;
unsigned int channels;
@@ -2115,7 +2111,7 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
channels = params_channels(params);
if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Invalid number of channels, only 1 to %d supported\n",
DA7218_DAI_CH_NUM_MAX);
return -EINVAL;
@@ -2160,11 +2156,11 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7218_DAI_CTRL,
+ 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_write(codec, DA7218_SR,
+ snd_soc_component_write(component, DA7218_SR,
(fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT));
return 0;
@@ -2198,9 +2194,9 @@ static struct snd_soc_dai_driver da7218_dai = {
.formats = DA7218_FORMATS,
},
.ops = &da7218_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.symmetric_channels = 1,
- .symmetric_samplebits = 1,
+ .symmetric_sample_bits = 1,
};
@@ -2208,15 +2204,15 @@ static struct snd_soc_dai_driver da7218_dai = {
* HP Detect
*/
-int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
if (da7218->dev_id == DA7217_DEV_ID)
return -EINVAL;
da7218->jack = jack;
- snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
DA7218_HPLDET_JACK_EN_MASK,
jack ? DA7218_HPLDET_JACK_EN_MASK : 0);
@@ -2224,23 +2220,23 @@ int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
}
EXPORT_SYMBOL_GPL(da7218_hpldet);
-static void da7218_micldet_irq(struct snd_soc_codec *codec)
+static void da7218_micldet_irq(struct snd_soc_component *component)
{
char *envp[] = {
"EVENT=MIC_LEVEL_DETECT",
NULL,
};
- kobject_uevent_env(&codec->dev->kobj, KOBJ_CHANGE, envp);
+ kobject_uevent_env(&component->dev->kobj, KOBJ_CHANGE, envp);
}
-static void da7218_hpldet_irq(struct snd_soc_codec *codec)
+static void da7218_hpldet_irq(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 jack_status;
int report;
- jack_status = snd_soc_read(codec, DA7218_EVENT_STATUS);
+ jack_status = snd_soc_component_read(component, DA7218_EVENT_STATUS);
if (jack_status & DA7218_HPLDET_JACK_STS_MASK)
report = SND_JACK_HEADPHONE;
@@ -2256,24 +2252,24 @@ static void da7218_hpldet_irq(struct snd_soc_codec *codec)
static irqreturn_t da7218_irq_thread(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
+ struct snd_soc_component *component = data;
u8 status;
/* Read IRQ status reg */
- status = snd_soc_read(codec, DA7218_EVENT);
+ 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(codec);
+ da7218_micldet_irq(component);
/* HP detect */
if (status & DA7218_HPLDET_JACK_EVENT_MASK)
- da7218_hpldet_irq(codec);
+ da7218_hpldet_irq(component);
/* Clear interrupts */
- snd_soc_write(codec, DA7218_EVENT, status);
+ snd_soc_component_write(component, DA7218_EVENT, status);
return IRQ_HANDLED;
}
@@ -2289,18 +2285,8 @@ static const struct of_device_id da7218_of_match[] = {
};
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_codec *codec, u32 val)
+ da7218_of_micbias_lvl(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1200:
@@ -2322,13 +2308,13 @@ static enum da7218_micbias_voltage
case 3000:
return DA7218_MICBIAS_3_0V;
default:
- dev_warn(codec->dev, "Invalid micbias level");
+ 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_codec *codec, const char *str)
+ 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;
@@ -2337,39 +2323,39 @@ static enum da7218_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) {
return DA7218_MIC_AMP_IN_SEL_SE_N;
} else {
- dev_warn(codec->dev, "Invalid mic input type selection");
+ 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_codec *codec, const char *str)
+ 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(codec->dev, "Invalid DMIC data type selection");
+ 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_codec *codec, const char *str)
+ 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(codec->dev, "Invalid DMIC sample phase");
+ 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_codec *codec, u32 val)
+ da7218_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1500000:
@@ -2377,13 +2363,13 @@ static enum da7218_dmic_clk_rate
case 3000000:
return DA7218_DMIC_CLK_3_0MHZ;
default:
- dev_warn(codec->dev, "Invalid DMIC clock rate");
+ 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_codec *codec, u32 val)
+ da7218_of_jack_rate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 5:
@@ -2403,13 +2389,13 @@ static enum da7218_hpldet_jack_rate
case 640:
return DA7218_HPLDET_JACK_RATE_640US;
default:
- dev_warn(codec->dev, "Invalid jack detect rate");
+ 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_codec *codec, u32 val)
+ da7218_of_jack_debounce(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 0:
@@ -2421,13 +2407,13 @@ static enum da7218_hpldet_jack_debounce
case 4:
return DA7218_HPLDET_JACK_DEBOUNCE_4;
default:
- dev_warn(codec->dev, "Invalid jack debounce");
+ 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_codec *codec, u32 val)
+ da7218_of_jack_thr(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 84:
@@ -2439,76 +2425,76 @@ static enum da7218_hpldet_jack_thr
case 96:
return DA7218_HPLDET_JACK_THR_96PCT;
default:
- dev_warn(codec->dev, "Invalid jack threshold level");
+ 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_codec *codec)
+static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
- struct device_node *np = codec->dev->of_node;
+ 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(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ 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(codec, of_val32);
+ 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(codec, of_val32);
+ 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(codec, of_str);
+ 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(codec, of_str);
+ 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(codec, 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(codec, of_str);
+ 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(codec, of_val32);
+ 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(codec, 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(codec, of_str);
+ 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(codec, of_val32);
+ pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
else
pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
@@ -2522,7 +2508,7 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
if (!hpldet_np)
return pdata;
- hpldet_pdata = devm_kzalloc(codec->dev, sizeof(*hpldet_pdata),
+ hpldet_pdata = devm_kzalloc(component->dev, sizeof(*hpldet_pdata),
GFP_KERNEL);
if (!hpldet_pdata) {
of_node_put(hpldet_np);
@@ -2533,14 +2519,14 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us",
&of_val32) >= 0)
hpldet_pdata->jack_rate =
- da7218_of_jack_rate(codec, of_val32);
+ 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(codec, of_val32);
+ da7218_of_jack_debounce(component, of_val32);
else
hpldet_pdata->jack_debounce =
DA7218_HPLDET_JACK_DEBOUNCE_2;
@@ -2548,7 +2534,7 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct",
&of_val32) >= 0)
hpldet_pdata->jack_thr =
- da7218_of_jack_thr(codec, of_val32);
+ da7218_of_jack_thr(component, of_val32);
else
hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT;
@@ -2572,10 +2558,10 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
* Codec driver functions
*/
-static int da7218_set_bias_level(struct snd_soc_codec *codec,
+static int da7218_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -2583,11 +2569,11 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/* Enable MCLK for transition to ON state */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
+ 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(codec->dev, "Failed to enable mclk\n");
+ dev_err(component->dev, "Failed to enable mclk\n");
return ret;
}
}
@@ -2595,14 +2581,14 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Master bias */
- snd_soc_update_bits(codec, DA7218_REFERENCES,
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK,
DA7218_BIAS_EN_MASK);
/* Internal LDO */
- snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
DA7218_LDO_EN_MASK,
DA7218_LDO_EN_MASK);
} else {
@@ -2615,11 +2601,11 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec,
/* Only disable if jack detection disabled */
if (!da7218->jack) {
/* Internal LDO */
- snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
DA7218_LDO_EN_MASK, 0);
/* Master bias */
- snd_soc_update_bits(codec, DA7218_REFERENCES,
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK, 0);
}
break;
@@ -2634,9 +2620,9 @@ static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = {
[DA7218_SUPPLY_VDDIO] = "VDDIO",
};
-static int da7218_handle_supplies(struct snd_soc_codec *codec)
+static int da7218_handle_supplies(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -2645,10 +2631,10 @@ static int da7218_handle_supplies(struct snd_soc_codec *codec)
for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
da7218->supplies[i].supply = da7218_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, DA7218_NUM_SUPPLIES,
+ ret = devm_regulator_bulk_get(component->dev, DA7218_NUM_SUPPLIES,
da7218->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to get supplies\n");
+ dev_err(component->dev, "Failed to get supplies\n");
return ret;
}
@@ -2656,29 +2642,29 @@ static int da7218_handle_supplies(struct snd_soc_codec *codec)
vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer;
ret = regulator_get_voltage(vddio);
if (ret < 1500000)
- dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ 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(codec->dev, "Failed to enable supplies\n");
+ dev_err(component->dev, "Failed to enable supplies\n");
return ret;
}
/* Ensure device in active mode */
- snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
/* Update IO voltage level range */
- snd_soc_write(codec, DA7218_IO_CTRL, io_voltage_lvl);
+ snd_soc_component_write(component, DA7218_IO_CTRL, io_voltage_lvl);
return 0;
}
-static void da7218_handle_pdata(struct snd_soc_codec *codec)
+static void da7218_handle_pdata(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct da7218_pdata *pdata = da7218->pdata;
if (pdata) {
@@ -2719,14 +2705,14 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_write(codec, DA7218_MICBIAS_CTRL, micbias_lvl);
+ 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_write(codec, DA7218_MIC_1_SELECT,
+ snd_soc_component_write(component, DA7218_MIC_1_SELECT,
pdata->mic1_amp_in_sel);
break;
}
@@ -2735,7 +2721,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
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_write(codec, DA7218_MIC_2_SELECT,
+ snd_soc_component_write(component, DA7218_MIC_2_SELECT,
pdata->mic2_amp_in_sel);
break;
}
@@ -2765,7 +2751,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, DA7218_DMIC_1_CTRL,
+ 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);
@@ -2795,7 +2781,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, DA7218_DMIC_2_CTRL,
+ 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);
@@ -2806,9 +2792,9 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
pdata->hp_diff_single_supply;
if (da7218->hp_single_supply) {
- snd_soc_write(codec, DA7218_HP_DIFF_UNLOCK,
+ snd_soc_component_write(component, DA7218_HP_DIFF_UNLOCK,
DA7218_HP_DIFF_UNLOCK_VAL);
- snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL,
DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK,
DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK);
}
@@ -2857,7 +2843,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
DA7218_HPLDET_JACK_THR_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
DA7218_HPLDET_JACK_RATE_MASK |
DA7218_HPLDET_JACK_DEBOUNCE_MASK |
DA7218_HPLDET_JACK_THR_MASK,
@@ -2873,109 +2859,105 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
if (hpldet_pdata->discharge)
hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK;
- snd_soc_write(codec, DA7218_HPLDET_CTRL, hpldet_cfg);
+ snd_soc_component_write(component, DA7218_HPLDET_CTRL, hpldet_cfg);
}
}
}
-static int da7218_probe(struct snd_soc_codec *codec)
+static int da7218_probe(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
int ret;
/* Regulator configuration */
- ret = da7218_handle_supplies(codec);
+ ret = da7218_handle_supplies(component);
if (ret)
return ret;
/* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7218->pdata = da7218_of_to_pdata(codec);
+ if (component->dev->of_node)
+ da7218->pdata = da7218_of_to_pdata(component);
else
- da7218->pdata = dev_get_platdata(codec->dev);
+ da7218->pdata = dev_get_platdata(component->dev);
- da7218_handle_pdata(codec);
+ da7218_handle_pdata(component);
/* Check if MCLK provided, if not the clock is NULL */
- da7218->mclk = devm_clk_get(codec->dev, "mclk");
+ da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
if (IS_ERR(da7218->mclk)) {
- if (PTR_ERR(da7218->mclk) != -ENOENT) {
- ret = PTR_ERR(da7218->mclk);
- goto err_disable_reg;
- } else {
- da7218->mclk = NULL;
- }
+ ret = PTR_ERR(da7218->mclk);
+ goto err_disable_reg;
}
/* Default PC to free-running */
- snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+ 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_write(codec, DA7218_DROUTING_OUTFILT_1L, 0);
- snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1R, 0);
+ 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_update_bits(codec, DA7218_CP_CTRL,
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL,
DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0);
/* Default gain ramping */
- snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ 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_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ 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_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_DGS_GAIN_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DGS_GAIN_CTRL,
DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_OUT_1L_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_OUT_1R_FILTER_CTRL,
+ 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_update_bits(codec, DA7218_HP_L_CTRL,
+ 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_update_bits(codec, DA7218_HP_R_CTRL,
+ 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_write(codec, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK);
+ 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_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ 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_write(codec, DA7218_EVENT_MASK,
+ snd_soc_component_write(component, DA7218_EVENT_MASK,
DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK);
}
if (da7218->irq) {
- ret = devm_request_threaded_irq(codec->dev, da7218->irq, NULL,
+ ret = devm_request_threaded_irq(component->dev, da7218->irq, NULL,
da7218_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "da7218", codec);
+ "da7218", component);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
+ dev_err(component->dev, "Failed to request IRQ %d: %d\n",
da7218->irq, ret);
goto err_disable_reg;
}
@@ -2990,39 +2972,37 @@ err_disable_reg:
return ret;
}
-static int da7218_remove(struct snd_soc_codec *codec)
+static void da7218_remove(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
-
- return 0;
}
#ifdef CONFIG_PM
-static int da7218_suspend(struct snd_soc_codec *codec)
+static int da7218_suspend(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
- da7218_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ da7218_set_bias_level(component, SND_SOC_BIAS_OFF);
/* Put device into standby mode if jack detection disabled */
if (!da7218->jack)
- snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, 0);
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, 0);
return 0;
}
-static int da7218_resume(struct snd_soc_codec *codec)
+static int da7218_resume(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, DA7218_SYSTEM_ACTIVE,
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE,
DA7218_SYSTEM_ACTIVE_MASK);
- da7218_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ da7218_set_bias_level(component, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -3031,21 +3011,21 @@ static int da7218_resume(struct snd_soc_codec *codec)
#define da7218_resume NULL
#endif
-static const struct snd_soc_codec_driver soc_codec_dev_da7218 = {
+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,
-
- .component_driver = {
- .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),
- },
+ .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,
};
@@ -3053,7 +3033,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_da7218 = {
* Regmap configs
*/
-static struct reg_default da7218_reg_defaults[] = {
+static const struct reg_default da7218_reg_defaults[] = {
{ DA7218_SYSTEM_ACTIVE, 0x00 },
{ DA7218_CIF_CTRL, 0x00 },
{ DA7218_SPARE1, 0x00 },
@@ -3263,8 +3243,7 @@ static const struct regmap_config da7218_regmap_config = {
* I2C layer
*/
-static int da7218_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7218_i2c_probe(struct i2c_client *i2c)
{
struct da7218_priv *da7218;
int ret;
@@ -3275,10 +3254,7 @@ static int da7218_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, da7218);
- if (i2c->dev.of_node)
- da7218->dev_id = da7218_of_get_id(&i2c->dev);
- else
- da7218->dev_id = id->driver_data;
+ da7218->dev_id = (uintptr_t)i2c_get_match_data(i2c);
if ((da7218->dev_id != DA7217_DEV_ID) &&
(da7218->dev_id != DA7218_DEV_ID)) {
@@ -3295,21 +3271,15 @@ static int da7218_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7218, &da7218_dai, 1);
+ 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 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da7218 component: %d\n",
ret);
}
return ret;
}
-static int da7218_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7218_i2c_id[] = {
{ "da7217", DA7217_DEV_ID },
{ "da7218", DA7218_DEV_ID },
@@ -3320,10 +3290,9 @@ MODULE_DEVICE_TABLE(i2c, da7218_i2c_id);
static struct i2c_driver da7218_i2c_driver = {
.driver = {
.name = "da7218",
- .of_match_table = of_match_ptr(da7218_of_match),
+ .of_match_table = da7218_of_match,
},
.probe = da7218_i2c_probe,
- .remove = da7218_i2c_remove,
.id_table = da7218_i2c_id,
};
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
index 4f7ec21069a4..7f6a4aea2c7a 100644
--- a/sound/soc/codecs/da7218.h
+++ b/sound/soc/codecs/da7218.h
@@ -1,14 +1,10 @@
+/* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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 _DA7218_H
@@ -1373,7 +1369,7 @@ enum da7218_sys_clk {
};
enum da7218_dev_id {
- DA7217_DEV_ID = 0,
+ DA7217_DEV_ID = 1,
DA7218_DEV_ID,
};
@@ -1410,6 +1406,6 @@ struct da7218_priv {
};
/* HP detect control */
-int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+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
index 1d1d10dd92ae..15e5e3eb592b 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -1,14 +1,10 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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>
@@ -32,9 +28,9 @@
* Detection control
*/
-void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
da7219->aad->jack = jack;
da7219->aad->jack_inserted = false;
@@ -43,11 +39,10 @@ void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
/* Enable/Disable jack detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_ACCDET_EN_MASK,
(jack ? DA7219_ACCDET_EN_MASK : 0));
}
-EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
/*
* Button/HPTest work
@@ -57,17 +52,18 @@ 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_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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;
/* Drive headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_OE_MASK,
DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK,
DA7219_HP_R_AMP_OE_MASK);
@@ -76,7 +72,7 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
snd_soc_dapm_sync(dapm);
do {
- statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ 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)
@@ -84,7 +80,9 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
} while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
- dev_warn(codec->dev, "Mic bias status check timed out");
+ 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
@@ -92,16 +90,16 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
*/
if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
/* Pulse higher level voltage */
- micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
- snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+ 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_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+ snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_ctrl);
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_BUTTON_CONFIG_MASK,
da7219_aad->btn_cfg);
}
@@ -110,13 +108,13 @@ 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_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
- u16 tonegen_freq_hptest;
+ __le16 tonegen_freq_hptest;
u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8;
- int report = 0, ret = 0;
+ int report = 0, ret;
/* Lock DAPM, Kcontrols affected by this test and the PLL */
snd_soc_dapm_mutex_lock(dapm);
@@ -127,7 +125,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
if (da7219->mclk) {
ret = clk_prepare_enable(da7219->mclk);
if (ret) {
- dev_err(codec->dev, "Failed to enable mclk - %d\n", 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);
@@ -142,90 +140,90 @@ static void da7219_aad_hptest_work(struct work_struct *work)
* 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_read(codec, DA7219_PLL_SRM_STS);
+ 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_read(codec, DA7219_PLL_CTRL);
+ pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)
- da7219_set_pll(codec, DA7219_SYSCLK_PLL,
+ 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);
}
/* Ensure gain ramping at fastest rate */
- gain_ramp_ctrl = snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL);
- snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
+ 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_write(codec, DA7219_TONE_GEN_CFG1, 0);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
/* Enable HPTest block, 1KOhms check */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ 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_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_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_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5,
DA7219_DAC_SOFTMUTE_EN_MASK, 0);
/* Enable HP left & right paths */
- snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
DA7219_CP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+ 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_update_bits(codec, DA7219_DAC_L_CTRL,
+ 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_update_bits(codec, DA7219_DAC_R_CTRL,
+ 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_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT,
DA7219_MIXOUT_L_MIX_SELECT_MASK,
DA7219_MIXOUT_L_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT,
DA7219_MIXOUT_R_MIX_SELECT_MASK,
DA7219_MIXOUT_R_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L,
DA7219_OUTFILT_ST_1L_SRC_MASK,
DA7219_DMIX_ST_SRC_OUTFILT1L);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R,
DA7219_OUTFILT_ST_1R_SRC_MASK,
DA7219_DMIX_ST_SRC_OUTFILT1R);
- snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_L_AMP_EN_MASK,
DA7219_MIXOUT_L_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL,
DA7219_MIXOUT_R_AMP_EN_MASK,
DA7219_MIXOUT_R_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ 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_update_bits(codec, DA7219_HP_R_CTRL,
+ 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_update_bits(codec, DA7219_HP_L_CTRL,
+ 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_update_bits(codec, DA7219_HP_R_CTRL,
+ 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);
@@ -237,26 +235,26 @@ static void da7219_aad_hptest_work(struct work_struct *work)
msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
/* Configure & start Tone Generator */
- snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+ 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_update_bits(codec, DA7219_TONE_GEN_CFG2,
+ 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_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+ 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_read(codec, DA7219_ACCDET_CONFIG_8);
+ 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_write(codec, DA7219_TONE_GEN_CFG1, 0);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
msleep(DA7219_AAD_HPTEST_PERIOD);
@@ -294,7 +292,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_cache_bypass(da7219->regmap, false);
/* Disable HPTest block */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
DA7219_HPTEST_EN_MASK, 0);
/*
@@ -305,18 +303,18 @@ static void da7219_aad_hptest_work(struct work_struct *work)
msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
/* Restore gain ramping rate */
- snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
/* Drive Headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_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(codec, DA7219_SYSCLK_MCLK, 0);
+ da7219_set_pll(component, DA7219_SYSCLK_MCLK, 0);
/* Remove MCLK, if previously enabled */
if (da7219->mclk)
@@ -335,6 +333,15 @@ static void da7219_aad_hptest_work(struct work_struct *work)
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
@@ -343,28 +350,44 @@ static void da7219_aad_hptest_work(struct work_struct *work)
static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
{
struct da7219_aad_priv *da7219_aad = data;
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ int i, ret, report = 0, mask = 0;
/* Read current IRQ events */
- regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
- events, DA7219_AAD_IRQ_REG_MAX);
+ ret = regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+ if (ret) {
+ dev_warn_ratelimited(component->dev, "Failed to read IRQ events: %d\n", ret);
+ return IRQ_NONE;
+ }
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_read(codec, DA7219_ACCDET_STATUS_A);
+ 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(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ 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);
@@ -392,12 +415,17 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
* handle a removal, and we can check at the end of
* hptest if we have a valid result or not.
*/
+
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
if (statusa & DA7219_JACK_TYPE_STS_MASK) {
report |= SND_JACK_HEADSET;
mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
- schedule_work(&da7219_aad->btn_det_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work);
} else {
- schedule_work(&da7219_aad->hptest_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work);
}
}
@@ -429,23 +457,29 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
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_update_bits(codec, DA7219_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK, 0);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_OE_MASK, 0);
/* Ensure button detection disabled */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ 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);
- /* Cancel any pending work */
- cancel_work_sync(&da7219_aad->btn_det_work);
- cancel_work_sync(&da7219_aad->hptest_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
}
}
@@ -459,7 +493,7 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
*/
static enum da7219_aad_micbias_pulse_lvl
- da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val)
{
switch (val) {
case 2800:
@@ -467,13 +501,13 @@ static enum da7219_aad_micbias_pulse_lvl
case 2900:
return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
default:
- dev_warn(codec->dev, "Invalid micbias pulse level");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_cfg(struct device *dev, u32 val)
{
switch (val) {
case 2:
@@ -491,13 +525,13 @@ static enum da7219_aad_btn_cfg
case 500:
return DA7219_AAD_BTN_CFG_500MS;
default:
- dev_warn(codec->dev, "Invalid button config");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_mic_det_thr(struct device *dev, u32 val)
{
switch (val) {
case 200:
@@ -509,13 +543,13 @@ static enum da7219_aad_mic_det_thr
case 1000:
return DA7219_AAD_MIC_DET_THR_1000_OHMS;
default:
- dev_warn(codec->dev, "Invalid mic detect threshold");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val)
{
switch (val) {
case 5:
@@ -535,30 +569,43 @@ static enum da7219_aad_jack_ins_deb
case 1000:
return DA7219_AAD_JACK_INS_DEB_1S;
default:
- dev_warn(codec->dev, "Invalid jack insert debounce");
+ dev_warn(dev, "Invalid jack insert debounce");
return DA7219_AAD_JACK_INS_DEB_20MS;
}
}
+static enum da7219_aad_jack_ins_det_pty
+ da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str)
+{
+ if (!strcmp(str, "low")) {
+ return DA7219_AAD_JACK_INS_DET_PTY_LOW;
+ } else if (!strcmp(str, "high")) {
+ return DA7219_AAD_JACK_INS_DET_PTY_HIGH;
+ } else {
+ dev_warn(dev, "Invalid jack insertion detection polarity");
+ return DA7219_AAD_JACK_INS_DET_PTY_LOW;
+ }
+}
+
static enum da7219_aad_jack_det_rate
- da7219_aad_fw_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+ da7219_aad_fw_jack_det_rate(struct device *dev, const char *str)
{
- if (!strcmp(str, "32ms_64ms")) {
+ if (!strcmp(str, "32_64")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS;
- } else if (!strcmp(str, "64ms_128ms")) {
+ } else if (!strcmp(str, "64_128")) {
return DA7219_AAD_JACK_DET_RATE_64_128MS;
- } else if (!strcmp(str, "128ms_256ms")) {
+ } else if (!strcmp(str, "128_256")) {
return DA7219_AAD_JACK_DET_RATE_128_256MS;
- } else if (!strcmp(str, "256ms_512ms")) {
+ } else if (!strcmp(str, "256_512")) {
return DA7219_AAD_JACK_DET_RATE_256_512MS;
} else {
- dev_warn(codec->dev, "Invalid jack detect rate");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -570,13 +617,13 @@ static enum da7219_aad_jack_rem_deb
case 20:
return DA7219_AAD_JACK_REM_DEB_20MS;
default:
- dev_warn(codec->dev, "Invalid jack removal debounce");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_avg(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -588,13 +635,13 @@ static enum da7219_aad_btn_avg
case 8:
return DA7219_AAD_BTN_AVG_8;
default:
- dev_warn(codec->dev, "Invalid button average value");
+ 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 snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -606,14 +653,13 @@ static enum da7219_aad_adc_1bit_rpt
case 8:
return DA7219_AAD_ADC_1BIT_RPT_8;
default:
- dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+ 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 snd_soc_codec *codec)
+static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
{
- struct device *dev = codec->dev;
struct i2c_client *i2c = to_i2c_client(dev);
struct fwnode_handle *aad_np;
struct da7219_aad_pdata *aad_pdata;
@@ -624,16 +670,18 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *cod
if (!aad_np)
return NULL;
- aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
- if (!aad_pdata)
+ aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata) {
+ fwnode_handle_put(aad_np);
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(codec, fw_val32);
+ da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32);
else
aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
@@ -642,31 +690,37 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *cod
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(codec, fw_val32);
+ 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(codec, fw_val32);
+ da7219_aad_fw_mic_det_thr(dev, fw_val32);
else
- aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_200_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(codec, fw_val32);
+ 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-ins-det-pty", &fw_str))
+ aad_pdata->jack_ins_det_pty =
+ da7219_aad_fw_jack_ins_det_pty(dev, fw_str);
+ else
+ aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW;
+
if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
aad_pdata->jack_det_rate =
- da7219_aad_fw_jack_det_rate(codec, fw_str);
+ 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(codec, fw_val32);
+ da7219_aad_fw_jack_rem_deb(dev, fw_val32);
else
aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
@@ -691,22 +745,24 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *cod
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(codec, fw_val32);
+ 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(codec, fw_val32);
+ da7219_aad_fw_adc_1bit_rpt(dev, fw_val32);
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+ fwnode_handle_put(aad_np);
+
return aad_pdata;
}
-static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+static void da7219_aad_handle_pdata(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_aad_priv *da7219_aad = da7219->aad;
struct da7219_pdata *pdata = da7219->pdata;
@@ -752,7 +808,7 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_MIC_DET_THRESH_SHIFT);
mask |= DA7219_MIC_DET_THRESH_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, cfg);
cfg = 0;
mask = 0;
@@ -787,15 +843,15 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_JACKDET_REM_DEB_SHIFT);
mask |= DA7219_JACKDET_REM_DEB_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, cfg);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3,
aad_pdata->a_d_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4,
aad_pdata->d_b_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5,
aad_pdata->b_c_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6,
aad_pdata->c_mic_btn_thr);
cfg = 0;
@@ -818,26 +874,72 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_ADC_1_BIT_REPEAT_SHIFT);
mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg);
+
+ switch (aad_pdata->jack_ins_det_pty) {
+ case DA7219_AAD_JACK_INS_DET_PTY_LOW:
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0x75, 0x80);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ break;
+ case DA7219_AAD_JACK_INS_DET_PTY_HIGH:
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0x75, 0x00);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ break;
+ default:
+ break;
+ }
}
}
+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_codec *codec)
+void da7219_aad_suspend(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
u8 micbias_ctrl;
+ disable_irq(da7219_aad->irq);
+
if (da7219_aad->jack) {
/* Disable jack detection during suspend */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_ACCDET_EN_MASK, 0);
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
/*
* If we have a 4-pole jack inserted, then micbias will be
@@ -846,7 +948,7 @@ void da7219_aad_suspend(struct snd_soc_codec *codec)
* suspend then this will be dealt with through the IRQ handler.
*/
if (da7219_aad->jack_inserted) {
- micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
+ 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);
@@ -856,11 +958,11 @@ void da7219_aad_suspend(struct snd_soc_codec *codec)
}
}
-void da7219_aad_resume(struct snd_soc_codec *codec)
+void da7219_aad_resume(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_get_dapm(codec);
+ 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 */
@@ -872,10 +974,12 @@ void da7219_aad_resume(struct snd_soc_codec *codec)
}
/* Re-enable jack detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_ACCDET_EN_MASK,
DA7219_ACCDET_EN_MASK);
}
+
+ enable_irq(da7219_aad->irq);
}
@@ -883,30 +987,31 @@ void da7219_aad_resume(struct snd_soc_codec *codec)
* Init/Exit
*/
-int da7219_aad_init(struct snd_soc_codec *codec)
+int da7219_aad_init(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad;
+ 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 = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
- if (!da7219_aad)
- return -ENOMEM;
-
- da7219->aad = da7219_aad;
- da7219_aad->codec = codec;
+ da7219_aad->component = component;
/* Handle any DT/ACPI/platform data */
- if (da7219->pdata && !da7219->pdata->aad_pdata)
- da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
-
- da7219_aad_handle_pdata(codec);
+ da7219_aad_handle_pdata(component);
/* Disable button detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ 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);
@@ -915,7 +1020,7 @@ int da7219_aad_init(struct snd_soc_codec *codec)
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"da7219-aad", da7219_aad);
if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
return ret;
}
@@ -926,11 +1031,10 @@ int da7219_aad_init(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(da7219_aad_init);
-void da7219_aad_exit(struct snd_soc_codec *codec)
+void da7219_aad_exit(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -941,11 +1045,36 @@ void da7219_aad_exit(struct snd_soc_codec *codec)
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;
}
-EXPORT_SYMBOL_GPL(da7219_aad_exit);
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
index 117a3d7ccd31..fbfbf3e67918 100644
--- a/sound/soc/codecs/da7219-aad.h
+++ b/sound/soc/codecs/da7219-aad.h
@@ -1,20 +1,17 @@
+/* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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 __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>
@@ -189,8 +186,9 @@ enum da7219_aad_event_regs {
/* Private data */
struct da7219_aad_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int irq;
+ int gnd_switch_delay;
u8 micbias_pulse_lvl;
u32 micbias_pulse_time;
@@ -199,6 +197,8 @@ struct da7219_aad_priv {
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;
@@ -206,14 +206,17 @@ struct da7219_aad_priv {
};
/* AAD control */
-void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack);
/* Suspend/Resume */
-void da7219_aad_suspend(struct snd_soc_codec *codec);
-void da7219_aad_resume(struct snd_soc_codec *codec);
+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_codec *codec);
-void da7219_aad_exit(struct snd_soc_codec *codec);
+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
index 6f088536df32..1742f91c788c 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -1,20 +1,18 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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/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/of.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -256,8 +254,8 @@ static const struct soc_enum da7219_cp_track_mode =
static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -270,8 +268,8 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -284,8 +282,8 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -298,8 +296,8 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -310,55 +308,55 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
}
/* ALC */
-static void da7219_alc_calib(struct snd_soc_codec *codec)
+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_read(codec, DA7219_MIC_1_CTRL);
+ mic_ctrl = snd_soc_component_read(component, DA7219_MIC_1_CTRL);
/* Save current state of input mixer control register */
- mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+ mixin_ctrl = snd_soc_component_read(component, DA7219_MIXIN_L_CTRL);
/* Save current state of input ADC control register */
- adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+ adc_ctrl = snd_soc_component_read(component, DA7219_ADC_L_CTRL);
/* Enable then Mute MIC PGAs */
- snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
DA7219_MIC_1_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+ 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_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ 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_update_bits(codec, DA7219_ADC_L_CTRL,
+ 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_update_bits(codec, DA7219_ALC_CTRL1,
+ 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_read(codec, DA7219_ALC_CTRL1);
+ 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(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed with overflow\n");
- snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ 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_update_bits(codec, DA7219_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
DA7219_ALC_OFFSET_EN_MASK |
DA7219_ALC_SYNC_MODE_MASK,
DA7219_ALC_OFFSET_EN_MASK |
@@ -366,20 +364,20 @@ static void da7219_alc_calib(struct snd_soc_codec *codec)
}
/* Restore input filter control register to original state */
- snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+ snd_soc_component_write(component, DA7219_ADC_L_CTRL, adc_ctrl);
/* Restore input mixer control registers to original state */
- snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+ snd_soc_component_write(component, DA7219_MIXIN_L_CTRL, mixin_ctrl);
/* Restore MIC control registers to original states */
- snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -389,7 +387,7 @@ static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
* make sure calibrated offsets are updated.
*/
if ((ret == 1) && (da7219->alc_en))
- da7219_alc_calib(codec);
+ da7219_alc_calib(component);
return ret;
}
@@ -397,13 +395,13 @@ static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ da7219_alc_calib(component);
da7219->alc_en = true;
} else {
da7219->alc_en = false;
@@ -416,12 +414,12 @@ static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
- u16 val;
+ __le16 val;
int ret;
mutex_lock(&da7219->ctrl_lock);
@@ -443,12 +441,12 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
- u16 val;
+ __le16 val_new, val_old;
int ret;
/*
@@ -456,13 +454,19 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
* Therefore we need to convert to little endian here to align with
* HW registers.
*/
- val = cpu_to_le16(ucontrol->value.integer.value[0]);
+ val_new = cpu_to_le16(ucontrol->value.integer.value[0]);
mutex_lock(&da7219->ctrl_lock);
- ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
+ 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);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
}
@@ -766,57 +770,100 @@ static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
* 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ int i = 0, ret;
bool srm_lock = false;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (da7219->master)
+ if (da7219->master) {
/* Enable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_CLK_EN_MASK,
- DA7219_DAI_CLK_EN_MASK);
+ 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_update_bits(codec, DA7219_PC_COUNT,
+ 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_read(codec, DA7219_PLL_CTRL);
+ 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_read(codec, DA7219_PLL_SRM_STS);
+ 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));
+ } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock));
if (!srm_lock)
- dev_warn(codec->dev, "SRM failed to lock\n");
+ dev_warn(component->dev, "SRM failed to lock\n");
return 0;
case SND_SOC_DAPM_POST_PMD:
/* PC free-running */
- snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ 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)
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_CLK_EN_MASK, 0);
+ 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;
@@ -841,7 +888,7 @@ static int da7219_settling_event(struct snd_soc_dapm_widget *w,
static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u8 hp_ctrl, min_gain_mask;
switch (w->reg) {
@@ -860,7 +907,7 @@ static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
/* Enable minimum gain on HP to avoid pops */
- snd_soc_update_bits(codec, hp_ctrl, min_gain_mask,
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask,
min_gain_mask);
msleep(DA7219_MIN_GAIN_DELAY);
@@ -868,7 +915,7 @@ static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMU:
/* Remove minimum gain on HP */
- snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, 0);
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask, 0);
break;
}
@@ -879,22 +926,22 @@ static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, DA7219_GAIN_RAMP_CTRL);
- snd_soc_write(codec, 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_write(codec, DA7219_GAIN_RAMP_CTRL,
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL,
da7219->gain_ramp_ctrl);
break;
}
@@ -917,12 +964,12 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC"),
/* Input PGAs */
- SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
- DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
- DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
+ 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,
@@ -1116,29 +1163,32 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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))
+ mutex_lock(&da7219->pll_lock);
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) {
+ mutex_unlock(&da7219->pll_lock);
return 0;
+ }
if ((freq < 2000000) || (freq > 54000000)) {
+ mutex_unlock(&da7219->pll_lock);
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_update_bits(codec, DA7219_PLL_CTRL,
+ 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_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_MCLK_SQR_EN_MASK, 0);
break;
default:
@@ -1167,9 +1217,9 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
+int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1178,7 +1228,7 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
if (da7219->mclk_rate < 2000000) {
- dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
da7219->mclk_rate);
return -EINVAL;
} else if (da7219->mclk_rate <= 4500000) {
@@ -1197,7 +1247,7 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
da7219->mclk_rate);
return -EINVAL;
}
@@ -1208,7 +1258,7 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
switch (source) {
case DA7219_SYSCLK_MCLK:
pll_ctrl |= DA7219_PLL_MODE_BYPASS;
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_INDIV_MASK |
DA7219_PLL_MODE_MASK, pll_ctrl);
return 0;
@@ -1219,7 +1269,7 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
pll_ctrl |= DA7219_PLL_MODE_SRM;
break;
default:
- dev_err(codec->dev, "Invalid PLL config\n");
+ dev_err(component->dev, "Invalid PLL config\n");
return -EINVAL;
}
@@ -1231,10 +1281,10 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
/* Write PLL config & dividers */
- snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ 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);
@@ -1244,12 +1294,12 @@ int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout)
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_codec *codec = codec_dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, source, fout);
+ ret = da7219_set_pll(component, source, fout);
mutex_unlock(&da7219->pll_lock);
return ret;
@@ -1257,15 +1307,15 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7219->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7219->master = false;
break;
default:
@@ -1332,126 +1382,138 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- /* By default 64 BCLKs per WCLK is supported */
- dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
-
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK |
+ 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_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ 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_codec *codec = dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- u8 dai_bclks_per_wclk;
+ 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, revert to 64-bit frames */
+ /* No channels enabled so disable TDM */
if (!tx_mask) {
- snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
DA7219_DAI_TDM_CH_EN_MASK |
DA7219_DAI_TDM_MODE_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- DA7219_DAI_BCLKS_PER_WCLK_64);
+ da7219->tdm_en = false;
return 0;
}
/* Check we have valid slots */
- if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
- dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ 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;
}
- /* Check we have a valid offset given */
- if (rx_mask > DA7219_DAI_OFFSET_MAX) {
- dev_err(codec->dev, "Invalid slot offset, max = %d\n",
- DA7219_DAI_OFFSET_MAX);
+ /*
+ * 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;
}
- /* Calculate & validate frame size based on slot info provided. */
- frame_size = slots * slot_width;
- switch (frame_size) {
- case 32:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
- break;
- case 64:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
- break;
- case 128:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
- break;
- case 256:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
- break;
- default:
- dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
- 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;
+ }
+ }
}
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- dai_bclks_per_wclk);
-
- offset = cpu_to_le16(rx_mask);
+ dai_offset = cpu_to_le16(offset);
regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
- &offset, sizeof(offset));
+ &dai_offset, sizeof(dai_offset));
- snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
DA7219_DAI_TDM_CH_EN_MASK |
DA7219_DAI_TDM_MODE_EN_MASK,
- (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
DA7219_DAI_TDM_MODE_EN_MASK);
+ da7219->tdm_en = true;
+
return 0;
}
-static int da7219_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int da7219_set_sr(struct snd_soc_component *component,
+ unsigned long rate)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 dai_ctrl = 0, fs;
- unsigned int channels;
-
- switch (params_width(params)) {
- 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(codec->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;
+ u8 fs;
- switch (params_rate(params)) {
+ switch (rate) {
case 8000:
fs = DA7219_SR_8000;
break;
@@ -1489,11 +1551,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+ 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);
- snd_soc_write(codec, DA7219_SR, fs);
return 0;
}
@@ -1509,26 +1678,32 @@ static const struct snd_soc_dai_ops da7219_dai_ops = {
#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 = SNDRV_PCM_RATE_8000_96000,
+ .rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = DA7219_DAI_CH_NUM_MAX,
- .rates = SNDRV_PCM_RATE_8000_96000,
+ .rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.ops = &da7219_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.symmetric_channels = 1,
- .symmetric_samplebits = 1,
+ .symmetric_sample_bits = 1,
};
@@ -1536,17 +1711,21 @@ static struct snd_soc_dai_driver da7219_dai = {
* 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)
@@ -1585,9 +1764,8 @@ static enum da7219_mic_amp_in_sel
}
}
-static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev)
{
- struct device *dev = codec->dev;
struct da7219_pdata *pdata;
const char *of_str;
u32 of_val32;
@@ -1598,6 +1776,15 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
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
@@ -1616,10 +1803,10 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
* Codec driver functions
*/
-static int da7219_set_bias_level(struct snd_soc_codec *codec,
+static int da7219_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1627,11 +1814,11 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/* Enable MCLK for transition to ON state */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
+ 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(codec->dev,
+ dev_err(component->dev,
"Failed to enable mclk\n");
return ret;
}
@@ -1640,13 +1827,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
/* Master bias */
- snd_soc_update_bits(codec, DA7219_REFERENCES,
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK,
DA7219_BIAS_EN_MASK);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) {
/* Remove MCLK */
if (da7219->mclk)
clk_disable_unprepare(da7219->mclk);
@@ -1655,7 +1842,7 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
/* Only disable master bias if we're not a wake-up source */
if (!da7219->wakeup_source)
- snd_soc_update_bits(codec, DA7219_REFERENCES,
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK, 0);
break;
@@ -1670,250 +1857,466 @@ static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
[DA7219_SUPPLY_VDDIO] = "VDDIO",
};
-static int da7219_handle_supplies(struct snd_soc_codec *codec)
+static int da7219_handle_supplies(struct snd_soc_component *component,
+ u8 *io_voltage_lvl)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct regulator *vddio;
- u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
int i, ret;
/* Get required supplies */
for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
da7219->supplies[i].supply = da7219_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
- da7219->supplies);
+ ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to get supplies");
+ 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(codec->dev, "Invalid VDDIO voltage\n");
+ dev_warn(component->dev, "Invalid VDDIO voltage\n");
else if (ret < 2800000)
- io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+ *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(codec->dev, "Failed to enable supplies");
+ dev_err(component->dev, "Failed to enable supplies");
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
- /* Ensure device in active mode */
- snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+ 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;
- /* Update IO voltage level range */
- snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+ 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_handle_pdata(struct snd_soc_codec *codec)
+static void da7219_wclk_unprepare(struct clk_hw *hw)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_pdata *pdata = da7219->pdata;
+ 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 (pdata) {
- u8 micbias_lvl = 0;
+ if (!da7219->master)
+ return;
- da7219->wakeup_source = pdata->wakeup_source;
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+}
- /* 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;
- }
+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;
- snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+ if (!da7219->master)
+ return -EINVAL;
- /* 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_write(codec, DA7219_MIC_1_SELECT,
- pdata->mic_amp_in_sel);
- break;
- }
- }
+ clk_reg = snd_soc_component_read(component, DA7219_DAI_CLK_MODE);
+
+ return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
}
-static struct reg_sequence da7219_rev_aa_patch[] = {
- { DA7219_REFERENCES, 0x08 },
-};
+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 int da7219_probe(struct snd_soc_codec *codec)
+static int da7219_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- unsigned int rev;
- int ret;
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
- mutex_init(&da7219->ctrl_lock);
- mutex_init(&da7219->pll_lock);
+ if (!da7219->master)
+ return -EINVAL;
- /* Regulator configuration */
- ret = da7219_handle_supplies(codec);
- if (ret)
- return ret;
+ if (req->rate < 11025)
+ req->rate = 8000;
+ else if (req->rate < 12000)
+ req->rate = 11025;
+ else if (req->rate < 16000)
+ req->rate = 12000;
+ else if (req->rate < 22050)
+ req->rate = 16000;
+ else if (req->rate < 24000)
+ req->rate = 22050;
+ else if (req->rate < 32000)
+ req->rate = 24000;
+ else if (req->rate < 44100)
+ req->rate = 32000;
+ else if (req->rate < 48000)
+ req->rate = 44100;
+ else if (req->rate < 88200)
+ req->rate = 48000;
+ else if (req->rate < 96000)
+ req->rate = 88200;
+ else
+ req->rate = 96000;
- ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
- if (ret) {
- dev_err(codec->dev, "Failed to read chip revision: %d\n", ret);
- goto err_disable_reg;
- }
+ return 0;
+}
- 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(codec->dev, "Failed to register AA patch: %d\n",
- ret);
- goto err_disable_reg;
- }
- break;
- default:
- break;
- }
+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;
- /* Handle DT/ACPI/Platform data */
- da7219->pdata = dev_get_platdata(codec->dev);
- if (!da7219->pdata)
- da7219->pdata = da7219_fw_to_pdata(codec);
+ if (!da7219->master)
+ return -EINVAL;
- da7219_handle_pdata(codec);
+ return da7219_set_sr(component, rate);
+}
- /* Check if MCLK provided */
- da7219->mclk = devm_clk_get(codec->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;
- }
+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;
}
+}
- /* Default PC counter to free-running */
- snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
- DA7219_PC_FREERUN_MASK);
+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;
+}
- /* Default gain ramping */
- snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
- DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
- DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
- DA7219_ADC_L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
- DA7219_DAC_L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
- DA7219_DAC_R_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_RAMP_EN_MASK,
- DA7219_HP_L_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_RAMP_EN_MASK,
- DA7219_HP_R_AMP_RAMP_EN_MASK);
+static int da7219_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ unsigned long factor;
- /* Default minimum gain on HP to avoid pops during DAPM sequencing */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
- DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
- DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
+ if (!req->best_parent_rate || !da7219->master)
+ return -EINVAL;
- /* Default infinite tone gen, start/stop by Kcontrol */
- snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+ /*
+ * 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(req->rate, req->best_parent_rate);
- /* Initialise AAD block */
- ret = da7219_aad_init(codec);
- if (ret)
- goto err_disable_reg;
+ req->rate = req->best_parent_rate * factor;
return 0;
-
-err_disable_reg:
- regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
-
- return ret;
}
-static int da7219_remove(struct snd_soc_codec *codec)
+static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
- da7219_aad_exit(codec);
+ if (!da7219->master)
+ return -EINVAL;
- /* Supplies */
- return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ factor = da7219_bclk_get_factor(rate, parent_rate);
+
+ return da7219_set_bclks_per_wclk(component, factor);
}
-#ifdef CONFIG_PM
-static int da7219_suspend(struct snd_soc_codec *codec)
+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,
+ .determine_rate = da7219_wclk_determine_rate,
+ .set_rate = da7219_wclk_set_rate,
+ },
+ [DA7219_DAI_BCLK_IDX] = {
+ .recalc_rate = da7219_bclk_recalc_rate,
+ .determine_rate = da7219_bclk_determine_rate,
+ .set_rate = da7219_bclk_set_rate,
+ },
+};
+
+static int da7219_register_dai_clks(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ 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;
- /* Suspend AAD if we're not a wake-up source */
- if (!da7219->wakeup_source)
- da7219_aad_suspend(codec);
+ /* 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;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ 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 int da7219_resume(struct snd_soc_codec *codec)
+static void da7219_free_dai_clks(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct device_node *np = component->dev->of_node;
+ int i;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (np)
+ of_clk_del_provider(np);
- /* Resume AAD if previously suspended */
- if (!da7219->wakeup_source)
- da7219_aad_resume(codec);
+ for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
- return 0;
+ clk_hw_unregister(&da7219->dai_clks_hw[i]);
+ }
+
+ if (np)
+ kfree(da7219->clk_hw_data);
}
#else
-#define da7219_suspend NULL
-#define da7219_resume NULL
-#endif
+static inline int da7219_register_dai_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
-static const struct snd_soc_codec_driver soc_codec_dev_da7219 = {
- .probe = da7219_probe,
- .remove = da7219_remove,
- .suspend = da7219_suspend,
- .resume = da7219_resume,
- .set_bias_level = da7219_set_bias_level,
+static void da7219_free_dai_clks(struct snd_soc_component *component) {}
+#endif /* CONFIG_COMMON_CLK */
- .component_driver = {
- .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),
- },
-};
+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[] = {
+static const struct reg_default da7219_reg_defaults[] = {
{ DA7219_MIC_1_SELECT, 0x00 },
{ DA7219_CIF_TIMEOUT_CTRL, 0x01 },
{ DA7219_SR_24_48, 0x00 },
@@ -2027,9 +2430,9 @@ static bool da7219_volatile_register(struct device *dev, unsigned int reg)
case DA7219_ACCDET_IRQ_EVENT_B:
case DA7219_ACCDET_CONFIG_8:
case DA7219_SYSTEM_STATUS:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -2044,31 +2447,25 @@ static const struct regmap_config da7219_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct reg_sequence da7219_rev_aa_patch[] = {
+ { DA7219_REFERENCES, 0x08 },
+};
-/*
- * I2C layer
- */
-
-static int da7219_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7219_probe(struct snd_soc_component *component)
{
- struct da7219_priv *da7219;
- unsigned int system_active, system_status;
+ 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 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
- GFP_KERNEL);
- if (!da7219)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, da7219);
+ da7219->component = component;
+ mutex_init(&da7219->ctrl_lock);
+ mutex_init(&da7219->pll_lock);
- da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
- if (IS_ERR(da7219->regmap)) {
- ret = PTR_ERR(da7219->regmap);
- dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(component, &io_voltage_lvl);
+ if (ret)
return ret;
- }
regcache_cache_bypass(da7219->regmap, true);
@@ -2090,7 +2487,7 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
}
}
- /* Soft reset codec */
+ /* 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,
@@ -2098,24 +2495,220 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
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);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
- &da7219_dai, 1);
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
- ret);
+ /* 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 int da7219_i2c_remove(struct i2c_client *client)
+static void da7219_remove(struct snd_soc_component *component)
{
- snd_soc_unregister_codec(&client->dev);
+ 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", },
{ }
@@ -2129,7 +2722,6 @@ static struct i2c_driver da7219_i2c_driver = {
.acpi_match_table = ACPI_PTR(da7219_acpi_match),
},
.probe = da7219_i2c_probe,
- .remove = da7219_i2c_remove,
.id_table = da7219_i2c_id,
};
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index 8d6c3c8c8026..94af88f52589 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -1,19 +1,18 @@
+/* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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 __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>
@@ -778,8 +777,10 @@
#define DA7219_SYS_STAT_CHECK_DELAY 50
/* Power up/down Delays */
-#define DA7219_SETTLING_DELAY 40
-#define DA7219_MIN_GAIN_DELAY 30
+#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,
@@ -804,6 +805,7 @@ struct da7219_aad_priv;
/* Private data */
struct da7219_priv {
+ struct snd_soc_component *component;
struct da7219_aad_priv *aad;
struct da7219_pdata *pdata;
@@ -813,15 +815,25 @@ struct da7219_priv {
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_codec *codec, int source, unsigned int fout);
+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
index 83db4d23c90b..016c9be3ebda 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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>
@@ -168,52 +165,47 @@ static const struct reg_default da732x_reg_cache[] = {
{ DA732X_REG_UNLOCK , 0x00 },
};
-static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk)
+static inline int da732x_get_input_div(struct snd_soc_component *component, int sysclk)
{
int val;
- int ret;
if (sysclk < DA732X_MCLK_10MHZ) {
- val = DA732X_MCLK_RET_0_10MHZ;
- ret = DA732X_MCLK_VAL_0_10MHZ;
+ val = DA732X_MCLK_VAL_0_10MHZ;
} else if ((sysclk >= DA732X_MCLK_10MHZ) &&
(sysclk < DA732X_MCLK_20MHZ)) {
- val = DA732X_MCLK_RET_10_20MHZ;
- ret = DA732X_MCLK_VAL_10_20MHZ;
+ val = DA732X_MCLK_VAL_10_20MHZ;
} else if ((sysclk >= DA732X_MCLK_20MHZ) &&
(sysclk < DA732X_MCLK_40MHZ)) {
- val = DA732X_MCLK_RET_20_40MHZ;
- ret = DA732X_MCLK_VAL_20_40MHZ;
+ val = DA732X_MCLK_VAL_20_40MHZ;
} else if ((sysclk >= DA732X_MCLK_40MHZ) &&
(sysclk <= DA732X_MCLK_54MHZ)) {
- val = DA732X_MCLK_RET_40_54MHZ;
- ret = DA732X_MCLK_VAL_40_54MHZ;
+ val = DA732X_MCLK_VAL_40_54MHZ;
} else {
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_PLL_CTRL, val);
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL, val);
- return ret;
+ return val;
}
-static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
+static void da732x_set_charge_pump(struct snd_soc_component *component, int state)
{
switch (state) {
case DA732X_ENABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
+ 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_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
DA732X_CP_CTRL_CPVDD1);
- snd_soc_write(codec, DA732X_REG_CP_CTRL2,
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL2,
DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
- snd_soc_write(codec, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
break;
case DA732X_DISABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
- snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
+ 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");
@@ -331,7 +323,7 @@ static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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];
@@ -351,7 +343,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits);
+ snd_soc_component_update_bits(component, reg, DA732X_HPF_MASK, bits);
return 0;
}
@@ -359,12 +351,12 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, reg) & DA732X_HPF_MASK;
+ val = snd_soc_component_read(component, reg) & DA732X_HPF_MASK;
switch (val) {
case DA732X_HPF_VOICE_EN:
@@ -609,18 +601,18 @@ static const struct snd_kcontrol_new da732x_snd_controls[] = {
static int da732x_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN,
DA732X_ADCC_BB_CLK_EN);
break;
@@ -628,24 +620,24 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
return -EINVAL;
}
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
DA732X_ADC_SET_ACT);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_ON);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_OFF);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCA_BB_CLK_EN, 0);
break;
case DA732X_REG_ADC2_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN, 0);
break;
default:
@@ -663,16 +655,16 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift) | DA732X_OUT_HIZ_EN,
(1 << w->shift) | DA732X_OUT_HIZ_DIS);
break;
@@ -949,7 +941,7 @@ static int da732x_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;
u32 aif = 0;
u32 reg_aif;
u32 fs;
@@ -1011,15 +1003,15 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif);
- snd_soc_update_bits(codec, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u32 aif_mclk, pc_count;
u32 reg_aif1, aif1;
u32 reg_aif3, aif3;
@@ -1042,11 +1034,11 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
aif1 = DA732X_AIF_SLAVE;
aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 = DA732X_AIF_CLK_FROM_SRC;
aif_mclk = DA732X_CLK_GENERATION_AIF_A;
break;
@@ -1107,29 +1099,29 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_AIF_MCLK, aif_mclk);
- snd_soc_update_bits(codec, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
- snd_soc_update_bits(codec, reg_aif3, DA732X_AIF_BCLK_INV |
+ 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_write(codec, DA732X_REG_PC_CTRL, pc_count);
+ snd_soc_component_write(component, DA732X_REG_PC_CTRL, pc_count);
return 0;
}
-static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
+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_codec_get_drvdata(codec);
+ 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_update_bits(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_EN, 0);
da732x->pll_en = false;
return 0;
@@ -1147,32 +1139,32 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
case 24576000:
case 45160000:
case 49152000:
- snd_soc_write(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_BYPASS);
return 0;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Cannot use PLL Bypass, invalid SYSCLK rate\n");
return -EINVAL;
}
}
- indiv = da732x_get_input_div(codec, da732x->sysclk);
+ indiv = da732x_get_input_div(component, da732x->sysclk);
if (indiv < 0)
return indiv;
- fref = (da732x->sysclk / 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_write(codec, DA732X_REG_PLL_DIV_LO, div_lo);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_MID, div_mid);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_HI, div_hi);
+ 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_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
DA732X_PLL_EN);
da732x->pll_en = true;
@@ -1183,8 +1175,8 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
da732x->sysclk = freq;
@@ -1268,21 +1260,21 @@ static const struct regmap_config da732x_regmap = {
};
-static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
+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_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
@@ -1290,9 +1282,9 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
msleep(DA732X_WAIT_FOR_STABILIZATION);
/* Check DAC offset sign */
- sign[DA732X_HPL_DAC] = (snd_soc_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ 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_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ 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) */
@@ -1302,17 +1294,17 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_DAC] |= step;
offset[DA732X_HPR_DAC] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ 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_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ 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_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ 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;
@@ -1320,19 +1312,19 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ 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_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ 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_codec *codec)
+static void da732x_output_offset_adjust(struct snd_soc_component *component)
{
u8 offset[DA732X_HP_AMPS];
u8 sign[DA732X_HP_AMPS];
@@ -1342,26 +1334,26 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
/* Initialize output offset calibration circuits and registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL,
+ 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_write(codec, DA732X_REG_HPR,
+ 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_read(codec, DA732X_REG_HPL) &
+ sign[DA732X_HPL_AMP] = snd_soc_component_read(component, DA732X_REG_HPL) &
DA732X_HP_OUT_COMPO;
- sign[DA732X_HPR_AMP] = snd_soc_read(codec, DA732X_REG_HPR) &
+ sign[DA732X_HPR_AMP] = snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO;
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
+ 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_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
+ 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);
@@ -1369,17 +1361,17 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_AMP] |= step;
offset[DA732X_HPR_AMP] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET,
offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET,
offset[DA732X_HPR_AMP]);
msleep(DA732X_WAIT_FOR_STABILIZATION);
- if ((snd_soc_read(codec, DA732X_REG_HPL) &
+ 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_read(codec, DA732X_REG_HPR) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
offset[DA732X_HPR_AMP] &= ~step;
@@ -1387,80 +1379,80 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+ 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_codec *codec)
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_component *component)
{
/* Make sure that we have Soft Mute enabled */
- snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+ snd_soc_component_write(component, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
+ 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_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
- snd_soc_write(codec, DA732X_REG_HPR, 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(codec);
- da732x_output_offset_adjust(codec);
+ da732x_dac_offset_adjust(component);
+ da732x_output_offset_adjust(component);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_DIS);
- snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_DIS);
+ 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_codec *codec,
+static int da732x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Init Codec */
- snd_soc_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_VMID_FASTCHG);
- snd_soc_write(codec, DA732X_REG_BIAS_EN,
+ 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_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_REFBUFX2_EN);
/* Enable bypass DSP routing */
- snd_soc_write(codec, DA732X_REG_DATA_ROUTE,
+ snd_soc_component_write(component, DA732X_REG_DATA_ROUTE,
DA732X_BYPASS_DSP);
/* Enable Digital subsystem */
- snd_soc_write(codec, DA732X_REG_DSP_CTRL,
+ snd_soc_component_write(component, DA732X_REG_DSP_CTRL,
DA732X_DIGITAL_EN);
- snd_soc_write(codec, DA732X_REG_SPARE1_OUT,
+ snd_soc_component_write(component, DA732X_REG_SPARE1_OUT,
DA732X_HP_DRIVER_EN |
DA732X_HP_GATE_LOW |
DA732X_HP_LOOP_GAIN_CTRL);
- snd_soc_write(codec, DA732X_REG_HP_LIN1_GNDSEL,
+ snd_soc_component_write(component, DA732X_REG_HP_LIN1_GNDSEL,
DA732X_HP_OUT_GNDSEL);
- da732x_set_charge_pump(codec, DA732X_ENABLE_CP);
+ da732x_set_charge_pump(component, DA732X_ENABLE_CP);
- snd_soc_write(codec, DA732X_REG_CLK_EN1,
+ snd_soc_component_write(component, DA732X_REG_CLK_EN1,
DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
/* Enable Zero Crossing */
- snd_soc_write(codec, DA732X_REG_INP_ZC_EN,
+ snd_soc_component_write(component, DA732X_REG_INP_ZC_EN,
DA732X_MIC1_PRE_ZC_EN |
DA732X_MIC1_ZC_EN |
DA732X_MIC2_PRE_ZC_EN |
@@ -1469,28 +1461,28 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
DA732X_AUXR_ZC_EN |
DA732X_MIC3_PRE_ZC_EN |
DA732X_MIC3_ZC_EN);
- snd_soc_write(codec, DA732X_REG_OUT_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(codec);
+ da732x_hp_dc_offset_cancellation(component);
regcache_cache_only(da732x->regmap, false);
regcache_sync(da732x->regmap);
} else {
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
DA732X_BIAS_BOOST_MASK,
DA732X_BIAS_BOOST_50PC);
- snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+ 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(codec, DA732X_DISABLE_CP);
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
+ 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;
@@ -1499,21 +1491,21 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_da732x = {
+static const struct snd_soc_component_driver soc_component_dev_da732x = {
.set_bias_level = da732x_set_bias_level,
- .component_driver = {
- .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),
- },
+ .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,
- const struct i2c_device_id *id)
+static int da732x_i2c_probe(struct i2c_client *i2c)
{
struct da732x_priv *da732x;
unsigned int reg;
@@ -1543,24 +1535,18 @@ static int da732x_i2c_probe(struct i2c_client *i2c,
(reg & DA732X_ID_MAJOR_MASK) >> 4,
(reg & DA732X_ID_MINOR_MASK));
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x,
+ 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 codec.\n");
+ dev_err(&i2c->dev, "Failed to register component.\n");
err:
return ret;
}
-static int da732x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id da732x_i2c_id[] = {
- { "da7320", 0},
+ { "da7320"},
{ }
};
MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
@@ -1570,7 +1556,6 @@ static struct i2c_driver da732x_i2c_driver = {
.name = "da7320",
},
.probe = da732x_i2c_probe,
- .remove = da732x_i2c_remove,
.id_table = da732x_i2c_id,
};
diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h
index f586cbd30b77..c2f784c3f359 100644
--- a/sound/soc/codecs/da732x.h
+++ b/sound/soc/codecs/da732x.h
@@ -1,13 +1,10 @@
+/* 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>
- *
- * 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 __DA732X_H_
@@ -51,14 +48,10 @@
#define DA732X_MCLK_20MHZ 20000000
#define DA732X_MCLK_40MHZ 40000000
#define DA732X_MCLK_54MHZ 54000000
-#define DA732X_MCLK_RET_0_10MHZ 0
-#define DA732X_MCLK_VAL_0_10MHZ 1
-#define DA732X_MCLK_RET_10_20MHZ 1
-#define DA732X_MCLK_VAL_10_20MHZ 2
-#define DA732X_MCLK_RET_20_40MHZ 2
-#define DA732X_MCLK_VAL_20_40MHZ 4
-#define DA732X_MCLK_RET_40_54MHZ 3
-#define DA732X_MCLK_VAL_40_54MHZ 8
+#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
diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h
index bdd03ca4b2de..a493e0b46f5d 100644
--- a/sound/soc/codecs/da732x_reg.h
+++ b/sound/soc/codecs/da732x_reg.h
@@ -1,13 +1,10 @@
+/* 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>
- *
- * 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 __DA732X_REG_H_
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index bd7faaee5802..eb795abe9acd 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA9055 ALSA Soc codec driver
*
@@ -6,11 +7,6 @@
* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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/delay.h>
@@ -19,7 +15,6 @@
#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>
@@ -451,7 +446,7 @@ static const char * const da9055_hold_time_txt[] = {
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_codec *codec, u8 reg_val)
+static int da9055_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -460,17 +455,17 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
+ 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_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ 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_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -481,7 +476,7 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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;
@@ -492,31 +487,31 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
*/
/* Save current values from Mic control registers */
- mic_left = snd_soc_read(codec, DA9055_MIC_L_CTRL);
- mic_right = snd_soc_read(codec, DA9055_MIC_R_CTRL);
+ 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_update_bits(codec, DA9055_MIC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIC_L_CTRL,
DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_MIC_R_CTRL,
+ 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_read(codec, DA9055_ADC_L_CTRL);
- adc_right = snd_soc_read(codec, DA9055_ADC_R_CTRL);
+ 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_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_ADC_L_EN, DA9055_ADC_L_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ 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(codec,
+ avg_left_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da9055_get_alc_data(codec,
+ avg_right_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -524,22 +519,22 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
offset_r = -avg_right_data;
reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_L, reg_val);
reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_L, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_R, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_R, reg_val);
/* Restore original values of ADC control registers */
- snd_soc_write(codec, DA9055_ADC_L_CTRL, adc_left);
- snd_soc_write(codec, DA9055_ADC_R_CTRL, adc_right);
+ 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_write(codec, DA9055_MIC_L_CTRL, mic_left);
- snd_soc_write(codec, DA9055_MIC_R_CTRL, mic_right);
+ 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);
@@ -1041,9 +1036,9 @@ static bool da9055_volatile_register(struct device *dev,
case DA9055_HP_R_GAIN_STATUS:
case DA9055_LINE_GAIN_STATUS:
case DA9055_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1052,8 +1047,8 @@ static int da9055_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 da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
u8 aif_ctrl, fs;
u32 sysclk;
@@ -1075,7 +1070,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
}
/* Set AIF format */
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
aif_ctrl);
switch (params_rate(params)) {
@@ -1125,7 +1120,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
if (da9055->mclk_rate) {
/* PLL Mode, Write actual FS */
- snd_soc_write(codec, DA9055_SR, fs);
+ snd_soc_component_write(component, DA9055_SR, fs);
} else {
/*
* Non-PLL Mode
@@ -1134,24 +1129,24 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
* 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_write(codec, DA9055_SR, DA9055_SR_48000);
+ 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_update_bits(codec, DA9055_PLL_CTRL,
+ 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_update_bits(codec, DA9055_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
DA9055_PLL_EN, DA9055_PLL_EN);
}
} else {
/* Non PLL Mode, disable PLL */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
}
return 0;
@@ -1160,17 +1155,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
/* Set DAI mode and Format */
static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* DA9055 in I2S Master Mode */
mode = 1;
aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
/* DA9055 in I2S Slave Mode */
mode = 0;
aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
@@ -1180,7 +1175,7 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* Don't allow change of mode if PLL is enabled */
- if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ if ((snd_soc_component_read(component, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
(da9055->master != mode))
return -EINVAL;
@@ -1207,27 +1202,27 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default only 32 BCLK per WCLK is supported */
aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
- snd_soc_update_bits(codec, DA9055_AIF_CLK_MODE,
+ snd_soc_component_update_bits(component, DA9055_AIF_CLK_MODE,
(DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK),
aif_clk_mode);
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
+ 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)
+static int da9055_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, 0);
}
@@ -1240,8 +1235,8 @@ static int da9055_mute(struct snd_soc_dai *dai, int mute)
static int da9055_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 da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1283,13 +1278,13 @@ static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ 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))
@@ -1312,9 +1307,9 @@ static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto pll_err;
/* Write PLL dividers */
- snd_soc_write(codec, DA9055_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA9055_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA9055_PLL_INTEGER, pll_integer);
+ 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:
@@ -1328,7 +1323,8 @@ static const struct snd_soc_dai_ops da9055_dai_ops = {
.set_fmt = da9055_set_dai_fmt,
.set_sysclk = da9055_set_dai_sysclk,
.set_pll = da9055_set_dai_pll,
- .digital_mute = da9055_mute,
+ .mute_stream = da9055_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da9055_dai = {
@@ -1350,10 +1346,10 @@ static struct snd_soc_dai_driver da9055_dai = {
.formats = DA9055_FORMATS,
},
.ops = &da9055_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da9055_set_bias_level(struct snd_soc_codec *codec,
+static int da9055_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -1361,48 +1357,48 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA9055_REFERENCES,
+ 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_update_bits(codec, DA9055_REFERENCES,
+ 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_codec *codec)
+static int da9055_probe(struct snd_soc_component *component)
{
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
/* Enable all Gain Ramps */
- snd_soc_update_bits(codec, DA9055_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA9055_LINE_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
/*
@@ -1412,28 +1408,28 @@ static int da9055_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_L_CTRL,
DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
+ 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_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
+ 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_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN,
DA9055_MICBIAS2_EN);
} else {
- snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN, 0);
}
/* set mic bias voltage */
@@ -1442,7 +1438,7 @@ static int da9055_probe(struct snd_soc_codec *codec)
case DA9055_MICBIAS_2_1V:
case DA9055_MICBIAS_1_8V:
case DA9055_MICBIAS_1_6V:
- snd_soc_update_bits(codec, DA9055_MIC_CONFIG,
+ snd_soc_component_update_bits(component, DA9055_MIC_CONFIG,
DA9055_MICBIAS_LEVEL_MASK,
(da9055->pdata->micbias) << 4);
break;
@@ -1451,18 +1447,18 @@ static int da9055_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_da9055 = {
+static const struct snd_soc_component_driver soc_component_dev_da9055 = {
.probe = da9055_probe,
.set_bias_level = da9055_set_bias_level,
-
- .component_driver = {
- .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),
- },
+ .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 = {
@@ -1475,8 +1471,7 @@ static const struct regmap_config da9055_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int da9055_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da9055_i2c_probe(struct i2c_client *i2c)
{
struct da9055_priv *da9055;
struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
@@ -1499,21 +1494,15 @@ static int da9055_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da9055, &da9055_dai, 1);
+ 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 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da9055 component: %d\n",
ret);
}
return ret;
}
-static int da9055_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
/*
* 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
@@ -1522,16 +1511,18 @@ static int da9055_remove(struct i2c_client *client)
* and PMIC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
- { "da9055-codec", 0 },
+ { "da9055-codec" },
{ }
};
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 = {
@@ -1540,7 +1531,6 @@ static struct i2c_driver da9055_i2c_driver = {
.of_match_table = of_match_ptr(da9055_of_match),
},
.probe = da9055_i2c_probe,
- .remove = da9055_remove,
.id_table = da9055_i2c_id,
};
diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/dio2125.c
deleted file mode 100644
index 09451cd44f9b..000000000000
--- a/sound/soc/codecs/dio2125.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2017 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.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, see <http://www.gnu.org/licenses/>.
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <sound/soc.h>
-
-#define DRV_NAME "dio2125"
-
-struct dio2125 {
- struct gpio_desc *gpiod_enable;
-};
-
-static int drv_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 dio2125 *priv = snd_soc_component_get_drvdata(c);
- int val;
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- val = 1;
- break;
- case SND_SOC_DAPM_PRE_PMD:
- val = 0;
- break;
- default:
- WARN(1, "Unexpected event");
- return -EINVAL;
- }
-
- gpiod_set_value_cansleep(priv->gpiod_enable, val);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = {
- SND_SOC_DAPM_INPUT("INL"),
- SND_SOC_DAPM_INPUT("INR"),
- SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
- (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
- SND_SOC_DAPM_OUTPUT("OUTL"),
- SND_SOC_DAPM_OUTPUT("OUTR"),
-};
-
-static const struct snd_soc_dapm_route dio2125_dapm_routes[] = {
- { "DRV", NULL, "INL" },
- { "DRV", NULL, "INR" },
- { "OUTL", NULL, "DRV" },
- { "OUTR", NULL, "DRV" },
-};
-
-static const struct snd_soc_component_driver dio2125_component_driver = {
- .dapm_widgets = dio2125_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dio2125_dapm_widgets),
- .dapm_routes = dio2125_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(dio2125_dapm_routes),
-};
-
-static int dio2125_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct dio2125 *priv;
- int err;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
- platform_set_drvdata(pdev, priv);
-
- priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
- if (IS_ERR(priv->gpiod_enable)) {
- err = PTR_ERR(priv->gpiod_enable);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'enable' gpio: %d", err);
- return err;
- }
-
- return devm_snd_soc_register_component(dev, &dio2125_component_driver,
- NULL, 0);
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id dio2125_ids[] = {
- { .compatible = "dioo,dio2125", },
- { }
-};
-MODULE_DEVICE_TABLE(of, dio2125_ids);
-#endif
-
-static struct platform_driver dio2125_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(dio2125_ids),
- },
- .probe = dio2125_probe,
-};
-
-module_platform_driver(dio2125_driver);
-
-MODULE_DESCRIPTION("ASoC DIO2125 output driver");
-MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index cf83c423394d..61e1bf1b3c9e 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* dmic.c -- SoC audio for Generic Digital MICs
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You 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/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -29,24 +17,32 @@
#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;
+ struct regulator *vref;
+ 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)
+ int cmd, struct snd_soc_dai *dai)
{
- struct gpio_desc *dmic_en = snd_soc_dai_get_drvdata(dai);
-
- if (!dmic_en)
- return 0;
+ struct snd_soc_component *component = dai->component;
+ struct dmic *dmic = snd_soc_component_get_drvdata(component);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- gpiod_set_value(dmic_en, 1);
- break;
case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- gpiod_set_value(dmic_en, 0);
+ if (dmic->modeswitch_delay)
+ mdelay(dmic->modeswitch_delay);
+
break;
}
@@ -57,6 +53,39 @@ 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);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 1);
+
+ if (dmic->vref) {
+ ret = regulator_enable(dmic->vref);
+ if (ret)
+ return ret;
+ }
+
+ 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);
+
+ if (dmic->vref)
+ ret = regulator_disable(dmic->vref);
+
+ break;
+ }
+
+ return ret;
+}
+
static struct snd_soc_dai_driver dmic_dai = {
.name = "dmic-hifi",
.capture = {
@@ -66,28 +95,58 @@ static struct snd_soc_dai_driver dmic_dai = {
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
- | SNDRV_PCM_FMTBIT_S16_LE,
+ | SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U8
+ | SNDRV_PCM_FMTBIT_DSD_U16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE
+ | SNDRV_PCM_FMTBIT_DSD_U16_BE
+ | SNDRV_PCM_FMTBIT_DSD_U32_BE,
},
.ops = &dmic_dai_ops,
};
-static int dmic_codec_probe(struct snd_soc_codec *codec)
+static int dmic_component_probe(struct snd_soc_component *component)
{
- struct gpio_desc *dmic_en;
+ struct dmic *dmic;
+
+ dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL);
+ if (!dmic)
+ return -ENOMEM;
+
+ dmic->vref = devm_regulator_get_optional(component->dev, "vref");
+ if (IS_ERR(dmic->vref)) {
+ if (PTR_ERR(dmic->vref) != -ENODEV)
+ return dev_err_probe(component->dev, PTR_ERR(dmic->vref),
+ "Failed to get vref\n");
+ dmic->vref = NULL;
+ }
- dmic_en = devm_gpiod_get_optional(codec->dev,
- "dmicen", GPIOD_OUT_LOW);
- if (IS_ERR(dmic_en))
- return PTR_ERR(dmic_en);
+ 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);
- snd_soc_codec_set_drvdata(codec, dmic_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("DMIC AIF", "Capture", 0,
- SND_SOC_NOPM, 0, 0),
+ 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"),
};
@@ -95,14 +154,15 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
-static const struct snd_soc_codec_driver soc_dmic = {
- .probe = dmic_codec_probe,
- .component_driver = {
- .dapm_widgets = dmic_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
- .dapm_routes = intercon,
- .num_dapm_routes = ARRAY_SIZE(intercon),
- },
+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)
@@ -129,22 +189,17 @@ static int dmic_dev_probe(struct platform_device *pdev)
}
}
- return snd_soc_register_codec(&pdev->dev,
+ return devm_snd_soc_register_component(&pdev->dev,
&soc_dmic, dai_drv, 1);
}
-static int dmic_dev_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
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 = {
@@ -152,7 +207,6 @@ static struct platform_driver dmic_driver = {
.of_match_table = dmic_dev_match,
},
.probe = dmic_dev_probe,
- .remove = dmic_dev_remove,
};
module_platform_driver(dmic_driver);
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
index 3869025754d8..f5150d2f95da 100644
--- a/sound/soc/codecs/es7134.c
+++ b/sound/soc/codecs/es7134.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.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, see <http://www.gnu.org/licenses/>.
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
*/
+#include <linux/of_platform.h>
#include <linux/module.h>
#include <sound/soc.h>
@@ -24,13 +12,89 @@
* 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_CBS_CFS)) {
+ SND_SOC_DAIFMT_CBC_CFC)) {
dev_err(codec_dai->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -38,8 +102,38 @@ static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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 = {
@@ -48,7 +142,11 @@ static struct snd_soc_dai_driver es7134_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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 |
@@ -58,43 +156,146 @@ static struct snd_soc_dai_driver es7134_dai = {
.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 const struct snd_soc_codec_driver es7134_codec_driver = {
- .component_driver = {
- .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),
+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)
{
- return snd_soc_register_codec(&pdev->dev,
- &es7134_codec_driver,
- &es7134_dai, 1);
-}
+ struct device *dev = &pdev->dev;
+ struct es7134_data *priv;
-static int es7134_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ 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", },
- { .compatible = "everest,es7144", },
+ { .compatible = "everest,es7134", .data = &es7134_chip },
+ { .compatible = "everest,es7144", .data = &es7134_chip },
+ { .compatible = "everest,es7154", .data = &es7154_chip },
{ }
};
MODULE_DEVICE_TABLE(of, es7134_ids);
@@ -106,7 +307,6 @@ static struct platform_driver es7134_driver = {
.of_match_table = of_match_ptr(es7134_ids),
},
.probe = es7134_probe,
- .remove = es7134_remove,
};
module_platform_driver(es7134_driver);
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/es8311.c b/sound/soc/codecs/es8311.c
new file mode 100644
index 000000000000..f557e33c26ad
--- /dev/null
+++ b/sound/soc/codecs/es8311.c
@@ -0,0 +1,973 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#include "linux/array_size.h"
+#include "sound/pcm.h"
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "es8311.h"
+
+#define ES8311_NUM_RATES 10
+#define ES8311_RATES (SNDRV_PCM_RATE_8000_96000)
+#define ES8311_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 | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct es8311_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned long mclk_freq;
+ bool provider;
+ unsigned int rates[ES8311_NUM_RATES];
+ struct snd_pcm_hw_constraint_list constraints;
+};
+
+static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0);
+
+#define ES8311_DB_LRCK_STEPS \
+ "0.25db/4LRCK", \
+ "0.25db/8LRCK", \
+ "0.25db/16LRCK", \
+ "0.25db/32LRCK", \
+ "0.25db/64LRCK", \
+ "0.25db/128LRCK", \
+ "0.25db/256LRCK", \
+ "0.25db/512LRCK", \
+ "0.25db/1024LRCK", \
+ "0.25db/2048LRCK", \
+ "0.25db/4096LRCK", \
+ "0.25db/8192LRCK", \
+ "0.25db/16384LRCK", \
+ "0.25db/32768LRCK", \
+ "0.25db/65536LRCK",
+
+static const char *const es8311_level_winsize_txt[] = {
+ "0.25db/2LRCK",
+ ES8311_DB_LRCK_STEPS
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ es8311_alc_winsize, ES8311_ADC4,
+ ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_level_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0),
+);
+
+static const char *const es8311_ramprate_txt[] = {
+ "Disabled",
+ ES8311_DB_LRCK_STEPS
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_adc_ramprate, ES8311_ADC1,
+ ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_automute_winsize_txt[] = {
+ "2048 samples",
+ "4096 samples",
+ "6144 samples",
+ "8192 samples",
+ "10240 samples",
+ "12288 samples",
+ "14336 samples",
+ "16384 samples",
+ "18432 samples",
+ "20480 samples",
+ "22528 samples",
+ "24576 samples",
+ "26624 samples",
+ "28672 samples",
+ "30720 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_automute_winsize, ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0),
+ 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0),
+);
+static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0);
+
+static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_drc_winsize, ES8311_DAC4,
+ ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_ramprate, ES8311_DAC6,
+ ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_out_mode_txt[] = {
+ "Lineout",
+ "Headphones"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_out_mode, ES8311_SYS9,
+ ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt);
+
+static const struct snd_kcontrol_new es8311_snd_controls[] = {
+ /* Capture path */
+ SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10,
+ ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0,
+ es8311_pga_gain_tlv),
+ SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2,
+ ES8311_ADC2_INV_SHIFT, 1, 0),
+ SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2,
+ ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0,
+ es8311_adc_scale_tlv),
+ SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3,
+ ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0,
+ es8311_adc_vol_tlv),
+ SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate),
+ SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4,
+ ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0),
+ SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_NG_SHIFT,
+ ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7,
+ ES8311_ADC7_AUTOMUTE_VOL_SHIFT,
+ ES8311_ADC7_AUTOMUTE_VOL_MAX, 0,
+ es8311_automute_vol_tlv),
+ SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT,
+ 1, 0),
+ SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8,
+ ES8311_ADC8_EQBYPASS_SHIFT, 1, 1),
+ SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MAXLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MINLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize),
+
+ /* Playback path */
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0,
+ ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv),
+ SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MAXLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MINLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize),
+ SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate),
+ SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6,
+ ES8311_DAC6_EQBYPASS_SHIFT, 1, 1),
+
+ SOC_ENUM("Output Mode", es8311_out_mode),
+};
+
+static const char *const es8311_diff_src_txt[] = {
+ "Disabled",
+ "MIC1P-MIC1N",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_diff_src_enum, ES8311_SYS10,
+ ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt);
+static const struct snd_kcontrol_new es8311_diff_src_mux =
+ SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum);
+
+static const char *const es8311_dmic_src_txt[] = {
+ "Disabled",
+ "DMIC from MIC1P",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dmic_src_enum, ES8311_SYS10,
+ ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt);
+static const struct snd_kcontrol_new es8311_dmic_src_mux =
+ SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum);
+
+static const char * const es8311_aif1tx_src_txt[] = {
+ "ADC + ADC",
+ "ADC + 0",
+ "0 + ADC",
+ "0 + 0",
+ "DACL + ADC",
+ "ADC + DACR",
+ "DACL + DACR",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_aif1tx_src_enum, ES8311_GPIO,
+ ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt);
+static const struct snd_kcontrol_new es8311_aif1tx_src_mux =
+ SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum);
+
+static const char * const es8311_dac_src_txt[] = {
+ "Left",
+ "Right"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_src_enum, ES8311_SDP_IN,
+ ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt);
+static const struct snd_kcontrol_new es8311_dac_src_mux =
+ SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum);
+
+static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT,
+ 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3,
+ ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1,
+ NULL, 0),
+
+ /* Capture path */
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_diff_src_mux),
+ SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL,
+ 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4,
+ ES8311_SYS4_PDN_MOD_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dmic_src_mux),
+ SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_aif1tx_src_mux),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT,
+ ES8311_SDP_MUTE_SHIFT, 1),
+
+ /* Playback path */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN,
+ ES8311_SDP_MUTE_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dac_src_mux),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8,
+ ES8311_SYS8_PDN_DAC_SHIFT, 1),
+ SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8311_dapm_routes[] = {
+ /* Capture Path */
+ { "MIC1", NULL, "Bias" },
+ { "MIC1", NULL, "Analog power" },
+ { "MIC1", NULL, "Vref" },
+ { "Differential Mux", "MIC1P-MIC1N", "MIC1" },
+ { "PGA", NULL, "Differential Mux" },
+ { "Mono ADC", NULL, "PGA" },
+ { "Mono ADC", NULL, "ADC Bias Gen" },
+ { "Mono ADC", NULL, "ADC Vref Gen" },
+ { "Mono ADC", NULL, "ADC Clock" },
+ { "Mono ADC", NULL, "ADC Analog Clock" },
+ { "Digital Mic Mux", "Disabled", "Mono ADC" },
+ { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" },
+
+ { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" },
+
+ { "AIF1TX", NULL, "AIF1TX Source Mux" },
+
+ /* Playback Path */
+ { "Mono DAC Source Mux", "Left", "AIF1RX" },
+ { "Mono DAC Source Mux", "Right", "AIF1RX" },
+ { "Mono DAC", NULL, "Mono DAC Source Mux" },
+ { "Mono DAC", NULL, "DAC Clock" },
+ { "Mono DAC", NULL, "DAC Analog Clock" },
+ { "OUT", NULL, "Mono DAC" },
+ { "OUT", NULL, "Bias" },
+ { "OUT", NULL, "Analog power" },
+ { "OUT", NULL, "Vref" },
+ { "OUT", NULL, "DAC Vref Gen" },
+};
+
+/* Bit clock divider values:
+ * from 1 to 20: the register takes the div value - 1
+ * above 20: the register takes the corresponding idx of the div value
+ * in the following table + 20
+ */
+#define ES8311_BCLK_DIV_IDX_OFFSET 20
+static const unsigned int es8311_bclk_divs[] = {
+ 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72
+};
+
+struct es8311_mclk_coeff {
+ unsigned int rate;
+ unsigned int mclk;
+ unsigned int div;
+ unsigned int mult;
+ unsigned int div_adc_dac;
+};
+
+#define ES8311_MCLK_MAX_FREQ 49200000
+
+/* Coefficients for common master clock frequencies based on clock table from
+ * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal
+ * to 256. This to keep the default adc and dac oversampling and adc scale
+ * settings. Internal mclk dividers and multipliers are dynamically adjusted to
+ * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed
+ * mclks frequencies (see es8311_cmp_adj_mclk_coeff).
+ * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512
+ * (upper limit due to max mclk freq of 49.2MHz).
+ */
+static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = {
+ { 8000, 2048000, 1, 1, 1 },
+ { 8000, 6144000, 3, 1, 1 },
+ { 8000, 18432000, 3, 1, 3 },
+ { 11025, 2822400, 1, 1, 1 },
+ { 11025, 8467200, 3, 1, 1 },
+ { 16000, 4096000, 1, 1, 1 },
+ { 16000, 12288000, 3, 1, 1 },
+ { 16000, 18432000, 3, 2, 3 },
+ { 22050, 5644800, 1, 1, 1 },
+ { 22050, 16934400, 3, 1, 1 },
+ { 32000, 8192000, 1, 1, 1 },
+ { 32000, 12288000, 3, 2, 1 },
+ { 32000, 18432000, 3, 4, 3 },
+ { 44100, 11289600, 1, 1, 1 },
+ { 44100, 33868800, 3, 1, 1 },
+ { 48000, 12288000, 1, 1, 1 },
+ { 48000, 18432000, 3, 2, 1 },
+ { 64000, 8192000, 1, 2, 1 },
+ { 64000, 12288000, 3, 4, 1 },
+ { 88200, 11289600, 1, 2, 1 },
+ { 88200, 33868800, 3, 2, 1 },
+ { 96000, 12288000, 1, 2, 1 },
+ { 96000, 18432000, 3, 4, 1 },
+};
+
+/* Compare coeff with provided mclk_freq and adjust it if needed.
+ * If frequencies match, return 0 and the unaltered coeff copy into out_coeff.
+ * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and
+ * the adjusted coeff copy into out_coeff.
+ * Return -EINVAL otherwise.
+ */
+static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq,
+ const struct es8311_mclk_coeff *coeff,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ if (WARN_ON_ONCE(!coeff))
+ return -EINVAL;
+
+ unsigned int div = coeff->div;
+ unsigned int mult = coeff->mult;
+ bool match = false;
+
+ if (coeff->mclk == mclk_freq) {
+ match = true;
+ } else if (mclk_freq % coeff->mclk == 0) {
+ div = mclk_freq / coeff->mclk;
+ div *= coeff->div;
+ if (div <= 8)
+ match = true;
+ } else if (coeff->mclk % mclk_freq == 0) {
+ mult = coeff->mclk / mclk_freq;
+ if (mult == 2 || mult == 4 || mult == 8) {
+ mult *= coeff->mult;
+ if (mult <= 8)
+ match = true;
+ }
+ }
+ if (!match)
+ return -EINVAL;
+ if (out_coeff) {
+ *out_coeff = *coeff;
+ out_coeff->div = div;
+ out_coeff->mult = mult;
+ }
+ return 0;
+}
+
+static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (coeff->rate != rate)
+ continue;
+
+ int ret =
+ es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff);
+ if (ret == 0)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void es8311_set_sysclk_constraints(unsigned int mclk_freq,
+ struct es8311_priv *es8311)
+{
+ unsigned int count = 0;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) &&
+ count < ARRAY_SIZE(es8311->rates); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (count > 0 && coeff->rate == es8311->rates[count - 1])
+ continue;
+
+ int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL);
+ if (ret == 0)
+ es8311->rates[count++] = coeff->rate;
+ }
+ if (count) {
+ es8311->constraints.list = es8311->rates;
+ es8311->constraints.count = count;
+ }
+}
+
+static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int mask = ES8311_DAC1_DAC_DSMMUTE |
+ ES8311_DAC1_DAC_DEMMUTE;
+ unsigned int val = mute ? mask : 0;
+
+ regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val);
+ }
+
+ return 0;
+}
+
+static int es8311_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (es8311->constraints.list) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8311->constraints);
+ }
+
+ return 0;
+}
+
+static int es8311_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 es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+ unsigned int wl;
+ int par_width = params_width(params);
+
+ switch (par_width) {
+ case 16:
+ wl = ES8311_SDP_WL_16;
+ break;
+ case 18:
+ wl = ES8311_SDP_WL_18;
+ break;
+ case 20:
+ wl = ES8311_SDP_WL_20;
+ break;
+ case 24:
+ wl = ES8311_SDP_WL_24;
+ break;
+ case 32:
+ wl = ES8311_SDP_WL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ unsigned int width = (unsigned int)par_width;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8311_SDP_IN,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ } else {
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ }
+
+ if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "mclk frequency %lu too high\n",
+ es8311->mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int mclk_freq = es8311->mclk_freq;
+ unsigned int rate = params_rate(params);
+ unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON;
+
+ if (!mclk_freq) {
+ if (es8311->provider) {
+ dev_err(component->dev,
+ "mclk not configured, cannot run as master\n");
+ return -EINVAL;
+ }
+ dev_dbg(component->dev,
+ "mclk not configured, use bclk as internal mclk\n");
+
+ clkmgr = ES8311_CLKMGR1_MCLK_SEL;
+
+ mclk_freq = rate * width * 2;
+ }
+
+ struct es8311_mclk_coeff coeff;
+ int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff);
+ if (ret) {
+ dev_err(component->dev, "unable to find mclk coefficient\n");
+ return ret;
+ }
+
+ unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON |
+ ES8311_CLKMGR1_BCLK_ON;
+
+ clkmgr |= ES8311_CLKMGR1_BCLK_ON;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr);
+
+ if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 ||
+ coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8))
+ return -EINVAL;
+
+ unsigned int mult;
+
+ switch (coeff.mult) {
+ case 1:
+ mult = 0;
+ break;
+ case 2:
+ mult = 1;
+ break;
+ case 4:
+ mult = 2;
+ break;
+ case 8:
+ mult = 3;
+ break;
+ default:
+ WARN_ON_ONCE(true);
+ return -EINVAL;
+ }
+
+ mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK;
+ clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT |
+ mult << ES8311_CLKMGR2_MULT_PRE_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr);
+
+ mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK;
+ clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT |
+ (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr);
+
+ if (es8311->provider) {
+ unsigned int div_lrclk = mclk_freq / rate;
+
+ if (WARN_ON_ONCE(div_lrclk == 0 ||
+ div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1))
+ return -EINVAL;
+
+ mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK;
+ clkmgr = (div_lrclk - 1) >> 8;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask,
+ clkmgr);
+ clkmgr = (div_lrclk - 1) & 0xFF;
+ snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr);
+
+ if (div_lrclk % (2 * width) != 0) {
+ dev_err(component->dev,
+ "unable to divide mclk %u to generate bclk\n",
+ mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int div_bclk = div_lrclk / (2 * width);
+
+ mask = ES8311_CLKMGR6_DIV_BCLK_MASK;
+ if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) {
+ clkmgr = div_bclk - 1;
+ } else {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) {
+ if (es8311_bclk_divs[i] == div_bclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(es8311_bclk_divs)) {
+ dev_err(component->dev,
+ "bclk divider %u not supported\n",
+ div_bclk);
+ return -EINVAL;
+ }
+
+ clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET;
+ }
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask,
+ clkmgr);
+ }
+
+ return 0;
+}
+
+static int es8311_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 es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "invalid frequency %u: too high\n",
+ freq);
+ return -EINVAL;
+ }
+
+ if (es8311->mclk_freq == freq)
+ return 0;
+
+ es8311->mclk_freq = freq;
+ es8311->constraints.list = NULL;
+ es8311->constraints.count = 0;
+
+ if (freq == 0)
+ return 0;
+
+ int ret = clk_set_rate(es8311->mclk, freq);
+ if (ret) {
+ dev_err(component->dev, "unable to set mclk rate\n");
+ return ret;
+ }
+
+ es8311_set_sysclk_constraints(freq, es8311);
+
+ return ret;
+}
+
+static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master mode */
+ es8311->provider = true;
+
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC,
+ ES8311_RESET_MSC);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave mode */
+ es8311->provider = false;
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int sdp = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sdp |= ES8311_SDP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ sdp |= ES8311_SDP_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "right justified mode not supported\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_DSP_B:
+ sdp |= ES8311_SDP_LRP;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_A:
+ sdp |= ES8311_SDP_FMT_DSP;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ dev_err(component->dev,
+ "inverted fsync not supported in dsp mode\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int clkmgr = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sdp |= ES8311_SDP_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ sdp |= ES8311_SDP_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int mask = ES8311_CLKMGR6_BCLK_INV;
+
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr);
+
+ mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP;
+ snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp);
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp);
+
+ return 0;
+}
+
+static int es8311_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8311_priv *es8311 = 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) {
+ int ret = clk_prepare_enable(es8311->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "unable to prepare mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3,
+ ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8311->mclk);
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8311_dai_ops = {
+ .startup = es8311_startup,
+ .hw_params = es8311_hw_params,
+ .mute_stream = es8311_mute,
+ .set_sysclk = es8311_set_sysclk,
+ .set_fmt = es8311_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8311_dai = {
+ .name = "es8311",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .ops = &es8311_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8311_reset(struct snd_soc_component *component, bool reset)
+{
+ /* Reset procedure:
+ * (1) power down state machine and reset codec blocks then,
+ * (2) after a short delay, power up state machine and leave reset mode.
+ * Specific delay is not documented, using the same as es8316.
+ */
+ unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK;
+
+ if (reset) {
+ /* Enter reset mode */
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_RST_MASK);
+ } else {
+ /* Leave reset mode */
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_CSM_ON);
+ }
+}
+
+static int es8311_suspend(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, true);
+
+ regcache_cache_only(es8311->regmap, true);
+ regcache_mark_dirty(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_resume(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, false);
+
+ regcache_cache_only(es8311->regmap, false);
+ regcache_sync(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_component_probe(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8311->mclk)) {
+ dev_err(component->dev, "invalid mclk\n");
+ return PTR_ERR(es8311->mclk);
+ }
+
+ es8311->mclk_freq = clk_get_rate(es8311->mclk);
+ if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ)
+ es8311_set_sysclk_constraints(es8311->mclk_freq, es8311);
+
+ es8311_reset(component, true);
+ es8311_reset(component, false);
+
+ /* Set minimal power up time */
+ snd_soc_component_write(component, ES8311_SYS1, 0);
+ snd_soc_component_write(component, ES8311_SYS2, 0);
+
+ return 0;
+}
+
+static const struct regmap_config es8311_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8311_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct snd_soc_component_driver es8311_component_driver = {
+ .probe = es8311_component_probe,
+ .suspend = es8311_suspend,
+ .resume = es8311_resume,
+ .set_bias_level = es8311_set_bias_level,
+ .controls = es8311_snd_controls,
+ .num_controls = ARRAY_SIZE(es8311_snd_controls),
+ .dapm_widgets = es8311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets),
+ .dapm_routes = es8311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8311_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8311_priv *es8311;
+
+ struct device *dev = &i2c_client->dev;
+
+ es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL);
+ if (es8311 == NULL)
+ return -ENOMEM;
+
+ es8311->regmap =
+ devm_regmap_init_i2c(i2c_client, &es8311_regmap_config);
+ if (IS_ERR(es8311->regmap))
+ return PTR_ERR(es8311->regmap);
+
+ i2c_set_clientdata(i2c_client, es8311);
+
+ return devm_snd_soc_register_component(dev, &es8311_component_driver,
+ &es8311_dai, 1);
+}
+
+static const struct i2c_device_id es8311_id[] = {
+ { "es8311" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8311_id);
+
+static const struct of_device_id es8311_of_match[] = {
+ {
+ .compatible = "everest,es8311",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8311_of_match);
+
+static struct i2c_driver es8311_i2c_driver = {
+ .driver = {
+ .name = "es8311",
+ .of_match_table = es8311_of_match,
+ },
+ .probe = es8311_i2c_probe,
+ .id_table = es8311_id,
+};
+
+module_i2c_driver(es8311_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8311 driver");
+MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8311.h b/sound/soc/codecs/es8311.h
new file mode 100644
index 000000000000..8a3105bb8443
--- /dev/null
+++ b/sound/soc/codecs/es8311.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#ifndef _ES8311_H
+#define _ES8311_H
+
+#include <linux/bitops.h>
+
+#define ES8311_RESET 0x00
+#define ES8311_RESET_CSM_ON BIT(7)
+#define ES8311_RESET_MSC BIT(6)
+#define ES8311_RESET_RST_MASK GENMASK(4, 0)
+
+/* Clock Manager Registers */
+#define ES8311_CLKMGR1 0x01
+#define ES8311_CLKMGR1_MCLK_SEL BIT(7)
+#define ES8311_CLKMGR1_MCLK_ON BIT(5)
+#define ES8311_CLKMGR1_BCLK_ON BIT(4)
+#define ES8311_CLKMGR1_CLKADC_ON_SHIFT 3
+#define ES8311_CLKMGR1_CLKDAC_ON_SHIFT 2
+#define ES8311_CLKMGR1_ANACLKADC_ON_SHIFT 1
+#define ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT 0
+#define ES8311_CLKMGR2 0x02
+#define ES8311_CLKMGR2_DIV_PRE_MASK GENMASK(7, 5)
+#define ES8311_CLKMGR2_DIV_PRE_SHIFT 5
+#define ES8311_CLKMGR2_DIV_PRE_MAX 0x07
+#define ES8311_CLKMGR2_MULT_PRE_MASK GENMASK(4, 3)
+#define ES8311_CLKMGR2_MULT_PRE_SHIFT 3
+#define ES8311_CLKMGR3 0x03
+#define ES8311_CLKMGR4 0x04
+#define ES8311_CLKMGR5 0x05
+#define ES8311_CLKMGR5_ADC_DIV_MASK GENMASK(7, 4)
+#define ES8311_CLKMGR5_ADC_DIV_SHIFT 4
+#define ES8311_CLKMGR5_DAC_DIV_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR5_DAC_DIV_SHIFT 0
+#define ES8311_CLKMGR6 0x06
+#define ES8311_CLKMGR6_BCLK_INV BIT(5)
+#define ES8311_CLKMGR6_DIV_BCLK_MASK GENMASK(4, 0)
+#define ES8311_CLKMGR7 0x07
+#define ES8311_CLKMGR7_LRCLK_DIV_H_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR8 0x08
+#define ES8311_CLKMGR_LRCLK_DIV_MAX 0x0FFF
+
+/* SDP Mode Registers */
+#define ES8311_SDP_IN 0x09
+#define ES8311_SDP_IN_SEL_SHIFT 7
+#define ES8311_SDP_OUT 0x0A
+/* Following values are the same for both SPD_IN and SDP_OUT */
+#define ES8311_SDP_MUTE_SHIFT 6
+#define ES8311_SDP_LRP BIT(5)
+#define ES8311_SDP_WL_MASK GENMASK(4, 2)
+#define ES8311_SDP_WL_SHIFT 2
+#define ES8311_SDP_WL_24 0x00
+#define ES8311_SDP_WL_20 0x01
+#define ES8311_SDP_WL_18 0x02
+#define ES8311_SDP_WL_16 0x03
+#define ES8311_SDP_WL_32 0x04
+#define ES8311_SDP_FMT_MASK GENMASK(1, 0)
+#define ES8311_SDP_FMT_I2S 0x00
+#define ES8311_SDP_FMT_LEFT_J 0x01
+#define ES8311_SDP_FMT_DSP 0x03
+
+/* System registers */
+#define ES8311_SYS1 0x0B
+#define ES8311_SYS2 0x0C
+#define ES8311_SYS3 0x0D
+#define ES8311_SYS3_PDN_ANA_SHIFT 7
+#define ES8311_SYS3_PDN_IBIASGEN_SHIFT 6
+#define ES8311_SYS3_PDN_ADCBIASGEN_SHIFT 5
+#define ES8311_SYS3_PDN_ADCVREFGEN_SHIFT 4
+#define ES8311_SYS3_PDN_DACVREFGEN_SHIFT 3
+#define ES8311_SYS3_PDN_VREF_SHIFT 2
+#define ES8311_SYS3_PDN_VMIDSEL_MASK GENMASK(1, 0)
+#define ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN 0
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED 1
+#define ES8311_SYS3_PDN_VMIDSEL_NORMAL_OPERATION 2
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_FAST_SPEED 3
+#define ES8311_SYS4 0x0E
+#define ES8311_SYS4_PDN_PGA_SHIFT 6
+#define ES8311_SYS4_PDN_MOD_SHIFT 5
+#define ES8311_SYS5 0x0F
+#define ES8311_SYS6 0x10
+#define ES8311_SYS7 0x11
+#define ES8311_SYS8 0x12
+#define ES8311_SYS8_PDN_DAC_SHIFT 1
+#define ES8311_SYS9 0x13
+#define ES8311_SYS9_HPSW_SHIFT 4
+#define ES8311_SYS10 0x14
+#define ES8311_SYS10_DMIC_ON_SHIFT 6
+#define ES8311_SYS10_LINESEL_SHIFT 4
+#define ES8311_SYS10_PGAGAIN_SHIFT 0
+#define ES8311_SYS10_PGAGAIN_MAX 0x0A
+
+/* ADC Registers*/
+#define ES8311_ADC1 0x15
+#define ES8311_ADC1_RAMPRATE_SHIFT 4
+#define ES8311_ADC2 0x16
+#define ES8311_ADC2_INV_SHIFT 4
+#define ES8311_ADC2_SCALE_SHIFT 0
+#define ES8311_ADC2_SCALE_MAX 0x07
+#define ES8311_ADC3 0x17
+#define ES8311_ADC3_VOLUME_SHIFT 0
+#define ES8311_ADC3_VOLUME_MAX 0xFF
+#define ES8311_ADC4 0x18
+#define ES8311_ADC4_ALC_EN_SHIFT 7
+#define ES8311_ADC4_AUTOMUTE_EN_SHIFT 6
+#define ES8311_ADC4_ALC_WINSIZE_SHIFT 0
+#define ES8311_ADC5 0x19
+#define ES8311_ADC5_ALC_MAXLEVEL_SHIFT 4
+#define ES8311_ADC5_ALC_MAXLEVEL_MAX 0x0F
+#define ES8311_ADC5_ALC_MINLEVEL_SHIFT 0
+#define ES8311_ADC5_ALC_MINLEVEL_MAX 0x0F
+#define ES8311_ADC6 0x1A
+#define ES8311_ADC6_AUTOMUTE_WS_SHIFT 4
+#define ES8311_ADC6_AUTOMUTE_NG_SHIFT 0
+#define ES8311_ADC6_AUTOMUTE_NG_MAX 0x0F
+
+#define ES8311_ADC7 0x1B
+#define ES8311_ADC7_AUTOMUTE_VOL_SHIFT 5
+#define ES8311_ADC7_AUTOMUTE_VOL_MAX 0x07
+#define ES8311_ADC8 0x1C
+#define ES8311_ADC8_EQBYPASS_SHIFT 6
+#define ES8311_ADC8_HPF_SHIFT 5
+
+/* DAC Registers */
+#define ES8311_DAC1 0x31
+#define ES8311_DAC1_DAC_DSMMUTE BIT(6)
+#define ES8311_DAC1_DAC_DEMMUTE BIT(5)
+#define ES8311_DAC2 0x32
+#define ES8311_DAC2_VOLUME_MAX 0xFF
+#define ES8311_DAC3 0x33
+#define ES8311_DAC4 0x34
+#define ES8311_DAC4_DRC_EN_SHIFT 7
+#define ES8311_DAC4_DRC_WINSIZE_SHIFT 0
+#define ES8311_DAC5 0x35
+#define ES8311_DAC5_DRC_MAXLEVEL_SHIFT 4
+#define ES8311_DAC5_DRC_MAXLEVEL_MAX 0x0F
+#define ES8311_DAC5_DRC_MINLEVEL_SHIFT 0
+#define ES8311_DAC5_DRC_MINLEVEL_MAX 0x0F
+#define ES8311_DAC6 0x37
+#define ES8311_DAC6_RAMPRATE_SHIFT 4
+#define ES8311_DAC6_EQBYPASS_SHIFT 3
+
+/* GPIO Registers */
+#define ES8311_GPIO 0x44
+#define ES8311_GPIO_ADC2DAC_SEL_SHIFT 7
+#define ES8311_GPIO_ADCDAT_SEL_SHIFT 4
+
+/* Chip Info Registers */
+#define ES8311_CHIPID1 0xFD /* 0x83 */
+#define ES8311_CHIPID2 0xFE /* 0x11 */
+#define ES8311_CHIPVER 0xFF
+
+#define ES8311_REG_MAX 0xFF
+
+#endif
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index da2d353af5ba..e7bd561a8f40 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -1,41 +1,49 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/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
+ 256, 384, 400, 500, 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];
+ /* ES83xx supports halving the MCLK so it supports twice as many rates
+ */
+ unsigned int allowed_rates[ARRAY_SIZE(supported_mclk_lrck_ratios) * 2];
struct snd_pcm_hw_constraint_list sysclk_constraints;
+ bool jd_inverted;
};
/*
@@ -45,21 +53,24 @@ 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_SCALE(hpmixer_gain_tlv, -1200, 150, 0);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv,
+ 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0),
+ 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 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, 4, TLV_DB_SCALE_ITEM(700, 0, 0),
- 5, 5, TLV_DB_SCALE_ITEM(1000, 0, 0),
- 6, 6, TLV_DB_SCALE_ITEM(1300, 0, 0),
- 7, 7, TLV_DB_SCALE_ITEM(1600, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(1800, 0, 0),
- 9, 9, TLV_DB_SCALE_ITEM(2100, 0, 0),
- 10, 10, TLV_DB_SCALE_ITEM(2400, 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,
@@ -84,16 +95,17 @@ 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,
- 0, 4, 7, 0, hpmixer_gain_tlv),
+ 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 Soft Ramp Rate", ES8316_DAC_SET1, 2, 3, 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),
@@ -109,7 +121,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = {
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,
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 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),
@@ -142,7 +154,7 @@ static const char * const es8316_dmic_txt[] = {
"dmic data at high level",
"dmic data at low level",
};
-static const unsigned int es8316_dmic_values[] = { 0, 1, 2 };
+static const unsigned int es8316_dmic_values[] = { 0, 2, 3 };
static const struct soc_enum es8316_dmic_src_enum =
SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3,
ARRAY_SIZE(es8316_dmic_txt),
@@ -159,8 +171,6 @@ static const char * const es8316_hpmux_texts[] = {
"lin-rin with Boost and PGA"
};
-static const unsigned int es8316_hpmux_values[] = { 0, 1, 2, 3 };
-
static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL,
4, es8316_hpmux_texts);
@@ -191,8 +201,6 @@ static const char * const es8316_dacsrc_texts[] = {
"RDATA TO LDAC, LDATA TO RDAC",
};
-static const unsigned int es8316_dacsrc_values[] = { 0, 1, 2, 3 };
-
static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1,
6, es8316_dacsrc_texts);
@@ -356,28 +364,42 @@ static const struct snd_soc_dapm_route es8316_dapm_routes[] = {
static int es8316_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 es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
- int i;
+ 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;
+ es8316->sysclk_constraints.list = NULL;
+ es8316->sysclk_constraints.count = 0;
if (freq == 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++) {
+ for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
const unsigned int ratio = supported_mclk_lrck_ratios[i];
if (freq % ratio == 0)
es8316->allowed_rates[count++] = freq / ratio;
+
+ /* We also check if the halved MCLK produces a valid rate
+ * since the codec supports halving the MCLK.
+ */
+ if ((freq / ratio) % 2 == 0)
+ es8316->allowed_rates[count++] = freq / ratio / 2;
}
- es8316->sysclk_constraints.list = es8316->allowed_rates;
- es8316->sysclk_constraints.count = count;
+ if (count) {
+ es8316->sysclk_constraints.list = es8316->allowed_rates;
+ es8316->sysclk_constraints.count = count;
+ }
return 0;
}
@@ -385,19 +407,17 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int es8316_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 serdata1 = 0;
u8 serdata2 = 0;
u8 clksw;
u8 mask;
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Codec driver only supports slave mode\n");
- return -EINVAL;
- }
+ 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(codec->dev, "Codec driver only supports I2S format\n");
+ dev_err(component->dev, "Codec driver only supports I2S format\n");
return -EINVAL;
}
@@ -420,15 +440,15 @@ static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV;
- snd_soc_update_bits(codec, ES8316_SERDATA1, mask, serdata1);
+ snd_soc_component_update_bits(component, ES8316_SERDATA1, mask, serdata1);
mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP;
- snd_soc_update_bits(codec, ES8316_SERDATA_ADC, mask, serdata2);
- snd_soc_update_bits(codec, ES8316_SERDATA_DAC, mask, serdata2);
+ 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_update_bits(codec, ES8316_CLKMGR_CLKSW, clksw, clksw);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, clksw, clksw);
return 0;
}
@@ -436,20 +456,13 @@ static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int es8316_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
-
- if (es8316->sysclk == 0) {
- dev_err(codec->dev, "No sysclk provided\n");
- return -EINVAL;
- }
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
- /* The set of sample rates that can be supported depends on the
- * MCLK supplied to the CODEC.
- */
- snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &es8316->sysclk_constraints);
+ if (es8316->sysclk_constraints.list)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8316->sysclk_constraints);
return 0;
}
@@ -458,56 +471,100 @@ 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_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
u8 wordlen = 0;
-
- if (!es8316->sysclk) {
- dev_err(codec->dev, "No MCLK configured\n");
- return -EINVAL;
+ u8 bclk_divider;
+ u16 lrck_divider;
+ int i;
+ unsigned int clk = es8316->sysclk / 2;
+ bool clk_valid = false;
+
+ /* We will start with halved sysclk and see if we can use it
+ * for proper clocking. This is to minimise the risk of running
+ * the CODEC with a too high frequency. We have an SKU where
+ * the sysclk frequency is 48Mhz and this causes the sound to be
+ * sped up. If we can run with a halved sysclk, we will use it,
+ * if we can't use it, then full sysclk will be used.
+ */
+ do {
+ /* Validate supported sample rates that are autodetected from MCLK */
+ for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (clk % ratio != 0)
+ continue;
+ if (clk / ratio == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(supported_mclk_lrck_ratios)) {
+ if (clk == es8316->sysclk)
+ return -EINVAL;
+ clk = es8316->sysclk;
+ } else {
+ clk_valid = true;
+ }
+ } while (!clk_valid);
+
+ if (clk != es8316->sysclk) {
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV);
}
+ lrck_divider = clk / 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:
+ case SNDRV_PCM_FORMAT_S24_3LE:
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_update_bits(codec, ES8316_SERDATA_DAC,
+ snd_soc_component_update_bits(component, ES8316_SERDATA_DAC,
ES8316_SERDATA2_LEN_MASK, wordlen);
- snd_soc_update_bits(codec, ES8316_SERDATA_ADC,
+ 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)
+static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ES8316_DAC_SET1, 0x20,
+ 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)
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_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,
- .digital_mute = es8316_mute,
+ .mute_stream = es8316_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver es8316_dai = {
@@ -527,59 +584,286 @@ static struct snd_soc_dai_driver es8316_dai = {
.formats = ES8316_FORMATS,
},
.ops = &es8316_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int es8316_probe(struct snd_soc_codec *codec)
+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_write(codec, ES8316_RESET, 0x3f);
+ snd_soc_component_write(component, ES8316_RESET, 0x3f);
usleep_range(5000, 5500);
- snd_soc_write(codec, ES8316_RESET, ES8316_RESET_CSM_ON);
+ 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_write(codec, ES8316_SYS_VMIDSEL, 0xff);
+ 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_write(codec, ES8316_CLKMGR_ADCOSR, 0x32);
+ snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, 0x32);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_es8316 = {
- .probe = es8316_probe,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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,
- .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = es8316_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
};
-static int es8316_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
{
+ struct device *dev = &i2c_client->dev;
struct es8316_priv *es8316;
- struct regmap *regmap;
+ int ret;
es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
GFP_KERNEL);
@@ -588,37 +872,50 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, es8316);
- regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
+ 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 snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_es8316,
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_es8316,
&es8316_dai, 1);
}
-static int es8316_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id es8316_i2c_id[] = {
- {"es8316", 0 },
+ {"es8316" },
{}
};
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 = {
@@ -627,7 +924,6 @@ static struct i2c_driver es8316_i2c_driver = {
.of_match_table = of_match_ptr(es8316_of_match),
},
.probe = es8316_i2c_probe,
- .remove = es8316_i2c_remove,
.id_table = es8316_i2c_id,
};
module_i2c_driver(es8316_i2c_driver);
diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h
index 6bcdd63ea459..0ff16f948690 100644
--- a/sound/soc/codecs/es8316.h
+++ b/sound/soc/codecs/es8316.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright Everest Semiconductor Co.,Ltd
*
* Author: David Yang <yangxiaohua@everest-semi.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 _ES8316_H
@@ -126,4 +122,14 @@
#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
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_DIV 0x80
+
#endif
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
new file mode 100644
index 000000000000..eb85b71e87f3
--- /dev/null
+++ b/sound/soc/codecs/es8323.c
@@ -0,0 +1,791 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8323.c -- es8323 ALSA SoC audio driver
+//
+// Copyright 2024 Rockchip Electronics Co. Ltd.
+// Copyright 2024 Everest Semiconductor Co.,Ltd.
+// Copyright 2024 Loongson Technology Co.,Ltd.
+//
+// Author: Mark Brown <broonie@kernel.org>
+// Jianqun Xu <jay.xu@rock-chips.com>
+// Nickey Yang <nickey.yang@rock-chips.com>
+// Further cleanup and restructuring by:
+// Binbin Zhou <zhoubinbin@loongson.cn>
+
+#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/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 "es8323.h"
+
+struct es8323_priv {
+ unsigned int sysclk;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ struct snd_soc_component *component;
+};
+
+/* es8323 register cache */
+static const struct reg_default es8323_reg_defaults[] = {
+ { ES8323_CONTROL1, 0x06 },
+ { ES8323_CONTROL2, 0x1c },
+ { ES8323_CHIPPOWER, 0xc3 },
+ { ES8323_ADCPOWER, 0xfc },
+ { ES8323_DACPOWER, 0xc0 },
+ { ES8323_CHIPLOPOW1, 0x00 },
+ { ES8323_CHIPLOPOW2, 0x00 },
+ { ES8323_ANAVOLMANAG, 0x7c },
+ { ES8323_MASTERMODE, 0x80 },
+ { ES8323_ADCCONTROL1, 0x00 },
+ { ES8323_ADCCONTROL2, 0x00 },
+ { ES8323_ADCCONTROL3, 0x06 },
+ { ES8323_ADCCONTROL4, 0x00 },
+ { ES8323_ADCCONTROL5, 0x06 },
+ { ES8323_ADCCONTROL6, 0x30 },
+ { ES8323_ADC_MUTE, 0x30 },
+ { ES8323_LADC_VOL, 0xc0 },
+ { ES8323_RADC_VOL, 0xc0 },
+ { ES8323_ADCCONTROL10, 0x38 },
+ { ES8323_ADCCONTROL11, 0xb0 },
+ { ES8323_ADCCONTROL12, 0x32 },
+ { ES8323_ADCCONTROL13, 0x06 },
+ { ES8323_ADCCONTROL14, 0x00 },
+ { ES8323_DACCONTROL1, 0x00 },
+ { ES8323_DACCONTROL2, 0x06 },
+ { ES8323_DAC_MUTE, 0x30 },
+ { ES8323_LDAC_VOL, 0xc0 },
+ { ES8323_RDAC_VOL, 0xc0 },
+ { ES8323_DACCONTROL6, 0x08 },
+ { ES8323_DACCONTROL7, 0x06 },
+ { ES8323_DACCONTROL8, 0x1f },
+ { ES8323_DACCONTROL9, 0xf7 },
+ { ES8323_DACCONTROL10, 0xfd },
+ { ES8323_DACCONTROL11, 0xff },
+ { ES8323_DACCONTROL12, 0x1f },
+ { ES8323_DACCONTROL13, 0xf7 },
+ { ES8323_DACCONTROL14, 0xfd },
+ { ES8323_DACCONTROL15, 0xff },
+ { ES8323_DACCONTROL16, 0x00 },
+ { ES8323_DACCONTROL17, 0x38 },
+ { ES8323_DACCONTROL18, 0x38 },
+ { ES8323_DACCONTROL19, 0x38 },
+ { ES8323_DACCONTROL20, 0x38 },
+ { ES8323_DACCONTROL21, 0x38 },
+ { ES8323_DACCONTROL22, 0x38 },
+ { ES8323_DACCONTROL23, 0x00 },
+ { ES8323_LOUT1_VOL, 0x00 },
+ { ES8323_ROUT1_VOL, 0x00 },
+};
+
+static const char *const es8323_stereo_3d_texts[] = { "No 3D ", "Level 1", "Level 2", "Level 3",
+ "Level 4", "Level 5", "Level 6", "Level 7" };
+static SOC_ENUM_SINGLE_DECL(es8323_stereo_3d_enum, ES8323_DACCONTROL7, 2, es8323_stereo_3d_texts);
+
+static const char *const es8323_alc_func_texts[] = { "Off", "Right", "Left", "Stereo" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_function_enum,
+ ES8323_ADCCONTROL10, 6, es8323_alc_func_texts);
+
+static const char *const es8323_ng_type_texts[] = { "Constant PGA Gain", "Mute ADC Output" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_ng_type_enum, ES8323_ADCCONTROL14, 1, es8323_ng_type_texts);
+
+static const char *const es8323_deemph_texts[] = { "None", "32Khz", "44.1Khz", "48Khz" };
+static SOC_ENUM_SINGLE_DECL(es8323_playback_deemphasis_enum,
+ ES8323_DACCONTROL6, 6, es8323_deemph_texts);
+
+static const char *const es8323_adcpol_texts[] = { "Normal", "L Invert",
+ "R Invert", "L + R Invert" };
+static SOC_ENUM_SINGLE_DECL(es8323_capture_polarity_enum,
+ ES8323_ADCCONTROL6, 6, es8323_adcpol_texts);
+
+static const DECLARE_TLV_DB_SCALE(es8323_adc_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_dac_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_out_tlv, -4500, 150, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv2, -15, 300, 0);
+
+static const struct snd_kcontrol_new es8323_snd_controls[] = {
+ SOC_ENUM("3D Mode", es8323_stereo_3d_enum),
+ SOC_ENUM("ALC Capture Function", es8323_alc_function_enum),
+ SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum),
+ SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum),
+ SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum),
+ SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
+ SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
+ SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0),
+ SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
+ 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
+ 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
+ 7, 1, es8323_bypass_tlv2),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
+ 3, 7, 1, es8323_bypass_tlv2),
+ SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL,
+ 0, 192, 1, es8323_dac_tlv),
+ SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL,
+ ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv),
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL,
+ ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL,
+ ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv),
+};
+
+/* Left DAC Route */
+static const char *const es8323_pga_sell[] = { "Line 1L", "Line 2L", "NC", "DifferentialL" };
+static SOC_ENUM_SINGLE_DECL(es8323_left_dac_enum, ES8323_ADCCONTROL2, 6, es8323_pga_sell);
+static const struct snd_kcontrol_new es8323_left_dac_mux_controls =
+ SOC_DAPM_ENUM("Left DAC Route", es8323_left_dac_enum);
+
+/* Right DAC Route */
+static const char *const es8323_pga_selr[] = { "Line 1R", "Line 2R", "NC", "DifferentialR" };
+static SOC_ENUM_SINGLE_DECL(es8323_right_dac_enum, ES8323_ADCCONTROL2, 4, es8323_pga_selr);
+static const struct snd_kcontrol_new es8323_right_dac_mux_controls =
+ SOC_DAPM_ENUM("Right DAC Route", es8323_right_dac_enum);
+
+/* Left Line Mux */
+static const char *const es8323_lin_sell[] = { "Line 1L", "Line 2L", "NC", "MicL" };
+static SOC_ENUM_SINGLE_DECL(es8323_llin_enum, ES8323_DACCONTROL16, 3, es8323_lin_sell);
+static const struct snd_kcontrol_new es8323_left_line_controls =
+ SOC_DAPM_ENUM("LLIN Mux", es8323_llin_enum);
+
+/* Right Line Mux */
+static const char *const es8323_lin_selr[] = { "Line 1R", "Line 2R", "NC", "MicR" };
+static SOC_ENUM_SINGLE_DECL(es8323_rlin_enum, ES8323_DACCONTROL16, 0, es8323_lin_selr);
+static const struct snd_kcontrol_new es8323_right_line_controls =
+ SOC_DAPM_ENUM("RLIN Mux", es8323_rlin_enum);
+
+/* Differential Mux */
+static const char *const es8323_diffmux_sel[] = { "Line 1", "Line 2" };
+static SOC_ENUM_SINGLE_DECL(es8323_diffmux_enum, ES8323_ADCCONTROL3, 7, es8323_diffmux_sel);
+static const struct snd_kcontrol_new es8323_diffmux_controls =
+ SOC_DAPM_ENUM("Route2", es8323_diffmux_enum);
+
+/* Mono ADC Mux */
+static const char *const es8323_mono_adc_mux[] = { "Stereo", "Mono (Left)", "Mono (Right)" };
+static SOC_ENUM_SINGLE_DECL(es8323_mono_adc_mux_enum, ES8323_ADCCONTROL3, 3, es8323_mono_adc_mux);
+static const struct snd_kcontrol_new es8323_mono_adc_mux_controls =
+ SOC_DAPM_ENUM("Mono Mux", es8323_mono_adc_mux_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1),
+
+ /* Muxes */
+ SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_left_mixer_controls[0],
+ ARRAY_SIZE(es8323_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_right_mixer_controls[0],
+ ARRAY_SIZE(es8323_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 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_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route es8323_dapm_routes[] = {
+ /*12.22*/
+ {"Left PGA Mux", "Line 1L", "LINPUT1"},
+ {"Left PGA Mux", "Line 2L", "LINPUT2"},
+ {"Left PGA Mux", "DifferentialL", "Differential Mux"},
+
+ {"Right PGA Mux", "Line 1R", "RINPUT1"},
+ {"Right PGA Mux", "Line 2R", "RINPUT2"},
+ {"Right PGA Mux", "DifferentialR", "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", "Right PGA Mux"},
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+
+ {"Right ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+
+ {"Left ADC Power", NULL, "Left ADC Mux"},
+ {"Right ADC Power", NULL, "Right ADC Mux"},
+ {"Left ADC", NULL, "Left ADC Power"},
+ {"Right ADC", NULL, "Right ADC Power"},
+
+ {"Left Line Mux", "Line 1L", "LINPUT1"},
+ {"Left Line Mux", "Line 2L", "LINPUT2"},
+ {"Left Line Mux", "MicL", "Left PGA Mux"},
+
+ {"Right Line Mux", "Line 1R", "RINPUT1"},
+ {"Right Line Mux", "Line 2R", "RINPUT2"},
+ {"Right Line Mux", "MicR", "Right PGA Mux"},
+
+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+
+ {"Right Mixer", "Right Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ {"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"},
+};
+
+struct coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct coeff_div es8323_coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0xa, 0x0},
+ {11289600, 8000, 1408, 0x9, 0x0},
+ {18432000, 8000, 2304, 0xc, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x0},
+ {12000000, 8000, 1500, 0xb, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x7, 0x0},
+ {16934400, 11025, 1536, 0xa, 0x0},
+ {12000000, 11025, 1088, 0x9, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0x6, 0x0},
+ {18432000, 16000, 1152, 0x8, 0x0},
+ {12000000, 16000, 750, 0x7, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x4, 0x0},
+ {16934400, 22050, 768, 0x6, 0x0},
+ {12000000, 22050, 544, 0x6, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x3, 0x0},
+ {18432000, 32000, 576, 0x5, 0x0},
+ {12000000, 32000, 375, 0x4, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x2, 0x0},
+ {16934400, 44100, 384, 0x3, 0x0},
+ {12000000, 44100, 272, 0x3, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x2, 0x0},
+ {18432000, 48000, 384, 0x3, 0x0},
+ {12000000, 48000, 250, 0x2, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x0, 0x0},
+ {16934400, 88200, 192, 0x1, 0x0},
+ {12000000, 88200, 136, 0x1, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x0, 0x0},
+ {18432000, 96000, 192, 0x1, 0x0},
+ {12000000, 96000, 125, 0x0, 0x1},
+};
+
+static unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static unsigned int rates_112896[] = {
+ 8000, 11025, 22050, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_112896 = {
+ .count = ARRAY_SIZE(rates_112896),
+ .list = rates_112896,
+};
+
+static unsigned int rates_12[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 48000, 88235, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12 = {
+ .count = ARRAY_SIZE(rates_12),
+ .list = rates_12,
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8323_coeff_div); i++) {
+ if (es8323_coeff_div[i].rate == rate &&
+ es8323_coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int es8323_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 es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 11289600:
+ case 18432000:
+ case 22579200:
+ case 36864000:
+ es8323->sysclk_constraints = &constraints_112896;
+ break;
+ case 12288000:
+ case 16934400:
+ case 24576000:
+ case 33868800:
+ es8323->sysclk_constraints = &constraints_12288;
+ break;
+ case 12000000:
+ case 24000000:
+ es8323->sysclk_constraints = &constraints_12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ es8323->sysclk = freq;
+ return 0;
+}
+
+static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE);
+ u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE);
+ u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ iface |= 0x80;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ iface &= 0x7f;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adciface &= 0xfc;
+ daciface &= 0xf8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adciface &= 0xfd;
+ daciface &= 0xf9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adciface &= 0xfe;
+ daciface &= 0xfa;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ adciface &= 0xff;
+ daciface &= 0xfb;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xdf;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x20;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xdf;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, iface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+
+ return 0;
+}
+
+static int es8323_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ if (es8323->sysclk) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8323->sysclk_constraints);
+ }
+
+ return 0;
+}
+
+static int es8323_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 es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80;
+ u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3;
+ u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7;
+ int coeff;
+
+ coeff = get_coeff(es8323->sysclk, params_rate(params));
+ if (coeff < 0) {
+ coeff = get_coeff(es8323->sysclk / 2, params_rate(params));
+ srate |= 0x40;
+ }
+
+ if (coeff < 0) {
+ dev_err(component->dev,
+ "Unable to configure sample rate %dHz with %dHz MCLK\n",
+ params_rate(params), es8323->sysclk);
+ return coeff;
+ }
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adciface |= 0xc;
+ daciface |= 0x18;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adciface |= 0x4;
+ daciface |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adciface |= 0x10;
+ daciface |= 0x20;
+ break;
+ }
+
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, srate);
+ snd_soc_component_write(component, ES8323_ADCCONTROL5,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+ snd_soc_component_write(component, ES8323_DACCONTROL2,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+
+ return 0;
+}
+
+static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 val = mute ? 0x6 : 0x2;
+
+ snd_soc_component_write(component, ES8323_DAC_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8323_ops = {
+ .startup = es8323_pcm_startup,
+ .hw_params = es8323_pcm_hw_params,
+ .set_fmt = es8323_set_dai_fmt,
+ .set_sysclk = es8323_set_dai_sysclk,
+ .mute_stream = es8323_mute_stream,
+};
+
+#define ES8323_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver es8323_dai = {
+ .name = "ES8323 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .ops = &es8323_ops,
+ .symmetric_rate = 1,
+};
+
+static int es8323_probe(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8323->component = component;
+
+ es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8323->mclk)) {
+ dev_err(component->dev, "unable to get mclk\n");
+ return PTR_ERR(es8323->mclk);
+ }
+
+ if (!es8323->mclk)
+ dev_warn(component->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+
+ return 0;
+}
+
+static int es8323_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret)
+ return ret;
+
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0);
+ usleep_range(18000, 20000);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x09);
+ snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x59);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8323->mclk);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0xC0);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b);
+ break;
+ }
+
+ return 0;
+}
+
+static void es8323_remove(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8323->mclk);
+ es8323_set_bias_level(component, SND_SOC_BIAS_OFF);
+}
+
+static int es8323_suspend(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, true);
+ regcache_mark_dirty(es8323->regmap);
+
+ return 0;
+}
+
+static int es8323_resume(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, false);
+ regcache_sync(es8323->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8323 = {
+ .probe = es8323_probe,
+ .remove = es8323_remove,
+ .suspend = es8323_suspend,
+ .resume = es8323_resume,
+ .set_bias_level = es8323_set_bias_level,
+ .controls = es8323_snd_controls,
+ .num_controls = ARRAY_SIZE(es8323_snd_controls),
+ .dapm_widgets = es8323_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8323_dapm_widgets),
+ .dapm_routes = es8323_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8323_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config es8323_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x53,
+ .reg_defaults = es8323_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(es8323_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int es8323_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8323_priv *es8323;
+ struct device *dev = &i2c_client->dev;
+
+ es8323 = devm_kzalloc(dev, sizeof(*es8323), GFP_KERNEL);
+ if (!es8323)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8323);
+
+ es8323->regmap = devm_regmap_init_i2c(i2c_client, &es8323_regmap);
+ if (IS_ERR(es8323->regmap))
+ return PTR_ERR(es8323->regmap);
+
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_es8323,
+ &es8323_dai, 1);
+}
+
+static const struct i2c_device_id es8323_i2c_id[] = {
+ { "es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
+
+static const struct acpi_device_id es8323_acpi_match[] = {
+ { "ESSX8323", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, es8323_acpi_match);
+
+static const struct of_device_id es8323_of_match[] = {
+ { .compatible = "everest,es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8323_of_match);
+
+static struct i2c_driver es8323_i2c_driver = {
+ .driver = {
+ .name = "ES8323",
+ .acpi_match_table = es8323_acpi_match,
+ .of_match_table = es8323_of_match,
+ },
+ .probe = es8323_i2c_probe,
+ .id_table = es8323_i2c_id,
+};
+module_i2c_driver(es8323_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8323 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h
new file mode 100644
index 000000000000..f986c9301dc6
--- /dev/null
+++ b/sound/soc/codecs/es8323.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ * Binbin Zhou <zhoubinbin@loongson.cn>
+ *
+ */
+
+#ifndef _ES8323_H
+#define _ES8323_H
+
+/* ES8323 register space */
+
+/* Chip Control and Power Management */
+#define ES8323_CONTROL1 0x00
+#define ES8323_CONTROL2 0x01
+#define ES8323_CHIPPOWER 0x02
+#define ES8323_ADCPOWER 0x03
+#define ES8323_DACPOWER 0x04
+#define ES8323_CHIPLOPOW1 0x05
+#define ES8323_CHIPLOPOW2 0x06
+#define ES8323_ANAVOLMANAG 0x07
+#define ES8323_MASTERMODE 0x08
+
+/* ADC Control */
+#define ES8323_ADCCONTROL1 0x09
+#define ES8323_ADCCONTROL2 0x0a
+#define ES8323_ADCCONTROL3 0x0b
+#define ES8323_ADCCONTROL4 0x0c
+#define ES8323_ADCCONTROL5 0x0d
+#define ES8323_ADCCONTROL6 0x0e
+#define ES8323_ADC_MUTE 0x0f
+#define ES8323_LADC_VOL 0x10
+#define ES8323_RADC_VOL 0x11
+#define ES8323_ADCCONTROL10 0x12
+#define ES8323_ADCCONTROL11 0x13
+#define ES8323_ADCCONTROL12 0x14
+#define ES8323_ADCCONTROL13 0x15
+#define ES8323_ADCCONTROL14 0x16
+
+/* DAC Control */
+#define ES8323_DACCONTROL1 0x17
+#define ES8323_DACCONTROL2 0x18
+#define ES8323_DAC_MUTE 0x19
+#define ES8323_LDAC_VOL 0x1a
+#define ES8323_RDAC_VOL 0x1b
+#define ES8323_DACCONTROL6 0x1c
+#define ES8323_DACCONTROL7 0x1d
+#define ES8323_DACCONTROL8 0x1e
+#define ES8323_DACCONTROL9 0x1f
+#define ES8323_DACCONTROL10 0x20
+#define ES8323_DACCONTROL11 0x21
+#define ES8323_DACCONTROL12 0x22
+#define ES8323_DACCONTROL13 0x23
+#define ES8323_DACCONTROL14 0x24
+#define ES8323_DACCONTROL15 0x25
+#define ES8323_DACCONTROL16 0x26
+#define ES8323_DACCONTROL17 0x27
+#define ES8323_DACCONTROL18 0x28
+#define ES8323_DACCONTROL19 0x29
+#define ES8323_DACCONTROL20 0x2a
+#define ES8323_DACCONTROL21 0x2b
+#define ES8323_DACCONTROL22 0x2c
+#define ES8323_DACCONTROL23 0x2d
+#define ES8323_LOUT1_VOL 0x2e
+#define ES8323_ROUT1_VOL 0x2f
+#define ES8323_LOUT2_VOL 0x30
+#define ES8323_ROUT2_VOL 0x31
+#define ES8323_DACCONTROL28 0x32
+#define ES8323_DACCONTROL29 0x33
+#define ES8323_DACCONTROL30 0x34
+
+#define ES8323_ADC_IFACE ES8323_ADCCONTROL4
+#define ES8323_ADC_SRATE ES8323_ADCCONTROL5
+#define ES8323_DAC_IFACE ES8323_DACCONTROL1
+#define ES8323_DAC_SRATE ES8323_DACCONTROL2
+#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
new file mode 100644
index 000000000000..78c4e68f6002
--- /dev/null
+++ b/sound/soc/codecs/es8326.c
@@ -0,0 +1,1374 @@
+// 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 jack_pol;
+ u8 interrupt_src;
+ u8 interrupt_clk;
+ u8 hpl_vol;
+ u8 hpr_vol;
+ bool jd_inverted;
+ unsigned int sysclk;
+
+ bool calibrated;
+ int version;
+ int hp;
+ int jack_remove_retry;
+};
+
+static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x20;
+ crosstalk_l &= 0xf0;
+ crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = (crosstalk & 0x10) << 1;
+ crosstalk_l &= 0x0f;
+ crosstalk_l |= (crosstalk & 0x0f) << 4;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x20, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x10;
+ crosstalk_l &= 0x0f;
+ crosstalk = crosstalk_h | crosstalk_l;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = crosstalk & 0x10;
+ crosstalk_l &= 0xf0;
+ crosstalk_l |= crosstalk & 0x0f;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x10, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_hplvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpl_vol;
+
+ return 0;
+}
+
+static int es8326_hplvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpl_vol != hp_vol) {
+ es8326->hpl_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x70, (hp_vol << 4));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int es8326_hprvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpr_vol;
+
+ return 0;
+}
+
+static int es8326_hprvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpr_vol != hp_vol) {
+ es8326->hpr_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x07, hp_vol);
+ return 1;
+ }
+
+ return 0;
+}
+
+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 char *const hp_spkvol_switch[] = {
+ "HPVOL: HPL+HPL, SPKVOL: HPL+HPL",
+ "HPVOL: HPL+HPR, SPKVOL: HPL+HPR",
+ "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR",
+ "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR",
+};
+
+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 soc_enum hpvol_spkvol_switch =
+ SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch);
+
+static const struct snd_kcontrol_new es8326_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_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),
+
+ SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk1_get, es8326_crosstalk1_set),
+ SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk2_get, es8326_crosstalk2_set),
+ SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hplvol_get, es8326_hplvol_set),
+ SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hprvol_get, es8326_hprvol_set),
+
+ SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+
+ SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch),
+};
+
+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),
+
+ /* 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("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),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
+ {"ADC L", NULL, "MIC1"},
+ {"ADC R", NULL, "MIC2"},
+ {"ADC L", NULL, "MIC3"},
+ {"ADC R", NULL, "MIC4"},
+
+ {"I2S OUT", NULL, "ADC L"},
+ {"I2S OUT", NULL, "ADC R"},
+
+ {"Right DAC", NULL, "I2S IN"},
+ {"Left DAC", NULL, "I2S IN"},
+
+ {"LHPMIX", NULL, "Left DAC"},
+ {"RHPMIX", NULL, "Right DAC"},
+
+ {"HPOL", NULL, "LHPMIX"},
+ {"HPOR", NULL, "RHPMIX"},
+};
+
+static bool es8326_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_HPL_OFFSET_INI:
+ case ES8326_HPR_OFFSET_INI:
+ case ES8326_HPDET_STA:
+ case ES8326_CTIA_OMTP_STA:
+ case ES8326_CSM_MUTE_STA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool es8326_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_BIAS_SW1:
+ case ES8326_BIAS_SW2:
+ case ES8326_BIAS_SW3:
+ case ES8326_BIAS_SW4:
+ case ES8326_ADC_HPFS1:
+ case ES8326_ADC_HPFS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .use_single_read = true,
+ .use_single_write = true,
+ .volatile_reg = es8326_volatile_register,
+ .writeable_reg = es8326_writeable_register,
+ .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_v0[] = {
+ {64, 8000, 512000, 0x60, 0x01, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 8000, 1024000, 0x60, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {192, 32000, 6144000, 0xE0, 0x02, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 8000, 2048000, 0x60, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {384, 32000, 12288000, 0xE0, 0x05, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0xE9, 0x04, 0x0F, 0x6d, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+};
+
+static const struct _coeff_div coeff_div_v3[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {64, 8000, 512000, 0x60, 0x00, 0x35, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0xB8, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+
+ {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {192, 32000, 6144000, 0xE0, 0x02, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0xCA, 0x1B, 0x1F, 0x3F},
+
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {384, 32000, 12288000, 0xE0, 0x02, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F},
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x08, 0x19, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+};
+
+static inline int get_coeff(int mclk, int rate, int array,
+ const struct _coeff_div *coeff_div)
+{
+ int i;
+
+ for (i = 0; i < array; 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;
+ const struct _coeff_div *coeff_div;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u8 srate = 0;
+ int coeff, array;
+
+ if (es8326->version == 0) {
+ coeff_div = coeff_div_v0;
+ array = ARRAY_SIZE(coeff_div_v0);
+ } else {
+ coeff_div = coeff_div_v3;
+ array = ARRAY_SIZE(coeff_div_v3);
+ }
+ coeff = get_coeff(es8326->sysclk, params_rate(params), array, coeff_div);
+ /* 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_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int offset_l, offset_r;
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
+ 0x30, 0x00);
+ } else {
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x0F);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ }
+ } else {
+ if (!es8326->calibrated) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
+ msleep(30);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
+ es8326->calibrated = true;
+ }
+ regmap_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x00);
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ } else {
+ msleep(300);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x00);
+ }
+ }
+ 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_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02);
+ usleep_range(5000, 10000);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
+ (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x10);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0xc0);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x80);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ 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,
+ .mute_stream = es8326_mute,
+ .no_capture_mute = 0,
+};
+
+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_HPDET_STA);
+ switch (iface) {
+ case 0x93:
+ /* pause button detected */
+ cur_button = SND_JACK_BTN_0;
+ break;
+ case 0x6f:
+ case 0x4b:
+ /* button volume up */
+ if ((iface == 0x6f) && (es8326->version > ES8326_VERSION_B))
+ cur_button = SND_JACK_BTN_0;
+ else
+ cur_button = SND_JACK_BTN_1;
+ break;
+ case 0x27:
+ /* button volume down */
+ cur_button = SND_JACK_BTN_2;
+ break;
+ case 0x1e:
+ case 0xe2:
+ /* button released or not pressed */
+ cur_button = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((prev_button == cur_button) && (cur_button != 0)) {
+ press_count++;
+ if (press_count > 3) {
+ /* report a press every 120ms */
+ 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(35));
+ } 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(35));
+ } 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;
+ }
+ es8326_disable_micbias(es8326->component);
+ }
+ 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_HPDET_STA);
+ dev_dbg(comp->dev, "gpio flag %#04x", iface);
+
+ if ((es8326->jack_remove_retry == 1) && (es8326->version < ES8326_VERSION_B)) {
+ if (iface & ES8326_HPINSERT_FLAG)
+ es8326->jack_remove_retry = 2;
+ else
+ es8326->jack_remove_retry = 0;
+
+ dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n",
+ es8326->jack_remove_retry);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ ~es8326->jack_pol : es8326->jack_pol));
+ goto exit;
+ }
+
+ if ((iface & ES8326_HPINSERT_FLAG) == 0) {
+ /* Jack unplugged or spurious IRQ */
+ dev_dbg(comp->dev, "No headset detected\n");
+ es8326_disable_micbias(es8326->component);
+ if (es8326->jack->status & SND_JACK_HEADPHONE) {
+ dev_dbg(comp->dev, "Report hp remove event\n");
+ snd_soc_jack_report(es8326->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ /* mute adc when mic path switch */
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ }
+ es8326->hp = 0;
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ if ((es8326->jack_remove_retry == 0) && (es8326->version < ES8326_VERSION_B)) {
+ es8326->jack_remove_retry = 1;
+ dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n",
+ es8326->jack_remove_retry);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ es8326->jack_pol : ~es8326->jack_pol));
+
+ } else {
+ es8326->jack_remove_retry = 0;
+ }
+ } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) {
+ es8326->jack_remove_retry = 0;
+ if (es8326->hp == 0) {
+ dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n");
+ /*
+ * set auto-check mode, then restart jack_detect_work after 400ms.
+ * Don't report jack status.
+ */
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x00);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x10);
+ usleep_range(50000, 70000);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x0d);
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(400));
+ es8326->hp = 1;
+ goto exit;
+ }
+ if (es8326->jack->status & SND_JACK_HEADSET) {
+ /* detect button */
+ dev_dbg(comp->dev, "button pressed\n");
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ es8326_enable_micbias(es8326->component);
+ queue_delayed_work(system_wq, &es8326->button_press_work, 10);
+ goto exit;
+ }
+ if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) {
+ dev_dbg(comp->dev, "Headphone detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ } else {
+ dev_dbg(comp->dev, "Headset detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x08);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN,
+ 0x80, 0x80);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x00);
+ usleep_range(10000, 15000);
+ }
+ }
+exit:
+ mutex_unlock(&es8326->lock);
+}
+
+static irqreturn_t es8326_irq(int irq, void *dev_id)
+{
+ struct es8326_priv *es8326 = dev_id;
+
+ if (!es8326->jack)
+ goto out;
+
+ 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_calibrate(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ unsigned int offset_l, offset_r;
+
+ regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
+ es8326->version = reg;
+
+ if ((es8326->version >= ES8326_VERSION_B) && (es8326->calibrated == false)) {
+ dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n");
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08);
+ regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1);
+ regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
+ usleep_range(15000, 20000);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ usleep_range(15000, 20000);
+ regmap_write(es8326->regmap, ES8326_RESET, 0xc0);
+ usleep_range(15000, 20000);
+
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, &reg);
+ if ((reg & 0xf0) != 0x40)
+ msleep(50);
+
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+
+ es8326->calibrated = true;
+ }
+
+ return 0;
+}
+
+static void es8326_init(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x3E);
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+ usleep_range(10000, 15000);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xd9);
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xd8);
+ /* set headphone default type and detect pin */
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83);
+ regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
+
+ /* set internal oscillator as clock source of headpone cp */
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89);
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ /* clock manager reset release */
+ regmap_write(es8326->regmap, ES8326_RESET, 0x17);
+ /* set headphone detection as half scan mode */
+ regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d);
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
+
+ /* enable headphone driver */
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81);
+ regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00);
+ /* calibrate for B version */
+ es8326_calibrate(component);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa);
+ regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00);
+ /* turn off headphone out */
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00);
+ /* set ADC and DAC in low power mode */
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ /* select vdda as micbias source */
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x03);
+ /* set dac dsmclip = 1 */
+ regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08);
+ regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
+
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
+ ((es8326->version >= ES8326_VERSION_B) ?
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
+ usleep_range(5000, 10000);
+ es8326_enable_micbias(es8326->component);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO,
+ es8326->interrupt_clk);
+ 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_ANA_PDN, 0x00);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK,
+ ES8326_MIC1_SEL);
+
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK,
+ ES8326_MUTE);
+
+ regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_LRCK, 0xff);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ es8326_disable_micbias(es8326->component);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x73, 0x10);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ }
+
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+}
+
+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_cache_bypass(es8326->regmap, true);
+ regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, &reg);
+ regcache_cache_bypass(es8326->regmap, false);
+ /* reset internal clock state */
+ if (reg == 0x05)
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ else
+ es8326_init(component);
+
+ regcache_sync(es8326->regmap);
+
+ 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);
+ es8326->calibrated = false;
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x00);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
+ regcache_cache_only(es8326->regmap, true);
+
+ /* reset register value to default */
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+
+ 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,jack-pol", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "jack-pol return %d", ret);
+ es8326->jack_pol = ES8326_HP_TYPE_AUTO;
+ }
+ 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 = 0x00;
+ }
+ dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+
+ es8326_init(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_HPDET_TYPE,
+ 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)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ es8326_disable_jack_detect(component);
+ es8326_set_bias_level(component, SND_SOC_BIAS_OFF);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+}
+
+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;
+ es8326->jack_remove_retry = 0;
+ es8326->hp = 0;
+ es8326->hpl_vol = 0x03;
+ es8326->hpr_vol = 0x03;
+ 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 void es8326_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct snd_soc_component *component;
+ struct es8326_priv *es8326;
+
+ es8326 = i2c_get_clientdata(i2c);
+ component = es8326->component;
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ cancel_delayed_work_sync(&es8326->button_press_work);
+
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+
+}
+
+static void es8326_i2c_remove(struct i2c_client *i2c)
+{
+ es8326_i2c_shutdown(i2c);
+}
+
+static const struct i2c_device_id es8326_i2c_id[] = {
+ {"es8326" },
+ {}
+};
+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 = es8326_i2c_probe,
+ .shutdown = es8326_i2c_shutdown,
+ .remove = es8326_i2c_remove,
+ .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..c3e52e7bdef5
--- /dev/null
+++ b/sound/soc/codecs/es8326.h
@@ -0,0 +1,200 @@
+/* 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
+
+/* 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_DACL_VOL 0x50
+#define ES8326_DRC_RECOVERY 0x53
+#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_DAC_CROSSTALK 0x55
+#define ES8326_HPJACK_TIMER 0x56
+#define ES8326_HPDET_TYPE 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_DACR_VOL 0xF4
+#define ES8326_SPKL_VOL 0xF5
+#define ES8326_SPKR_VOL 0xF6
+#define ES8326_HP_MISC 0xF7
+#define ES8326_CTIA_OMTP_STA 0xF8
+#define ES8326_PULLUP_CTL 0xF9
+#define ES8326_CSM_I2C_STA 0xFA
+#define ES8326_HPDET_STA 0xFB
+#define ES8326_CSM_MUTE_STA 0xFC
+#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)
+#define ES8326_MUTE_MASK (3 << 0)
+#define ES8326_MUTE (3 << 0)
+
+/* 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_HP_OFF 0
+#define ES8326_HP_FORCE_CAL ((1 << 7) | (1 << 3))
+#define ES8326_HP_ON ((7 << 4) | (7 << 0))
+
+/* 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_HPDET_TYPE */
+#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_INT_SOURCE */
+#define ES8326_INT_SRC_DAC_MOZ (1 << 0)
+#define ES8326_INT_SRC_ADC_MOZ (1 << 1)
+#define ES8326_INT_SRC_BUTTON (1 << 2)
+#define ES8326_INT_SRC_PIN9 (1 << 3)
+#define ES8326_INT_SRC_PIN27 (1 << 4)
+
+/* 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_HPDET_STA */
+#define ES8326_HPINSERT_FLAG (1 << 1)
+#define ES8326_HPBUTTON_FLAG (1 << 0)
+
+/* ES8326_CHIP_VERSION 0xFF */
+#define ES8326_VERSION (1 << 0)
+#define ES8326_VERSION_B (3 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 318ab28c5351..56bfbe9261ce 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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,8 +16,8 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "es8328", 0 },
- { "es8388", 0 },
+ { "es8328" },
+ { "es8388" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
@@ -32,26 +29,18 @@ static const struct of_device_id es8328_of_match[] = {
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
-static int es8328_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int es8328_i2c_probe(struct i2c_client *i2c)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
}
-static int es8328_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
-
static struct i2c_driver es8328_i2c_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
- .probe = es8328_i2c_probe,
- .remove = es8328_i2c_remove,
+ .probe = es8328_i2c_probe,
.id_table = es8328_id,
};
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c
index 8fbd935e1c76..88e353ae52a1 100644
--- a/sound/soc/codecs/es8328-spi.c
+++ b/sound/soc/codecs/es8328-spi.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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>
@@ -28,19 +25,12 @@ static int es8328_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &es8328_regmap_config));
}
-static int es8328_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver es8328_spi_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_spi_probe,
- .remove = es8328_spi_remove,
};
module_spi_driver(es8328_spi_driver);
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index bcdb8914ec16..76159c45e6b5 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -1,18 +1,14 @@
+// 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>
- *
- * 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/of_device.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -87,7 +83,7 @@ struct es8328_priv {
int mclkdiv2;
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
const int *mclk_ratios;
- bool master;
+ bool provider;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
};
@@ -102,7 +98,6 @@ static SOC_ENUM_SINGLE_DECL(adcpol,
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(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
@@ -116,9 +111,9 @@ static const struct {
{ 48000, ES8328_DACCONTROL6_DEEMPH_48k },
};
-static int es8328_set_deemph(struct snd_soc_codec *codec)
+static int es8328_set_deemph(struct snd_soc_component *component)
{
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int val, i, best;
/*
@@ -138,17 +133,17 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6,
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -157,21 +152,24 @@ static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ 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;
- ret = es8328_set_deemph(codec);
+ if (es8328->deemph == deemph)
+ return 0;
+
+ ret = es8328_set_deemph(component);
if (ret < 0)
return ret;
es8328->deemph = deemph;
- return 0;
+ return 1;
}
@@ -231,11 +229,10 @@ static const struct soc_enum es8328_rline_enum =
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_right_line_controls =
- SOC_DAPM_ENUM("Route", es8328_lline_enum);
+ 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),
@@ -245,7 +242,6 @@ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
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),
};
@@ -338,10 +334,10 @@ static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
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,
+ SND_SOC_DAPM_MIXER("Left Mixer", ES8328_DACCONTROL17, 7, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
- SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_MIXER("Right Mixer", ES8328_DACCONTROL20, 7, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
@@ -420,19 +416,14 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "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", NULL, "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", NULL, "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },
@@ -453,9 +444,9 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "ROUT2", NULL, "Right Out 2" },
};
-static int es8328_mute(struct snd_soc_dai *dai, int mute)
+static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
+ return snd_soc_component_update_bits(dai->component, ES8328_DACCONTROL3,
ES8328_DACCONTROL3_DACMUTE,
mute ? ES8328_DACCONTROL3_DACMUTE : 0);
}
@@ -463,10 +454,10 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
static int es8328_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
- if (es8328->master && es8328->sysclk_constraints)
+ if (es8328->provider && es8328->sysclk_constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
es8328->sysclk_constraints);
@@ -478,8 +469,8 @@ static int es8328_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 es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int i;
int reg;
int wl;
@@ -490,9 +481,9 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
else
reg = ES8328_ADCCONTROL5;
- if (es8328->master) {
+ if (es8328->provider) {
if (!es8328->sysclk_constraints) {
- dev_err(codec->dev, "No MCLK configured\n");
+ dev_err(component->dev, "No MCLK configured\n");
return -EINVAL;
}
@@ -502,7 +493,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
break;
if (i == es8328->sysclk_constraints->count) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"LRCLK %d unsupported with current clock\n",
params_rate(params));
return -EINVAL;
@@ -513,7 +504,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
es8328->mclkdiv2 = 0;
}
- snd_soc_update_bits(codec, ES8328_MASTERMODE,
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2,
es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
@@ -538,42 +529,49 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+ 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(codec);
+ es8328_set_deemph(component);
} else
- snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCWL_MASK,
wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
- return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
+ 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_codec *codec = codec_dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int mclkdiv2 = 0;
+ unsigned int round_freq;
+
+ /*
+ * Allow a small tolerance for frequencies within 100hz. Note
+ * this value is chosen arbitrarily.
+ */
+ round_freq = DIV_ROUND_CLOSEST(freq, 100) * 100;
- switch (freq) {
+ switch (round_freq) {
case 0:
es8328->sysclk_constraints = NULL;
es8328->mclk_ratios = NULL;
break;
case 22579200:
mclkdiv2 = 1;
- /* fallthru */
+ fallthrough;
case 11289600:
es8328->sysclk_constraints = &constraints_11289;
es8328->mclk_ratios = ratios_11289;
break;
case 24576000:
mclkdiv2 = 1;
- /* fallthru */
+ fallthrough;
case 12288000:
es8328->sysclk_constraints = &constraints_12288;
es8328->mclk_ratios = ratios_12288;
@@ -589,24 +587,24 @@ static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
/* Master serial port mode, with BCLK generated automatically */
- snd_soc_update_bits(codec, ES8328_MASTERMODE,
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC,
ES8328_MASTERMODE_MSC);
- es8328->master = true;
+ es8328->provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
/* Slave serial port mode */
- snd_soc_update_bits(codec, ES8328_MASTERMODE,
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC, 0);
- es8328->master = false;
+ es8328->provider = false;
break;
default:
return -EINVAL;
@@ -634,15 +632,15 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
- snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
- snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+ 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_codec *codec,
+static int es8328_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -651,8 +649,8 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- snd_soc_write(codec, ES8328_CHIPPOWER, 0);
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ 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 |
@@ -660,8 +658,8 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ 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 |
@@ -671,12 +669,12 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
msleep(100);
}
- snd_soc_write(codec, ES8328_CONTROL2,
+ snd_soc_component_write(component, ES8328_CONTROL2,
ES8328_CONTROL2_OVERCURRENT_ON |
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_500k |
@@ -684,7 +682,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
0);
@@ -696,9 +694,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops es8328_dai_ops = {
.startup = es8328_startup,
.hw_params = es8328_hw_params,
- .digital_mute = es8328_mute,
+ .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 = {
@@ -718,83 +717,83 @@ static struct snd_soc_dai_driver es8328_dai = {
.formats = ES8328_FORMATS,
},
.ops = &es8328_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int es8328_suspend(struct snd_soc_codec *codec)
+static int es8328_suspend(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "unable to disable regulators\n");
+ dev_err(component->dev, "unable to disable regulators\n");
return ret;
}
return 0;
}
-static int es8328_resume(struct snd_soc_codec *codec)
+static int es8328_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = clk_prepare_enable(es8328->clk);
if (ret) {
- dev_err(codec->dev, "unable to enable clock\n");
+ 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(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
regcache_mark_dirty(regmap);
ret = regcache_sync(regmap);
if (ret) {
- dev_err(codec->dev, "unable to sync regcache\n");
+ dev_err(component->dev, "unable to sync regcache\n");
return ret;
}
return 0;
}
-static int es8328_codec_probe(struct snd_soc_codec *codec)
+static int es8328_component_probe(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
- dev_err(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
/* Setup clocks */
- es8328->clk = devm_clk_get(codec->dev, NULL);
+ es8328->clk = devm_clk_get(component->dev, NULL);
if (IS_ERR(es8328->clk)) {
- dev_err(codec->dev, "codec clock missing or invalid\n");
+ 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(codec->dev, "unable to prepare codec clk\n");
+ dev_err(component->dev, "unable to prepare codec clk\n");
goto clk_fail;
}
@@ -806,46 +805,44 @@ clk_fail:
return ret;
}
-static int es8328_remove(struct snd_soc_codec *codec)
+static void es8328_remove(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
- if (es8328->clk)
- clk_disable_unprepare(es8328->clk);
+ clk_disable_unprepare(es8328->clk);
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
-
- return 0;
}
const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
- .use_single_rw = true,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
EXPORT_SYMBOL_GPL(es8328_regmap_config);
-static const struct snd_soc_codec_driver es8328_codec_driver = {
- .probe = es8328_codec_probe,
- .suspend = es8328_suspend,
- .resume = es8328_resume,
- .remove = es8328_remove,
- .set_bias_level = es8328_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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)
@@ -875,8 +872,8 @@ int es8328_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, es8328);
- return snd_soc_register_codec(dev,
- &es8328_codec_driver, &es8328_dai, 1);
+ return devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai, 1);
}
EXPORT_SYMBOL_GPL(es8328_probe);
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
new file mode 100644
index 000000000000..36b0ebdce514
--- /dev/null
+++ b/sound/soc/codecs/es8375.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8375.c -- ES8375 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <linux/acpi.h>
+#include "es8375.h"
+
+struct es8375_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
+ unsigned int mclk_freq;
+ int mastermode;
+ u8 mclk_src;
+ u8 vddd;
+ enum snd_soc_bias_level bias_level;
+};
+
+static const char * const es8375_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
+static const DECLARE_TLV_DB_SCALE(es8375_adc_osr_gain_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_automute_attn_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_dmic_volume_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_vppscale_tlv, -388, 12, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_automute_attn_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_automute_ng_tlv, -9600, 600, 0);
+
+static const char *const es8375_ramprate_txt[] = {
+ "0.125dB/LRCK",
+ "0.125dB/2LRCK",
+ "0.125dB/4LRCK",
+ "0.125dB/8LRCK",
+ "0.125dB/16LRCK",
+ "0.125dB/32LRCK",
+ "0.125dB/64LRCK",
+ "0.125dB/128LRCK",
+ "disable softramp",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_ramprate, ES8375_ADC2,
+ ADC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_ramprate, ES8375_DAC2,
+ DAC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+
+static const char *const es8375_automute_ws_txt[] = {
+ "256 samples",
+ "512 samples",
+ "1024 samples",
+ "2048 samples",
+ "4096 samples",
+ "8192 samples",
+ "16384 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_automute_ws, ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_WS_SHIFT_3, es8375_automute_ws_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_automute_ws, ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_WS_SHIFT_5, es8375_automute_ws_txt);
+
+static const char *const es8375_dmic_pol_txt[] = {
+ "Low",
+ "High",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_dmic_pol, ES8375_ADC1,
+ DMIC_POL_SHIFT_4, es8375_dmic_pol_txt);
+
+static const char *const es8375_adc_hpf_txt[] = {
+ "Freeze Offset",
+ "Dynamic HPF",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_adc_hpf, ES8375_HPF1,
+ ADC_HPF_SHIFT_5, es8375_adc_hpf_txt);
+
+static const char *const es8375_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+static const struct soc_enum es8375_dmic_mux_enum =
+ SOC_ENUM_SINGLE(ES8375_ADC1, ADC_SRC_SHIFT_7,
+ ARRAY_SIZE(es8375_dmic_mux_txt), es8375_dmic_mux_txt);
+
+static const struct snd_kcontrol_new es8375_dmic_mux_controls =
+ SOC_DAPM_ENUM("ADC MUX", es8375_dmic_mux_enum);
+
+static const struct snd_kcontrol_new es8375_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8375_ADC_OSR_GAIN,
+ ADC_OSR_GAIN_SHIFT_0, ES8375_ADC_OSR_GAIN_MAX, 0,
+ es8375_adc_osr_gain_tlv),
+ SOC_SINGLE("ADC Invert Switch", ES8375_ADC1, ADC_INV_SHIFT_6, 1, 0),
+ SOC_SINGLE("ADC RAM Clear", ES8375_ADC1, ADC_RAMCLR_SHIFT_5, 1, 0),
+ SOC_ENUM("DMIC Polarity", es8375_dmic_pol),
+ SOC_SINGLE_TLV("DMIC Volume", ES8375_ADC1,
+ DMIC_GAIN_SHIFT_2, ES8375_DMIC_GAIN_MAX,
+ 0, es8375_adc_dmic_volume_tlv),
+ SOC_ENUM("ADC Ramp Rate", es8375_adc_ramprate),
+ SOC_SINGLE_TLV("ADC Volume", ES8375_ADC_VOLUME,
+ ADC_VOLUME_SHIFT_0, ES8375_ADC_VOLUME_MAX,
+ 0, es8375_adc_volume_tlv),
+ SOC_SINGLE("ADC Automute Switch", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_SHIFT_7, 1, 0),
+ SOC_ENUM("ADC Automute Winsize", es8375_adc_automute_ws),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Volume", ES8375_ADC_AUTOMUTE_ATTN,
+ ADC_AUTOMUTE_ATTN_SHIFT_0, ES8375_ADC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_adc_automute_attn_tlv),
+ SOC_ENUM("ADC HPF", es8375_adc_hpf),
+
+ SOC_SINGLE("DAC DSM Mute Switch", ES8375_DAC1, DAC_DSMMUTE_SHIFT_7, 1, 0),
+ SOC_SINGLE("DAC DEM Mute Switch", ES8375_DAC1, DAC_DEMMUTE_SHIFT_6, 1, 0),
+ SOC_SINGLE("DAC Invert Switch", ES8375_DAC1, DAC_INV_SHIFT_5, 1, 0),
+ SOC_SINGLE("DAC RAM Clear", ES8375_DAC1, DAC_RAMCLR_SHIFT_4, 1, 0),
+ SOC_ENUM("DAC Ramp Rate", es8375_dac_ramprate),
+ SOC_SINGLE_TLV("DAC Volume", ES8375_DAC_VOLUME,
+ DAC_VOLUME_SHIFT_0, ES8375_DAC_VOLUME_MAX,
+ 0, es8375_dac_volume_tlv),
+ SOC_SINGLE_TLV("DAC VPP Scale", ES8375_DAC_VPPSCALE,
+ DAC_VPPSCALE_SHIFT_0, ES8375_DAC_VPPSCALE_MAX,
+ 0, es8375_dac_vppscale_tlv),
+ SOC_SINGLE("DAC Automute Switch", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_EN_SHIFT_7, 1, 0),
+ SOC_SINGLE_TLV("DAC Automute Noise Gate", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_ENUM("DAC Automute Winsize", es8375_dac_automute_ws),
+ SOC_SINGLE_TLV("DAC Automute Volume", ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_ATTN_SHIFT_0, ES8375_DAC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_dac_automute_attn_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8375_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8375_SDP2,
+ ES8375_ADC_P2S_MUTE_SHIFT_5, 1),
+
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8375_dmic_mux_controls),
+
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8375_SDP,
+ SND_SOC_NOPM, 0),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8375_dapm_routes[] = {
+ {"ADC MUX", "AMIC", "MIC1"},
+ {"ADC MUX", "DMIC", "DMIC"},
+ {"PGA", NULL, "ADC MUX"},
+ {"Mono ADC", NULL, "PGA"},
+ {"AIF1TX", NULL, "Mono ADC"},
+
+ {"Mono DAC", NULL, "AIF1RX"},
+ {"OUT", NULL, "Mono DAC"},
+};
+
+struct _coeff_div {
+ u16 mclk_lrck_ratio;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0B;
+ u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
+};
+
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
+ {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
+ {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2},
+ {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2},
+ {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2},
+ {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2},
+ {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2},
+ {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2},
+ {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2},
+ {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2},
+ {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2},
+ {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2},
+ {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2},
+};
+
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
+{
+ int i;
+ u8 dmic_det, vddd_det;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int es8375_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 es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int par_width = params_width(params);
+ u8 dmic_enable, iface = 0;
+ unsigned int regv;
+ int coeff, ret;
+
+ if (es8375->mclk_src == ES8375_BCLK_PIN) {
+ regmap_update_bits(es8375->regmap,
+ ES8375_MCLK_SEL, 0x80, 0x80);
+
+ es8375->mclk_freq = 2 * (unsigned int)par_width * params_rate(params);
+ }
+
+ regmap_read(es8375->regmap, ES8375_ADC1, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8375->core_supply[ES8375_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8375->vddd = ES8375_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8375->vddd = ES8375_3V3;
+ break;
+ default:
+ es8375->vddd = ES8375_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8375->vddd, dmic_enable, es8375->mclk_freq, params_rate(params));
+ if (coeff < 0) {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ return coeff;
+ }
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4,
+ coeff_div[coeff].Reg0x04);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5,
+ coeff_div[coeff].Reg0x05);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6,
+ coeff_div[coeff].Reg0x06);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7,
+ coeff_div[coeff].Reg0x07);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8,
+ coeff_div[coeff].Reg0x08);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9,
+ coeff_div[coeff].Reg0x09);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10,
+ coeff_div[coeff].Reg0x0A);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR11,
+ coeff_div[coeff].Reg0x0B);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN,
+ coeff_div[coeff].Reg0x19);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iface |= 0x0c;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x10;
+ break;
+ }
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x1c, iface);
+
+ return 0;
+}
+
+static int es8375_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mclk_freq = freq;
+
+ return 0;
+}
+
+static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int iface, codeciface;
+
+ regmap_read(es8375->regmap, ES8375_SDP, &codeciface);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ es8375->mastermode = 1;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8375->mastermode = 0;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codeciface &= 0xFC;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ codeciface &= 0xFC;
+ codeciface |= 0x01;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ codeciface &= 0xDC;
+ codeciface |= 0x03;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ codeciface &= 0xDC;
+ codeciface |= 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_read(es8375->regmap, ES8375_CLK_MGR3, &iface);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xFE;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x01;
+ codeciface |= 0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x01;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xFE;
+ codeciface |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, iface);
+ regmap_write(es8375->regmap, ES8375_SDP, codeciface);
+
+ return 0;
+}
+
+static int es8375_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+ regmap_write(es8375->regmap, ES8375_CSM1, 0xA6);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ clk_disable_unprepare(es8375->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x00);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x00);
+ }
+
+ return 0;
+}
+
+#define es8375_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8375_ops = {
+ .hw_params = es8375_hw_params,
+ .mute_stream = es8375_mute,
+ .set_sysclk = es8375_set_sysclk,
+ .set_fmt = es8375_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8375_dai = {
+ .name = "ES8375 HiFi",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .ops = &es8375_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8375_init(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x95);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_DIV_SPKCLK, 0x18);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x82);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20);
+ regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0);
+ regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE);
+ regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ msleep(80);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x86);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6, 0x31);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7, 0x11);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8, 0x1F);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9, 0x00);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, 0x1F);
+ regmap_write(es8375->regmap, ES8375_ADC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7);
+ regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0);
+ regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE);
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+}
+
+static int es8375_suspend(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ regcache_cache_only(es8375->regmap, true);
+ regcache_mark_dirty(es8375->regmap);
+ return 0;
+}
+
+static int es8375_resume(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8375->regmap, false);
+ regcache_cache_bypass(es8375->regmap, true);
+ regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
+ regcache_cache_bypass(es8375->regmap, false);
+
+ if (reg == 0x00)
+ es8375_init(component);
+ else
+ es8375_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8375->regmap);
+
+ return 0;
+}
+
+static int es8375_codec_probe(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mastermode = 0;
+
+ es8375_init(component);
+
+ return 0;
+}
+
+static bool es8375_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8375_CHIP_VERSION:
+ case ES8375_CHIP_ID0:
+ case ES8375_CHIP_ID1:
+ case ES8375_SPK_OFFSET:
+ case ES8375_FLAGS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8375_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8375_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .writeable_reg = es8375_writeable_register,
+};
+
+static struct snd_soc_component_driver es8375_codec_driver = {
+ .probe = es8375_codec_probe,
+ .suspend = es8375_suspend,
+ .resume = es8375_resume,
+ .set_bias_level = es8375_set_bias_level,
+ .controls = es8375_snd_controls,
+ .num_controls = ARRAY_SIZE(es8375_snd_controls),
+ .dapm_widgets = es8375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8375_dapm_widgets),
+ .dapm_routes = es8375_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8375_dapm_routes),
+
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+};
+
+static int es8375_read_device_properities(struct device *dev, struct es8375_priv *es8375)
+{
+ int ret, i;
+
+ ret = device_property_read_u8(dev, "everest,mclk-src", &es8375->mclk_src);
+ if (ret != 0)
+ es8375->mclk_src = ES8375_MCLK_SOURCE;
+ dev_dbg(dev, "mclk-src %x", es8375->mclk_src);
+
+ for (i = 0; i < ARRAY_SIZE(es8375_core_supplies); i++)
+ es8375->core_supply[i].supply = es8375_core_supplies[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ es8375->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(es8375->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8375->mclk), "unable to get mclk\n");
+
+ if (!es8375->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(dev, "unable to enable mclk\n");
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8375->mclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8375_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8375_priv *es8375;
+ struct device *dev = &i2c_client->dev;
+ int ret;
+ unsigned int val;
+
+ es8375 = devm_kzalloc(&i2c_client->dev, sizeof(*es8375), GFP_KERNEL);
+ if (!es8375)
+ return -ENOMEM;
+
+ es8375->regmap = devm_regmap_init_i2c(i2c_client,
+ &es8375_regmap_config);
+ if (IS_ERR(es8375->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8375->regmap),
+ "regmap_init() failed\n");
+
+ i2c_set_clientdata(i2c_client, es8375);
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID1, &val);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+
+ if (val != 0x83) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID0, &val);
+ if (val != 0x75) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = es8375_read_device_properities(dev, es8375);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "get an error from dts info %X\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev, &es8375_codec_driver,
+ &es8375_dai, 1);
+}
+
+static void es8375_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8375_priv *es8375;
+
+ es8375 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3C);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x80);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3E);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x15);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x0C);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ clk_disable_unprepare(es8375->mclk);
+}
+
+static const struct i2c_device_id es8375_id[] = {
+ {"es8375"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8375_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8375_acpi_match[] = {
+ {"ESSX8375", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, es8375_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8375_of_match[] = {
+ {.compatible = "everest,es8375",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, es8375_of_match);
+#endif
+
+static struct i2c_driver es8375_i2c_driver = {
+ .driver = {
+ .name = "es8375",
+ .of_match_table = of_match_ptr(es8375_of_match),
+ .acpi_match_table = ACPI_PTR(es8375_acpi_match),
+ },
+ .shutdown = es8375_i2c_shutdown,
+ .probe = es8375_i2c_probe,
+ .id_table = es8375_id,
+};
+module_i2c_driver(es8375_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8375 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h
new file mode 100644
index 000000000000..11e3ceec9b68
--- /dev/null
+++ b/sound/soc/codecs/es8375.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8375.h -- ES8375 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8375.h by Michael Zhang
+*/
+#ifndef _ES8375_H
+#define _ES8375_H
+
+// Registors
+#define ES8375_RESET1 0x00
+#define ES8375_MCLK_SEL 0x01
+#define ES8375_CLK_MGR2 0x02
+#define ES8375_CLK_MGR3 0x03
+#define ES8375_CLK_MGR4 0x04
+#define ES8375_CLK_MGR5 0x05
+#define ES8375_CLK_MGR6 0x06
+#define ES8375_CLK_MGR7 0x07
+#define ES8375_CLK_MGR8 0x08
+#define ES8375_CLK_MGR9 0x09
+#define ES8375_CLK_MGR10 0x0A
+#define ES8375_CLK_MGR11 0x0B
+#define ES8375_CLK_MGR12 0x0C
+#define ES8375_DIV_SPKCLK 0x0E
+#define ES8375_CSM1 0x0F
+#define ES8375_CSM2 0x10
+#define ES8375_VMID_CHARGE2 0x11
+#define ES8375_VMID_CHARGE3 0x12
+#define ES8375_SDP 0x15
+#define ES8375_SDP2 0x16
+#define ES8375_ADC1 0x17
+#define ES8375_ADC2 0x18
+#define ES8375_ADC_OSR_GAIN 0x19
+#define ES8375_ADC_VOLUME 0x1A
+#define ES8375_ADC_AUTOMUTE 0x1B
+#define ES8375_ADC_AUTOMUTE_ATTN 0x1C
+#define ES8375_HPF1 0x1D
+#define ES8375_DAC1 0x1F
+#define ES8375_DAC2 0x20
+#define ES8375_DAC_VOLUME 0x21
+#define ES8375_DAC_VPPSCALE 0x22
+#define ES8375_DAC_AUTOMUTE1 0x23
+#define ES8375_DAC_AUTOMUTE 0x24
+#define ES8375_DAC_CAL 0x25
+#define ES8375_DAC_OTP 0x27
+#define ES8375_ANALOG_SPK1 0x28
+#define ES8375_ANALOG_SPK2 0x29
+#define ES8375_VMID_SEL 0x2D
+#define ES8375_ANALOG1 0x2E
+#define ES8375_ANALOG2 0x32
+#define ES8375_ANALOG3 0x37
+#define ES8375_ADC2DAC_CLKTRI 0xF8
+#define ES8375_SYS_CTRL2 0xF9
+#define ES8375_FLAGS2 0xFB
+#define ES8375_SPK_OFFSET 0xFC
+#define ES8375_CHIP_ID1 0xFD
+#define ES8375_CHIP_ID0 0xFE
+#define ES8375_CHIP_VERSION 0xFF
+
+// Bit Shifts
+#define ADC_OSR_GAIN_SHIFT_0 0
+#define ADC_RAMPRATE_SHIFT_0 0
+#define ADC_VOLUME_SHIFT_0 0
+#define ADC_AUTOMUTE_NG_SHIFT_0 0
+#define ADC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DAC_RAMPRATE_SHIFT_0 0
+#define DAC_VOLUME_SHIFT_0 0
+#define DAC_VPPSCALE_SHIFT_0 0
+#define DAC_AUTOMUTE_NG_SHIFT_0 0
+#define DAC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DMIC_GAIN_SHIFT_2 2
+#define ADC_AUTOMUTE_WS_SHIFT_3 3
+#define DMIC_POL_SHIFT_4 4
+#define DAC_RAMCLR_SHIFT_4 4
+#define ES8375_EN_MODL_SHIFT_4 4
+#define ADC_RAMCLR_SHIFT_5 5
+#define ADC_HPF_SHIFT_5 5
+#define DAC_INV_SHIFT_5 5
+#define DAC_AUTOMUTE_WS_SHIFT_5 5
+#define ES8375_EN_PGAL_SHIFT_5 5
+#define ES8375_ADC_P2S_MUTE_SHIFT_5 5
+#define ADC_INV_SHIFT_6 6
+#define DAC_DEMMUTE_SHIFT_6 6
+#define ES8375_DAC_S2P_MUTE_SHIFT_6 6
+#define ADC_SRC_SHIFT_7 7
+#define ADC_AUTOMUTE_SHIFT_7 7
+#define DAC_DSMMUTE_SHIFT_7 7
+#define DAC_AUTOMUTE_EN_SHIFT_7 7
+
+// Function values
+#define ES8375_ADC_OSR_GAIN_MAX 0x3F
+#define ES8375_DMIC_GAIN_MAX 0x04
+#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F
+#define ES8375_AUTOMUTE_NG_MAX 0x07
+#define ES8375_ADC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VPPSCALE_MAX 0x3F
+#define ES8375_DAC_AUTOMUTE_ATTN_MAX 0x17
+#define ES8375_REG_MAX 0xFF
+
+enum ES8375_supplies {
+ ES8375_SUPPLY_VD = 0,
+ ES8375_SUPPLY_VA,
+};
+
+// Properties
+#define ES8375_3V3 1
+#define ES8375_1V8 0
+
+#define ES8375_MCLK_PIN 0
+#define ES8375_BCLK_PIN 1
+#define ES8375_MCLK_SOURCE ES8375_MCLK_PIN
+
+#define DMIC_POSITIVE_EDGE 0
+#define DMIC_NEGATIVE_EDGE 1
+#define DMIC_POL DMIC_POSITIVE_EDGE
+
+#define PA_SHUTDOWN 0
+#define PA_ENABLE 1
+
+#endif
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
new file mode 100644
index 000000000000..6e4c75d288ef
--- /dev/null
+++ b/sound/soc/codecs/es8389.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8389.c -- ES8389 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.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/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.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 "es8389.h"
+
+
+/* codec private data */
+
+struct es8389_private {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int sysclk;
+ int mastermode;
+
+ u8 mclk_src;
+ enum snd_soc_bias_level bias_level;
+};
+
+static bool es8389_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ if ((reg <= 0xff))
+ return true;
+ else
+ return false;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0);
+
+static int es8389_dmic_set(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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ bool changed1, changed2;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > 1)
+ return -EINVAL;
+
+ if (val) {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0xC0, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x03, &changed2);
+ } else {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00, &changed2);
+ }
+
+ if (changed1 & changed2)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL);
+ else
+ return 0;
+}
+
+static const char *const alc[] = {
+ "ALC OFF",
+ "ADCR ALC ON",
+ "ADCL ALC ON",
+ "ADCL & ADCL ALC ON",
+};
+
+static const char *const ramprate[] = {
+ "0.125db/1 LRCK",
+ "0.125db/4 LRCK",
+ "0.125db/8 LRCK",
+ "0.125db/16 LRCK",
+ "0.125db/32 LRCK",
+ "0.125db/64 LRCK",
+ "0.125db/128 LRCK",
+ "0.125db/256 LRCK",
+ "0.125db/512 LRCK",
+ "0.125db/1024 LRCK",
+ "0.125db/2048 LRCK",
+ "0.125db/4096 LRCK",
+ "0.125db/8192 LRCK",
+ "0.125db/16384 LRCK",
+ "0.125db/32768 LRCK",
+ "0.125db/65536 LRCK",
+};
+
+static const char *const winsize[] = {
+ "2 LRCK",
+ "4 LRCK",
+ "8 LRCK",
+ "16 LRCK",
+ "32 LRCK",
+ "64 LRCK",
+ "128 LRCK",
+ "256 LRCK",
+ "512 LRCK",
+ "1024 LRCK",
+ "2048 LRCK",
+ "4096 LRCK",
+ "8192 LRCK",
+ "16384 LRCK",
+ "32768 LRCK",
+ "65536 LRCK",
+};
+
+static const struct soc_enum alc_enable =
+ SOC_ENUM_SINGLE(ES8389_ALC_ON, 5, 4, alc);
+static const struct soc_enum alc_ramprate =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 4, 16, ramprate);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize);
+
+static const char *const es8389_outl_mux_txt[] = {
+ "Normal",
+ "DAC2 channel to DAC1 channel",
+};
+
+static const char *const es8389_outr_mux_txt[] = {
+ "Normal",
+ "DAC1 channel to DAC2 channel",
+};
+
+static const char *const es8389_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static const char *const es8389_pga1_texts[] = {
+ "DifferentialL", "Line 1P", "Line 2P"
+};
+
+static const char *const es8389_pga2_texts[] = {
+ "DifferentialR", "Line 2N", "Line 1N"
+};
+
+static const unsigned int es8389_pga_values[] = {
+ 1, 5, 6
+};
+
+static const struct soc_enum es8389_outl_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5,
+ ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outl_mux_controls =
+ SOC_DAPM_ENUM("OUTL MUX", es8389_outl_mux_enum);
+
+static const struct soc_enum es8389_outr_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 4,
+ ARRAY_SIZE(es8389_outr_mux_txt), es8389_outr_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outr_mux_controls =
+ SOC_DAPM_ENUM("OUTR MUX", es8389_outr_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ es8389_dmic_mux_enum, ES8389_DMIC_EN, 6, es8389_dmic_mux_txt);
+
+static const struct soc_enum es8389_pgal_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC1_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga1_texts), es8389_pga1_texts,
+ es8389_pga_values);
+
+static const struct soc_enum es8389_pgar_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC2_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga2_texts), es8389_pga2_texts,
+ es8389_pga_values);
+
+static const struct snd_kcontrol_new es8389_dmic_mux_controls =
+ SOC_DAPM_ENUM_EXT("ADC MUX", es8389_dmic_mux_enum,
+ snd_soc_dapm_get_enum_double, es8389_dmic_set);
+
+static const struct snd_kcontrol_new es8389_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR DACL Mixer", ES8389_DAC_MIX, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL DACR Mixer", ES8389_DAC_MIX, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_leftadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCL DACL Mixer", ES8389_DAC_MIX, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_rightadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCR DACR Mixer", ES8389_DAC_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL ADCL Mixer", ES8389_ADC_RESET, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR ADCR Mixer", ES8389_ADC_RESET, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_snd_controls[] = {
+ SOC_SINGLE_TLV("ADCL Capture Volume", ES8389_ADCL_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCR Capture Volume", ES8389_ADCR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCL PGA Volume", ES8389_MIC1_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+ SOC_SINGLE_TLV("ADCR PGA Volume", ES8389_MIC2_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+
+ SOC_ENUM("PGAL Select", es8389_pgal_enum),
+ SOC_ENUM("PGAR Select", es8389_pgar_enum),
+ SOC_ENUM("ALC Capture Switch", alc_enable),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8389_ALC_TARGET,
+ 0, 0x0f, 0, alc_target_tlv),
+ SOC_SINGLE_TLV("ALC Capture Max Gain", ES8389_ALC_GAIN,
+ 0, 0x0f, 0, alc_max_level),
+ SOC_ENUM("ADC Ramp Rate", alc_ramprate),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0),
+
+ SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC OUTPUT Invert Switch", ES8389_DAC_INV, 5, 6, 1, 0),
+ SOC_SINGLE_TLV("ADC2DAC Mixer Volume", ES8389_MIX_VOL, 0, 0x7F, 0, mix_vol_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = {
+ /*Input Side*/
+ SND_SOC_DAPM_INPUT("INPUT1"),
+ SND_SOC_DAPM_INPUT("INPUT2"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGAL", SND_SOC_NOPM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAR", SND_SOC_NOPM, 4, 0, NULL, 0),
+
+ /*ADCs*/
+ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /*DACs*/
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /*Output Side*/
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MIXER("IF DACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_left_mixer_controls[0],
+ ARRAY_SIZE(es8389_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF DACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_right_mixer_controls[0],
+ ARRAY_SIZE(es8389_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_leftadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_leftadc_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_rightadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_rightadc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_adc_mixer_controls[0],
+ ARRAY_SIZE(es8389_adc_mixer_controls)),
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls),
+
+ SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls),
+ SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls),
+};
+
+
+static const struct snd_soc_dapm_route es8389_dapm_routes[] = {
+ {"PGAL", NULL, "INPUT1"},
+ {"PGAR", NULL, "INPUT2"},
+
+ {"ADCL", NULL, "PGAL"},
+ {"ADCR", NULL, "PGAR"},
+
+ {"ADC Mixer", "DACL ADCL Mixer", "DACL"},
+ {"ADC Mixer", "DACR ADCR Mixer", "DACR"},
+ {"ADC Mixer", NULL, "ADCL"},
+ {"ADC Mixer", NULL, "ADCR"},
+
+ {"ADC MUX", "AMIC", "ADC Mixer"},
+ {"ADC MUX", "DMIC", "DMIC"},
+
+ {"I2S OUT", NULL, "ADC MUX"},
+
+ {"DACL", NULL, "I2S IN"},
+ {"DACR", NULL, "I2S IN"},
+
+ {"IF DACL1", NULL, "DACL"},
+ {"IF DACR1", NULL, "DACR"},
+ {"IF DACL2", NULL, "DACL"},
+ {"IF DACR2", NULL, "DACR"},
+ {"IF DACL3", NULL, "DACL"},
+ {"IF DACR3", NULL, "DACR"},
+
+ {"IF DACL Mixer", NULL, "IF DACL2"},
+ {"IF DACL Mixer", "DACR DACL Mixer", "IF DACR1"},
+ {"IF DACR Mixer", NULL, "IF DACR2"},
+ {"IF DACR Mixer", "DACL DACR Mixer", "IF DACL1"},
+
+ {"IF ADCDACL Mixer", NULL, "IF DACL Mixer"},
+ {"IF ADCDACL Mixer", "ADCL DACL Mixer", "IF DACL3"},
+ {"IF ADCDACR Mixer", NULL, "IF DACR Mixer"},
+ {"IF ADCDACR Mixer", "ADCR DACR Mixer", "IF DACR3"},
+
+ {"OUTL MUX", "Normal", "IF ADCDACL Mixer"},
+ {"OUTL MUX", "DAC2 channel to DAC1 channel", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "Normal", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "DAC1 channel to DAC2 channel", "IF ADCDACL Mixer"},
+
+ {"HPOL", NULL, "OUTL MUX"},
+ {"HPOR", NULL, "OUTR MUX"},
+
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0F;
+ u8 Reg0x11;
+ u8 Reg0x21;
+ u8 Reg0x22;
+ u8 Reg0x26;
+ u8 Reg0x30;
+ u8 Reg0x41;
+ u8 Reg0x42;
+ u8 Reg0x43;
+ u8 Reg0xF0;
+ u8 Reg0xF1;
+ u8 Reg0x16;
+ u8 Reg0x18;
+ u8 Reg0x19;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
+
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+};
+
+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;
+}
+
+/*
+ * if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source.
+ */
+static int es8389_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389->sysclk = freq;
+
+ return 0;
+}
+
+static int es8389_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(es8389->regmap, ES8389_PTDM_SLOT,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+ regmap_update_bits(es8389->regmap, ES8389_DAC_RAMP,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+
+ return 0;
+}
+
+static int es8389_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ u8 state = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_MODE,
+ ES8389_MASTER_MODE_EN, ES8389_MASTER_MODE_EN);
+ es8389->mastermode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8389->mastermode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ state |= ES8389_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "component driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ state |= ES8389_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ state |= ES8389_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ state |= ES8389_DAIFMT_DSP_B;
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+
+ return 0;
+}
+
+static int es8389_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ int coeff;
+ u8 state = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ state |= ES8389_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ state |= ES8389_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ state |= ES8389_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ state |= ES8389_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ state |= ES8389_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+
+ if (es8389->mclk_src == ES8389_SCLK_PIN) {
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK,
+ ES8389_MCLK_SOURCE, es8389->mclk_src);
+ es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
+ }
+
+ coeff = get_coeff(es8389->sysclk, params_rate(params));
+ if (coeff >= 0) {
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, coeff_div[coeff].Reg0x06);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, coeff_div[coeff].Reg0x07);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, coeff_div[coeff].Reg0x08);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, coeff_div[coeff].Reg0x09);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, coeff_div[coeff].Reg0x0A);
+ regmap_update_bits(es8389->regmap, ES8389_OSC_CLK,
+ 0xC0, coeff_div[coeff].Reg0x0F);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV2, coeff_div[coeff].Reg0x11);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, coeff_div[coeff].Reg0x21);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, coeff_div[coeff].Reg0x22);
+ regmap_write(es8389->regmap, ES8389_OSR_VOL, coeff_div[coeff].Reg0x26);
+ regmap_update_bits(es8389->regmap, ES8389_SYSTEM30,
+ 0xC0, coeff_div[coeff].Reg0x30);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, coeff_div[coeff].Reg0x41);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, coeff_div[coeff].Reg0x42);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_MISC,
+ 0x81, coeff_div[coeff].Reg0x43);
+ regmap_update_bits(es8389->regmap, ES8389_CHIP_MISC,
+ 0x72, coeff_div[coeff].Reg0xF0);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, coeff_div[coeff].Reg0xF1);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, coeff_div[coeff].Reg0x16);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, coeff_div[coeff].Reg0x18);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, coeff_div[coeff].Reg0x19);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8389_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x3E);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
+ usleep_range(8000, 8500);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
+
+ clk_disable_unprepare(es8389->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+
+
+static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x03);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x03);
+ }
+ } else {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x00);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+#define es8389_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8389_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8389_ops = {
+ .hw_params = es8389_pcm_hw_params,
+ .set_fmt = es8389_set_dai_fmt,
+ .set_sysclk = es8389_set_dai_sysclk,
+ .set_tdm_slot = es8389_set_tdm_slot,
+ .mute_stream = es8389_mute,
+};
+
+static struct snd_soc_dai_driver es8389_dai = {
+ .name = "ES8389 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .ops = &es8389_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8389_init(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF2, 0x04);
+ regmap_write(es8389->regmap, ES8389_DAC_INV, 0x03);
+
+ regmap_write(es8389->regmap, ES8389_VMID, 0x2A);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xC9);
+ regmap_write(es8389->regmap, ES8389_ANA_VSEL, 0x4F);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL2, 0x06);
+ regmap_write(es8389->regmap, ES8389_LOW_POWER1, 0x00);
+ regmap_write(es8389->regmap, ES8389_DMIC_EN, 0x16);
+
+ regmap_write(es8389->regmap, ES8389_PGA_SW, 0xAA);
+ regmap_write(es8389->regmap, ES8389_MOD_SW1, 0x66);
+ regmap_write(es8389->regmap, ES8389_MOD_SW2, 0x99);
+ regmap_write(es8389->regmap, ES8389_ADC_MODE, (0x00 | ES8389_TDM_MODE));
+ regmap_update_bits(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00);
+
+ regmap_update_bits(es8389->regmap, ES8389_MIC1_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_update_bits(es8389->regmap, ES8389_MIC2_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xC4);
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x08);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, 0x00);
+ regmap_write(es8389->regmap, ES8389_SYSTEM12, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM13, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM14, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM15, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, 0x35);
+ regmap_write(es8389->regmap, ES8389_SYSTEM17, 0x09);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, 0x91);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, 0x28);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1A, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1B, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1C, 0x11);
+
+ regmap_write(es8389->regmap, ES8389_CHIP_MISC, 0x13);
+ regmap_write(es8389->regmap, ES8389_MASTER_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, 0x10);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, 0x80);
+ regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00);
+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F);
+ regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0);
+ regmap_write(es8389->regmap, ES8389_SYSTEM30, 0xF4);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_MISC, 0x10);
+ regmap_write(es8389->regmap, ES8389_DAC_RAMP, 0x0F);
+ regmap_write(es8389->regmap, ES8389_SYSTEM4C, 0xC0);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC1);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0x02);
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x03);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x03);
+}
+
+static int es8389_suspend(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+ regcache_cache_only(es8389->regmap, true);
+ regcache_mark_dirty(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_resume(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
+
+ regcache_cache_only(es8389->regmap, false);
+ regcache_cache_bypass(es8389->regmap, true);
+ regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ regcache_cache_bypass(es8389->regmap, false);
+
+ if (regv == 0xff)
+ es8389_init(component);
+ else
+ es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mclk-src return %d", ret);
+ es8389->mclk_src = ES8389_MCLK_SOURCE;
+ }
+
+ es8389->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(es8389->mclk))
+ return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
+ "ES8389 is unable to get mclk\n");
+
+ if (!es8389->mclk)
+ dev_err(component->dev, "%s, assuming static mclk\n", __func__);
+
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret) {
+ dev_err(component->dev, "%s, unable to enable mclk\n", __func__);
+ return ret;
+ }
+
+ es8389_init(component);
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static void es8389_remove(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_es8389 = {
+ .probe = es8389_probe,
+ .remove = es8389_remove,
+ .suspend = es8389_suspend,
+ .resume = es8389_resume,
+ .set_bias_level = es8389_set_bias_level,
+
+ .controls = es8389_snd_controls,
+ .num_controls = ARRAY_SIZE(es8389_snd_controls),
+ .dapm_widgets = es8389_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8389_dapm_widgets),
+ .dapm_routes = es8389_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8389_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+};
+
+static const struct regmap_config es8389_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ES8389_MAX_REGISTER,
+
+ .volatile_reg = es8389_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static void es8389_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8389_private *es8389;
+
+ es8389 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+}
+
+static int es8389_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8389_private *es8389;
+ int ret;
+
+ es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL);
+ if (es8389 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8389);
+ es8389->regmap = devm_regmap_init_i2c(i2c_client, &es8389_regmap);
+ if (IS_ERR(es8389->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap),
+ "regmap_init() failed\n");
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_codec_dev_es8389,
+ &es8389_dai,
+ 1);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8389_if_dt_ids[] = {
+ { .compatible = "everest,es8389", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8389_if_dt_ids);
+#endif
+
+static const struct i2c_device_id es8389_i2c_id[] = {
+ {"es8389"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8389_i2c_id);
+
+static struct i2c_driver es8389_i2c_driver = {
+ .driver = {
+ .name = "es8389",
+ .of_match_table = of_match_ptr(es8389_if_dt_ids),
+ },
+ .shutdown = es8389_i2c_shutdown,
+ .probe = es8389_i2c_probe,
+ .id_table = es8389_i2c_id,
+};
+module_i2c_driver(es8389_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8389 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
new file mode 100644
index 000000000000..123d1e4b2d53
--- /dev/null
+++ b/sound/soc/codecs/es8389.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8389.h -- ES8389 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8374.h by Michael Zhang
+*/
+
+#ifndef _ES8389_H
+#define _ES8389_H
+
+/*
+* ES8389_REGISTER NAME_REG_REGISTER ADDRESS
+*/
+#define ES8389_RESET 0x00 /*reset digital,csm,clock manager etc.*/
+
+/*
+* Clock Scheme Register definition
+*/
+#define ES8389_MASTER_MODE 0x01
+#define ES8389_MASTER_CLK 0x02
+#define ES8389_CLK_OFF1 0x03
+#define ES8389_CLK_DIV1 0x04
+#define ES8389_CLK_MUL 0x05
+#define ES8389_CLK_MUX1 0x06
+#define ES8389_CLK_MUX2 0x07
+#define ES8389_CLK_CTL1 0x08
+#define ES8389_CLK_CTL2 0x09
+#define ES8389_CLK_CTL3 0x0A
+#define ES8389_SCLK_DIV 0x0B
+#define ES8389_LRCK_DIV1 0x0C
+#define ES8389_LRCK_DIV2 0x0D
+#define ES8389_CLK_OFF2 0x0E
+#define ES8389_OSC_CLK 0x0F
+#define ES8389_CSM_JUMP 0x10
+#define ES8389_CLK_DIV2 0x11
+#define ES8389_SYSTEM12 0x12
+#define ES8389_SYSTEM13 0x13
+#define ES8389_SYSTEM14 0x14
+#define ES8389_SYSTEM15 0x15
+#define ES8389_SYSTEM16 0x16
+#define ES8389_SYSTEM17 0x17
+#define ES8389_SYSTEM18 0x18
+#define ES8389_SYSTEM19 0x19
+#define ES8389_SYSTEM1A 0x1A
+#define ES8389_SYSTEM1B 0x1B
+#define ES8389_SYSTEM1C 0x1C
+#define ES8389_ADC_FORMAT_MUTE 0x20
+#define ES8389_ADC_OSR 0x21
+#define ES8389_ADC_DSP 0x22
+#define ES8389_ADC_MODE 0x23
+#define ES8389_ADC_HPF1 0x24
+#define ES8389_ADC_HPF2 0x25
+#define ES8389_OSR_VOL 0x26
+#define ES8389_ADCL_VOL 0x27
+#define ES8389_ADCR_VOL 0x28
+#define ES8389_ALC_CTL 0x29
+#define ES8389_PTDM_SLOT 0x2A
+#define ES8389_ALC_ON 0x2B
+#define ES8389_ALC_TARGET 0x2C
+#define ES8389_ALC_GAIN 0x2D
+#define ES8389_SYSTEM2E 0x2E
+#define ES8389_ADC_MUTE 0x2F
+#define ES8389_SYSTEM30 0x30
+#define ES8389_ADC_RESET 0x31
+#define ES8389_DAC_FORMAT_MUTE 0x40
+#define ES8389_DAC_DSM_OSR 0x41
+#define ES8389_DAC_DSP_OSR 0x42
+#define ES8389_DAC_MISC 0x43
+#define ES8389_DAC_MIX 0x44
+#define ES8389_DAC_INV 0x45
+#define ES8389_DACL_VOL 0x46
+#define ES8389_DACR_VOL 0x47
+#define ES8389_MIX_VOL 0x48
+#define ES8389_DAC_RAMP 0x49
+#define ES8389_SYSTEM4C 0x4C
+#define ES8389_DAC_RESET 0x4D
+#define ES8389_VMID 0x60
+#define ES8389_ANA_CTL1 0x61
+#define ES8389_ANA_VSEL 0x62
+#define ES8389_ANA_CTL2 0x63
+#define ES8389_ADC_EN 0x64
+#define ES8389_HPSW 0x69
+#define ES8389_LOW_POWER1 0x6B
+#define ES8389_LOW_POWER2 0x6C
+#define ES8389_DMIC_EN 0x6D
+#define ES8389_PGA_SW 0x6E
+#define ES8389_MOD_SW1 0x6F
+#define ES8389_MOD_SW2 0x70
+#define ES8389_MOD_SW3 0x71
+#define ES8389_MIC1_GAIN 0x72
+#define ES8389_MIC2_GAIN 0x73
+
+#define ES8389_CHIP_MISC 0xF0
+#define ES8389_CSM_STATE1 0xF1
+#define ES8389_PULL_DOWN 0xF2
+#define ES8389_ISO_CTL 0xF3
+#define ES8389_CSM_STATE2 0xF4
+
+#define ES8389_CHIP_ID0 0xFD
+#define ES8389_CHIP_ID1 0xFE
+
+#define ES8389_MAX_REGISTER 0xFF
+
+#define ES8389_MIC_SEL_MASK (7 << 4)
+#define ES8389_MIC_DEFAULT (1 << 4)
+
+#define ES8389_MASTER_MODE_EN (1 << 0)
+
+#define ES8389_TDM_OFF (0 << 0)
+#define ES8389_STDM_ON (1 << 7)
+#define ES8389_PTDM_ON (1 << 6)
+
+#define ES8389_TDM_MODE ES8389_TDM_OFF
+#define ES8389_TDM_SLOT (0x70 << 0)
+#define ES8389_TDM_SHIFT 4
+
+#define ES8389_MCLK_SOURCE (1 << 6)
+#define ES8389_MCLK_PIN (1 << 6)
+#define ES8389_SCLK_PIN (0 << 6)
+
+/* ES8389_FMT */
+#define ES8389_S24_LE (0 << 5)
+#define ES8389_S20_3_LE (1 << 5)
+#define ES8389_S18_LE (2 << 5)
+#define ES8389_S16_LE (3 << 5)
+#define ES8389_S32_LE (4 << 5)
+#define ES8389_DATA_LEN_MASK (7 << 5)
+
+#define ES8389_DAIFMT_MASK (7 << 2)
+#define ES8389_DAIFMT_I2S 0
+#define ES8389_DAIFMT_LEFT_J (1 << 2)
+#define ES8389_DAIFMT_DSP_A (1 << 3)
+#define ES8389_DAIFMT_DSP_B (3 << 3)
+
+#define ES8389_STATE_ON (13 << 0)
+#define ES8389_STATE_STANDBY (7 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es83xx-dsm-common.c b/sound/soc/codecs/es83xx-dsm-common.c
new file mode 100644
index 000000000000..94fd7d54c53b
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) Intel Corporation, 2022
+// Copyright Everest Semiconductor Co.,Ltd
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "es83xx-dsm-common.h"
+
+/* UUID ("a9800c04-e016-343e-41f4-6bcce70f4332") */
+static const guid_t es83xx_dsm_guid =
+ GUID_INIT(0xa9800c04, 0xe016, 0x343e,
+ 0x41, 0xf4, 0x6b, 0xcc, 0xe7, 0x0f, 0x43, 0x32);
+
+#define ES83xx_DSM_REVID 1
+
+int es83xx_dsm(struct device *dev, int arg, int *value)
+{
+ acpi_handle dhandle;
+ union acpi_object *obj;
+ int ret = 0;
+
+ dhandle = ACPI_HANDLE(dev);
+ if (!dhandle)
+ return -ENOENT;
+
+ obj = acpi_evaluate_dsm(dhandle, &es83xx_dsm_guid, ES83xx_DSM_REVID,
+ arg, NULL);
+ if (!obj) {
+ dev_err(dev, "%s: acpi_evaluate_dsm() failed\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ dev_err(dev, "%s: object is not ACPI_TYPE_INTEGER\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ *value = obj->integer.value;
+err:
+ ACPI_FREE(obj);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm);
+
+int es83xx_dsm_dump(struct device *dev)
+{
+ int value;
+ int ret;
+
+ ret = es83xx_dsm(dev, PLATFORM_MAINMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MAINMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_SPK_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_SPK_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPDET_INV_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPDET_INV %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_PCM_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_PCM_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_MIC_DE_POP_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MIC_DE_POP %#x\n", value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm_dump);
+
+MODULE_DESCRIPTION("Everest Semi ES83xx DSM helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es83xx-dsm-common.h b/sound/soc/codecs/es83xx-dsm-common.h
new file mode 100644
index 000000000000..91c9a89e75e9
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Intel Corporation, 2022
+ * Copyright Everest Semiconductor Co.,Ltd
+ */
+
+/* Definitions extracted from ASL file provided at
+ * https://github.com/thesofproject/linux/files/9398723/ESSX8326.zip
+ */
+
+#ifndef _ES83XX_DSM_COMMON_H
+#define _ES83XX_DSM_COMMON_H
+
+/***************************************************
+ * DSM arguments *
+ ***************************************************/
+
+#define PLATFORM_MAINMIC_TYPE_ARG 0x00
+#define PLATFORM_HPMIC_TYPE_ARG 0x01
+#define PLATFORM_SPK_TYPE_ARG 0x02
+#define PLATFORM_HPDET_INV_ARG 0x03
+#define PLATFORM_PCM_TYPE_ARG 0x04
+
+#define PLATFORM_MIC_DE_POP_ARG 0x06
+#define PLATFORM_CODEC_TYPE_ARG 0x0E
+#define PLATFORM_BUS_SLOT_ARG 0x0F
+
+#define HP_CODEC_LINEIN_PGA_GAIN_ARG 0x10
+#define MAIN_CODEC_LINEIN_PGA_GAIN_ARG 0x20
+
+#define HP_CODEC_D2SEPGA_GAIN_ARG 0x11
+#define MAIN_CODEC_D2SEPGA_GAIN_ARG 0x21
+
+#define HP_CODEC_ADC_VOLUME_ARG 0x12
+#define MAIN_CODEC_ADC_VOLUME_ARG 0x22
+
+#define HP_CODEC_ADC_ALC_ENABLE_ARG 0x13
+#define MAIN_CODEC_ADC_ALC_ENABLE_ARG 0x23
+
+#define HP_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x14
+#define MAIN_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x24
+
+#define HP_CODEC_ADC_ALC_MAXGAIN_ARG 0x15
+#define MAIN_CODEC_ADC_ALC_MAXGAIN_ARG 0x25
+
+#define HP_CODEC_ADC_ALC_MINGAIN_ARG 0x16
+#define MAIN_CODEC_ADC_ALC_MINGAIN_ARG 0x26
+
+#define HP_CODEC_ADC_ALC_HLDTIME_ARG 0x17
+#define MAIN_CODEC_ADC_ALC_HLDTIME_ARG 0x27
+
+#define HP_CODEC_ADC_ALC_DCYTIME_ARG 0x18
+#define MAIN_CODEC_ADC_ALC_DCYTIME_ARG 0x28
+
+#define HP_CODEC_ADC_ALC_ATKTIME_ARG 0x19
+#define MAIN_CODEC_ADC_ALC_ATKTIME_ARG 0x29
+
+#define HP_CODEC_ADC_ALC_NGTYPE_ARG 0x1a
+#define MAIN_CODEC_ADC_ALC_NGTYPE_ARG 0x2a
+
+#define HP_CODEC_ADC_ALC_NGTHLD_ARG 0x1b
+#define MAIN_CODEC_ADC_ALC_NGTHLD_ARG 0x2b
+
+#define MAIN_CODEC_ADC_GUI_STEP_ARG 0x2c
+#define MAIN_CODEC_ADC_GUI_GAIN_RANGE_ARG 0x2c
+
+#define HEADPHONE_DUMMY_REMOVE_ENABLE_ARG 0x2e
+
+#define HP_CODEC_DAC_HPMIX_HIGAIN_ARG 0x40
+#define SPK_CODEC_DAC_HPMIX_HIGAIN_ARG 0x50
+
+#define HP_CODEC_DAC_HPMIX_VOLUME_ARG 0x41
+#define SPK_CODEC_DAC_HPMIX_VOLUME_ARG 0x51
+
+#define HP_CODEC_DAC_HPOUT_VOLUME_ARG 0x42
+#define SPK_CODEC_DAC_HPOUT_VOLUME_ARG 0x52
+
+#define HP_CODEC_LDAC_VOLUME_ARG 0x44
+#define HP_CODEC_RDAC_VOLUME_ARG 0x54
+
+#define SPK_CODEC_LDAC_VOLUME_ARG 0x45
+#define SPK_CODEC_RDAC_VOLUME_ARG 0x55
+
+#define HP_CODEC_DAC_AUTOMUTE_ARG 0x46
+#define SPK_CODEC_DAC_AUTOMUTE_ARG 0x56
+
+#define HP_CODEC_DAC_MONO_ARG 0x4A
+#define SPK_CODEC_DAC_MONO_ARG 0x5A
+
+#define HP_CTL_IO_LEVEL_ARG 0x4B
+#define SPK_CTL_IO_LEVEL_ARG 0x5B
+
+#define CODEC_GPIO0_FUNC_ARG 0x80
+#define CODEC_GPIO1_FUNC_ARG 0x81
+#define CODEC_GPIO2_FUNC_ARG 0x82
+#define CODEC_GPIO3_FUNC_ARG 0x83
+#define CODEC_GPIO4_FUNC_ARG 0x84
+
+#define PLATFORM_MCLK_LRCK_FREQ_ARG 0x85
+
+/***************************************************
+ * Values for arguments *
+ ***************************************************/
+
+/* Main and HP Mic */
+#define PLATFORM_MIC_DMIC_HIGH_LEVEL 0xAA
+#define PLATFORM_MIC_DMIC_LOW_LEVEL 0x55
+#define PLATFORM_MIC_AMIC_LIN1RIN1 0xBB
+#define PLATFORM_MIC_AMIC_LIN2RIN2 0xCC
+
+/* Speaker */
+#define PLATFORM_SPK_NONE 0x00
+#define PLATFORM_SPK_MONO 0x01
+#define PLATFORM_SPK_STEREO 0x02
+
+/* Jack Detection */
+#define PLATFORM_HPDET_NORMAL 0x00
+#define PLATFORM_HPDET_INVERTED 0x01
+
+/* PCM type (Port number + protocol) */
+/*
+ * RETURNED VALUE = 0x00, PCM PORT0, I2S
+ * 0x01, PCM PORT0, LJ
+ * 0x02, PCM PORT0, RJ
+ * 0x03, PCM PORT0, DSP-A
+ * 0x04, PCM PORT0, DSP-B
+ * 0x10, PCM PORT1, I2S
+ * 0x11, PCM PORT1, LJ
+ * 0x12, PCM PORT1, RJ
+ * 0x13, PCM PORT1, DSP-A
+ * 0x14, PCM PORT1, DSP-B
+ * 0xFF, Use default
+ *
+ * This is not used in Linux (defined by topology) and in
+ * Windows it's always DSP-A
+ */
+
+/* Depop */
+#define PLATFORM_MIC_DE_POP_OFF 0x00
+#define PLATFORM_MIC_DE_POP_ON 0x01
+
+/* Codec type */
+#define PLATFORM_CODEC_8316 16
+#define PLATFORM_CODEC_8326 26
+#define PLATFORM_CODEC_8336 36
+#define PLATFORM_CODEC_8395 95
+#define PLATFORM_CODEC_8396 96
+
+/* Bus slot (on the host) */
+/* BIT[3:0] FOR BUS NUMBER, BIT[7:4] FOR SLOT NUMBER
+ * BIT[3:0] 0 for I2S0, 1 for IS21, 2 for I2S2.
+ *
+ * On Intel platforms this refers to SSP0..2. This information
+ * is not really useful for Linux, the information is already
+ * inferred from NHLT but can be used to double-check NHLT
+ */
+
+/* Volume - Gain */
+#define LINEIN_GAIN_0db 0x00 /* gain = 0db */
+#define LINEIN_GAIN_3db 0x01 /* gain = +3db */
+#define LINEIN_GAIN_6db 0x02 /* gain = +6db */
+#define LINEIN_GAIN_9db 0x03 /* gain = +9db */
+#define LINEIN_GAIN_12db 0x04 /* gain = +12db */
+#define LINEIN_GAIN_15db 0x05 /* gain = +15db */
+#define LINEIN_GAIN_18db 0x06 /* gain = +18db */
+#define LINEIN_GAIN_21db 0x07 /* gain = +21db */
+#define LINEIN_GAIN_24db 0x08 /* gain = +24db */
+#define LINEIN_GAIN_27db 0x09 /* gain = +27db */
+#define LINEIN_GAIN_30db 0x0a /* gain = +30db */
+
+#define ADC_GUI_STEP_3db 0x03 /* gain = +3db */
+#define ADC_GUI_STEP_6db 0x06 /* gain = +6db */
+#define ADC_GUI_STEP_10db 0x0a /* gain = +10db */
+
+#define D2SEPGA_GAIN_0db 0x00 /* gain = 0db */
+#define D2SEPGA_GAIN_15db 0x01 /* gain = +15db */
+
+/* ADC volume: base = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+#define ADC_ALC_DISABLE 0x00
+#define ADC_ALC_ENABLE 0x01
+
+#define ADC_ALC_TARGET_LEVEL_m16_5db 0x00 /* gain = -16.5db */
+#define ADC_ALC_TARGET_LEVEL_m15db 0x01 /* gain = -15db */
+#define ADC_ALC_TARGET_LEVEL_m13_5db 0x02 /* gain = -13.5db */
+#define ADC_ALC_TARGET_LEVEL_m12db 0x03 /* gain = -12db */
+#define ADC_ALC_TARGET_LEVEL_m10_5db 0x04 /* gain = -10.5db */
+#define ADC_ALC_TARGET_LEVEL_m9db 0x05 /* gain = -9db */
+#define ADC_ALC_TARGET_LEVEL_m7_5db 0x06 /* gain = -7.5db */
+#define ADC_ALC_TARGET_LEVEL_m6db 0x07 /* gain = -6db */
+#define ADC_ALC_TARGET_LEVEL_m4_5db 0x08 /* gain = -4.5db */
+#define ADC_ALC_TARGET_LEVEL_m_3db 0x09 /* gain = -3db */
+#define ADC_ALC_TARGET_LEVEL_m1_5db 0x0a /* gain = -1.5db */
+
+#define ADC_ALC_MAXGAIN_m6_5db 0x00 /* gain = -6.5db */
+#define ADC_ALC_MAXGAIN_m5db 0x01 /* gain = -5db */
+#define ADC_ALC_MAXGAIN_m3_5db 0x02 /* gain = -3.5db */
+#define ADC_ALC_MAXGAIN_m2db 0x03 /* gain = -2db */
+#define ADC_ALC_MAXGAIN_m0_5db 0x04 /* gain = -0.5db */
+#define ADC_ALC_MAXGAIN_1db 0x05 /* gain = +1db */
+#define ADC_ALC_MAXGAIN_2_5db 0x06 /* gain = +2.5db */
+#define ADC_ALC_MAXGAIN_4db 0x07 /* gain = +4db */
+#define ADC_ALC_MAXGAIN_5_5db 0x08 /* gain = +5.5db */
+#define ADC_ALC_MAXGAIN_7db 0x09 /* gain = +7db */
+#define ADC_ALC_MAXGAIN_8_5db 0x0a /* gain = +8.5db */
+#define ADC_ALC_MAXGAIN_10db 0x0b /* gain = +10db */
+#define ADC_ALC_MAXGAIN_11_5db 0x0c /* gain = +11.5db */
+#define ADC_ALC_MAXGAIN_13db 0x0d /* gain = +13db */
+#define ADC_ALC_MAXGAIN_14_5db 0x0e /* gain = +14.5db */
+#define ADC_ALC_MAXGAIN_16db 0x0f /* gain = +16db */
+#define ADC_ALC_MAXGAIN_17_5db 0x10 /* gain = +17.5db */
+#define ADC_ALC_MAXGAIN_19db 0x11 /* gain = +19db */
+#define ADC_ALC_MAXGAIN_20_5db 0x12 /* gain = +20.5db */
+#define ADC_ALC_MAXGAIN_22db 0x13 /* gain = +22db */
+#define ADC_ALC_MAXGAIN_23_5db 0x14 /* gain = +23.5db */
+#define ADC_ALC_MAXGAIN_25db 0x15 /* gain = +25db */
+#define ADC_ALC_MAXGAIN_26_5db 0x16 /* gain = +26.5db */
+#define ADC_ALC_MAXGAIN_28db 0x17 /* gain = +28db */
+#define ADC_ALC_MAXGAIN_29_5db 0x18 /* gain = +29.5db */
+#define ADC_ALC_MAXGAIN_31db 0x19 /* gain = +31db */
+#define ADC_ALC_MAXGAIN_32_5db 0x1a /* gain = +32.5db */
+#define ADC_ALC_MAXGAIN_34db 0x1b /* gain = +34db */
+#define ADC_ALC_MAXGAIN_35_5db 0x1c /* gain = +35.5db */
+
+#define ADC_ALC_MINGAIN_m12db 0x00 /* gain = -12db */
+#define ADC_ALC_MINGAIN_m10_5db 0x01 /* gain = -10.5db */
+#define ADC_ALC_MINGAIN_m9db 0x02 /* gain = -9db */
+#define ADC_ALC_MINGAIN_m7_5db 0x03 /* gain = -7.5db */
+#define ADC_ALC_MINGAIN_m6db 0x04 /* gain = -6db */
+#define ADC_ALC_MINGAIN_m4_51db 0x05 /* gain = -4.51db */
+#define ADC_ALC_MINGAIN_m3db 0x06 /* gain = -3db */
+#define ADC_ALC_MINGAIN_m1_5db 0x07 /* gain = -1.5db */
+#define ADC_ALC_MINGAIN_0db 0x08 /* gain = 0db */
+#define ADC_ALC_MINGAIN_1_5db 0x09 /* gain = +1.5db */
+#define ADC_ALC_MINGAIN_3db 0x0a /* gain = +3db */
+#define ADC_ALC_MINGAIN_4_5db 0x0b /* gain = +4.5db */
+#define ADC_ALC_MINGAIN_6db 0x0c /* gain = +6db */
+#define ADC_ALC_MINGAIN_7_5db 0x0d /* gain = +7.5db */
+#define ADC_ALC_MINGAIN_9db 0x0e /* gain = +9db */
+#define ADC_ALC_MINGAIN_10_5db 0x0f /* gain = +10.5db */
+#define ADC_ALC_MINGAIN_12db 0x10 /* gain = +12db */
+#define ADC_ALC_MINGAIN_13_5db 0x11 /* gain = +13.5db */
+#define ADC_ALC_MINGAIN_15db 0x12 /* gain = +15db */
+#define ADC_ALC_MINGAIN_16_5db 0x13 /* gain = +16.5db */
+#define ADC_ALC_MINGAIN_18db 0x14 /* gain = +18db */
+#define ADC_ALC_MINGAIN_19_5db 0x15 /* gain = +19.5db */
+#define ADC_ALC_MINGAIN_21db 0x16 /* gain = +21db */
+#define ADC_ALC_MINGAIN_22_5db 0x17 /* gain = +22.5db */
+#define ADC_ALC_MINGAIN_24db 0x18 /* gain = +24db */
+#define ADC_ALC_MINGAIN_25_5db 0x19 /* gain = +25.5db */
+#define ADC_ALC_MINGAIN_27db 0x1a /* gain = +27db */
+#define ADC_ALC_MINGAIN_28_5db 0x1b /* gain = +28.5db */
+#define ADC_ALC_MINGAIN_30db 0x1c /* gain = +30db */
+
+/* ADC volume: step 1dB */
+
+/* ALC Hold, Decay, Attack */
+#define ADC_ALC_HLDTIME_0_US 0x00
+#define ADC_ALC_HLDTIME_0000266_US 0x01 //time = 2.67ms
+#define ADC_ALC_HLDTIME_0000533_US 0x02 //time = 5.33ms
+#define ADC_ALC_HLDTIME_0001066_US 0x03 //time = 10.66ms
+#define ADC_ALC_HLDTIME_0002132_US 0x04 //time = 21.32ms
+#define ADC_ALC_HLDTIME_0004264_US 0x05 //time = 42.64ms
+#define ADC_ALC_HLDTIME_0008538_US 0x06 //time = 85.38ms
+#define ADC_ALC_HLDTIME_0017076_US 0x07 //time = 170.76ms
+#define ADC_ALC_HLDTIME_0034152_US 0x08 //time = 341.52ms
+#define ADC_ALC_HLDTIME_0680000_US 0x09 //time = 0.68s
+#define ADC_ALC_HLDTIME_1360000_US 0x0a //time = 1.36s
+
+#define ADC_ALC_DCYTIME_000410_US 0x00 //time = 410us
+#define ADC_ALC_DCYTIME_000820_US 0x01 //time = 820us
+#define ADC_ALC_DCYTIME_001640_US 0x02 //time = 1.64ms
+#define ADC_ALC_DCYTIME_003280_US 0x03 //time = 3.28ms
+#define ADC_ALC_DCYTIME_006560_US 0x04 //time = 6.56ms
+#define ADC_ALC_DCYTIME_013120_US 0x05 //time = 13.12ms
+#define ADC_ALC_DCYTIME_026240_US 0x06 //time = 26.24ms
+#define ADC_ALC_DCYTIME_058480_US 0x07 //time = 52.48ms
+#define ADC_ALC_DCYTIME_104960_US 0x08 //time = 104.96ms
+#define ADC_ALC_DCYTIME_209920_US 0x09 //time = 209.92ms
+#define ADC_ALC_DCYTIME_420000_US 0x0a //time = 420ms
+
+#define ADC_ALC_ATKTIME_000104_US 0x00 //time = 104us
+#define ADC_ALC_ATKTIME_000208_US 0x01 //time = 208us
+#define ADC_ALC_ATKTIME_000416_US 0x02 //time = 416ms
+#define ADC_ALC_ATKTIME_003832_US 0x03 //time = 832ms
+#define ADC_ALC_ATKTIME_001664_US 0x04 //time = 1.664ms
+#define ADC_ALC_ATKTIME_003328_US 0x05 //time = 3.328ms
+#define ADC_ALC_ATKTIME_006656_US 0x06 //time = 6.656ms
+#define ADC_ALC_ATKTIME_013312_US 0x07 //time = 13.312ms
+#define ADC_ALC_ATKTIME_026624_US 0x08 //time = 26.624ms
+#define ADC_ALC_ATKTIME_053248_US 0x09 //time = 53.248ms
+#define ADC_ALC_ATKTIME_106496_US 0x0a //time = 106.496ms
+
+/* ALC Noise Gate */
+#define ADC_ALC_NGTYPE_DISABLE 0x00 //noise gate disable
+#define ADC_ALC_NGTYPE_ENABLE_HOLD 0x01 //noise gate enable, hold gain type
+#define ADC_ALC_NGTYPE_ENABLE_MUTE 0x03 //noise gate enable, mute type
+
+#define ADC_ALC_NGTHLD_m76_5db 0x00 /* Threshold = -76.5db */
+#define ADC_ALC_NGTHLD_m75db 0x01 /* Threshold = -75db */
+#define ADC_ALC_NGTHLD_m73_5db 0x02 /* Threshold = -73.5db */
+#define ADC_ALC_NGTHLD_m72db 0x03 /* Threshold = -72db */
+#define ADC_ALC_NGTHLD_m70_5db 0x04 /* Threshold = -70.5db */
+#define ADC_ALC_NGTHLD_m69db 0x05 /* Threshold = -69db */
+#define ADC_ALC_NGTHLD_m67_5db 0x06 /* Threshold = -67.5db */
+#define ADC_ALC_NGTHLD_m66db 0x07 /* Threshold = -66db */
+#define ADC_ALC_NGTHLD_m64_5db 0x08 /* Threshold = -64.5db */
+#define ADC_ALC_NGTHLD_m63db 0x09 /* Threshold = -63db */
+#define ADC_ALC_NGTHLD_m61_5db 0x0a /* Threshold = -61.5db */
+#define ADC_ALC_NGTHLD_m60db 0x0b /* Threshold = -60db */
+#define ADC_ALC_NGTHLD_m58_5db 0x0c /* Threshold = -58.5db */
+#define ADC_ALC_NGTHLD_m57db 0x0d /* Threshold = -57db */
+#define ADC_ALC_NGTHLD_m55_5db 0x0e /* Threshold = -55.5db */
+#define ADC_ALC_NGTHLD_m54db 0x0f /* Threshold = -54db */
+#define ADC_ALC_NGTHLD_m52_5db 0x10 /* Threshold = -52.5db */
+#define ADC_ALC_NGTHLD_m51db 0x11 /* Threshold = -51db */
+#define ADC_ALC_NGTHLD_m49_5db 0x12 /* Threshold = -49.5db */
+#define ADC_ALC_NGTHLD_m48db 0x13 /* Threshold = -48db */
+#define ADC_ALC_NGTHLD_m46_5db 0x14 /* Threshold = -46.5db */
+#define ADC_ALC_NGTHLD_m45db 0x15 /* Threshold = -45db */
+#define ADC_ALC_NGTHLD_m43_5db 0x16 /* Threshold = -43.5db */
+#define ADC_ALC_NGTHLD_m42db 0x17 /* Threshold = -42db */
+#define ADC_ALC_NGTHLD_m40_5db 0x18 /* Threshold = -40.5db */
+#define ADC_ALC_NGTHLD_m39db 0x19 /* Threshold = -39db */
+#define ADC_ALC_NGTHLD_m37_5db 0x1a /* Threshold = -37.5db */
+#define ADC_ALC_NGTHLD_m36db 0x1b /* Threshold = -36db */
+#define ADC_ALC_NGTHLD_m34_5db 0x1c /* Threshold = -34.5db */
+#define ADC_ALC_NGTHLD_m33db 0x1d /* Threshold = -33db */
+#define ADC_ALC_NGTHLD_m31_5db 0x1e /* Threshold = -31.5db */
+#define ADC_ALC_NGTHLD_m30db 0x1f /* Threshold = -30db */
+
+/* Headphone dummy - Windows Specific flag, not needed for Linux */
+
+/* HPMIX HIGAIN and VOLUME */
+#define DAC_HPMIX_HIGAIN_0db 0x00 /* gain = 0db */
+#define DAC_HPMIX_HIGAIN_m6db 0x88 /* gain = -6db */
+
+#define DAC_HPMIX_VOLUME_m12db 0x00 /* volume = -12db */
+#define DAC_HPMIX_VOLUME_m10_5db 0x11 /* volume = -10.5db */
+#define DAC_HPMIX_VOLUME_m9db 0x22 /* volume = -9db */
+#define DAC_HPMIX_VOLUME_m7_5db 0x33 /* volume = -7.5db */
+#define DAC_HPMIX_VOLUME_m6db 0x44 /* volume = -6db */
+#define DAC_HPMIX_VOLUME_m4_5db 0x88 /* volume = -4.5db */
+#define DAC_HPMIX_VOLUME_m3db 0x99 /* volume = -3db */
+#define DAC_HPMIX_VOLUME_m1_5db 0xaa /* volume = -1.5db */
+#define DAC_HPMIX_VOLUME_0db 0xbb /* volume = 0db */
+
+/* HPOUT VOLUME */
+#define DAC_HPOUT_VOLUME_0db 0x00 /* volume = 0db */
+#define DAC_HPOUT_VOLUME_m12db 0x11 /* volume = -12db */
+#define DAC_HPOUT_VOLUME_m24db 0x22 /* volume = -24db */
+#define DAC_HPOUT_VOLUME_m48db 0x33 /* volume = -48db */
+
+/* LDAC/RDAC volume = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+/* Automute */
+#define DAC_AUTOMUTE_NONE 0x00 /* no automute */
+#define DAC_AUTOMUTE_DIGITAL 0x01 /* digital mute */
+#define DAC_AUTOMUTE_ANALOG 0x02 /* analog mute */
+
+/* Mono - Windows specific, on Linux the information comes from DAI/topology */
+#define HEADPHONE_MONO 0x01 /* on channel */
+#define HEADPHONE_STEREO 0x00 /* stereo */
+
+/* Speaker and headphone GPIO control */
+#define GPIO_CTL_IO_LEVEL_LOW 0x00 /* low level enable */
+#define GPIO_CTL_IO_LEVEL_HIGH 0x01 /* high level enable */
+
+/* GPIO */
+/* FIXME: for ES8396, no need to use */
+
+/* Platform clocks */
+/*
+ * BCLK AND MCLK FREQ
+ * BIT[7:4] MCLK FREQ
+ * 0 - 19.2MHz
+ * 1 - 24MHz
+ * 2 - 12.288MHz
+ * F - Default for 19.2MHz
+ *
+ * BIT[3:0] BCLK FREQ
+ * 0 - 4.8MHz
+ * 1 - 2.4MHz
+ * 2 - 2.304MHz
+ * 3 - 3.072MHz
+ * 4 - 4.096MHz
+ * F - Default for 4.8MHz
+ */
+
+int es83xx_dsm(struct device *dev, int arg, int *value);
+int es83xx_dsm_dump(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c
new file mode 100644
index 000000000000..6f57a3aeecc8
--- /dev/null
+++ b/sound/soc/codecs/framer-codec.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Framer ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/framer/framer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define FRAMER_NB_CHANNEL 32
+#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT)
+
+struct framer_codec {
+ struct framer *framer;
+ struct device *dev;
+ struct snd_soc_jack jack;
+ struct notifier_block nb;
+ struct work_struct carrier_work;
+ int max_chan_playback;
+ int max_chan_capture;
+};
+
+static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ 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;
+ }
+
+ framer->max_chan_playback = hweight32(tx_mask);
+ if (framer->max_chan_playback > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n",
+ tx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ framer->max_chan_capture = hweight32(rx_mask);
+ if (framer->max_chan_capture > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n",
+ rx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_pcm_format_t format = params_format(params);
+ struct snd_interval ch = {0};
+ int width;
+
+ width = snd_pcm_format_physical_width(format);
+ if (width == 8 || width == 16 || width == 32 || width == 64) {
+ ch.max = nb_ts * 8 / width;
+ } else {
+ dev_err(dai->dev, "format physical width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ ch.min = ch.max ? 1 : 0;
+
+ return snd_interval_refine(c, &ch);
+}
+
+static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture);
+}
+
+static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ unsigned int channels = params_channels(params);
+ unsigned int slot_width;
+ snd_pcm_format_t format;
+ struct snd_mask f_new;
+
+ if (!channels || channels > nb_ts) {
+ dev_err(dai->dev, "channels %u not supported\n", nb_ts);
+ return -EINVAL;
+ }
+
+ slot_width = (nb_ts / channels) * 8;
+
+ snd_mask_none(&f_new);
+ pcm_for_each_format(format) {
+ if (snd_mask_test_format(f_old, format)) {
+ if (snd_pcm_format_physical_width(format) <= slot_width)
+ snd_mask_set_format(&f_new, format);
+ }
+ }
+
+ return snd_mask_refine(f_old, &f_new);
+}
+
+static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture);
+}
+
+static u64 framer_formats(u8 nb_ts)
+{
+ unsigned int format_width;
+ unsigned int chan_width;
+ snd_pcm_format_t format;
+ u64 formats_mask;
+
+ if (!nb_ts)
+ return 0;
+
+ formats_mask = 0;
+ chan_width = nb_ts * 8;
+ pcm_for_each_format(format) {
+ /* Support physical width multiple of 8bit */
+ format_width = snd_pcm_format_physical_width(format);
+ if (format_width == 0 || format_width % 8)
+ continue;
+
+ /*
+ * And support physical width that can fit N times in the
+ * channel
+ */
+ if (format_width > chan_width || chan_width % format_width)
+ continue;
+
+ formats_mask |= pcm_format_to_bits(format);
+ }
+ return formats_mask;
+}
+
+static int framer_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+ snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+ snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+ unsigned int frame_bits;
+ u64 format;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ format = framer_formats(framer->max_chan_capture);
+ hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels;
+ frame_bits = framer->max_chan_capture * 8;
+ } else {
+ format = framer_formats(framer->max_chan_playback);
+ hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels;
+ frame_bits = framer->max_chan_playback * 8;
+ }
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT, format);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels_by_format, dai,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_format_by_channels, dai,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const u64 framer_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops framer_dai_ops = {
+ .startup = framer_dai_startup,
+ .set_tdm_slot = framer_dai_set_tdm_slot,
+ .auto_selectable_formats = framer_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
+};
+
+static struct snd_soc_dai_driver framer_dai_driver = {
+ .name = "framer",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .ops = &framer_dai_ops,
+};
+
+static void framer_carrier_work(struct work_struct *work)
+{
+ struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work);
+ struct framer_status framer_status;
+ int jack_status;
+ int ret;
+
+ ret = framer_get_status(framer->framer, &framer_status);
+ if (ret) {
+ dev_err(framer->dev, "get framer status failed (%d)\n", ret);
+ return;
+ }
+
+ jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0;
+ snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK);
+}
+
+static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct framer_codec *framer = container_of(nb, struct framer_codec, nb);
+
+ switch (action) {
+ case FRAMER_EVENT_STATUS:
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int framer_component_probe(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+ struct framer_status status;
+ char *name;
+ int ret;
+
+ INIT_WORK(&framer->carrier_work, framer_carrier_work);
+
+ name = "carrier";
+ if (component->name_prefix) {
+ name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack);
+ if (component->name_prefix)
+ kfree(name); /* A copy is done by snd_soc_card_jack_new */
+ if (ret) {
+ dev_err(component->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ ret = framer_init(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer init failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = framer_power_on(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer power-on failed (%d)\n", ret);
+ goto framer_exit;
+ }
+
+ /* Be sure that get_status is supported */
+ ret = framer_get_status(framer->framer, &status);
+ if (ret) {
+ dev_err(component->dev, "get framer status failed (%d)\n", ret);
+ goto framer_power_off;
+ }
+
+ framer->nb.notifier_call = framer_carrier_notifier;
+ ret = framer_notifier_register(framer->framer, &framer->nb);
+ if (ret) {
+ dev_err(component->dev, "Cannot register event notifier\n");
+ goto framer_power_off;
+ }
+
+ /* Queue work to set the initial value */
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+
+ return 0;
+
+framer_power_off:
+ framer_power_off(framer->framer);
+framer_exit:
+ framer_exit(framer->framer);
+ return ret;
+}
+
+static void framer_component_remove(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+
+ framer_notifier_unregister(framer->framer, &framer->nb);
+ cancel_work_sync(&framer->carrier_work);
+ framer_power_off(framer->framer);
+ framer_exit(framer->framer);
+}
+
+static const struct snd_soc_component_driver framer_component_driver = {
+ .probe = framer_component_probe,
+ .remove = framer_component_remove,
+ .endianness = 1,
+};
+
+static int framer_codec_probe(struct platform_device *pdev)
+{
+ struct framer_codec *framer;
+
+ framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL);
+ if (!framer)
+ return -ENOMEM;
+
+ framer->dev = &pdev->dev;
+
+ /* Get framer from parents node */
+ framer->framer = devm_framer_get(&pdev->dev, NULL);
+ if (IS_ERR(framer->framer))
+ return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n");
+
+ platform_set_drvdata(pdev, framer);
+
+ return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver,
+ &framer_dai_driver, 1);
+}
+
+static struct platform_driver framer_codec_driver = {
+ .driver = {
+ .name = "framer-codec",
+ },
+ .probe = framer_codec_probe,
+};
+module_platform_driver(framer_codec_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("FRAMER ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c
new file mode 100644
index 000000000000..c8f56617e370
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fs-amp-lib.h"
+
+static int fs_get_scene_count(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ int count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ if (!table)
+ return -EFAULT;
+
+ count = table->size / sizeof(struct fs_scene_index);
+ if (count < 1 || count > FS_SCENE_COUNT_MAX) {
+ dev_err(amp_lib->dev, "Invalid scene count: %d\n", count);
+ return -ERANGE;
+ }
+
+ return count;
+}
+
+static void fs_get_fwm_string(struct fs_amp_lib *amp_lib,
+ int offset, const char **pstr)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !pstr)
+ return;
+
+ table = amp_lib->table[FS_INDEX_STRING];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ *pstr = (char *)table + offset;
+ else
+ *pstr = NULL;
+}
+
+static void fs_get_scene_reg(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_REG];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->reg = (struct fs_reg_table *)((char *)table + offset);
+ else
+ scene->reg = NULL;
+}
+
+static void fs_get_scene_model(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_MODEL];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->model = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->model = NULL;
+}
+
+static void fs_get_scene_effect(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_EFFECT];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->effect = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->effect = NULL;
+}
+
+static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_scene_index *scene_index;
+ const struct fs_fwm_table *table;
+ struct fs_amp_scene *scene;
+ int idx, count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ count = fs_get_scene_count(amp_lib);
+ if (count <= 0)
+ return -EFAULT;
+
+ scene = devm_kcalloc(amp_lib->dev, count, sizeof(*scene), GFP_KERNEL);
+ if (!scene)
+ return -ENOMEM;
+
+ amp_lib->scene_count = count;
+ amp_lib->scene = scene;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ scene_index = (struct fs_scene_index *)table->buf;
+
+ for (idx = 0; idx < count; idx++) {
+ fs_get_fwm_string(amp_lib, scene_index->name, &scene->name);
+ if (!scene->name)
+ scene->name = devm_kasprintf(amp_lib->dev,
+ GFP_KERNEL, "S%d", idx);
+ dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name);
+ fs_get_scene_reg(amp_lib, scene_index->reg, scene);
+ fs_get_scene_model(amp_lib, scene_index->model, scene);
+ fs_get_scene_effect(amp_lib, scene_index->effect, scene);
+ scene++;
+ scene_index++;
+ }
+
+ return 0;
+}
+
+static int fs_parse_all_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ const struct fs_fwm_index *index;
+ const char *ptr;
+ int idx, count;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ /* Parse all fwm tables */
+ table = (struct fs_fwm_table *)amp_lib->hdr->params;
+ index = (struct fs_fwm_index *)table->buf;
+ count = table->size / sizeof(*index);
+
+ for (idx = 0; idx < count; idx++, index++) {
+ if (index->type >= FS_INDEX_MAX)
+ return -ERANGE;
+ ptr = (char *)table + (int)index->offset;
+ amp_lib->table[index->type] = (struct fs_fwm_table *)ptr;
+ }
+
+ /* Parse all scene tables */
+ ret = fs_parse_scene_tables(amp_lib);
+ if (ret)
+ dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret);
+
+ return ret;
+}
+
+static int fs_verify_firmware(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ int crcsum;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ hdr = amp_lib->hdr;
+
+ /* Verify the crcsum code */
+ crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size);
+ if (crcsum != hdr->crc16) {
+ dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n",
+ crcsum, hdr->crc16);
+ return -EFAULT;
+ }
+
+ /* Verify the devid(chip_type) */
+ if (amp_lib->devid != LO_U16(hdr->chip_type)) {
+ dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n",
+ amp_lib->devid, hdr->chip_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fs_print_firmware_info(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ const char *pro_name = NULL;
+ const char *dev_name = NULL;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return;
+
+ hdr = amp_lib->hdr;
+
+ fs_get_fwm_string(amp_lib, hdr->project, &pro_name);
+ fs_get_fwm_string(amp_lib, hdr->device, &dev_name);
+
+ dev_info(amp_lib->dev, "Project: %s Device: %s\n",
+ pro_name ? pro_name : "null",
+ dev_name ? dev_name : "null");
+
+ dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n",
+ hdr->date.year, hdr->date.month, hdr->date.day,
+ hdr->date.hour, hdr->date.minute);
+}
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name)
+{
+ const struct firmware *cont;
+ struct fs_fwm_header *hdr;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !name)
+ return -EINVAL;
+
+ ret = request_firmware(&cont, name, amp_lib->dev);
+ if (ret) {
+ dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret);
+ return ret;
+ }
+
+ dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size);
+
+ hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL);
+ release_firmware(cont);
+ if (!hdr)
+ return -ENOMEM;
+
+ amp_lib->hdr = hdr;
+ ret = fs_verify_firmware(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ ret = fs_parse_all_tables(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ fs_print_firmware_info(amp_lib);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fs_amp_load_firmware);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FourSemi audio amplifier library");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h
new file mode 100644
index 000000000000..4a77c7b383cd
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS_AMP_LIB_H__
+#define __FS_AMP_LIB_H__
+
+#define HI_U16(a) (((a) >> 8) & 0xFF)
+#define LO_U16(a) ((a) & 0xFF)
+#define FS_TABLE_NAME_LEN (4)
+#define FS_SCENE_COUNT_MAX (16)
+#define FS_CMD_DELAY_MS_MAX (100) /* 100ms */
+
+#define FS_CMD_DELAY (0xFF)
+#define FS_CMD_BURST (0xFE)
+#define FS_CMD_UPDATE (0xFD)
+
+#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = xhandler_info, \
+ .get = xhandler_get, .put = xhandler_put \
+}
+
+enum fs_index_type {
+ FS_INDEX_INFO = 0,
+ FS_INDEX_STCOEF,
+ FS_INDEX_SCENE,
+ FS_INDEX_MODEL,
+ FS_INDEX_REG,
+ FS_INDEX_EFFECT,
+ FS_INDEX_STRING,
+ FS_INDEX_WOOFER,
+ FS_INDEX_MAX,
+};
+
+#pragma pack(push, 1)
+
+struct fs_reg_val {
+ u8 reg;
+ u16 val;
+};
+
+struct fs_reg_bits {
+ u8 cmd; /* FS_CMD_UPDATE */
+ u8 reg;
+ u16 val;
+ u16 mask;
+};
+
+struct fs_cmd_pkg {
+ union {
+ u8 cmd;
+ struct fs_reg_val regv;
+ struct fs_reg_bits regb;
+ };
+};
+
+struct fs_fwm_index {
+ /* Index type */
+ u16 type;
+ /* Offset address starting from the end of header */
+ u16 offset;
+};
+
+struct fs_fwm_table {
+ char name[FS_TABLE_NAME_LEN];
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_scene_index {
+ /* Offset address(scene name) in string table */
+ u16 name;
+ /* Offset address(scene reg) in register table */
+ u16 reg;
+ /* Offset address(scene model) in model table */
+ u16 model;
+ /* Offset address(scene effect) in effect table */
+ u16 effect;
+};
+
+struct fs_reg_table {
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_file_table {
+ u16 name;
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_fwm_date {
+ u32 year:12;
+ u32 month:4;
+ u32 day:5;
+ u32 hour:5;
+ u32 minute:6;
+};
+
+struct fs_fwm_header {
+ u16 version;
+ u16 project; /* Offset address(project name) in string table */
+ u16 device; /* Offset address(device name) in string table */
+ struct fs_fwm_date date;
+ u16 crc16;
+ u16 crc_size; /* Starting position for CRC checking */
+ u16 chip_type;
+ u16 addr; /* 7-bit i2c address */
+ u16 spkid;
+ u16 rsvd[6];
+ u8 params[];
+};
+
+#pragma pack(pop)
+
+struct fs_i2s_srate {
+ u32 srate; /* Sample rate */
+ u16 i2ssr; /* Value of Bit field[I2SSR] */
+};
+
+struct fs_pll_div {
+ unsigned int bclk; /* Rate of bit clock */
+ u16 pll1;
+ u16 pll2;
+ u16 pll3;
+};
+
+struct fs_amp_scene {
+ const char *name;
+ const struct fs_reg_table *reg;
+ const struct fs_file_table *model;
+ const struct fs_file_table *effect;
+};
+
+struct fs_amp_lib {
+ const struct fs_fwm_header *hdr;
+ const struct fs_fwm_table *table[FS_INDEX_MAX];
+ struct fs_amp_scene *scene;
+ struct device *dev;
+ int scene_count;
+ u16 devid;
+};
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name);
+
+#endif // __FS_AMP_LIB_H__
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
new file mode 100644
index 000000000000..e2f85714972d
--- /dev/null
+++ b/sound/soc/codecs/fs210x.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// fs210x.c -- Driver for the FS2104/5S Audio Amplifier
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "fs210x.h"
+#include "fs-amp-lib.h"
+
+#define FS210X_DEFAULT_FWM_NAME "fs210x_fwm.bin"
+#define FS210X_DEFAULT_DAI_NAME "fs210x-aif"
+#define FS2105S_DEVICE_ID 0x20 /* FS2105S */
+#define FS210X_DEVICE_ID 0x45 /* FS2104 */
+#define FS210X_REG_MAX 0xF8
+#define FS210X_INIT_SCENE 0
+#define FS210X_DEFAULT_SCENE 1
+#define FS210X_START_DELAY_MS 5
+#define FS210X_FAULT_CHECK_INTERVAL_MS 2000
+#define FS2105S_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+#define FS210X_RATES (SNDRV_PCM_RATE_16000 | FS2105S_RATES)
+#define FS210X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define FS210X_NUM_SUPPLIES ARRAY_SIZE(fs210x_supply_names)
+
+static const char *const fs210x_supply_names[] = {
+ "pvdd",
+ "dvdd",
+};
+
+struct fs210x_platform_data {
+ const char *fwm_name;
+};
+
+struct fs210x_priv {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ struct fs210x_platform_data pdata;
+ struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES];
+ struct gpio_desc *gpio_sdz;
+ struct delayed_work start_work;
+ struct delayed_work fault_check_work;
+ struct fs_amp_lib amp_lib;
+ const struct fs_amp_scene *cur_scene;
+ struct clk *clk_bclk;
+ /*
+ * @lock: Mutex ensuring exclusive access for critical device operations
+ *
+ * This lock serializes access between the following actions:
+ * - Device initialization procedures(probe)
+ * - Enable/disable device(DAPM event)
+ * - Suspend/resume device(PM)
+ * - Runtime scene switching(control)
+ * - Scheduling/execution of delayed works items(delayed works)
+ */
+ struct mutex lock;
+ unsigned int check_interval_ms;
+ unsigned int bclk;
+ unsigned int srate;
+ int scene_id;
+ u16 devid;
+ bool is_inited;
+ bool is_suspended;
+ bool is_bclk_on;
+ bool is_playing;
+};
+
+static const unsigned int fs2105s_rates[] = {
+ 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs2105s_constraints = {
+ .count = ARRAY_SIZE(fs2105s_rates),
+ .list = fs2105s_rates,
+};
+
+static const unsigned int fs210x_rates[] = {
+ 16000, 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs210x_constraints = {
+ .count = ARRAY_SIZE(fs210x_rates),
+ .list = fs210x_rates,
+};
+
+static const struct fs_pll_div fs210x_pll_div[] = {
+ /* bclk, pll1, pll2, pll3 */
+ { 512000, 0x006C, 0x0120, 0x0001 },
+ { 768000, 0x016C, 0x00C0, 0x0001 },
+ { 1024000, 0x016C, 0x0090, 0x0001 },
+ { 1536000, 0x016C, 0x0060, 0x0001 },
+ { 2048000, 0x016C, 0x0090, 0x0002 },
+ { 2304000, 0x016C, 0x0080, 0x0002 },
+ { 3072000, 0x016C, 0x0090, 0x0003 },
+ { 4096000, 0x016C, 0x0090, 0x0004 },
+ { 4608000, 0x016C, 0x0080, 0x0004 },
+ { 6144000, 0x016C, 0x0090, 0x0006 },
+ { 8192000, 0x016C, 0x0090, 0x0008 },
+ { 9216000, 0x016C, 0x0090, 0x0009 },
+ { 12288000, 0x016C, 0x0090, 0x000C },
+ { 16384000, 0x016C, 0x0090, 0x0010 },
+ { 18432000, 0x016C, 0x0090, 0x0012 },
+ { 24576000, 0x016C, 0x0090, 0x0018 },
+ { 1411200, 0x016C, 0x0060, 0x0001 },
+ { 2116800, 0x016C, 0x0080, 0x0002 },
+ { 2822400, 0x016C, 0x0090, 0x0003 },
+ { 4233600, 0x016C, 0x0080, 0x0004 },
+ { 5644800, 0x016C, 0x0090, 0x0006 },
+ { 8467200, 0x016C, 0x0090, 0x0009 },
+ { 11289600, 0x016C, 0x0090, 0x000C },
+ { 16934400, 0x016C, 0x0090, 0x0012 },
+ { 22579200, 0x016C, 0x0090, 0x0018 },
+ { 2000000, 0x017C, 0x0093, 0x0002 },
+};
+
+static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on)
+{
+ int ret = 0;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if ((fs210x->is_bclk_on ^ on) == 0)
+ return 0;
+
+ if (on) {
+ clk_set_rate(fs210x->clk_bclk, fs210x->bclk);
+ ret = clk_prepare_enable(fs210x->clk_bclk);
+ fs210x->is_bclk_on = true;
+ fsleep(2000); /* >= 2ms */
+ } else {
+ clk_disable_unprepare(fs210x->clk_bclk);
+ fs210x->is_bclk_on = false;
+ }
+
+ return ret;
+}
+
+static int fs210x_reg_write(struct fs210x_priv *fs210x,
+ u8 reg, u16 val)
+{
+ int ret;
+
+ ret = regmap_write(fs210x->regmap, reg, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_read(struct fs210x_priv *fs210x,
+ u8 reg, u16 *pval)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(fs210x->regmap, reg, &val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ *pval = (u16)val;
+
+ return 0;
+}
+
+static int fs210x_reg_update_bits(struct fs210x_priv *fs210x,
+ u8 reg, u16 mask, u16 val)
+{
+ int ret;
+
+ ret = regmap_update_bits(fs210x->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x,
+ u8 reg, const void *val, u32 size)
+{
+ int ret;
+
+ ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x,
+ const struct fs_reg_val *regv)
+{
+ return fs210x_reg_write(fs210x, regv->reg, regv->val);
+}
+
+static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x,
+ const struct fs_reg_bits *regu)
+{
+ return fs210x_reg_update_bits(fs210x,
+ regu->reg,
+ regu->mask,
+ regu->val);
+}
+
+static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x,
+ const struct fs_cmd_pkg *pkg,
+ unsigned int *offset)
+{
+ int delay_us;
+
+ if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) {
+ *offset = sizeof(pkg->regv);
+ return fs210x_write_reg_val(fs210x, &pkg->regv);
+ } else if (pkg->cmd == FS_CMD_UPDATE) {
+ *offset = sizeof(pkg->regb);
+ return fs210x_write_reg_bits(fs210x, &pkg->regb);
+ } else if (pkg->cmd == FS_CMD_DELAY) {
+ if (pkg->regv.val > FS_CMD_DELAY_MS_MAX)
+ return -EOPNOTSUPP;
+ delay_us = pkg->regv.val * 1000; /* ms -> us */
+ fsleep(delay_us);
+ *offset = sizeof(pkg->regv);
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static int fs210x_reg_write_table(struct fs210x_priv *fs210x,
+ const struct fs_reg_table *reg)
+{
+ const struct fs_cmd_pkg *pkg;
+ unsigned int index, offset;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (!reg || reg->size == 0)
+ return -EFAULT;
+
+ for (index = 0; index < reg->size; index += offset) {
+ pkg = (struct fs_cmd_pkg *)(reg->buf + index);
+ ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n",
+ pkg->cmd, ret);
+ return ret;
+ }
+ }
+
+ if (index != reg->size) {
+ dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n",
+ index, reg->size);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int fs210x_dev_play(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PLAY);
+ if (!ret)
+ fs210x->is_playing = true;
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_dev_stop(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (!fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fs210x->is_playing = false;
+
+ fsleep(30000); /* >= 30ms */
+
+ return ret;
+}
+
+static int fs210x_set_reg_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_reg_table *reg;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->reg || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing reg table\n");
+ return 0;
+ }
+
+ reg = scene->reg;
+ dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size);
+
+ return fs210x_reg_write_table(fs210x, reg);
+}
+
+static int fs210x_set_woofer_table(struct fs210x_priv *fs210x)
+{
+ const struct fs_file_table *woofer;
+ const struct fs_fwm_table *table;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ /* NOTE: fs2105s has woofer ram only */
+ if (fs210x->devid != FS2105S_DEVICE_ID)
+ return 0;
+
+ table = fs210x->amp_lib.table[FS_INDEX_WOOFER];
+ if (!table) {
+ dev_dbg(fs210x->dev, "Skip writing woofer table\n");
+ return 0;
+ }
+
+ woofer = (struct fs_file_table *)table->buf;
+ dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size);
+ /* Unit of woofer data is u32(4 bytes) */
+ if (woofer->size == 0 || (woofer->size & 0x3)) {
+ dev_err(fs210x->dev, "Invalid woofer size: %d\n",
+ woofer->size);
+ return -EINVAL;
+ }
+
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS2105S_46H_CAM_BURST_W);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ woofer->buf, woofer->size);
+
+ return ret;
+}
+
+static int fs210x_set_effect_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_file_table *effect;
+ int half_size;
+ int ret;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->effect || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing effect table\n");
+ return 0;
+ }
+
+ effect = scene->effect;
+ dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size);
+
+ /* Unit of effect data is u32(4 bytes), 2 channels */
+ if (effect->size == 0 || (effect->size & 0x7)) {
+ dev_err(fs210x->dev, "Invalid effect size: %d\n",
+ effect->size);
+ return -EINVAL;
+ }
+
+ half_size = effect->size / 2;
+
+ /* Left channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_L);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf, half_size);
+ if (ret)
+ return ret;
+
+ /* Right channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_R);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf + half_size, half_size);
+
+ return ret;
+}
+
+static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable)
+{
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (enable) {
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_HIZ);
+ ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_ON);
+ } else {
+ ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_OFF);
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene,
+ int scene_id)
+{
+ int ret;
+
+ if (!fs210x || !scene)
+ return -EINVAL;
+
+ ret = fs210x_access_dsp_ram(fs210x, true);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret);
+ goto tag_exit;
+ }
+
+ ret = fs210x_set_effect_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set effect: %d\n", ret);
+ goto tag_exit;
+ }
+
+ if (scene_id == FS210X_INIT_SCENE)
+ ret = fs210x_set_woofer_table(fs210x);
+
+tag_exit:
+ fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_CLEAR);
+ fs210x_access_dsp_ram(fs210x, false);
+
+ return ret;
+}
+
+static int fs210x_check_scene(struct fs210x_priv *fs210x,
+ int scene_id, bool *skip_set)
+{
+ struct fs_amp_lib *amp_lib;
+
+ if (!fs210x || !skip_set)
+ return -EINVAL;
+
+ amp_lib = &fs210x->amp_lib;
+ if (amp_lib->scene_count == 0 || !amp_lib->scene) {
+ dev_err(fs210x->dev, "There's no scene data\n");
+ return -EINVAL;
+ }
+
+ if (scene_id < 0 || scene_id >= amp_lib->scene_count) {
+ dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id);
+ return -EINVAL;
+ }
+
+ if (fs210x->scene_id == scene_id) {
+ dev_dbg(fs210x->dev, "Skip to set same scene\n");
+ return 0;
+ }
+
+ *skip_set = false;
+
+ return 0;
+}
+
+static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id)
+{
+ const struct fs_amp_scene *scene;
+ bool skip_set = true;
+ bool is_playing;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = fs210x_check_scene(fs210x, scene_id, &skip_set);
+ if (ret || skip_set)
+ return ret;
+
+ scene = fs210x->amp_lib.scene + scene_id;
+ dev_info(fs210x->dev, "Switch scene.%d: %s\n",
+ scene_id, scene->name);
+
+ is_playing = fs210x->is_playing;
+ if (is_playing)
+ fs210x_dev_stop(fs210x);
+
+ ret = fs210x_set_reg_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = fs210x_write_dsp_effect(fs210x, scene, scene_id);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write ram: %d\n", ret);
+ return ret;
+ }
+
+ fs210x->cur_scene = scene;
+ fs210x->scene_id = scene_id;
+
+ if (is_playing)
+ fs210x_dev_play(fs210x);
+
+ return 0;
+}
+
+static int fs210x_init_chip(struct fs210x_priv *fs210x)
+{
+ int scene_id;
+ int ret;
+
+ regcache_cache_bypass(fs210x->regmap, true);
+
+ if (!fs210x->gpio_sdz) {
+ /* Gpio is not found, i2c reset */
+ ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL,
+ FS210X_10H_I2C_RESET);
+ if (ret)
+ goto tag_power_down;
+ } else {
+ /* gpio reset, deactivate */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ /* Backup scene id */
+ scene_id = fs210x->scene_id;
+ fs210x->scene_id = -1;
+
+ /* Init registers/RAM by init scene */
+ ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE);
+ if (ret)
+ goto tag_power_down;
+
+ /*
+ * If the firmware has effect scene(s),
+ * we load effect scene by default scene or scene_id
+ */
+ if (fs210x->amp_lib.scene_count > 1) {
+ if (scene_id < FS210X_DEFAULT_SCENE)
+ scene_id = FS210X_DEFAULT_SCENE;
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ goto tag_power_down;
+ }
+
+tag_power_down:
+ /* Power down the device */
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fsleep(10000); /* >= 10ms */
+
+ regcache_cache_bypass(fs210x->regmap, false);
+ if (!ret) {
+ regcache_mark_dirty(fs210x->regmap);
+ regcache_sync(fs210x->regmap);
+ fs210x->is_inited = true;
+ }
+
+ return ret;
+}
+
+static int fs210x_set_i2s_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_i2s_srate params[] = {
+ { 16000, 0x3 },
+ { 32000, 0x7 },
+ { 44100, 0x8 },
+ { 48000, 0x9 },
+ { 88200, 0xA },
+ { 96000, 0xB },
+ };
+ u16 val;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ if (params[i].srate != fs210x->srate)
+ continue;
+ val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT;
+ ret = fs210x_reg_update_bits(fs210x,
+ FS210X_17H_I2SCTRL,
+ FS210X_17H_I2SSR_MASK,
+ val);
+ return ret;
+ }
+
+ dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate);
+
+ return -EINVAL;
+}
+
+static int fs210x_get_pll_div(struct fs210x_priv *fs210x,
+ const struct fs_pll_div **pll_div)
+{
+ int i;
+
+ if (!fs210x || !pll_div)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) {
+ if (fs210x_pll_div[i].bclk != fs210x->bclk)
+ continue;
+ *pll_div = fs210x_pll_div + i;
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk);
+
+ return -EFAULT;
+}
+
+static int fs210x_set_hw_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_pll_div *pll_div;
+ int ret;
+
+ ret = fs210x_set_i2s_params(fs210x);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret);
+ return ret;
+ }
+
+ /* Set pll params */
+ ret = fs210x_get_pll_div(fs210x, &pll_div);
+ if (ret)
+ return ret;
+
+ ret = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1);
+ ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2);
+ ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3);
+
+ return ret;
+}
+
+static int fs210x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ const struct snd_pcm_hw_constraint_list *list;
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+ if (!fs210x) {
+ pr_err("dai_startup: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ if (!substream->runtime)
+ return 0;
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ FS210X_FORMATS);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param format: %d\n", ret);
+ return ret;
+ }
+
+ if (fs210x->devid == FS2105S_DEVICE_ID)
+ list = &fs2105s_constraints;
+ else
+ list = &fs210x_constraints;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ list);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Only supports consumer mode */
+ break;
+ default:
+ dev_err(fs210x->dev, "Only supports consumer mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+ int chn_num;
+ int ret;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ fs210x->srate = params_rate(params);
+ fs210x->bclk = snd_soc_params_to_bclk(params);
+ chn_num = params_channels(params);
+ if (chn_num == 1) /* mono */
+ fs210x->bclk *= 2; /* I2S bus has 2 channels */
+
+ /* The FS2105S can't support 16kHz sample rate. */
+ if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_set_hw_params(fs210x);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret);
+
+ return ret;
+}
+
+static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct fs210x_priv *fs210x;
+ unsigned long delay;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ if (mute) {
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+ cancel_delayed_work_sync(&fs210x->start_work);
+ } else {
+ delay = msecs_to_jiffies(fs210x->check_interval_ms);
+ schedule_delayed_work(&fs210x->fault_check_work, delay);
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * According to the power up/down sequence of FS210x,
+ * it requests the I2S clock has been present
+ * and stable(>= 2ms) before playing.
+ */
+ schedule_delayed_work(&fs210x->start_work,
+ msecs_to_jiffies(FS210X_START_DELAY_MS));
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void fs210x_start_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, start_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ ret = fs210x_dev_play(fs210x);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to start playing: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+}
+
+static void fs210x_fault_check_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ u16 status;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, fault_check_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return;
+ }
+
+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ return;
+
+ if (!(status & FS210X_05H_PVDD_MASK))
+ dev_err(fs210x->dev, "PVDD fault\n");
+ if (status & FS210X_05H_OCDL_MASK)
+ dev_err(fs210x->dev, "OC detected\n");
+ if (status & FS210X_05H_UVDL_MASK)
+ dev_err(fs210x->dev, "UV detected\n");
+ if (status & FS210X_05H_OVDL_MASK)
+ dev_err(fs210x->dev, "OV detected\n");
+ if (status & FS210X_05H_OTPDL_MASK)
+ dev_err(fs210x->dev, "OT detected\n");
+ if (status & FS210X_05H_OCRDL_MASK)
+ dev_err(fs210x->dev, "OCR detected\n");
+ if (status & FS210X_05H_OCLDL_MASK)
+ dev_err(fs210x->dev, "OCL detected\n");
+ if (status & FS210X_05H_DCRDL_MASK)
+ dev_err(fs210x->dev, "DCR detected\n");
+ if (status & FS210X_05H_DCLDL_MASK)
+ dev_err(fs210x->dev, "DCL detected\n");
+ if (status & FS210X_05H_SRDL_MASK)
+ dev_err(fs210x->dev, "SR detected\n");
+ if (status & FS210X_05H_OTWDL_MASK)
+ dev_err(fs210x->dev, "OTW detected\n");
+ if (!(status & FS210X_05H_AMPS_MASK))
+ dev_dbg(fs210x->dev, "Amplifier unready\n");
+ if (!(status & FS210X_05H_PLLS_MASK))
+ dev_err(fs210x->dev, "PLL unlock\n");
+ if (!(status & FS210X_05H_ANAS_MASK))
+ dev_err(fs210x->dev, "Analog power fault\n");
+
+ schedule_delayed_work(&fs210x->fault_check_work,
+ msecs_to_jiffies(fs210x->check_interval_ms));
+}
+
+static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl,
+ struct fs210x_priv **fs210x)
+{
+ struct snd_soc_component *cmpnt;
+
+ if (!kctrl) {
+ pr_err("fs210x: kcontrol is null\n");
+ return -EINVAL;
+ }
+
+ cmpnt = snd_soc_kcontrol_component(kctrl);
+ if (!cmpnt) {
+ pr_err("fs210x: component is null\n");
+ return -EINVAL;
+ }
+
+ *fs210x = snd_soc_component_get_drvdata(cmpnt);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct fs_amp_scene *scene;
+ struct fs210x_priv *fs210x;
+ const char *name = "N/A";
+ int idx, count;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_info: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (count < 1) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ idx = uinfo->value.enumerated.item;
+ scene = fs210x->amp_lib.scene + idx + 1;
+ if (scene->name)
+ name = scene->name;
+
+ strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int index;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_get: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ /* The id of effect scene is from 1 to N. */
+ if (fs210x->scene_id < 1)
+ return -EINVAL;
+
+ mutex_lock(&fs210x->lock);
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0
+ * effect scene(s): id = 1~N (optional)
+ * effect_index = scene_id - 1
+ */
+ index = fs210x->scene_id - 1;
+ ucontrol->value.integer.value[0] = index;
+ mutex_unlock(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int scene_id, scene_count;
+ bool is_changed = false;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_put: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0 (It's set in fs210x_init_chip() only)
+ * effect scene(s): id = 1~N (optional)
+ * scene_id = effect_index + 1.
+ */
+ scene_id = ucontrol->value.integer.value[0] + 1;
+ scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (scene_id < 1 || scene_id > scene_count) {
+ mutex_unlock(&fs210x->lock);
+ return -ERANGE;
+ }
+
+ if (scene_id != fs210x->scene_id)
+ is_changed = true;
+
+ if (fs210x->is_suspended) {
+ fs210x->scene_id = scene_id;
+ mutex_unlock(&fs210x->lock);
+ return is_changed;
+ }
+
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set scene: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+
+ if (!ret && is_changed)
+ return 1;
+
+ return ret;
+}
+
+static int fs210x_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+
+ mutex_lock(&fs210x->lock);
+
+ if (fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * If there is no bclk for us to set the clock output,
+ * we will enable the device(start_work) in dai trigger.
+ */
+ if (!fs210x->clk_bclk)
+ break;
+ fs210x_bclk_set(fs210x, true);
+ ret = fs210x_dev_play(fs210x);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = fs210x_dev_stop(fs210x);
+ fs210x_bclk_set(fs210x, false);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops fs210x_dai_ops = {
+ .startup = fs210x_dai_startup,
+ .set_fmt = fs210x_dai_set_fmt,
+ .hw_params = fs210x_dai_hw_params,
+ .mute_stream = fs210x_dai_mute,
+ .trigger = fs210x_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver fs210x_dai = {
+ .name = FS210X_DEFAULT_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .ops = &fs210x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1);
+static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1);
+
+static const struct snd_kcontrol_new fs2105s_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 7, 0x1FF, 0, fs2105s_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 6, 0x2BF, 0, fs210x_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_controls[] = {
+ SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0),
+ SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new fs210x_scene_control[] = {
+ FS_SOC_ENUM_EXT("Effect Scene",
+ fs210x_effect_scene_info,
+ fs210x_effect_scene_get,
+ fs210x_effect_scene_put),
+};
+
+static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0,
+ fs210x_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_INPUT("SDO"),
+};
+
+static const struct snd_soc_dapm_route fs210x_dapm_routes[] = {
+ { "OUTL", NULL, "AIF IN" },
+ { "OUTR", NULL, "AIF IN" },
+ { "AIF OUT", NULL, "SDO" },
+};
+
+static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x,
+ struct snd_soc_component *cmpnt)
+{
+ const struct snd_kcontrol_new *kctrl;
+ int count;
+ int ret;
+
+ if (!fs210x || !cmpnt)
+ return -EINVAL;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ kctrl = fs2105s_vol_control;
+ count = ARRAY_SIZE(fs2105s_vol_control);
+ } else {
+ kctrl = fs210x_vol_control;
+ count = ARRAY_SIZE(fs210x_vol_control);
+ }
+
+ ret = snd_soc_add_component_controls(cmpnt, kctrl, count);
+ if (ret)
+ return ret;
+
+ /*
+ * If the firmware has no scene or only init scene,
+ * we skip adding this mixer control.
+ */
+ if (fs210x->amp_lib.scene_count < 2)
+ return 0;
+
+ kctrl = fs210x_scene_control;
+ count = ARRAY_SIZE(fs210x_scene_control);
+
+ return snd_soc_add_component_controls(cmpnt, kctrl, count);
+}
+
+static int fs210x_probe(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ fs210x->amp_lib.dev = fs210x->dev;
+ fs210x->amp_lib.devid = fs210x->devid;
+
+ ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name);
+ if (ret)
+ return ret;
+
+ ret = fs210x_add_mixer_controls(fs210x, cmpnt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_init_chip(fs210x);
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static void fs210x_remove(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return;
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+}
+
+#ifdef CONFIG_PM
+static int fs210x_suspend(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ regcache_cache_only(fs210x->regmap, true);
+
+ mutex_lock(&fs210x->lock);
+ fs210x->cur_scene = NULL;
+ fs210x->is_inited = false;
+ fs210x->is_playing = false;
+ fs210x->is_suspended = true;
+
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(30000); /* >= 30ms */
+ mutex_unlock(&fs210x->lock);
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+
+ ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_resume(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ fs210x->is_suspended = false;
+ ret = fs210x_init_chip(fs210x);
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+#else
+#define fs210x_suspend NULL
+#define fs210x_resume NULL
+#endif // CONFIG_PM
+
+static bool fs210x_volatile_registers(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR:
+ case FS210X_ABH_INTSTAT:
+ case FS210X_ACH_INTSTATR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_soc_component_driver fs210x_soc_component_dev = {
+ .probe = fs210x_probe,
+ .remove = fs210x_remove,
+ .suspend = fs210x_suspend,
+ .resume = fs210x_resume,
+ .controls = fs210x_controls,
+ .num_controls = ARRAY_SIZE(fs210x_controls),
+ .dapm_widgets = fs210x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(fs210x_dapm_widgets),
+ .dapm_routes = fs210x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(fs210x_dapm_routes),
+};
+
+static const struct regmap_config fs210x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = FS210X_REG_MAX,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = fs210x_volatile_registers,
+};
+
+static int fs210x_detect_device(struct fs210x_priv *fs210x)
+{
+ u16 devid;
+ int ret;
+
+ ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ fs210x->devid = HI_U16(devid);
+
+ switch (fs210x->devid) {
+ case FS210X_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2104 detected\n");
+ break;
+ case FS2105S_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2105S detected\n");
+ break;
+ default:
+ dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int fs210x_parse_dts(struct fs210x_priv *fs210x,
+ struct fs210x_platform_data *pdata)
+{
+ struct device_node *node = fs210x->dev->of_node;
+ int i, ret;
+
+ if (!node)
+ return 0;
+
+ ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name);
+ if (ret)
+ pdata->fwm_name = FS210X_DEFAULT_FWM_NAME;
+
+ fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(fs210x->gpio_sdz))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz),
+ "Failed to get reset-gpios\n");
+
+ for (i = 0; i < FS210X_NUM_SUPPLIES; i++)
+ fs210x->supplies[i].supply = fs210x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(fs210x->dev,
+ ARRAY_SIZE(fs210x->supplies),
+ fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to get supplies\n");
+
+ return 0;
+}
+
+static void fs210x_deinit(struct fs210x_priv *fs210x)
+{
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(10000); /* >= 10ms */
+
+ regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+}
+
+static int fs210x_init(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ ret = fs210x_parse_dts(fs210x, &fs210x->pdata);
+ if (ret)
+ return ret;
+
+ fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk");
+ if (IS_ERR(fs210x->clk_bclk))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk),
+ "Failed to get bclk\n");
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to enable supplies\n");
+
+ /* Make sure the SDZ pin is pulled down enough time. */
+ fsleep(10000); /* >= 10ms */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */
+ fsleep(10000); /* >= 10ms */
+
+ ret = fs210x_detect_device(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return ret;
+ }
+
+ fs210x->scene_id = -1; /* Invalid scene */
+ fs210x->cur_scene = NULL;
+ fs210x->is_playing = false;
+ fs210x->is_inited = false;
+ fs210x->is_suspended = false;
+ fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work);
+ INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work);
+ mutex_init(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_register_snd_component(struct fs210x_priv *fs210x)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ static int instance_id;
+ int ret;
+
+ dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai,
+ sizeof(fs210x_dai), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ dai_drv->name = devm_kasprintf(fs210x->dev,
+ GFP_KERNEL, "%s-%d",
+ dai_drv->name, instance_id);
+ if (!dai_drv->name)
+ return -ENOMEM;
+
+ instance_id++;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ dai_drv->playback.rates = FS2105S_RATES;
+ dai_drv->capture.rates = FS2105S_RATES;
+ }
+
+ ret = snd_soc_register_component(fs210x->dev,
+ &fs210x_soc_component_dev,
+ dai_drv, 1);
+ return ret;
+}
+
+static ssize_t check_interval_ms_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms);
+}
+
+static ssize_t check_interval_ms_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fs210x->check_interval_ms);
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_interval_ms);
+
+static struct attribute *fs210x_attrs[] = {
+ &dev_attr_check_interval_ms.attr,
+ NULL,
+};
+
+static struct attribute_group fs210x_attr_group = {
+ .attrs = fs210x_attrs,
+};
+
+static int fs210x_i2c_probe(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL);
+ if (!fs210x)
+ return -ENOMEM;
+
+ fs210x->i2c = client;
+ fs210x->dev = &client->dev;
+ i2c_set_clientdata(client, fs210x);
+
+ fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap);
+ if (IS_ERR(fs210x->regmap))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap),
+ "Failed to get regmap\n");
+
+ ret = fs210x_init(fs210x);
+ if (ret)
+ return ret;
+
+ ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to create sysfs group\n");
+ }
+
+ ret = fs210x_register_snd_component(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to register component\n");
+ }
+
+ return 0;
+}
+
+static void fs210x_i2c_remove(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x = i2c_get_clientdata(client);
+
+ snd_soc_unregister_component(fs210x->dev);
+ fs210x_deinit(fs210x);
+}
+
+static const struct i2c_device_id fs210x_i2c_id[] = {
+ { "fs2104" },
+ { "fs2105s" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
+
+static const struct of_device_id fs210x_of_match[] = {
+ { .compatible = "foursemi,fs2105s", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fs210x_of_match);
+
+static struct i2c_driver fs210x_i2c_driver = {
+ .driver = {
+ .name = "fs210x",
+ .of_match_table = fs210x_of_match,
+ },
+ .id_table = fs210x_i2c_id,
+ .probe = fs210x_i2c_probe,
+ .remove = fs210x_i2c_remove,
+};
+
+module_i2c_driver(fs210x_i2c_driver);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h
new file mode 100644
index 000000000000..78e1760332ca
--- /dev/null
+++ b/sound/soc/codecs/fs210x.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * fs210x.h -- Driver for the FS2104/5S Audio Amplifier
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS210X_H__
+#define __FS210X_H__
+
+#define FS210X_00H_STATUS 0x00
+#define FS210X_03H_DEVID 0x03
+#define FS210X_05H_ANASTAT 0x05
+#define FS210X_06H_DIGSTAT 0x06
+#define FS210X_0BH_ACCKEY 0x0B
+#define FS210X_0FH_I2CADDR 0x0F
+#define FS210X_10H_PWRCTRL 0x10
+#define FS210X_11H_SYSCTRL 0x11
+#define FS210X_17H_I2SCTRL 0x17
+#define FS210X_30H_DACCTRL 0x30
+#define FS210X_39H_LVOLCTRL 0x39
+#define FS210X_3AH_RVOLCTRL 0x3A
+#define FS210X_42H_DACEQWL 0x42
+#define FS210X_46H_DACEQA 0x46
+#define FS210X_A1H_PLLCTRL1 0xA1
+#define FS210X_A2H_PLLCTRL2 0xA2
+#define FS210X_A3H_PLLCTRL3 0xA3
+#define FS210X_ABH_INTSTAT 0xAB
+#define FS210X_ACH_INTSTATR 0xAC
+
+#define FS210X_05H_PVDD_SHIFT 14
+#define FS210X_05H_PVDD_MASK BIT(14)
+#define FS210X_05H_OCDL_SHIFT 13
+#define FS210X_05H_OCDL_MASK BIT(13)
+#define FS210X_05H_UVDL_SHIFT 12
+#define FS210X_05H_UVDL_MASK BIT(12)
+#define FS210X_05H_OVDL_SHIFT 11
+#define FS210X_05H_OVDL_MASK BIT(11)
+#define FS210X_05H_OTPDL_SHIFT 10
+#define FS210X_05H_OTPDL_MASK BIT(10)
+#define FS210X_05H_OCRDL_SHIFT 9
+#define FS210X_05H_OCRDL_MASK BIT(9)
+#define FS210X_05H_OCLDL_SHIFT 8
+#define FS210X_05H_OCLDL_MASK BIT(8)
+#define FS210X_05H_DCRDL_SHIFT 7
+#define FS210X_05H_DCRDL_MASK BIT(7)
+#define FS210X_05H_DCLDL_SHIFT 6
+#define FS210X_05H_DCLDL_MASK BIT(6)
+#define FS210X_05H_SRDL_SHIFT 5
+#define FS210X_05H_SRDL_MASK BIT(5)
+#define FS210X_05H_OTWDL_SHIFT 4
+#define FS210X_05H_OTWDL_MASK BIT(4)
+#define FS210X_05H_AMPS_SHIFT 3
+#define FS210X_05H_AMPS_MASK BIT(3)
+#define FS210X_05H_PLLS_SHIFT 1
+#define FS210X_05H_PLLS_MASK BIT(1)
+#define FS210X_05H_ANAS_SHIFT 0
+#define FS210X_05H_ANAS_MASK BIT(0)
+#define FS210X_17H_I2SSR_SHIFT 12
+#define FS210X_17H_I2SSR_MASK GENMASK(15, 12)
+#define FS210X_30H_RMUTE_SHIFT 8
+#define FS210X_30H_LMUTE_SHIFT 4
+
+#define FS210X_0BH_ACCKEY_ON 0x0091
+#define FS210X_0BH_ACCKEY_OFF 0x0000
+#define FS210X_10H_I2C_RESET 0x0002
+#define FS210X_11H_DPS_HIZ 0x0100
+#define FS210X_11H_DPS_PWDN 0x0000
+#define FS210X_11H_DPS_PLAY 0x0300
+#define FS210X_46H_CAM_BURST_L 0x8000
+#define FS210X_46H_CAM_BURST_R 0x8200
+#define FS2105S_46H_CAM_BURST_W 0x8400
+#define FS210X_46H_CAM_CLEAR 0x0000
+
+#endif /* __FS210X_H__ */
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
index 926b1a4e37d4..1f165e46701f 100644
--- a/sound/soc/codecs/gtm601.c
+++ b/sound/soc/codecs/gtm601.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This is a simple driver for the GTM601 Voice PCM interface
*
@@ -6,20 +7,15 @@
* Author: Marek Belisko <marek@goldelico.com>
*
* Based on wm8727.c driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
@@ -51,34 +47,51 @@ static struct snd_soc_dai_driver gtm601_dai = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = {
- .component_driver = {
- .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),
+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)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_gtm601, &gtm601_dai, 1);
-}
+ const struct snd_soc_dai_driver *dai_driver;
-static int gtm601_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ 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);
}
-#if defined(CONFIG_OF)
-static const struct of_device_id gtm601_codec_of_match[] = {
- { .compatible = "option,gtm601", },
+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);
-#endif
static struct platform_driver gtm601_codec_driver = {
.driver = {
@@ -86,7 +99,6 @@ static struct platform_driver gtm601_codec_driver = {
.of_match_table = of_match_ptr(gtm601_codec_of_match),
},
.probe = gtm601_platform_probe,
- .remove = gtm601_platform_remove,
};
module_platform_driver(gtm601_codec_driver);
diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c
new file mode 100644
index 000000000000..b9caae7e4817
--- /dev/null
+++ b/sound/soc/codecs/hda-dai.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// 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;
+ unsigned int bits;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream = substream->runtime->private_data;
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->maxbps);
+ format = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+
+ 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..ede980cc6050
--- /dev/null
+++ b/sound/soc/codecs/hda.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// 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->subformats = pcm->stream[dir].subformats;
+ 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->subformats = pcm->stream[dir].subformats;
+ 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);
+ return ret;
+ }
+
+ /* 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);
+
+ /* Complement pm_runtime_get_sync(bus) in probe */
+ 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 hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ 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, "codec create 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, "set name: %s failed: %d\n", codec->preset->name, ret);
+ goto err;
+ }
+
+ ret = snd_hdac_regmap_init(&codec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "regmap init failed: %d\n", ret);
+ goto err;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = driver->ops->probe(codec, codec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "codec init 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 (driver->ops->remove)
+ driver->ops->remove(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_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 hda_codec_driver *driver = hda_codec_to_driver(codec);
+ 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 (driver->ops->remove)
+ driver->ops->remove(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_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;
+
+ if (hda_codec_is_display(codec) && !hdev->bus->audio_component) {
+ dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id);
+ return -ENODEV;
+ }
+
+ 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..59308cc6afef
--- /dev/null
+++ b/sound/soc/codecs/hda.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * 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..afd8edf10fdc
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.c
@@ -0,0 +1,679 @@
+// 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/firmware.h>
+#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)
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *loadable_patch[HDA_MAX_CODECS];
+
+module_param_array_named(patch, loadable_patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file array for Intel HD audio interface. The array index is the codec address.");
+#endif
+
+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,
+ },
+},
+};
+
+static struct snd_soc_dai_driver hdac_hda_hdmi_dais[] = {
+{
+ .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;
+ unsigned int bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ maxbps = dai->driver->playback.sig_bits;
+ else
+ maxbps = dai->driver->capture.sig_bits;
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps);
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
+ if (!format_val) {
+ dev_err(dai->dev,
+ "%s: invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
+ __func__,
+ 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;
+ unsigned int format_val;
+ struct hda_pcm *pcm;
+ unsigned int stream;
+ int ret = 0;
+
+ 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];
+
+ 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(dai->dev, "%s: failed %d\n", __func__, 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;
+ int ret;
+
+ 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];
+
+ ret = hda_stream->ops.open(hda_stream, hda_pvt->codec, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "%s: failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+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(dai->dev, "%s: invalid dai id %d\n", __func__, 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(dai->dev, "%s: didn't find PCM for DAI %s\n", __func__, 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 hda_codec_driver *driver = hda_codec_to_driver(hcodec);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "%s: hdac link not found\n", __func__);
+ 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, "%s: failed to create hda codec %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) {
+ const struct firmware *fw;
+
+ dev_info(&hdev->dev, "Applying patch firmware '%s'\n",
+ loadable_patch[hda_pvt->dev_index]);
+ ret = request_firmware(&fw, loadable_patch[hda_pvt->dev_index],
+ &hdev->dev);
+ if (ret < 0)
+ goto error_no_pm;
+ if (fw) {
+ ret = snd_hda_load_patch(hcodec->bus, fw->size, fw->data);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+ release_firmware(fw);
+ }
+ }
+#endif
+ /*
+ * 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, "%s: name failed %s\n", __func__, hcodec->preset->name);
+ goto error_pm;
+ }
+
+ ret = snd_hdac_regmap_init(&hcodec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: regmap init failed\n", __func__);
+ goto error_pm;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto error_regmap;
+ }
+
+ ret = driver->ops->probe(hcodec, hcodec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
+ goto error_regmap;
+ }
+
+ ret = snd_hda_codec_parse_pcms(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: unable to map pcms to dai %d\n", __func__, 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, "%s: unable to create controls %d\n",
+ __func__, 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 (driver->ops->remove)
+ driver->ops->remove(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 hda_codec_driver *driver = hda_codec_to_driver(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, "%s: hdac link not found\n", __func__);
+ return;
+ }
+
+ pm_runtime_disable(&hdev->dev);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ if (driver->ops->remove)
+ driver->ops->remove(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 const struct snd_soc_component_driver hdac_hda_hdmi_codec = {
+ .probe = hdac_hda_codec_probe,
+ .remove = hdac_hda_codec_remove,
+ .idle_bias_on = false,
+ .endianness = 1,
+};
+
+static int hdac_hda_dev_probe(struct hdac_device *hdev)
+{
+ struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev);
+ 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, "%s: hdac link not found\n", __func__);
+ return -EIO;
+ }
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ /* ASoC specific initialization */
+ if (hda_pvt->need_display_power)
+ ret = devm_snd_soc_register_component(&hdev->dev,
+ &hdac_hda_hdmi_codec, hdac_hda_hdmi_dais,
+ ARRAY_SIZE(hdac_hda_hdmi_dais));
+ else
+ 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, "%s: failed to register HDA codec %d\n", __func__, 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..d03a5d4e7288
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.h
@@ -0,0 +1,34 @@
+/* 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;
+ int dev_index;
+};
+
+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
index dba6f4c5074a..e50afd0bfcec 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
*
@@ -6,23 +7,16 @@
* Subhransu S. Prusty <subhransu.s.prusty@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; 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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+
#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 <drm/drm_eld.h>
#include <sound/pcm_params.h>
#include <sound/jack.h>
#include <sound/soc.h>
@@ -30,8 +24,6 @@
#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
@@ -85,7 +77,7 @@ struct hdac_hdmi_pin {
bool mst_capable;
struct hdac_hdmi_port *ports;
int num_ports;
- struct hdac_ext_device *edev;
+ struct hdac_device *hdev;
};
struct hdac_hdmi_port {
@@ -96,8 +88,10 @@ struct hdac_hdmi_port {
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 {
@@ -113,6 +107,7 @@ struct hdac_hdmi_pcm {
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 {
@@ -126,6 +121,9 @@ struct hdac_hdmi_drv_data {
};
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;
@@ -139,32 +137,28 @@ struct hdac_hdmi_priv {
struct snd_soc_dai_driver *dai_drv;
};
-#define hdev_to_hdmi_priv(_hdev) ((to_ehdac_device(_hdev))->private_data)
+#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 = NULL;
+ struct hdac_hdmi_pcm *pcm;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->cvt == cvt)
- break;
+ return pcm;
}
- 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_ext_device *edev = port->pin->edev;
-
- if (is_connect)
- snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
- else
- snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
+ struct hdac_device *hdev = port->pin->hdev;
+ port->is_connect = is_connect;
if (is_connect) {
/*
* Report Jack connect event when a device is connected
@@ -172,7 +166,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
* ports.
*/
if (pcm->jack_event == 0) {
- dev_dbg(&edev->hdev.dev,
+ dev_dbg(&hdev->dev,
"jack report for pcm=%d\n",
pcm->pcm_id);
snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT,
@@ -190,27 +184,48 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
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_ext_device *edev, hda_nid_t nid)
+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(&edev->hdev, nid);
- type = get_wcaps_type(caps);
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
return 0;
- param = snd_hdac_read_parm_uncached(&edev->hdev, nid,
- AC_PAR_DEVLIST_LEN);
+ param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN);
if (param == -1)
return param;
@@ -222,10 +237,10 @@ static int hdac_hdmi_get_port_len(struct hdac_ext_device *edev, hda_nid_t nid)
* 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_ext_device *edev,
+static int hdac_hdmi_port_select_get(struct hdac_device *hdev,
struct hdac_hdmi_port *port)
{
- return snd_hdac_codec_read(&edev->hdev, port->pin->nid,
+ return snd_hdac_codec_read(hdev, port->pin->nid,
0, AC_VERB_GET_DEVICE_SEL, 0);
}
@@ -233,7 +248,7 @@ static int hdac_hdmi_port_select_get(struct hdac_ext_device *edev,
* 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_ext_device *edev,
+static int hdac_hdmi_port_select_set(struct hdac_device *hdev,
struct hdac_hdmi_port *port)
{
int num_ports;
@@ -242,8 +257,7 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev,
return 0;
/* AC_PAR_DEVLIST_LEN is 0 based. */
- num_ports = hdac_hdmi_get_port_len(edev, port->pin->nid);
-
+ num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid);
if (num_ports < 0)
return -EIO;
/*
@@ -253,13 +267,13 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev,
if (num_ports + 1 < port->id)
return 0;
- snd_hdac_codec_write(&edev->hdev, port->pin->nid, 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(edev, port))
+ if (port->id != hdac_hdmi_port_select_get(hdev, port))
return -EIO;
- dev_dbg(&edev->hdev.dev, "Selected the port=%d\n", port->id);
+ dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id);
return 0;
}
@@ -277,13 +291,6 @@ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
return NULL;
}
-static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
-{
- struct hdac_device *hdev = dev_to_hdac_dev(dev);
-
- return to_ehdac_device(hdev);
-}
-
static unsigned int sad_format(const u8 *sad)
{
return ((sad[0] >> 0x3) & 0x1f);
@@ -324,15 +331,13 @@ format_constraint:
}
static void
-hdac_hdmi_set_dip_index(struct hdac_ext_device *edev, hda_nid_t pin_nid,
+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(&edev->hdev, pin_nid, 0,
- AC_VERB_SET_HDMI_DIP_INDEX, val);
+ snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
}
struct dp_audio_infoframe {
@@ -347,14 +352,14 @@ struct dp_audio_infoframe {
u8 LFEPBL01_LSV36_DM_INH7;
};
-static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev,
+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(&edev->hdev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_cvt *cvt = pcm->cvt;
u8 *dip;
int ret;
@@ -363,11 +368,11 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev,
u8 conn_type;
int channels, ca;
- ca = snd_hdac_channel_allocation(&edev->hdev, port->eld.info.spk_alloc,
+ 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(&edev->hdev, cvt->nid, channels);
+ 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);
@@ -400,54 +405,58 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev,
break;
default:
- dev_err(&edev->hdev.dev, "Invalid connection type: %d\n",
- conn_type);
+ dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type);
return -EIO;
}
/* stop infoframe transmission */
- hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0);
- snd_hdac_codec_write(&edev->hdev, pin->nid, 0,
+ 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(edev, pin->nid, 0x0, 0x0);
+ 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(&edev->hdev, pin->nid, 0,
+ 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(&edev->hdev, pin->nid, 0,
+ snd_hdac_codec_write(hdev, pin->nid, 0,
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
}
/* Start infoframe */
- hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0);
- snd_hdac_codec_write(&edev->hdev, pin->nid, 0,
+ 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_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width)
+static int hdac_hdmi_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
{
- struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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(&edev->hdev.dev, "%s: strm_tag: %d\n", __func__, tx_mask);
+ 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 = (tx_mask << 4);
+ pcm->stream_tag = (hstream->stream_tag << 4);
return 0;
}
@@ -455,29 +464,17 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai,
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_ext_device *edev = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_dai_port_map *dai_map;
- struct hdac_hdmi_port *port;
struct hdac_hdmi_pcm *pcm;
+ unsigned int bits;
int format;
dai_map = &hdmi->dai_map[dai->id];
- port = dai_map->port;
-
- if (!port)
- return -ENODEV;
- if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) {
- dev_err(&edev->hdev.dev,
- "device is not configured for this pin:port%d:%d\n",
- port->pin->nid, port->id);
- return -ENODEV;
- }
-
- format = snd_hdac_calc_stream_format(params_rate(hparams),
- params_channels(hparams), params_format(hparams),
- dai->driver->playback.sig_bits, 0);
+ bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD,
+ dai->driver->playback.sig_bits);
+ format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams));
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
if (!pcm)
@@ -489,28 +486,28 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev,
+static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev,
struct hdac_hdmi_pin *pin,
struct hdac_hdmi_port *port)
{
- if (!(get_wcaps(&edev->hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
- dev_warn(&edev->hdev.dev,
+ if (!(snd_hdac_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(&edev->hdev, pin->nid));
+ pin->nid, snd_hdac_get_wcaps(hdev, pin->nid));
return -EINVAL;
}
- if (hdac_hdmi_port_select_set(edev, port) < 0)
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
return -EIO;
- port->num_mux_nids = snd_hdac_get_connections(&edev->hdev, pin->nid,
+ 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(&edev->hdev.dev,
+ dev_warn(&hdev->dev,
"No connections found for pin:port %d:%d\n",
pin->nid, port->id);
- dev_dbg(&edev->hdev.dev, "num_mux_nids %d for pin:port %d:%d\n",
+ 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;
@@ -526,12 +523,12 @@ static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev,
* connected.
*/
static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
- struct hdac_ext_device *edev,
+ struct hdac_device *hdev,
struct hdac_hdmi_priv *hdmi,
struct hdac_hdmi_cvt *cvt)
{
struct hdac_hdmi_pcm *pcm;
- struct hdac_hdmi_port *port = NULL;
+ struct hdac_hdmi_port *port;
int ret, i;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
@@ -541,7 +538,7 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
list_for_each_entry(port, &pcm->port_list, head) {
mutex_lock(&pcm->lock);
- ret = hdac_hdmi_query_port_connlist(edev,
+ ret = hdac_hdmi_query_port_connlist(hdev,
port->pin, port);
mutex_unlock(&pcm->lock);
if (ret < 0)
@@ -561,6 +558,29 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
}
/*
+ * 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.
@@ -568,8 +588,8 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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;
@@ -578,7 +598,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
cvt = dai_map->cvt;
- port = hdac_hdmi_get_port_from_cvt(edev, hdmi, cvt);
+ port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
/*
* To make PA and other userland happy.
@@ -589,7 +609,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
if ((!port->eld.monitor_present) ||
(!port->eld.eld_valid)) {
- dev_warn(&edev->hdev.dev,
+ 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);
@@ -611,8 +631,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_dai_port_map *dai_map;
struct hdac_hdmi_pcm *pcm;
@@ -639,8 +658,8 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
int err;
- chans = get_wcaps(hdev, cvt->nid);
- chans = get_wcaps_channels(chans);
+ chans = snd_hdac_get_wcaps(hdev, cvt->nid);
+ chans = snd_hdac_get_wcaps_channels(chans);
cvt->params.channels_min = 2;
@@ -651,6 +670,7 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
err = snd_hdac_query_supported_pcm(hdev, cvt->nid,
&cvt->params.rates,
&cvt->params.formats,
+ NULL,
&cvt->params.maxbps);
if (err < 0)
dev_err(&hdev->dev,
@@ -695,11 +715,11 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
route->connected = handler;
}
-static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
+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(&edev->hdev);
- struct hdac_hdmi_pcm *pcm = NULL;
+ 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) {
@@ -715,21 +735,32 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
return NULL;
}
-static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
+static void hdac_hdmi_set_power_state(struct hdac_device *hdev,
hda_nid_t nid, unsigned int pwr_state)
{
- if (get_wcaps(&edev->hdev, nid) & AC_WCAP_POWER) {
- if (!snd_hdac_check_power_state(&edev->hdev, nid, pwr_state))
- snd_hdac_codec_write(&edev->hdev, nid, 0,
- AC_VERB_SET_POWER_STATE, pwr_state);
+ int count;
+ unsigned int state;
+
+ if (snd_hdac_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_ext_device *edev,
+static void hdac_hdmi_set_amp(struct hdac_device *hdev,
hda_nid_t nid, int val)
{
- if (get_wcaps(&edev->hdev, nid) & AC_WCAP_OUT_AMP)
- snd_hdac_codec_write(&edev->hdev, nid, 0,
+ if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
+ snd_hdac_codec_write(hdev, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, val);
}
@@ -738,40 +769,40 @@ 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_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+ struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
struct hdac_hdmi_pcm *pcm;
- dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n",
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
- pcm = hdac_hdmi_get_pcm(edev, port);
+ 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(edev, port) < 0)
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
return -EIO;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0);
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0);
/* Enable out path for this pin widget */
- snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0,
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE);
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE);
- return hdac_hdmi_setup_audio_infoframe(edev, pcm, port);
+ return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
case SND_SOC_DAPM_POST_PMD:
- hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE);
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE);
/* Disable out path for this pin widget */
- snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0,
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3);
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3);
break;
}
@@ -783,11 +814,11 @@ 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_ext_device *edev = to_hda_ext_device(w->dapm->dev);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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(&edev->hdev.dev, "%s: widget: %s event: %x\n",
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
@@ -796,29 +827,37 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0);
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0);
/* Enable transmission */
- snd_hdac_codec_write(&edev->hdev, cvt->nid, 0,
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, 1);
/* Category Code (CC) to zero */
- snd_hdac_codec_write(&edev->hdev, cvt->nid, 0,
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0);
- snd_hdac_codec_write(&edev->hdev, cvt->nid, 0,
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
- snd_hdac_codec_write(&edev->hdev, cvt->nid, 0,
+ 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(&edev->hdev, cvt->nid, 0,
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hdac_codec_write(&edev->hdev, cvt->nid, 0,
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
AC_VERB_SET_STREAM_FORMAT, 0);
- hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3);
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3);
break;
}
@@ -830,10 +869,10 @@ 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_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+ struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
int mux_idx;
- dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n",
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
if (!kc)
@@ -842,11 +881,11 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
mux_idx = dapm_kcontrol_get_value(kc);
/* set the device if pin is mst_capable */
- if (hdac_hdmi_port_select_set(edev, port) < 0)
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
return -EIO;
if (mux_idx > 0) {
- snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0,
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
}
@@ -865,9 +904,9 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
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_ext_device *edev = to_hda_ext_device(dapm->dev);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
- struct hdac_hdmi_pcm *pcm = NULL;
+ 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);
@@ -885,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
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(pcm, port, false);
+ hdac_hdmi_jack_report_sync(pcm, port, false);
list_del(&p->head);
}
}
@@ -899,7 +938,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
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(pcm, port, true);
+ hdac_hdmi_jack_report_sync(pcm, port, true);
mutex_unlock(&hdmi->pin_mutex);
return ret;
}
@@ -919,12 +958,12 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
* care of selecting the right one and leaving all other inputs selected to
* "NONE"
*/
-static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev,
+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(&edev->hdev);
+ 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;
@@ -936,17 +975,17 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev,
int i = 0;
int num_items = hdmi->num_cvt + 1;
- kc = devm_kzalloc(&edev->hdev.dev, sizeof(*kc), GFP_KERNEL);
+ kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL);
if (!kc)
return -ENOMEM;
- se = devm_kzalloc(&edev->hdev.dev, sizeof(*se), GFP_KERNEL);
+ 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(&edev->hdev.dev, kc_name, GFP_KERNEL);
+ kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL);
if (!kc->name)
return -ENOMEM;
@@ -964,35 +1003,34 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev,
se->mask = roundup_pow_of_two(se->items) - 1;
sprintf(mux_items, "NONE");
- items[i] = devm_kstrdup(&edev->hdev.dev, mux_items, GFP_KERNEL);
+ 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(&edev->hdev.dev, mux_items, GFP_KERNEL);
+ items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
if (!items[i])
return -ENOMEM;
}
- se->texts = devm_kmemdup(&edev->hdev.dev, items,
- (num_items * sizeof(char *)), GFP_KERNEL);
+ se->texts = devm_kmemdup_array(&hdev->dev, items, num_items, sizeof(items[0]), GFP_KERNEL);
if (!se->texts)
return -ENOMEM;
- return hdac_hdmi_fill_widget_info(&edev->hdev.dev, widget,
+ 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_ext_device *edev,
+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(&edev->hdev);
+ 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;
@@ -1034,8 +1072,8 @@ 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_ext_device *edev = to_hda_ext_device(dapm->dev);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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;
@@ -1087,7 +1125,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
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(edev,
+ ret = hdac_hdmi_create_pin_port_muxs(hdev,
&pin->ports[j], &widgets[i],
widget_name);
if (ret < 0)
@@ -1122,7 +1160,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
}
}
- hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, 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));
@@ -1134,9 +1172,9 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
}
-static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+static int hdac_hdmi_init_dai_map(struct hdac_device *hdev)
{
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->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;
@@ -1152,7 +1190,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
dai_id++;
if (dai_id == HDA_MAX_CVTS) {
- dev_warn(&edev->hdev.dev,
+ dev_warn(&hdev->dev,
"Max dais supported: %d\n", dai_id);
break;
}
@@ -1161,27 +1199,29 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
return 0;
}
-static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
+static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid)
{
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_cvt *cvt;
char name[NAME_SIZE];
- cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
+ cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL);
if (!cvt)
return -ENOMEM;
cvt->nid = nid;
sprintf(name, "cvt %d", cvt->nid);
- cvt->name = kstrdup(name, GFP_KERNEL);
+ 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(&edev->hdev, cvt);
+ return hdac_hdmi_query_cvt_params(hdev, cvt);
}
-static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
+static int hdac_hdmi_parse_eld(struct hdac_device *hdev,
struct hdac_hdmi_port *port)
{
unsigned int ver, mnl;
@@ -1190,7 +1230,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
>> DRM_ELD_VER_SHIFT;
if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
- dev_err(&edev->hdev.dev, "HDMI: Unknown ELD version %d\n", ver);
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: Unknown ELD version %d\n", ver);
return -EINVAL;
}
@@ -1198,7 +1239,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
if (mnl > ELD_MAX_MNL) {
- dev_err(&edev->hdev.dev, "HDMI: MNL Invalid %d\n", mnl);
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: MNL Invalid %d\n", mnl);
return -EINVAL;
}
@@ -1210,11 +1252,12 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
struct hdac_hdmi_port *port)
{
- struct hdac_ext_device *edev = pin->edev;
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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;
@@ -1229,17 +1272,19 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
if (pin->mst_capable)
port_id = port->id;
- size = snd_hdac_acomp_get_eld(&edev->hdev, pin->nid, 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(edev, port) < 0)
+ 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;
@@ -1248,38 +1293,50 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
port->eld.eld_size = 0;
}
- pcm = hdac_hdmi_get_pcm(edev, port);
+ 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(&edev->hdev.dev, "%s: disconnect for pin:port %d:%d\n",
- __func__, pin->nid, port->id);
+ dev_dbg(&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)
+ 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)
+ 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_hdmi_priv *hdmi,
- struct hdac_hdmi_pin *pin)
+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;
@@ -1291,33 +1348,34 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi,
* implemented.
*/
- ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL);
+ 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_ext_device *edev, hda_nid_t nid)
+static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid)
{
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_pin *pin;
int ret;
- pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+ pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL);
if (!pin)
return -ENOMEM;
pin->nid = nid;
pin->mst_capable = false;
- pin->edev = edev;
- ret = hdac_hdmi_add_ports(hdmi, pin);
+ pin->hdev = hdev;
+ ret = hdac_hdmi_add_ports(hdev, pin);
if (ret < 0)
return ret;
@@ -1377,7 +1435,7 @@ 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_tdm_slot = hdac_hdmi_set_tdm_slot,
+ .set_stream = hdac_hdmi_set_stream,
};
/*
@@ -1405,10 +1463,16 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev,
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
ret = snd_hdac_query_supported_pcm(hdev, cvt->nid,
- &rates, &formats, &bps);
+ &rates, &formats, NULL, &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);
@@ -1447,15 +1511,12 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev,
* 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_ext_device *edev,
+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_cvt *temp_cvt, *cvt_next;
- struct hdac_hdmi_pin *temp_pin, *pin_next;
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
- struct hdac_device *hdev = &edev->hdev;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
int ret;
hdac_hdmi_skl_enable_all_pins(hdev);
@@ -1471,8 +1532,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
unsigned int caps;
unsigned int type;
- caps = get_wcaps(hdev, nid);
- type = get_wcaps_type(caps);
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
@@ -1480,69 +1541,58 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
switch (type) {
case AC_WID_AUD_OUT:
- ret = hdac_hdmi_add_cvt(edev, nid);
+ ret = hdac_hdmi_add_cvt(hdev, nid);
if (ret < 0)
- goto free_widgets;
+ return ret;
break;
case AC_WID_PIN:
- ret = hdac_hdmi_add_pin(edev, nid);
+ ret = hdac_hdmi_add_pin(hdev, nid);
if (ret < 0)
- goto free_widgets;
+ return ret;
break;
}
}
if (!hdmi->num_pin || !hdmi->num_cvt) {
ret = -EIO;
- goto free_widgets;
+ 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);
- goto free_widgets;
+ ret);
+ return ret;
}
*num_dais = hdmi->num_cvt;
- ret = hdac_hdmi_init_dai_map(edev);
+ ret = hdac_hdmi_init_dai_map(hdev);
if (ret < 0)
- goto free_widgets;
-
+ dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n",
+ ret);
return ret;
+}
-free_widgets:
- list_for_each_entry_safe(temp_cvt, cvt_next, &hdmi->cvt_list, head) {
- list_del(&temp_cvt->head);
- kfree(temp_cvt->name);
- kfree(temp_cvt);
- }
-
- list_for_each_entry_safe(temp_pin, pin_next, &hdmi->pin_list, head) {
- for (i = 0; i < temp_pin->num_ports; i++)
- temp_pin->ports[i].pin = NULL;
- kfree(temp_pin->ports);
- list_del(&temp_pin->head);
- kfree(temp_pin);
- }
-
- 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_ext_device *edev = aptr;
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
- struct hdac_hdmi_pin *pin = NULL;
+ 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_codec *codec = edev->scodec;
+ 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(&edev->hdev.dev, "%s: for pin:%d port=%d\n", __func__,
+ dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__,
pin_nid, pipe);
/*
@@ -1551,11 +1601,11 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
* 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(codec->component.card->snd_card) !=
+ if (snd_power_get_state(component->card->snd_card) !=
SNDRV_CTL_POWER_D0)
return;
- if (atomic_read(&edev->hdev.in_pm))
+ if (atomic_read(&hdev->in_pm))
return;
list_for_each_entry(pin, &hdmi->pin_list, head) {
@@ -1583,184 +1633,12 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
}
-static struct i915_audio_component_audio_ops aops = {
+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;
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
- 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_ext_device *edev)
-{
- 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 snd_soc_codec *codec = edev->scodec;
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
-
- kc = devm_kcalloc(codec->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(codec->dev, xname, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
- snprintf(kc_name, sizeof(kc_name), "%s Switch", xname);
- kc[i].name = devm_kstrdup(codec->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_codec *codec,
- struct snd_soc_dapm_context *dapm)
-{
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->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, edev);
-
- 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_codec *codec = dai->codec;
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->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 = kzalloc(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(&edev->hdev.dev,
- "chmap control add failed with err: %d for pcm: %d\n",
- err, device);
- kfree(pcm);
- 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_ext_device *edev,
+static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev,
struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
{
int i;
@@ -1769,7 +1647,7 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev,
list_for_each_entry(pin, &hdmi->pin_list, head) {
if (detect_pin_caps) {
- if (hdac_hdmi_get_port_len(edev, pin->nid) == 0)
+ if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0)
pin->mst_capable = false;
else
pin->mst_capable = true;
@@ -1784,117 +1662,108 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev,
}
}
-static int hdmi_codec_probe(struct snd_soc_codec *codec)
+static int hdmi_codec_probe(struct snd_soc_component *component)
{
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
+ 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(&codec->component);
- struct hdac_ext_link *hlink = NULL;
+ snd_soc_component_get_dapm(component);
+ struct hdac_ext_link *hlink;
int ret;
- edev->scodec = codec;
+ 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_link(edev->ebus, dev_name(&edev->hdev.dev));
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
if (!hlink) {
- dev_err(&edev->hdev.dev, "hdac link not found\n");
+ dev_err(&hdev->dev, "hdac link not found\n");
return -EIO;
}
- snd_hdac_ext_bus_link_get(edev->ebus, hlink);
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
ret = create_fill_widget_route_map(dapm);
if (ret < 0)
return ret;
- aops.audio_ptr = edev;
- ret = snd_hdac_i915_register_notifier(&aops);
+ aops.audio_ptr = hdev;
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops);
if (ret < 0) {
- dev_err(&edev->hdev.dev, "notifier register failed: err: %d\n",
- ret);
+ dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret);
return ret;
}
- hdac_hdmi_present_sense_all_pins(edev, hdmi, true);
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, true);
/* Imp: Store the card pointer in hda_codec */
- edev->card = dapm->card->snd_card;
+ 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(&edev->hdev.dev);
- pm_runtime_put(&edev->hdev.dev);
- pm_runtime_suspend(&edev->hdev.dev);
-
- return 0;
-}
-
-static int hdmi_codec_remove(struct snd_soc_codec *codec)
-{
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+ pm_runtime_enable(&hdev->dev);
+ pm_runtime_put(&hdev->dev);
+ pm_runtime_suspend(&hdev->dev);
- pm_runtime_disable(&edev->hdev.dev);
return 0;
}
-#ifdef CONFIG_PM
-static int hdmi_codec_prepare(struct device *dev)
+static void hdmi_codec_remove(struct snd_soc_component *component)
{
- struct hdac_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_device *hdev = &edev->hdev;
-
- pm_runtime_get_sync(&edev->hdev.dev);
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ int ret;
- /*
- * 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);
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
+ if (ret < 0)
+ dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
+ ret);
- return 0;
+ pm_runtime_disable(&hdev->dev);
}
-static void hdmi_codec_complete(struct device *dev)
+static int hdmi_codec_resume(struct device *dev)
{
- struct hdac_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
- struct hdac_device *hdev = &edev->hdev;
-
- /* Power up afg */
- snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
-
- hdac_hdmi_skl_enable_all_pins(&edev->hdev);
- hdac_hdmi_skl_enable_dp12(&edev->hdev);
+ 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(edev, hdmi, false);
-
- pm_runtime_put_sync(&edev->hdev.dev);
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, false);
+ return 0;
}
-#else
-#define hdmi_codec_prepare NULL
-#define hdmi_codec_complete NULL
-#endif
-
-static const struct snd_soc_codec_driver hdmi_hda_codec = {
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
- .idle_bias_off = true,
+
+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,
@@ -1909,7 +1778,6 @@ static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx,
static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx,
unsigned char *chmap, int prepared)
{
- struct hdac_ext_device *edev = to_ehdac_device(hdev);
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;
@@ -1925,7 +1793,7 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx,
memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap));
list_for_each_entry(port, &pcm->port_list, head)
if (prepared)
- hdac_hdmi_setup_audio_infoframe(edev, pcm, port);
+ hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
mutex_unlock(&pcm->lock);
}
@@ -1957,9 +1825,6 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
- if (!port)
- return 0;
-
if (!port || !port->eld.eld_valid)
return 0;
@@ -1974,36 +1839,35 @@ static struct hdac_hdmi_drv_data intel_drv_data = {
.vendor_nid = INTEL_VENDOR_NID,
};
-static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
+static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
{
- struct hdac_device *hdev = &edev->hdev;
struct hdac_hdmi_priv *hdmi_priv;
struct snd_soc_dai_driver *hdmi_dais = NULL;
- struct hdac_ext_link *hlink = NULL;
+ struct hdac_ext_link *hlink;
int num_dais = 0;
- int ret = 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_link(edev->ebus, dev_name(&edev->hdev.dev));
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
if (!hlink) {
- dev_err(&edev->hdev.dev, "hdac link not found\n");
+ dev_err(&hdev->dev, "hdac link not found\n");
return -EIO;
}
- snd_hdac_ext_bus_link_get(edev->ebus, hlink);
+ 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;
- edev->private_data = hdmi_priv;
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;
@@ -2014,7 +1878,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
else
hdmi_priv->drv_data = &intel_drv_data;
- dev_set_drvdata(&hdev->dev, edev);
+ dev_set_drvdata(&hdev->dev, hdmi_priv);
INIT_LIST_HEAD(&hdmi_priv->pin_list);
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
@@ -2025,81 +1889,49 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
* Turned off in the runtime_suspend during the first explicit
* pm_runtime_suspend call.
*/
- ret = snd_hdac_display_power(edev->hdev.bus, true);
- if (ret < 0) {
- dev_err(&edev->hdev.dev,
- "Cannot turn on display power on i915 err: %d\n",
- ret);
- return ret;
- }
+ snd_hdac_display_power(hdev->bus, hdev->addr, true);
- ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
+ 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, true);
+ snd_hdac_refresh_widgets(hdev);
/* ASoC specific initialization */
- ret = snd_soc_register_codec(&hdev->dev, &hdmi_hda_codec,
+ ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec,
hdmi_dais, num_dais);
- snd_hdac_ext_bus_link_put(edev->ebus, hlink);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
return ret;
}
-static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
+static void clear_dapm_works(struct hdac_device *hdev)
{
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev);
- struct hdac_hdmi_pin *pin, *pin_next;
- struct hdac_hdmi_cvt *cvt, *cvt_next;
- struct hdac_hdmi_pcm *pcm, *pcm_next;
- struct hdac_hdmi_port *port, *port_next;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
int i;
- snd_soc_unregister_codec(&edev->hdev.dev);
-
- list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
- pcm->cvt = NULL;
- if (list_empty(&pcm->port_list))
- continue;
-
- list_for_each_entry_safe(port, port_next,
- &pcm->port_list, head)
- list_del(&port->head);
-
- list_del(&pcm->head);
- kfree(pcm);
- }
-
- list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
- list_del(&cvt->head);
- kfree(cvt->name);
- kfree(cvt);
- }
-
- list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
+ list_for_each_entry(pin, &hdmi->pin_list, head)
for (i = 0; i < pin->num_ports; i++)
- pin->ports[i].pin = NULL;
- kfree(pin->ports);
- list_del(&pin->head);
- kfree(pin);
- }
+ 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_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_device *hdev = &edev->hdev;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
struct hdac_bus *bus = hdev->bus;
- struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
- struct hdac_ext_link *hlink = NULL;
- int err;
+ struct hdac_ext_link *hlink;
dev_dbg(dev, "Enter: %s\n", __func__);
@@ -2116,31 +1948,26 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
*/
snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
AC_PWRST_D3);
- err = snd_hdac_display_power(bus, false);
- if (err < 0) {
- dev_err(bus->dev, "Cannot turn on display power on i915\n");
- return err;
- }
- hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ 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_put(ebus, hlink);
+ 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_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_device *hdev = &edev->hdev;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
struct hdac_bus *bus = hdev->bus;
- struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
- struct hdac_ext_link *hlink = NULL;
- int err;
+ struct hdac_ext_link *hlink;
dev_dbg(dev, "Enter: %s\n", __func__);
@@ -2148,22 +1975,19 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
if (!bus)
return 0;
- hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ 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(ebus, hlink);
+ snd_hdac_ext_bus_link_get(bus, hlink);
+ snd_hdac_codec_link_up(hdev);
- err = snd_hdac_display_power(bus, true);
- if (err < 0) {
- dev_err(bus->dev, "Cannot turn on display power on i915\n");
- return err;
- }
+ snd_hdac_display_power(bus, hdev->addr, true);
- hdac_hdmi_skl_enable_all_pins(&edev->hdev);
- hdac_hdmi_skl_enable_dp12(&edev->hdev);
+ 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,
@@ -2171,15 +1995,10 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
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)
- .prepare = hdmi_codec_prepare,
- .complete = hdmi_codec_complete,
+ RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume)
};
static const struct hda_device_id hdmi_list[] = {
@@ -2195,14 +2014,12 @@ static const struct hda_device_id hdmi_list[] = {
MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
-static struct hdac_ext_driver hdmi_driver = {
- . hdac = {
- .driver = {
- .name = "HDMI HDA Codec",
- .pm = &hdac_hdmi_pm,
- },
- .id_table = hdmi_list,
+static struct hdac_driver hdmi_driver = {
+ .driver = {
+ .name = "HDMI HDA Codec",
+ .pm = pm_ptr(&hdac_hdmi_pm),
},
+ .id_table = hdmi_list,
.probe = hdac_hdmi_dev_probe,
.remove = hdac_hdmi_dev_remove,
};
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
deleted file mode 100644
index b5b57a5cbbfd..000000000000
--- a/sound/soc/codecs/hdac_hdmi.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* 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 pcm,
- struct snd_soc_jack *jack);
-
-int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec,
- 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
index 5672e516bec3..e1933f733af1 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -1,20 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC codec for HDMI encoder drivers
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
* Author: Jyri Sarha <jsarha@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#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>
@@ -24,14 +17,10 @@
#include <sound/pcm_iec958.h>
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+#include <drm/drm_eld.h>
#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
-struct hdmi_codec_channel_map_table {
- unsigned char map; /* ALSA API channel map position */
- unsigned long spk_mask; /* speaker position bit mask */
-};
-
/*
* CEA speaker placement for HDMI 1.4:
*
@@ -196,103 +185,116 @@ static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
/*
* hdmi_codec_channel_alloc: speaker configuration available for CEA
*
- * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
+ * This is an ordered list where ca_id must exist in hdmi_codec_8ch_chmaps
* 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 */
+ .mask = FL | FR },
+ { .ca_id = 0x03, .n_ch = 4,
+ .mask = FL | FR | LFE | FC },
{ .ca_id = 0x02, .n_ch = 4,
.mask = FL | FR | FC },
- /* surround51 */
+ { .ca_id = 0x01, .n_ch = 4,
+ .mask = FL | FR | LFE },
{ .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 */
+ .mask = FL | FR | LFE | FC | RL | RR },
{ .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 = 0x09, .n_ch = 6,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 6,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 6,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 6,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 6,
+ .mask = FL | FR | RC },
{ .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 = 0x1f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
{ .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 },
+ { .ca_id = 0x11, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RLC | RRC },
+ { .ca_id = 0x1d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | FLC | FRC },
+ { .ca_id = 0x10, .n_ch = 8,
+ .mask = FL | FR | RL | RR | RLC | RRC },
+ { .ca_id = 0x1c, .n_ch = 8,
+ .mask = FL | FR | RL | RR | FLC | FRC },
+ { .ca_id = 0x0f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RC },
+ { .ca_id = 0x1b, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FC | FLC | FRC },
+ { .ca_id = 0x0e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RC },
+ { .ca_id = 0x1a, .n_ch = 8,
+ .mask = FL | FR | RC | FC | FLC | FRC },
+ { .ca_id = 0x0d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RC },
+ { .ca_id = 0x19, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FLC | FRC },
+ { .ca_id = 0x0c, .n_ch = 8,
+ .mask = FL | FR | RC | RL | RR },
+ { .ca_id = 0x18, .n_ch = 8,
+ .mask = FL | FR | RC | FLC | FRC },
+ { .ca_id = 0x17, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | FLC | FRC },
+ { .ca_id = 0x16, .n_ch = 8,
+ .mask = FL | FR | FC | FLC | FRC },
+ { .ca_id = 0x15, .n_ch = 8,
+ .mask = FL | FR | LFE | FLC | FRC },
+ { .ca_id = 0x14, .n_ch = 8,
+ .mask = FL | FR | FLC | FRC },
+ { .ca_id = 0x0b, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR },
+ { .ca_id = 0x0a, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR },
+ { .ca_id = 0x09, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 8,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 8,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 8,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 8,
+ .mask = FL | FR | RC },
+ { .ca_id = 0x03, .n_ch = 8,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x02, .n_ch = 8,
+ .mask = FL | FR | FC },
+ { .ca_id = 0x01, .n_ch = 8,
+ .mask = FL | FR | LFE },
};
struct hdmi_codec_priv {
struct hdmi_codec_pdata hcd;
- struct snd_soc_dai_driver *daidrv;
- struct hdmi_codec_daifmt daifmt[2];
- struct mutex current_stream_lock;
- struct snd_pcm_substream *current_stream;
uint8_t eld[MAX_ELD_BYTES];
+ struct snd_parsed_hdmi_eld eld_parsed;
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];
+ struct snd_info_entry *proc_entry;
};
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
- { "TX", NULL, "Playback" },
+ SND_SOC_DAPM_OUTPUT("RX"),
};
enum {
@@ -304,7 +306,7 @@ 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 = FIELD_SIZEOF(struct hdmi_codec_priv, eld);
+ uinfo->count = sizeof_field(struct hdmi_codec_priv, eld);
return 0;
}
@@ -384,7 +386,8 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = info->private_data;
- map = info->chmap[hcp->chmap_idx].map;
+ if (hcp->chmap_idx != HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+ map = info->chmap[hcp->chmap_idx].map;
for (i = 0; i < info->max_channels; i++) {
if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
@@ -396,77 +399,164 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
- int ret = 0;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
- mutex_lock(&hcp->current_stream_lock);
- if (!hcp->current_stream) {
- hcp->current_stream = substream;
- } else if (hcp->current_stream != substream) {
- dev_err(dai->dev, "Only one simultaneous stream supported!\n");
- ret = -EINVAL;
- }
- mutex_unlock(&hcp->current_stream_lock);
+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);
- return ret;
+ 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;
- dev_dbg(dai->dev, "%s()\n", __func__);
+ if (!((has_playback && tx) || (has_capture && !tx)))
+ return 0;
- ret = hdmi_codec_new_stream(substream, dai);
- if (ret)
- return ret;
+ 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) {
- mutex_lock(&hcp->current_stream_lock);
- hcp->current_stream = NULL;
- mutex_unlock(&hcp->current_stream_lock);
- return ret;
- }
+ if (ret)
+ goto err;
}
- if (hcp->hcd.ops->get_eld) {
+ 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;
+
+ snd_parse_eld(dai->dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+
+ ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld);
+ if (ret)
+ goto err;
- if (!ret) {
- ret = snd_pcm_hw_constraint_eld(substream->runtime,
- hcp->eld);
- if (ret)
- return ret;
- }
/* Select chmap supported */
hdmi_codec_eld_chmap(hcp);
}
- return 0;
+
+ 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;
- dev_dbg(dai->dev, "%s()\n", __func__);
-
- WARN_ON(hcp->current_stream != substream);
+ 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->current_stream_lock);
- hcp->current_stream = NULL;
- mutex_unlock(&hcp->current_stream_lock);
+ 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 = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ u8 ca_id = 0;
+ bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO);
+
+ if (pcm_audio) {
+ /* 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;
+ }
+
+ ca_id = hdmi_codec_channel_alloc[idx].ca_id;
+ }
+
+ memset(hp, 0, sizeof(*hp));
+
+ hdmi_audio_infoframe_init(&hp->cea);
+
+ if (pcm_audio)
+ hp->cea.channels = channels;
+ else
+ hp->cea.channels = 0;
+
+ 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 = ca_id;
+
+ hp->sample_width = sample_width;
+ hp->sample_rate = sample_rate;
+ hp->channels = channels;
+
+ if (pcm_audio)
+ hcp->chmap_idx = ca_id;
+ else
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+ return 0;
}
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
@@ -474,6 +564,7 @@ static int hdmi_codec_hw_params(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 hdmi_codec_params hp = {
.iec = {
.status = { 0 },
@@ -482,157 +573,186 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
.dig_subframe = { 0 },
}
};
- int ret, idx;
+ 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));
- if (params_width(params) > 24)
- params->msbits = 24;
+ ret = hdmi_codec_fill_codec_params(dai,
+ params_width(params),
+ params_rate(params),
+ params_channels(params),
+ &hp);
+ if (ret < 0)
+ return ret;
- ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
- sizeof(hp.iec.status));
+ 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;
}
- ret = hdmi_codec_new_stream(substream, dai);
- if (ret)
- return ret;
-
- hdmi_audio_infoframe_init(&hp.cea);
- hp.cea.channels = params_channels(params);
- 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;
-
- /* Select a channel allocation that matches with ELD and pcm channels */
- idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.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;
- }
- hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
- hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
-
- hp.sample_width = params_width(params);
- hp.sample_rate = params_rate(params);
- hp.channels = params_channels(params);
-
+ cf->bit_fmt = params_format(params);
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
- &hcp->daifmt[dai->id], &hp);
+ cf, &hp);
}
-static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
+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 = { 0 };
- int ret = 0;
+ 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;
- dev_dbg(dai->dev, "%s()\n", __func__);
+ if (!hcp->hcd.ops->prepare)
+ return 0;
- if (dai->id == DAI_ID_SPDIF) {
- cf.fmt = HDMI_SPDIF;
- } else {
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- cf.bit_clk_master = 1;
- cf.frame_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- cf.frame_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- cf.bit_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- break;
- default:
- return -EINVAL;
- }
+ dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+ width, rate, channels);
- 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;
- }
+ ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+ if (ret < 0)
+ return ret;
- 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;
- }
+ 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;
}
- hcp->daifmt[dai->id] = cf;
-
- return ret;
+ cf->bit_fmt = runtime->format;
+ return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+ cf, &hp);
}
-static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
+static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
{
- struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ 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;
+ }
- dev_dbg(dai->dev, "%s()\n", __func__);
+ 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;
+ }
- if (hcp->hcd.ops->digital_mute)
- return hcp->hcd.ops->digital_mute(dai->dev->parent,
- hcp->hcd.data, mute);
+ 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 const struct snd_soc_dai_ops hdmi_dai_ops = {
- .startup = hdmi_codec_startup,
- .shutdown = hdmi_codec_shutdown,
- .hw_params = hdmi_codec_hw_params,
- .set_fmt = hdmi_codec_set_fmt,
- .digital_mute = hdmi_codec_digital_mute,
-};
+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.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 const 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;
#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_S16_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)
+#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
@@ -642,31 +762,43 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {
* problems, we should add the video side driver an option to disable
* them.
*/
-#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_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 |\
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+#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);
- 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 = hdmi_eld_ctl_info,
- .get = hdmi_eld_ctl_get,
- .device = rtd->pcm->device,
- };
+ unsigned int i;
int ret;
- dev_dbg(dai->dev, "%s()\n", __func__);
-
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
NULL, drv->playback.channels_max, 0,
&hcp->chmap_info);
@@ -681,14 +813,224 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
- /* add ELD ctl with the device number corresponding to the PCM stream */
- kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component);
- if (!kctl)
+ 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;
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_codec_priv *hcp = entry->private_data;
+
+ snd_print_eld_info(&hcp->eld_parsed, buffer);
+}
+
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_dai *d;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_info_entry *entry;
+ char name[32];
+ int err, i, id = 0;
+
+ /*
+ * To avoid duplicate proc entry, find its rtd and use rtd->id
+ * instead of dai->id
+ */
+ for_each_card_rtds(card, rtd) {
+ for_each_rtd_dais(rtd, i, d)
+ if (d == dai) {
+ id = rtd->id;
+ goto found;
+ }
+ }
+found:
+ snprintf(name, sizeof(name), "eld#%d", id);
+ err = snd_card_proc_new(card->snd_card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, hcp, print_eld_info);
+ hcp->proc_entry = entry;
+
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+ snd_info_free_entry(hcp->proc_entry);
+ hcp->proc_entry = NULL;
+}
+#else
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+}
+#endif
+
+static int hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+ 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;
- return snd_ctl_add(rtd->card->snd_card, kctl);
+ snd_soc_dai_dma_data_set_playback(dai, daifmt);
+
+ return hdmi_dai_proc_new(hcp, dai);
}
+static int hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+
+ hdmi_dai_proc_free(hcp);
+ return 0;
+}
+
+static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
+ unsigned int jack_status)
+{
+ if (jack_status != hcp->jack_status) {
+ if (hcp->jack)
+ snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_AVOUT);
+ hcp->jack_status = jack_status;
+ }
+}
+
+static void plugged_cb(struct device *dev, bool plugged)
+{
+ struct hdmi_codec_priv *hcp = dev_get_drvdata(dev);
+ int ret;
+
+ if (plugged) {
+ if (hcp->hcd.ops->get_eld) {
+ hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ ret = snd_parse_eld(dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+ if (ret < 0)
+ dev_dbg(dev, "Failed to parse ELD: %d\n", ret);
+ else
+ snd_show_eld(dev, &hcp->eld_parsed);
+ }
+ hdmi_codec_jack_report(hcp, SND_JACK_AVOUT);
+ } 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);
+
+ if (hcp->hcd.ops->hook_plugged_cb) {
+ hcp->jack = jack;
+
+ /*
+ * Report the initial jack status which may have been provided
+ * by the parent hdmi driver while the hpd hook was registered.
+ */
+ snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_AVOUT);
+
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+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_ops hdmi_codec_i2s_dai_ops = {
+ .probe = hdmi_dai_probe,
+ .remove = hdmi_dai_remove,
+ .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,
+ .pcm_new = hdmi_codec_pcm_new,
+ .auto_selectable_formats = &hdmi_codec_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
+ .probe = hdmi_dai_spdif_probe,
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
+ .mute_stream = hdmi_codec_mute,
+ .pcm_new = hdmi_codec_pcm_new,
+};
+
static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
@@ -700,8 +1042,15 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.formats = I2S_FORMATS,
.sig_bits = 24,
},
- .ops = &hdmi_dai_ops,
- .pcm_new = hdmi_codec_pcm_new,
+ .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,
};
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
@@ -714,8 +1063,14 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.rates = HDMI_RATES,
.formats = SPDIF_FORMATS,
},
- .ops = &hdmi_dai_ops,
- .pcm_new = hdmi_codec_pcm_new,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = HDMI_RATES,
+ .formats = SPDIF_FORMATS,
+ },
+ .ops = &hdmi_codec_spdif_dai_ops,
};
static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
@@ -725,38 +1080,64 @@ static int hdmi_of_xlate_dai_id(struct snd_soc_component *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);
+ ret = hcp->hcd.ops->get_dai_id(component, endpoint, hcp->hcd.data);
return ret;
}
-static const struct snd_soc_codec_driver hdmi_codec = {
- .component_driver = {
- .dapm_widgets = hdmi_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
- .of_xlate_dai_id = hdmi_of_xlate_dai_id,
- },
+static int hdmi_probe(struct snd_soc_component *component)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (hcp->hcd.ops->hook_plugged_cb) {
+ ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
+ hcp->hcd.data,
+ plugged_cb,
+ component->dev);
+ }
+
+ 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 = {
+ .probe = hdmi_probe,
+ .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;
- dev_dbg(dev, "%s()\n", __func__);
-
if (!hcd) {
- dev_err(dev, "%s: No plalform data\n", __func__);
+ 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 ||
+ 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;
@@ -767,44 +1148,52 @@ static int hdmi_codec_probe(struct platform_device *pdev)
return -ENOMEM;
hcp->hcd = *hcd;
- mutex_init(&hcp->current_stream_lock);
+ mutex_init(&hcp->lock);
- hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
- GFP_KERNEL);
- if (!hcp->daidrv)
+ 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) {
- hcp->daidrv[i] = hdmi_i2s_dai;
- hcp->daidrv[i].playback.channels_max =
- hcd->max_i2s_channels;
+ daidrv[i] = hdmi_i2s_dai;
+ daidrv[i].playback.channels_max = hcd->max_i2s_channels;
+ if (hcd->i2s_formats) {
+ daidrv[i].playback.formats = hcd->i2s_formats;
+ daidrv[i].capture.formats = hcd->i2s_formats;
+ }
+ 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)
- hcp->daidrv[i] = hdmi_spdif_dai;
+ 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 = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
- dai_count);
+ ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv,
+ dai_count);
if (ret) {
- dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
+ dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n",
__func__, ret);
return ret;
}
-
- dev_set_drvdata(dev, hcp);
- return 0;
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hdmi_codec_priv *hcp;
-
- hcp = dev_get_drvdata(dev);
- kfree(hcp->chmap_info);
- snd_soc_unregister_codec(dev);
-
return 0;
}
@@ -813,7 +1202,6 @@ static struct platform_driver hdmi_codec_driver = {
.name = HDMI_CODEC_DRV_NAME,
},
.probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
};
module_platform_driver(hdmi_codec_driver);
diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c
index 651206273f36..58a382254718 100644
--- a/sound/soc/codecs/ics43432.c
+++ b/sound/soc/codecs/ics43432.c
@@ -1,12 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * I2S MEMS microphone driver for InvenSense ICS-43432
+ * 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
- *
- * Licensed under GPL v2.
*/
#include <linux/module.h>
@@ -37,24 +37,23 @@ static struct snd_soc_dai_driver ics43432_dai = {
},
};
-static const struct snd_soc_codec_driver ics43432_codec_driver = {
+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 snd_soc_register_codec(&pdev->dev, &ics43432_codec_driver,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &ics43432_component_driver,
&ics43432_dai, 1);
}
-static int ics43432_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#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);
@@ -66,7 +65,6 @@ static struct platform_driver ics43432_driver = {
.of_match_table = of_match_ptr(ics43432_ids),
},
.probe = ics43432_probe,
- .remove = ics43432_remove,
};
module_platform_driver(ics43432_driver);
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
new file mode 100644
index 000000000000..a03d4e5e7d14
--- /dev/null
+++ b/sound/soc/codecs/idt821034.c
@@ -0,0 +1,1183 @@
+// 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 const 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 int 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);
+
+ mutex_unlock(&idt821034->mutex);
+
+ if (ret)
+ dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+
+ return ret;
+}
+
+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;
+
+ ret = idt821034_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ 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 out 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
index 6b59b6f08298..fdd19f8e8864 100644
--- a/sound/soc/codecs/inno_rk3036.c
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver of Inno codec for rk3036 by Rockchip Inc.
*
@@ -47,11 +48,9 @@ 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, ret, regval;
+ int val, regval;
- ret = snd_soc_component_read(component, INNO_R09, &regval);
- if (ret)
- return ret;
+ 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;
@@ -196,22 +195,22 @@ static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
static int rk3036_codec_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;
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
- dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
+ dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
INNO_R01_I2SMODE_MASTER;
break;
default:
- dev_err(codec->dev, "invalid fmt\n");
+ dev_err(component->dev, "invalid fmt\n");
return -EINVAL;
}
@@ -229,7 +228,7 @@ static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg02_val |= INNO_R02_DACM_LJM;
break;
default:
- dev_err(codec->dev, "set dai format failed\n");
+ dev_err(component->dev, "set dai format failed\n");
return -EINVAL;
}
@@ -251,15 +250,15 @@ static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
default:
- dev_err(codec->dev, "set dai format failed\n");
+ dev_err(component->dev, "set dai format failed\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
+ snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
INNO_R01_PINDIR_MSK, reg01_val);
- snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_DACM_MSK, reg02_val);
- snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
return 0;
}
@@ -268,7 +267,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int reg02_val = 0, reg03_val = 0;
switch (params_format(hw_params)) {
@@ -291,9 +290,9 @@ static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
- snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_VWL_MSK, reg02_val);
- snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
INNO_R03_FWL_MSK, reg03_val);
return 0;
}
@@ -326,47 +325,46 @@ static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
.formats = RK3036_CODEC_FMTS,
},
.ops = &rk3036_codec_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static void rk3036_codec_reset(struct snd_soc_codec *codec)
+static void rk3036_codec_reset(struct snd_soc_component *component)
{
- snd_soc_write(codec, INNO_R00,
+ snd_soc_component_write(component, INNO_R00,
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
- snd_soc_write(codec, INNO_R00,
+ snd_soc_component_write(component, INNO_R00,
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
}
-static int rk3036_codec_probe(struct snd_soc_codec *codec)
+static int rk3036_codec_probe(struct snd_soc_component *component)
{
- rk3036_codec_reset(codec);
+ rk3036_codec_reset(component);
return 0;
}
-static int rk3036_codec_remove(struct snd_soc_codec *codec)
+static void rk3036_codec_remove(struct snd_soc_component *component)
{
- rk3036_codec_reset(codec);
- return 0;
+ rk3036_codec_reset(component);
}
-static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
+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_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
/* start precharge */
- snd_soc_write(codec, INNO_R06, INNO_R06_DAC_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_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
/* start discharge. */
- snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
+ snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
break;
default:
@@ -376,18 +374,19 @@ static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static const struct snd_soc_codec_driver rk3036_codec_driver = {
+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,
- .component_driver = {
- .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),
- },
+ .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 = {
@@ -403,7 +402,6 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv;
struct device_node *of_node = pdev->dev.of_node;
- struct resource *res;
void __iomem *base;
struct regmap *grf;
int ret;
@@ -412,8 +410,7 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -449,7 +446,7 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, priv);
- ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
+ ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
rk3036_codec_dai_driver,
ARRAY_SIZE(rk3036_codec_dai_driver));
if (ret) {
@@ -460,17 +457,14 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
return ret;
}
-static int rk3036_codec_platform_remove(struct platform_device *pdev)
+static void rk3036_codec_platform_remove(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_codec(&pdev->dev);
clk_disable_unprepare(priv->pclk);
-
- return 0;
}
-static const struct of_device_id rk3036_codec_of_match[] = {
+static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
{ .compatible = "rockchip,rk3036-codec", },
{}
};
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index 5ca99280ae00..b7a94631d77d 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* isabelle.c - Low power high fidelity audio codec driver
*
* Copyright (c) 2012 Texas Instruments, 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; version 2 of the License.
- *
- *
* Initially based on sound/soc/codecs/twl6040.c
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -865,31 +860,31 @@ static const struct snd_soc_dapm_route isabelle_intercon[] = {
{ "LINEOUT2", NULL, "LINEOUT2 Driver" },
};
-static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute)
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG,
+ 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)
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG,
+ 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)
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG,
+ 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_codec *codec,
+static int isabelle_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -899,12 +894,12 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, BIT(0));
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, 0);
break;
}
@@ -916,7 +911,7 @@ static int isabelle_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;
u16 aif = 0;
unsigned int fs_val = 0;
@@ -952,7 +947,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_FS_RATE_CFG_REG,
ISABELLE_FS_RATE_MASK, fs_val);
/* bit size */
@@ -967,7 +962,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
ISABELLE_AIF_LENGTH_MASK, aif);
return 0;
@@ -975,14 +970,14 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
static int isabelle_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;
unsigned int aif_val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val &= ~ISABELLE_AIF_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val |= ISABELLE_AIF_MS;
break;
default:
@@ -1003,7 +998,7 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
(ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
return 0;
@@ -1019,19 +1014,22 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_hs_mute,
+ .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,
- .digital_mute = isabelle_hf_mute,
+ .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,
- .digital_mute = isabelle_line_mute,
+ .mute_stream = isabelle_line_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
@@ -1087,17 +1085,16 @@ static struct snd_soc_dai_driver isabelle_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_isabelle = {
- .set_bias_level = isabelle_set_bias_level,
- .component_driver = {
- .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),
- },
- .idle_bias_off = true,
+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 = {
@@ -1110,8 +1107,7 @@ static const struct regmap_config isabelle_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int isabelle_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int isabelle_i2c_probe(struct i2c_client *i2c)
{
struct regmap *isabelle_regmap;
int ret = 0;
@@ -1125,25 +1121,19 @@ static int isabelle_i2c_probe(struct i2c_client *i2c,
}
i2c_set_clientdata(i2c, isabelle_regmap);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_isabelle, isabelle_dai,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int isabelle_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id isabelle_i2c_id[] = {
- { "isabelle", 0 },
+ { "isabelle" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
@@ -1153,7 +1143,6 @@ static struct i2c_driver isabelle_i2c_driver = {
.name = "isabelle",
},
.probe = isabelle_i2c_probe,
- .remove = isabelle_i2c_remove,
.id_table = isabelle_i2c_id,
};
diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h
index 96d839a8c956..23afc77cdc99 100644
--- a/sound/soc/codecs/isabelle.h
+++ b/sound/soc/codecs/isabelle.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* isabelle.h - Low power high fidelity audio codec driver header file
*
* Copyright (c) 2012 Texas Instruments, 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; version 2 of the License.
- *
*/
#ifndef _ISABELLE_H
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 6324ccdc8a5c..d1cea93bdb59 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -1,15 +1,8 @@
-/*
- * 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>
@@ -157,7 +150,7 @@ 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_codec_get_drvdata(dai->codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(dai->component);
uint32_t val;
switch (params_rate(params)) {
@@ -221,28 +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 regmap *regmap)
{
- regmap_update_bits(regmap, 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);
- regmap_update_bits(regmap, 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);
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_codec_get_drvdata(codec);
+ 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:
@@ -251,29 +242,23 @@ 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;
- regmap_update_bits(regmap, 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 (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ 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;
- regmap_update_bits(regmap, 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;
-
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
regcache_mark_dirty(regmap);
break;
default:
@@ -283,9 +268,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
+static int jz4740_codec_dev_probe(struct snd_soc_component *component)
{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
@@ -293,19 +278,19 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
- .probe = jz4740_codec_dev_probe,
- .set_bias_level = jz4740_codec_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 const struct regmap_config jz4740_codec_regmap_config = {
@@ -316,14 +301,13 @@ static const struct regmap_config jz4740_codec_regmap_config = {
.reg_defaults = jz4740_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
- struct resource *mem;
void __iomem *base;
jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
@@ -331,8 +315,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
if (!jz4740_codec)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -343,7 +326,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
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)
dev_err(&pdev->dev, "Failed to register codec\n");
@@ -351,18 +334,17 @@ static int jz4740_codec_probe(struct platform_device *pdev)
return ret;
}
-static int jz4740_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- 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 = jz4740_codec_remove,
.driver = {
.name = "jz4740-codec",
+ .of_match_table = jz4740_codec_of_matches,
},
};
diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c
new file mode 100644
index 000000000000..e04af1b9ace8
--- /dev/null
+++ b/sound/soc/codecs/jz4760.c
@@ -0,0 +1,871 @@
+// 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[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR6, JZ4760_CODEC_REG_GCR5,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR2, JZ4760_CODEC_REG_GCR1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+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 const 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..312202ab5cea
--- /dev/null
+++ b/sound/soc/codecs/jz4770.c
@@ -0,0 +1,919 @@
+// 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[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_DACL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_HPL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+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 const 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
deleted file mode 100644
index a10ea3c716c6..000000000000
--- a/sound/soc/codecs/l3.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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>
-
-/*
- * Send one byte of data to the chip. Data is latched into the chip on
- * the rising edge of the clock.
- */
-static void sendbyte(struct l3_pins *adap, unsigned int byte)
-{
- int i;
-
- for (i = 0; i < 8; i++) {
- adap->setclk(adap, 0);
- udelay(adap->data_hold);
- adap->setdat(adap, byte & 1);
- udelay(adap->data_setup);
- adap->setclk(adap, 1);
- udelay(adap->clock_high);
- byte >>= 1;
- }
-}
-
-/*
- * Send a set of bytes to the chip. We need to pulse the MODE line
- * between each byte, but never at the start nor at the end of the
- * transfer.
- */
-static void sendbytes(struct l3_pins *adap, const u8 *buf,
- int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (i) {
- udelay(adap->mode_hold);
- adap->setmode(adap, 0);
- udelay(adap->mode);
- }
- adap->setmode(adap, 1);
- udelay(adap->mode_setup);
- sendbyte(adap, buf[i]);
- }
-}
-
-int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
-{
- adap->setclk(adap, 1);
- adap->setdat(adap, 1);
- adap->setmode(adap, 1);
- udelay(adap->mode);
-
- adap->setmode(adap, 0);
- udelay(adap->mode_setup);
- sendbyte(adap, addr);
- udelay(adap->mode_hold);
-
- sendbytes(adap, data, len);
-
- 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
index 1e964079642a..26cdb750cbca 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LM4857 AMP driver
*
@@ -5,12 +6,6 @@
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com
* Copyright 2011 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -120,8 +115,7 @@ static const struct regmap_config lm4857_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
-static int lm4857_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm4857_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -134,7 +128,7 @@ static int lm4857_i2c_probe(struct i2c_client *i2c,
}
static const struct i2c_device_id lm4857_i2c_id[] = {
- { "lm4857", 0 },
+ { "lm4857" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 41e09d1287b8..ab89af7965bf 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lm49453.c - LM49453 ALSA Soc Audio driver
*
* Copyright (c) 2012 Texas Instruments, 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; version 2 of the License.
- *
* Initially based on sound/soc/codecs/wm8350.c
*/
@@ -1110,7 +1107,7 @@ static int lm49453_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;
u16 clk_div = 0;
/* Setting DAC clock dividers based on substream sample rate. */
@@ -1134,32 +1131,32 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
- snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+ 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_codec *codec = codec_dai->codec;
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
@@ -1185,11 +1182,11 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 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_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+ snd_soc_component_write(component, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
return 0;
}
@@ -1197,7 +1194,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int lm49453_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;
u16 pll_clk = 0;
switch (freq) {
@@ -1209,57 +1206,55 @@ static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
break;
case 48000:
case 32576:
- /* fll clk slection */
- pll_clk = BIT(4);
return 0;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+ 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)
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+ 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)
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+ 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)
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+ 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)
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4),
+ 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)
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+ 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_codec *codec,
+static int lm49453_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ struct lm49453_priv *lm49453 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1267,15 +1262,15 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(lm49453->regmap);
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ 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_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
LM49453_PMC_SETUP_CHIP_EN, 0);
break;
}
@@ -1291,35 +1286,40 @@ 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,
- .digital_mute = lm49453_hp_mute,
+ .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,
- .digital_mute = lm49453_ls_mute,
+ .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,
- .digital_mute = lm49453_ha_mute,
+ .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,
- .digital_mute = lm49453_ep_mute,
+ .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,
- .digital_mute = lm49453_lo_mute,
+ .mute_stream = lm49453_lo_mute,
+ .no_capture_mute = 1,
};
/* LM49453 dai structure. */
@@ -1341,7 +1341,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
.formats = LM49453_FORMATS,
},
.ops = &lm49453_headset_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "LM49453 Speaker",
@@ -1389,17 +1389,16 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
- .set_bias_level = lm49453_set_bias_level,
- .component_driver = {
- .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),
- },
- .idle_bias_off = true,
+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 = {
@@ -1412,8 +1411,7 @@ static const struct regmap_config lm49453_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int lm49453_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm49453_i2c_probe(struct i2c_client *i2c)
{
struct lm49453_priv *lm49453;
int ret = 0;
@@ -1434,23 +1432,17 @@ static int lm49453_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_lm49453,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int lm49453_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id lm49453_i2c_id[] = {
- { "lm49453", 0 },
+ { "lm49453" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
@@ -1460,7 +1452,6 @@ static struct i2c_driver lm49453_i2c_driver = {
.name = "lm49453",
},
.probe = lm49453_i2c_probe,
- .remove = lm49453_i2c_remove,
.id_table = lm49453_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
index a63cfa5c0883..578a773e6fc9 100644
--- a/sound/soc/codecs/lm49453.h
+++ b/sound/soc/codecs/lm49453.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* lm49453.h - LM49453 ALSA Soc Audio drive
*
* Copyright (c) 2012 Texas Instruments, 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; version 2 of the License.
- *
*/
#ifndef _LM49453_H
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
new file mode 100644
index 000000000000..a3d6318c9050
--- /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_CBC_CFC);
+}
+
+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_CBP_CFP);
+}
+
+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..6e3b8d0897dd
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -0,0 +1,93 @@
+// 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.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "lpass-macro-common.h"
+
+static DEFINE_MUTEX(lpass_codec_mutex);
+static enum lpass_codec_version lpass_codec_version;
+
+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);
+
+void lpass_macro_set_codec_version(enum lpass_codec_version version)
+{
+ mutex_lock(&lpass_codec_mutex);
+ lpass_codec_version = version;
+ mutex_unlock(&lpass_codec_mutex);
+}
+EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version);
+
+enum lpass_codec_version lpass_macro_get_codec_version(void)
+{
+ enum lpass_codec_version ver;
+
+ mutex_lock(&lpass_codec_mutex);
+ ver = lpass_codec_version;
+ mutex_unlock(&lpass_codec_mutex);
+
+ return ver;
+}
+EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version);
+
+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..10ad682019fa
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -0,0 +1,77 @@
+/* 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)
+/* The soundwire block should be internally reset at probe */
+#define LPASS_MACRO_FLAG_RESET_SWR BIT(1)
+
+enum lpass_version {
+ LPASS_VER_9_0_0,
+ LPASS_VER_9_2_0,
+ LPASS_VER_10_0_0,
+ LPASS_VER_11_0_0,
+};
+
+enum lpass_codec_version {
+ LPASS_CODEC_VERSION_UNKNOWN,
+ LPASS_CODEC_VERSION_1_0,
+ LPASS_CODEC_VERSION_1_1,
+ LPASS_CODEC_VERSION_1_2,
+ LPASS_CODEC_VERSION_2_0,
+ LPASS_CODEC_VERSION_2_1,
+ LPASS_CODEC_VERSION_2_5,
+ LPASS_CODEC_VERSION_2_6,
+ LPASS_CODEC_VERSION_2_7,
+ LPASS_CODEC_VERSION_2_8,
+ LPASS_CODEC_VERSION_2_9,
+};
+
+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);
+void lpass_macro_set_codec_version(enum lpass_codec_version version);
+enum lpass_codec_version lpass_macro_get_codec_version(void);
+
+static inline void lpass_macro_pds_exit_action(void *pds)
+{
+ lpass_macro_pds_exit(pds);
+}
+
+static inline const char *lpass_macro_get_codec_version_string(int version)
+{
+ switch (version) {
+ case LPASS_CODEC_VERSION_1_0:
+ return "v1.0";
+ case LPASS_CODEC_VERSION_1_1:
+ return "v1.1";
+ case LPASS_CODEC_VERSION_1_2:
+ return "v1.2";
+ case LPASS_CODEC_VERSION_2_0:
+ return "v2.0";
+ case LPASS_CODEC_VERSION_2_1:
+ return "v2.1";
+ case LPASS_CODEC_VERSION_2_5:
+ return "v2.5";
+ case LPASS_CODEC_VERSION_2_6:
+ return "v2.6";
+ case LPASS_CODEC_VERSION_2_7:
+ return "v2.7";
+ case LPASS_CODEC_VERSION_2_8:
+ return "v2.8";
+ default:
+ break;
+ }
+ return "NA";
+}
+
+#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..a8fc842cc94e
--- /dev/null
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -0,0 +1,4039 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#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(rx, n) (0x0400 + rx->rxn_reg_stride * 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(rx, n) (0x0404 + rx->rxn_reg_stride * 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(rx, n) (0x0408 + rx->rxn_reg_stride * 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(rx, n) (0x040C + rx->rxn_reg_stride * 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(rx, n) (0x0410 + rx->rxn_reg_stride * 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(rx, n) (0x0414 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_VOL_CTL (0x0414)
+#define CDC_RX_RXn_RX_PATH_MIX_CTL(rx, n) (0x0418 + rx->rxn_reg_stride * 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(rx, n) (0x0420 + rx->rxn_reg_stride * 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_RXn_RX_PATH_SEC3(rx, n) (0x042c + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_PATH_SEC4 (0x0430)
+#define CDC_RX_RX0_RX_PATH_SEC7 (0x0434)
+#define CDC_RX_RXn_RX_PATH_SEC7(rx, n) \
+ (0x0434 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#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(rx, n) \
+ (0x0440 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#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)
+/* RX offsets prior to 2.5 codec version */
+#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)
+
+/* LPASS CODEC version 2.5 rx reg offsets */
+#define CDC_2_5_RX_RX1_RX_PATH_CTL (0x04c0)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG0 (0x04c4)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG1 (0x04c8)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG2 (0x04cC)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG3 (0x04d0)
+#define CDC_2_5_RX_RX1_RX_VOL_CTL (0x04d4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CTL (0x04d8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CFG (0x04dC)
+#define CDC_2_5_RX_RX1_RX_VOL_MIX_CTL (0x04e0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC1 (0x04e4)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC2 (0x04e8)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC3 (0x04eC)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC4 (0x04f0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC7 (0x04f4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0 (0x04f8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1 (0x04fC)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_CTL (0x0500)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1 (0x0504)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2 (0x0508)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3 (0x050C)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4 (0x0510)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5 (0x0514)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6 (0x0518)
+
+#define CDC_2_5_RX_RX2_RX_PATH_CTL (0x0580)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG0 (0x0584)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG1 (0x0588)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG2 (0x058C)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG3 (0x0590)
+#define CDC_2_5_RX_RX2_RX_VOL_CTL (0x0594)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CTL (0x0598)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CFG (0x059C)
+#define CDC_2_5_RX_RX2_RX_VOL_MIX_CTL (0x05a0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC0 (0x05a4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC1 (0x05a8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC2 (0x05aC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC3 (0x05b0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC4 (0x05b4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC5 (0x05b8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC6 (0x05bC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC7 (0x05c0)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0 (0x05c4)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1 (0x05c8)
+#define CDC_2_5_RX_RX2_RX_PATH_DSM_CTL (0x05cC)
+
+#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},
+ },
+};
+
+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},
+};
+
+/* Matches also rx_macro_mux_text */
+enum {
+ 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;
+ enum lpass_codec_version codec_version;
+ int rxn_reg_stride;
+ int rxn_reg_stride2;
+ 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",
+};
+
+/* Order must match RX_MACRO_MAX_DAIS enum (offset by 1) */
+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_2_5_int1_dem_inp_enum, CDC_2_5_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_PROC2, 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_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 const struct reg_default rx_2_5_defaults[] = {
+ { CDC_2_5_RX_RX1_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_2_5_RX_RX2_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC0, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC5, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC6, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_DSM_CTL, 0x00 },
+};
+
+static const struct reg_default rx_pre_2_5_defaults[] = {
+ { 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 },
+
+};
+
+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_pre_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_RX_RX1_RX_PATH_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX1_RX_VOL_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX1_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6:
+ case CDC_2_5_RX_RX2_RX_PATH_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX2_RX_VOL_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX2_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC5:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC6:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_DSM_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ return rx_pre_2_5_is_rw_register(dev, reg);
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ return rx_2_5_is_rw_register(dev, reg);
+ default:
+ break;
+ }
+
+ 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,
+ .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 rx_macro *rx = snd_soc_component_get_drvdata(component);
+ 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_RXn_RX_PATH_CFG1(rx, 0))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ else if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 1))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+
+ /* 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 const struct snd_kcontrol_new rx_2_5_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_2_5_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(rx, 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(rx, 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(const 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;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(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(rx, j);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 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);
+ }
+
+ 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, reg;
+
+ reg = CDC_RX_RXn_RX_PATH_CTL(rx, w->shift);
+ gain_reg = CDC_RX_RXn_RX_VOL_CTL(rx, 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(rx, 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(rx, 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(rx, 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(rx, 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(rx, 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_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x00);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Reset to default (HPF=ON) */
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 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_RXn_RX_PATH_CFG0(rx, 0),
+ 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_RXn_RX_PATH_CFG0(rx, 1),
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_AUX:
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ CDC_RX_RX2_DLY_Z_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ 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)
+{
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 hd2_scale_reg, hd2_enable_reg;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 0);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 1);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+ 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];
+ unsigned int dai_id;
+ 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:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (RX_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (rx->active_ch_cnt[dai_id]) {
+ clear_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]++;
+ 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_RXn_RX_PATH_CFG1(rx, 0),
+ 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_RXn_RX_PATH_CFG1(rx, 0),
+ 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(rx, interp_idx);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, interp_idx);
+ rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(rx, 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, mix_reg;
+
+ gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(rx, w->shift);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ 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(rx, w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(rx, 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(rx, 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_def_snd_controls[] = {
+ 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_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),
+};
+
+static const struct snd_kcontrol_new rx_macro_2_5_snd_controls[] = {
+
+ SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_2_5_RX_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_2_5_RX_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_2_5_RX_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_2_5_RX_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+};
+
+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_RX0 Mix Digital Volume", CDC_RX_RX0_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 (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX0 MUX")))
+ ec_tx = ((val & 0xf0) >> 0x4) - 1;
+ else if (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX1 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (!(snd_soc_dapm_widget_name_cmp(w, "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_2_5_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_2_5_int1_dem_inp_mux),
+};
+
+static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+};
+
+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_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 snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ const struct snd_soc_dapm_widget *widgets;
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls, num_widgets;
+ int ret;
+
+ snd_soc_component_init_regmap(component, rx->regmap);
+
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 0),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 1),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 2),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 0),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 1),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 2),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ controls = rx_macro_def_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_def_snd_controls);
+ widgets = rx_macro_def_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ controls = rx_macro_2_5_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_2_5_snd_controls);
+ widgets = rx_macro_2_5_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_2_5_dapm_widgets);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rx->component = component;
+
+ ret = snd_soc_add_component_controls(component, controls, num_controls);
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+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, def_count;
+
+ 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 dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n");
+
+ rx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(rx->dcodec))
+ return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n");
+
+ rx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(rx->mclk))
+ return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n");
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ rx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(rx->npl))
+ return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n");
+ }
+
+ rx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(rx->fsgen))
+ return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n");
+
+ rx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(rx->pds))
+ return PTR_ERR(rx->pds);
+
+ ret = devm_add_action_or_reset(dev, lpass_macro_pds_exit_action, rx->pds);
+ if (ret)
+ return ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rx->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ rx->rxn_reg_stride = 0x80;
+ rx->rxn_reg_stride2 = 0xc;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults));
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ rx->rxn_reg_stride = 0xc0;
+ rx->rxn_reg_stride2 = 0x0;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_2_5_defaults, sizeof(rx_2_5_defaults));
+ break;
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ rx->regmap = devm_regmap_init_mmio(dev, base, reg_config);
+ if (IS_ERR(rx->regmap))
+ return PTR_ERR(rx->regmap);
+
+ 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)
+ return ret;
+
+ 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);
+
+ 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);
+}
+
+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 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 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 = {
+ 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 = pm_ptr(&rx_macro_pm_ops),
+ },
+ .probe = rx_macro_probe,
+ .remove = 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..1da34cb3505f
--- /dev/null
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -0,0 +1,2542 @@
+// 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)
+/* Default divider for AMIC and DMIC clock: DIV2 */
+#define CDC_TX_SWR_MIC_CLK_DEFAULT 0
+#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_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_data {
+ unsigned int flags;
+ unsigned int ver;
+ const struct snd_soc_dapm_widget *extra_widgets;
+ size_t extra_widgets_num;
+ const struct snd_soc_dapm_route *extra_routes;
+ size_t extra_routes_num;
+};
+
+struct tx_macro {
+ struct device *dev;
+ const struct tx_macro_data *data;
+ 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;
+ 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:
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_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,
+ struct tx_macro *tx, 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) {
+ if (tx->data->ver > LPASS_VER_9_0_0)
+ return true;
+
+ /* else: LPASS <= v9.0.0 */
+ 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, tx, 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 void tx_macro_update_smic_sel_v9(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+}
+
+static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ if (widget->shift) {
+ /* MSM DMIC */
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ } else {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 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;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val >= e->items)
+ return -EINVAL;
+
+ 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;
+ default:
+ dev_err(component->dev, "Error in configuration!!\n");
+ return -EINVAL;
+ }
+
+ 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 (tx->data->ver <= LPASS_VER_9_0_0)
+ tx_macro_update_smic_sel_v9(component, widget, tx,
+ mic_sel_reg, val);
+ else
+ tx_macro_update_smic_sel_v9_2(component, widget, tx,
+ mic_sel_reg, val);
+ }
+
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+ }
+ 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, tx, 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, tx, 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, tx, 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, tx, 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(const 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 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 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 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 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 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 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 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 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 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 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"},
+};
+
+/* Controls and routes specific to LPASS <= v9.0.0 */
+static const char * const smic_mux_text_v9[] = {
+ "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_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9),
+
+ 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"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9[] = {
+ {"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", "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", "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", "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", "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", "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", "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", "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"},
+};
+
+/* Controls and routes specific to LPASS >= v9.2.0 */
+static const char * const smic_mux_text_v9_2[] = {
+ "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3",
+ "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7",
+ "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2),
+
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT0"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT1"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT2"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT3"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT4"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT5"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT6"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT7"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT8"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT9"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT10"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT11"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = {
+ {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+ {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"},
+ {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"},
+
+ {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+ {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+ {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+ {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+ {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+ {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+ {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+ {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"},
+};
+
+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_extend(struct snd_soc_component *comp)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ if (tx->data->extra_widgets_num) {
+ ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets,
+ tx->data->extra_widgets_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra widgets: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (tx->data->extra_routes_num) {
+ ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes,
+ tx->data->extra_routes_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra routes: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tx_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int i, ret;
+
+ ret = tx_macro_component_extend(comp);
+ if (ret)
+ return ret;
+
+ 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_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+
+ 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 = "TX-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;
+ struct tx_macro *tx;
+ void __iomem *base;
+ int ret, reg;
+
+ tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->data = device_get_match_data(dev);
+
+ tx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(tx->macro))
+ return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n");
+
+ tx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(tx->dcodec))
+ return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n");
+
+ tx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(tx->mclk))
+ return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n");
+
+ if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ tx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(tx->npl))
+ return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n");
+ }
+
+ tx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(tx->fsgen))
+ return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n");
+
+ 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 active_decimator default value */
+ tx->active_decimator[TX_MACRO_AIF1_CAP] = -1;
+ tx->active_decimator[TX_MACRO_AIF2_CAP] = -1;
+ tx->active_decimator[TX_MACRO_AIF3_CAP] = -1;
+
+ /* 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 */
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ 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);
+
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ 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 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 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 = {
+ RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
+};
+
+static const struct tx_macro_data lpass_ver_9 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9),
+ .extra_routes = tx_audio_map_v9,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9),
+};
+
+static const struct tx_macro_data lpass_ver_9_2 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_2_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_10_sm6115 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ .ver = LPASS_VER_10_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_11 = {
+ .flags = LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_11_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct of_device_id tx_macro_dt_match[] = {
+ {
+ /*
+ * The block is actually LPASS v9.4, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC7280 fine, so apparently the fix
+ * is not necessary.
+ */
+ .compatible = "qcom,sc7280-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm6115-lpass-tx-macro",
+ .data = &lpass_ver_10_sm6115,
+ }, {
+ .compatible = "qcom,sm8250-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm8450-lpass-tx-macro",
+ .data = &lpass_ver_9_2,
+ }, {
+ .compatible = "qcom,sm8550-lpass-tx-macro",
+ .data = &lpass_ver_11,
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-tx-macro",
+ /*
+ * The block is actually LPASS v9.3, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC8280xp fine, so apparently the
+ * fix is not necessary.
+ */
+ .data = &lpass_ver_9,
+ },
+ { }
+};
+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 = pm_ptr(&tx_macro_pm_ops),
+ },
+ .probe = tx_macro_probe,
+ .remove = 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..2e1b77973a3e
--- /dev/null
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -0,0 +1,1747 @@
+// 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_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;
+ bool has_npl_clk;
+
+ int dec_mode[VA_MACRO_NUM_DECIMATORS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ 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;
+ bool has_npl_clk;
+ int version;
+};
+
+static const struct va_macro_data sm8250_va_data = {
+ .has_swr_master = false,
+ .has_npl_clk = false,
+ .version = LPASS_CODEC_VERSION_1_0,
+};
+
+static const struct va_macro_data sm8450_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = true,
+};
+
+static const struct va_macro_data sm8550_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = false,
+};
+
+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(const 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;
+
+ if (va->has_swr_master) {
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ return 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);
+ if (va->has_swr_master)
+ clk_disable_unprepare(va->mclk);
+}
+
+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;
+
+ if (va->has_npl_clk)
+ parent = va->npl;
+
+ 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 void va_macro_set_lpass_codec_version(struct va_macro *va)
+{
+ int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0;
+ int version = LPASS_CODEC_VERSION_UNKNOWN;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &core_id_0);
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &core_id_1);
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_2, &core_id_2);
+
+ if ((core_id_0 == 0x01) && (core_id_1 == 0x0F))
+ version = LPASS_CODEC_VERSION_2_0;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && core_id_2 == 0x01)
+ version = LPASS_CODEC_VERSION_2_0;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0E))
+ version = LPASS_CODEC_VERSION_2_1;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x50 || core_id_2 == 0x51))
+ version = LPASS_CODEC_VERSION_2_5;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x60 || core_id_2 == 0x61))
+ version = LPASS_CODEC_VERSION_2_6;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x70 || core_id_2 == 0x71))
+ version = LPASS_CODEC_VERSION_2_7;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81))
+ version = LPASS_CODEC_VERSION_2_8;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x90 || core_id_2 == 0x91))
+ version = LPASS_CODEC_VERSION_2_9;
+
+ if (version == LPASS_CODEC_VERSION_UNKNOWN)
+ dev_warn(va->dev, "Unknown Codec version, ID: %02x / %02x / %02x\n",
+ core_id_0, core_id_1, core_id_2);
+
+ lpass_macro_set_codec_version(version);
+
+ dev_dbg(va->dev, "LPASS Codec Version %s\n", lpass_macro_get_codec_version_string(version));
+}
+
+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 dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n");
+
+ va->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(va->dcodec))
+ return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n");
+
+ va->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(va->mclk))
+ return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n");
+
+ 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;
+ va->has_npl_clk = data->has_npl_clk;
+
+ /* mclk rate */
+ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
+
+ if (va->has_npl_clk) {
+ va->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(va->npl)) {
+ ret = PTR_ERR(va->npl);
+ goto err;
+ }
+
+ clk_set_rate(va->npl, 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_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret)
+ goto err_npl;
+ }
+
+ /**
+ * old version of codecs do not have a reliable way to determine the
+ * version from registers, get them from soc specific data
+ */
+ if (data->version)
+ lpass_macro_set_codec_version(data->version);
+ else /* read version from register */
+ va_macro_set_lpass_codec_version(va);
+
+ 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:
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+err_npl:
+ 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);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+ clk_disable_unprepare(va->dcodec);
+ clk_disable_unprepare(va->macro);
+
+ lpass_macro_pds_exit(va->pds);
+}
+
+static int 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);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+
+ return 0;
+}
+
+static int 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;
+ }
+
+ if (va->has_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret) {
+ clk_disable_unprepare(va->mclk);
+ dev_err(va->dev, "unable to prepare npl\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 = {
+ 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,sm8550-lpass-va-macro", .data = &sm8550_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 = pm_ptr(&va_macro_pm_ops),
+ },
+ .probe = va_macro_probe,
+ .remove = 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..38faa9074ca3
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -0,0 +1,3048 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#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_INP_MUX_RX_INT0_CFG1 (0x0104)
+#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_TX_SPKR_PROT_PCM_RATE_16K 1
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K 2
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K 3
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K 4
+#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)
+/* CDC_WSA_COMPANDER1_CTLx and CDC_WSA_SOFTCLIPx differ per LPASS codec versions */
+#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)
+
+/* LPASS codec version <=2.4 register offsets */
+#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)
+
+/* LPASS codec version >=2.5 register offsets */
+#define CDC_WSA_TOP_FS_UNGATE (0x00AC)
+#define CDC_WSA_TOP_GRP_SEL (0x00B0)
+#define CDC_WSA_TOP_FS_UNGATE2 (0x00DC)
+#define CDC_2_5_WSA_COMPANDER0_CTL8 (0x05A0)
+#define CDC_2_5_WSA_COMPANDER0_CTL9 (0x05A4)
+#define CDC_2_5_WSA_COMPANDER0_CTL10 (0x05A8)
+#define CDC_2_5_WSA_COMPANDER0_CTL11 (0x05AC)
+#define CDC_2_5_WSA_COMPANDER0_CTL12 (0x05B0)
+#define CDC_2_5_WSA_COMPANDER0_CTL13 (0x05B4)
+#define CDC_2_5_WSA_COMPANDER0_CTL14 (0x05B8)
+#define CDC_2_5_WSA_COMPANDER0_CTL15 (0x05BC)
+#define CDC_2_5_WSA_COMPANDER0_CTL16 (0x05C0)
+#define CDC_2_5_WSA_COMPANDER0_CTL17 (0x05C4)
+#define CDC_2_5_WSA_COMPANDER0_CTL18 (0x05C8)
+#define CDC_2_5_WSA_COMPANDER0_CTL19 (0x05CC)
+#define CDC_2_5_WSA_COMPANDER1_CTL0 (0x05E0)
+#define CDC_2_5_WSA_COMPANDER1_CTL1 (0x05E4)
+#define CDC_2_5_WSA_COMPANDER1_CTL2 (0x05E8)
+#define CDC_2_5_WSA_COMPANDER1_CTL3 (0x05EC)
+#define CDC_2_5_WSA_COMPANDER1_CTL4 (0x05F0)
+#define CDC_2_5_WSA_COMPANDER1_CTL5 (0x05F4)
+#define CDC_2_5_WSA_COMPANDER1_CTL6 (0x05F8)
+#define CDC_2_5_WSA_COMPANDER1_CTL7 (0x05FC)
+#define CDC_2_5_WSA_COMPANDER1_CTL8 (0x0600)
+#define CDC_2_5_WSA_COMPANDER1_CTL9 (0x0604)
+#define CDC_2_5_WSA_COMPANDER1_CTL10 (0x0608)
+#define CDC_2_5_WSA_COMPANDER1_CTL11 (0x060C)
+#define CDC_2_5_WSA_COMPANDER1_CTL12 (0x0610)
+#define CDC_2_5_WSA_COMPANDER1_CTL13 (0x0614)
+#define CDC_2_5_WSA_COMPANDER1_CTL14 (0x0618)
+#define CDC_2_5_WSA_COMPANDER1_CTL15 (0x061C)
+#define CDC_2_5_WSA_COMPANDER1_CTL16 (0x0620)
+#define CDC_2_5_WSA_COMPANDER1_CTL17 (0x0624)
+#define CDC_2_5_WSA_COMPANDER1_CTL18 (0x0628)
+#define CDC_2_5_WSA_COMPANDER1_CTL19 (0x062C)
+#define CDC_2_5_WSA_SOFTCLIP0_CRC (0x0640)
+#define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644)
+#define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660)
+#define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664)
+
+#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_CFG_OFFSET 0x8
+#define WSA_MACRO_MUX_CFG1_OFFSET 0x4
+#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 */
+};
+
+/* Matches also rx_mux_text */
+enum {
+ WSA_MACRO_AIF1_PB,
+ WSA_MACRO_AIF_MIX1_PB,
+ WSA_MACRO_AIF_VI,
+ WSA_MACRO_AIF_ECHO,
+ WSA_MACRO_MAX_DAIS,
+};
+
+/**
+ * struct wsa_reg_layout - Register layout differences
+ * @rx_intx_1_mix_inp0_sel_mask: register mask for RX_INTX_1_MIX_INP0_SEL_MASK
+ * @rx_intx_1_mix_inp1_sel_mask: register mask for RX_INTX_1_MIX_INP1_SEL_MASK
+ * @rx_intx_1_mix_inp2_sel_mask: register mask for RX_INTX_1_MIX_INP2_SEL_MASK
+ * @rx_intx_2_sel_mask: register mask for RX_INTX_2_SEL_MASK
+ * @compander1_reg_offset: offset between compander registers (compander1 - compander0)
+ * @softclip0_reg_base: base address of softclip0 register
+ * @softclip1_reg_offset: offset between compander registers (softclip1 - softclip0)
+ */
+struct wsa_reg_layout {
+ unsigned int rx_intx_1_mix_inp0_sel_mask;
+ unsigned int rx_intx_1_mix_inp1_sel_mask;
+ unsigned int rx_intx_1_mix_inp2_sel_mask;
+ unsigned int rx_intx_2_sel_mask;
+ unsigned int compander1_reg_offset;
+ unsigned int softclip0_reg_base;
+ unsigned int softclip1_reg_offset;
+};
+
+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;
+ enum lpass_codec_version codec_version;
+ const struct wsa_reg_layout *reg_layout;
+ 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;
+ u32 pcm_rate_vi;
+ 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 struct wsa_reg_layout wsa_codec_v2_1 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(5, 3),
+ .rx_intx_2_sel_mask = GENMASK(2, 0),
+ .compander1_reg_offset = 0x40,
+ .softclip0_reg_base = 0x600,
+ .softclip1_reg_offset = 0x40,
+};
+
+static const struct wsa_reg_layout wsa_codec_v2_5 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(3, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(7, 4),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(7, 4),
+ .rx_intx_2_sel_mask = GENMASK(3, 0),
+ .compander1_reg_offset = 0x60,
+ .softclip0_reg_base = 0x640,
+ .softclip1_reg_offset = 0x20,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char *const rx_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1"
+};
+
+static const char *const rx_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8", "DEC0", "DEC1"
+};
+
+static const char *const rx_mix_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1"
+};
+
+static const char *const rx_mix_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8"
+};
+
+static const char *const rx_mix_ec_text[] = {
+ "ZERO", "RX_MIX_TX0", "RX_MIX_TX1"
+};
+
+/* Order must match WSA_MACRO_MAX_DAIS enum (offset by 1) */
+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_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+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_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_5);
+
+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_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_5);
+
+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_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_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 const struct reg_default wsa_defaults_v2_1[] = {
+ { 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_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},
+};
+
+static const struct reg_default wsa_defaults_v2_5[] = {
+ { CDC_WSA_TOP_FS_UNGATE, 0xFF},
+ { CDC_WSA_TOP_GRP_SEL, 0x08},
+ { CDC_WSA_TOP_FS_UNGATE2, 0x1F},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_2_5_WSA_COMPANDER0_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER0_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER0_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER0_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER0_CTL19, 0x16},
+ { CDC_2_5_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_2_5_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_2_5_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_2_5_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_2_5_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_2_5_WSA_COMPANDER1_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER1_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER1_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER1_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER1_CTL19, 0x16},
+ { CDC_2_5_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_2_5_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+};
+
+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_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_TOP_FS_UNGATE:
+ case CDC_WSA_TOP_GRP_SEL:
+ case CDC_WSA_TOP_FS_UNGATE2:
+ case CDC_2_5_WSA_COMPANDER0_CTL8:
+ case CDC_2_5_WSA_COMPANDER0_CTL9:
+ case CDC_2_5_WSA_COMPANDER0_CTL10:
+ case CDC_2_5_WSA_COMPANDER0_CTL11:
+ case CDC_2_5_WSA_COMPANDER0_CTL12:
+ case CDC_2_5_WSA_COMPANDER0_CTL13:
+ case CDC_2_5_WSA_COMPANDER0_CTL14:
+ case CDC_2_5_WSA_COMPANDER0_CTL15:
+ case CDC_2_5_WSA_COMPANDER0_CTL16:
+ case CDC_2_5_WSA_COMPANDER0_CTL17:
+ case CDC_2_5_WSA_COMPANDER0_CTL18:
+ case CDC_2_5_WSA_COMPANDER0_CTL19:
+ case CDC_2_5_WSA_COMPANDER1_CTL0:
+ case CDC_2_5_WSA_COMPANDER1_CTL1:
+ case CDC_2_5_WSA_COMPANDER1_CTL2:
+ case CDC_2_5_WSA_COMPANDER1_CTL3:
+ case CDC_2_5_WSA_COMPANDER1_CTL4:
+ case CDC_2_5_WSA_COMPANDER1_CTL5:
+ case CDC_2_5_WSA_COMPANDER1_CTL7:
+ case CDC_2_5_WSA_COMPANDER1_CTL8:
+ case CDC_2_5_WSA_COMPANDER1_CTL9:
+ case CDC_2_5_WSA_COMPANDER1_CTL10:
+ case CDC_2_5_WSA_COMPANDER1_CTL11:
+ case CDC_2_5_WSA_COMPANDER1_CTL12:
+ case CDC_2_5_WSA_COMPANDER1_CTL13:
+ case CDC_2_5_WSA_COMPANDER1_CTL14:
+ case CDC_2_5_WSA_COMPANDER1_CTL15:
+ case CDC_2_5_WSA_COMPANDER1_CTL16:
+ case CDC_2_5_WSA_COMPANDER1_CTL17:
+ case CDC_2_5_WSA_COMPANDER1_CTL18:
+ case CDC_2_5_WSA_COMPANDER1_CTL19:
+ case CDC_2_5_WSA_SOFTCLIP0_CRC:
+ case CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_2_5_WSA_SOFTCLIP1_CRC:
+ case CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_rw_register_v2_5(dev, reg);
+
+ return wsa_is_rw_register_v2_1(dev, reg);
+}
+
+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_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_readable_register_v2_5(dev, reg);
+
+ return wsa_is_readable_register_v2_1(dev, reg);
+}
+
+static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ /* 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_volatile_register_v2_5(dev, reg);
+
+ return wsa_is_volatile_register_v2_1(dev, reg);
+}
+
+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 and .num_reg_defaults set in probe() */
+ .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,
+ wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(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;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (dai->id == WSA_MACRO_AIF_VI)
+ wsa->pcm_rate_vi = params_rate(params);
+
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_get_channel_map(const 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 void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *component, bool enable,
+ u32 tx_reg0, u32 tx_reg1, u32 val)
+{
+ if (enable) {
+ /* 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,
+ val);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ val);
+ 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);
+ } else {
+ 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);
+ }
+}
+
+static void wsa_macro_enable_disable_vi_feedback(struct snd_soc_component *component,
+ bool enable, u32 rate)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX0_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX1_SPKR_PROT_PATH_CTL, rate);
+
+ if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX2_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX3_SPKR_PROT_PATH_CTL, rate);
+}
+
+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 rate_val;
+
+ switch (wsa->pcm_rate_vi) {
+ case 8000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ case 16000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_16K;
+ break;
+ case 24000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K;
+ break;
+ case 32000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K;
+ break;
+ case 48000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K;
+ break;
+ default:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, true, rate_val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, false, rate_val);
+ 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->reg_layout->compander1_reg_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 = wsa->reg_layout->softclip0_reg_base +
+ (path * wsa->reg_layout->softclip1_reg_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->reg_layout->softclip1_reg_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)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ 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,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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;
+ 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);
+ }
+ val = snd_soc_component_read(component, gain_reg);
+ 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);
+ }
+ 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 (!snd_soc_dapm_widget_name_cmp(w, "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 (!snd_soc_dapm_widget_name_cmp(w, "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;
+ } else {
+ dev_warn(component->dev, "Incorrect widget name in the driver\n");
+ return -EINVAL;
+ }
+
+ 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;
+ unsigned int dai_id;
+ 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:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (wsa->active_ch_cnt[dai_id]) {
+ clear_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ 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;
+ u32 dai_id = widget->shift;
+
+ if (enable) {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ !test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ !test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ } else {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ }
+ 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_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_widget wsa_macro_dapm_widgets_v2_1[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0,
+ 0, &rx0_mix_mux_v2_1, 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_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1,
+ 0, &rx1_mix_mux_v2_1, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_5[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0,
+ 0, &rx0_mix_mux_v2_5, 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_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1,
+ 0, &rx1_mix_mux_v2_5, wsa_macro_enable_mix_path,
+ 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 snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp);
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned int num_widgets;
+
+ 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);
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ widgets = wsa_macro_dapm_widgets_v2_1;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ widgets = wsa_macro_dapm_widgets_v2_5;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+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, def_count;
+
+ 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 dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n");
+
+ wsa->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(wsa->dcodec))
+ return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n");
+
+ wsa->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wsa->mclk))
+ return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n");
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ wsa->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(wsa->npl))
+ return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n");
+ }
+
+ wsa->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(wsa->fsgen))
+ return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n");
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ wsa->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ wsa->reg_layout = &wsa_codec_v2_1;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1));
+ break;
+
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ wsa->reg_layout = &wsa_codec_v2_5;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_5, sizeof(wsa_defaults_v2_5));
+ break;
+
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", wsa->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ wsa->regmap = devm_regmap_init_mmio(dev, base, reg_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 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 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 = {
+ 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 = pm_ptr(&wsa_macro_pm_ops),
+ },
+ .probe = wsa_macro_probe,
+ .remove = 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..bc3470cf2c54
--- /dev/null
+++ b/sound/soc/codecs/madera.c
@@ -0,0 +1,4812 @@
+// 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 <linux/string_choices.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 = 6000;
+ break;
+ default:
+ out_up_delay = 17000;
+ 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) {
+ fsleep(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 += 1000;
+ 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) {
+ fsleep(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_CBP_CFP) {
+ 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_CBP_CFP) {
+ 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_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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_SRC_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",
+ str_enabled_disabled(already_enabled));
+
+ 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",
+ str_enabled_disabled(already_enabled));
+
+ /* 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",
+ str_enabled_disabled(already_enabled));
+
+ /* 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
index 7017c0389e73..8af3c7e5317f 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -1,18 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX9768 AMP driver
*
* Copyright (C) 2011, 2012 by Wolfram Sang, Pengutronix e.K.
- *
- * This program is free software; 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -30,8 +27,8 @@
struct max9768 {
struct regmap *regmap;
- int mute_gpio;
- int shdn_gpio;
+ struct gpio_desc *mute;
+ struct gpio_desc *shdn;
u32 flags;
};
@@ -45,7 +42,7 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
{
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);
+ int val = gpiod_get_value_cansleep(max9768->mute);
ucontrol->value.integer.value[0] = !val;
@@ -57,10 +54,17 @@ static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+ bool val = !ucontrol->value.integer.value[0];
+ int ret;
- gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
+ if (val != gpiod_get_value_cansleep(max9768->mute))
+ ret = 1;
+ else
+ ret = 0;
- return 0;
+ gpiod_set_value_cansleep(max9768->mute, val);
+
+ return ret;
}
static const DECLARE_TLV_DB_RANGE(volume_tlv,
@@ -141,7 +145,7 @@ static int max9768_probe(struct snd_soc_component *component)
return ret;
}
- if (gpio_is_valid(max9768->mute_gpio)) {
+ if (max9768->mute) {
ret = snd_soc_add_component_controls(component, max9768_mute,
ARRAY_SIZE(max9768_mute));
if (ret)
@@ -170,33 +174,33 @@ static const struct regmap_config max9768_i2c_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9768_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+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;
-
+ /* Mute on powerup to avoid clicks */
+ max9768->mute = devm_gpiod_get_optional(&client->dev,
+ "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->mute))
+ return PTR_ERR(max9768->mute);
+ gpiod_set_consumer_name(max9768->mute, "MAX9768 Mute");
+
+ /* Activate chip by releasing shutdown, enables I2C */
+ max9768->shdn = devm_gpiod_get_optional(&client->dev,
+ "shutdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->shdn))
+ return PTR_ERR(max9768->shdn);
+ gpiod_set_consumer_name(max9768->shdn, "MAX9768 Shutdown");
+
+ if (pdata)
max9768->flags = pdata->flags;
- } else {
- max9768->shdn_gpio = -EINVAL;
- max9768->mute_gpio = -EINVAL;
- }
i2c_set_clientdata(client, max9768);
@@ -209,7 +213,7 @@ static int max9768_i2c_probe(struct i2c_client *client,
}
static const struct i2c_device_id max9768_i2c_id[] = {
- { "max9768", 0 },
+ { "max9768" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
@@ -223,6 +227,6 @@ static struct i2c_driver max9768_i2c_driver = {
};
module_i2c_driver(max9768_i2c_driver);
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+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 f0bb830874e5..37e61d8d4be6 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>
@@ -16,6 +13,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -42,6 +40,8 @@ struct max98088_priv {
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;
@@ -307,27 +307,27 @@ static const struct regmap_config max98088_regmap = {
/*
* 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;
+ 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 ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+ /* Load the base register address */
+ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
- /* Add the band address offset, note adjustment for word address */
- eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98088_COEFS_PER_BAND << 1);
- /* 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]));
- }
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i]));
+ }
}
/*
@@ -380,12 +380,12 @@ static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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;
@@ -394,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_soc_kcontrol_codec(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;
@@ -404,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_soc_kcontrol_codec(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;
@@ -418,8 +418,8 @@ 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_soc_kcontrol_codec(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;
@@ -474,15 +474,24 @@ 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),
+ SOC_SINGLE("DACL Volume", M98088_REG_2F_LVL_DAI1_PLAY, 0, 15, 1),
+ SOC_SINGLE("DACR Volume", M98088_REG_31_LVL_DAI2_PLAY, 0, 15, 1),
+
SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+ SOC_SINGLE("Left HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 4, 1, 0),
+ SOC_SINGLE("Right HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 5, 1, 0),
+
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
@@ -512,10 +521,8 @@ 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, 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("Left DAC Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -526,10 +533,8 @@ static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
/* Right speaker mixer switch */
static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
@@ -540,10 +545,8 @@ 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, 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("Left DAC Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -554,10 +557,8 @@ static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
/* Right headphone mixer switch */
static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
@@ -568,10 +569,8 @@ 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, 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("Left DAC Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -582,10 +581,8 @@ static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
/* Right earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
@@ -617,21 +614,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 = snd_soc_dapm_to_codec(w->dapm);
- 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;
@@ -647,8 +644,8 @@ 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 = snd_soc_dapm_to_codec(w->dapm);
- 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;
if (WARN_ON(!(channel == 1 || channel == 2)))
@@ -668,13 +665,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;
@@ -714,13 +711,9 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
- SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
- M98088_REG_4D_PWR_EN_OUT, 1, 0),
- SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
- M98088_REG_4D_PWR_EN_OUT, 0, 0),
- SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 1, 0),
- SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 0, 0),
SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
@@ -816,10 +809,8 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
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"},
- {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left HP Mixer", "Left DAC Switch", "DACL"},
+ {"Left HP Mixer", "Right DAC Switch", "DACR"},
{"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -828,10 +819,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left HP Mixer", "INB2 Switch", "INB2 Input"},
/* Right headphone output mixer */
- {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right HP Mixer", "Left DAC2 Switch", "DACL2" },
- {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right HP Mixer", "Left DAC Switch", "DACL"},
+ {"Right HP Mixer", "Right DAC Switch", "DACR"},
{"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -840,10 +829,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Right HP Mixer", "INB2 Switch", "INB2 Input"},
/* Left speaker output mixer */
- {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
- {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
- {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left SPK Mixer", "Left DAC Switch", "DACL"},
+ {"Left SPK Mixer", "Right DAC Switch", "DACR"},
{"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -852,10 +839,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
/* Right speaker output mixer */
- {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
- {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right SPK Mixer", "Left DAC Switch", "DACL"},
+ {"Right SPK Mixer", "Right DAC Switch", "DACR"},
{"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -864,10 +849,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
/* Earpiece/Receiver output mixer */
- {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
- {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
- {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left REC Mixer", "Left DAC Switch", "DACL"},
+ {"Left REC Mixer", "Right DAC Switch", "DACR"},
{"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -876,10 +859,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left REC Mixer", "INB2 Switch", "INB2 Input"},
/* Earpiece/Receiver output mixer */
- {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
- {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right REC Mixer", "Left DAC Switch", "DACL"},
+ {"Right REC Mixer", "Right DAC Switch", "DACR"},
{"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -963,8 +944,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;
@@ -976,51 +957,54 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 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;
@@ -1030,8 +1014,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;
@@ -1043,51 +1027,54 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 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;
@@ -1096,30 +1083,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;
+ 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);
}
@@ -1132,8 +1126,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;
@@ -1143,22 +1137,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;
}
@@ -1188,14 +1180,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;
@@ -1204,8 +1196,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;
@@ -1214,22 +1206,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;
}
@@ -1259,20 +1249,21 @@ 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 int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai1_mute(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 reg;
if (mute)
@@ -1280,14 +1271,15 @@ static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ snd_soc_component_update_bits(component, M98088_REG_2F_LVL_DAI1_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
-static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai2_mute(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 reg;
if (mute)
@@ -1295,33 +1287,51 @@ static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ 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)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = 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(max98088->mclk)) {
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(max98088->mclk);
+ } else {
+ ret = clk_prepare_enable(max98088->mclk);
+ if (ret)
+ return ret;
+ }
+ }
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(max98088->regmap);
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, 0);
regcache_mark_dirty(max98088->regmap);
break;
@@ -1336,14 +1346,16 @@ 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,
- .digital_mute = max98088_dai1_digital_mute,
+ .mute_stream = max98088_dai1_mute,
+ .no_capture_mute = 1,
};
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,
- .digital_mute = max98088_dai2_digital_mute,
+ .mute_stream = max98088_dai2_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -1380,22 +1392,19 @@ static struct snd_soc_dai_driver max98088_dai[] = {
static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"};
-static int max98088_get_channel(struct snd_soc_codec *codec, const char *name)
+static int max98088_get_channel(struct snd_soc_component *component, const char *name)
{
- int i;
+ int ret;
- for (i = 0; i < ARRAY_SIZE(eq_mode_name); i++)
- if (strcmp(name, eq_mode_name[i]) == 0)
- return i;
-
- /* Shouldn't happen */
- dev_err(codec->dev, "Bad EQ channel name '%s'\n", name);
- return -EINVAL;
+ 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;
@@ -1420,29 +1429,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;
@@ -1467,34 +1476,34 @@ 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_soc_kcontrol_codec(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(codec, kcontrol->id.name);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
int sel = ucontrol->value.enumerated.item[0];
@@ -1510,10 +1519,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;
}
@@ -1523,9 +1532,9 @@ 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_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- int channel = max98088_get_channel(codec, 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)
@@ -1536,9 +1545,9 @@ static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
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;
@@ -1591,19 +1600,19 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
max98088->eq_enum.texts = max98088->eq_texts;
max98088->eq_enum.items = max98088->eq_textcnt;
- ret = snd_soc_add_codec_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;
}
@@ -1616,21 +1625,21 @@ 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);
+ max98088_handle_eq_pdata(component);
}
-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;
@@ -1658,106 +1667,109 @@ 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 - 0x40 + 'A');
+ dev_info(component->dev, "revision %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+ 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_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);
kfree(max98088->eq_texts);
-
- return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_max98088 = {
- .probe = max98088_probe,
- .remove = max98088_remove,
- .set_bias_level = max98088_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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;
- max98088 = devm_kzalloc(&i2c->dev, 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->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
- if (IS_ERR(max98088->regmap))
- return PTR_ERR(max98088->regmap);
+ max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
+ if (IS_ERR(max98088->regmap))
+ return PTR_ERR(max98088->regmap);
- max98088->devtype = id->driver_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);
- i2c_set_clientdata(i2c, max98088);
- max98088->pdata = i2c->dev.platform_data;
+ max98088->devtype = (uintptr_t)i2c_get_match_data(i2c);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98088, &max98088_dai[0], 2);
- return ret;
-}
+ i2c_set_clientdata(i2c, max98088);
+ max98088->pdata = i2c->dev.platform_data;
-static int max98088_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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",
+ .of_match_table = of_match_ptr(max98088_of_match),
},
- .probe = max98088_i2c_probe,
- .remove = max98088_i2c_remove,
+ .probe = max98088_i2c_probe,
.id_table = max98088_i2c_id,
};
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index efa39bf46742..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
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index f5075d1f79e6..22177c1ce160 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98090.c -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 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/delay.h>
@@ -13,7 +10,6 @@
#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>
@@ -284,7 +280,7 @@ static int max98090_reset(struct max98090_priv *max98090)
ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
M98090_SWRESET_MASK);
if (ret < 0) {
- dev_err(max98090->codec->dev,
+ dev_err(max98090->component->dev,
"Failed to reset codec: %d\n", ret);
return ret;
}
@@ -314,9 +310,6 @@ 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_sidetone_tlv, -6050, 200, 0);
-
-static const DECLARE_TLV_DB_SCALE(max98090_alc_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);
@@ -354,12 +347,12 @@ static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv,
static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, mc->reg);
+ unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int *select;
switch (mc->reg) {
@@ -394,14 +387,16 @@ static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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 sel = ucontrol->value.integer.value[0];
- unsigned int val = snd_soc_read(codec, mc->reg);
+ 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:
@@ -419,6 +414,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
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 */
@@ -429,11 +429,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
sel = val;
}
- snd_soc_update_bits(codec, mc->reg,
+ snd_soc_component_update_bits(component, mc->reg,
mask << mc->shift,
sel << mc->shift);
- return 0;
+ return change;
}
static const char *max98090_perf_pwr_text[] =
@@ -733,10 +733,10 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, w->reg);
+ 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;
@@ -768,10 +768,10 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
}
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA1EN_MASK,
val << M98090_MIC_PA1EN_SHIFT);
else
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA2EN_MASK,
val << M98090_MIC_PA2EN_SHIFT);
return 0;
@@ -780,8 +780,8 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -817,18 +817,6 @@ 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);
-static const char *max98090_micpre_text[] = { "Off", "On" };
-
-static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum,
- M98090_REG_MIC1_INPUT_LEVEL,
- M98090_MIC_PA1EN_SHIFT,
- max98090_micpre_text);
-
-static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum,
- M98090_REG_MIC2_INPUT_LEVEL,
- M98090_MIC_PA2EN_SHIFT,
- max98090_micpre_text);
-
/* LINEA mixer switch */
static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG,
@@ -1209,14 +1197,14 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_rcv_mixer_controls[0],
ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
- SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
- M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux),
+ SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_linmod_mux),
- SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux),
+ SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhplsel_mux),
- SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_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),
@@ -1441,16 +1429,16 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
{"DMIC4", NULL, "AHPF"},
};
-static int max98090_add_widgets(struct snd_soc_codec *codec)
+static int max98090_add_widgets(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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_codec_controls(codec, max98090_snd_controls,
+ snd_soc_add_component_controls(component, max98090_snd_controls,
ARRAY_SIZE(max98090_snd_controls));
if (max98090->devtype == MAX98091) {
- snd_soc_add_codec_controls(codec, max98091_snd_controls,
+ snd_soc_add_component_controls(component, max98091_snd_controls,
ARRAY_SIZE(max98091_snd_controls));
}
@@ -1497,24 +1485,24 @@ static const unsigned long long mi_value[] = {
8125, 1625, 1500, 25
};
-static void max98090_configure_bclk(struct snd_soc_codec *codec)
+static void max98090_configure_bclk(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
unsigned long long ni;
int i;
if (!max98090->sysclk) {
- dev_err(codec->dev, "No SYSCLK configured\n");
+ dev_err(component->dev, "No SYSCLK configured\n");
return;
}
if (!max98090->bclk || !max98090->lrclk) {
- dev_err(codec->dev, "No audio clocks configured\n");
+ dev_err(component->dev, "No audio clocks configured\n");
return;
}
/* Skip configuration when operating as slave */
- if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) &
+ if (!(snd_soc_component_read(component, M98090_REG_MASTER_MODE) &
M98090_MAS_MASK)) {
return;
}
@@ -1523,14 +1511,14 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
if ((pclk_rates[i] == max98090->sysclk) &&
(lrclk_rates[i] == max98090->lrclk)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Found supported PCLK to LRCLK rates 0x%x\n",
i + 0x8);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK,
(i + 0x8) << M98090_FREQ_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
return;
}
@@ -1540,24 +1528,24 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
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(codec->dev,
+ dev_dbg(component->dev,
"Found user supported PCLK to LRCLK rates\n");
- dev_dbg(codec->dev, "i %d ni %lld mi %lld\n",
+ dev_dbg(component->dev, "i %d ni %lld mi %lld\n",
i, ni_value[i], mi_value[i]);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK,
1 << M98090_USE_M1_SHIFT);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
(ni_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB,
ni_value[i] & 0xFF);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_MSB,
(mi_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_LSB,
mi_value[i] & 0xFF);
return;
@@ -1567,9 +1555,9 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
/*
* Calculate based on MI = 65536 (not as good as either method above)
*/
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
/*
@@ -1580,20 +1568,20 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)max98090->lrclk;
do_div(ni, (unsigned long long int)max98090->sysclk);
- dev_info(codec->dev, "No better method found\n");
- dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ 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_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
+ 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_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct max98090_cdata *cdata;
- u8 regval;
+ u8 regval, tdm_regval;
max98090->dai_fmt = fmt;
cdata = &max98090->dai[0];
@@ -1602,19 +1590,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
cdata->fmt = fmt;
regval = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Set to slave mode PLL - MAS mode off */
- snd_soc_write(codec,
+ tdm_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_write(codec,
+ snd_soc_component_write(component,
M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
max98090->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
if (max98090->tdm_slots == 4) {
/* TDM */
regval |= M98090_MAS_MASK |
@@ -1630,13 +1619,11 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
}
max98090->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
- snd_soc_write(codec, M98090_REG_MASTER_MODE, regval);
+ snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
regval = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1649,9 +1636,10 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_RJ_MASK;
break;
case SND_SOC_DAIFMT_DSP_A:
- /* Not supported mode */
+ tdm_regval |= M98090_TDM_MASK;
+ break;
default:
- dev_err(codec->dev, "DAI format unsupported");
+ dev_err(component->dev, "DAI format unsupported");
return -EINVAL;
}
@@ -1668,7 +1656,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_BCI_MASK|M98090_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(component->dev, "DAI invert mode unsupported");
return -EINVAL;
}
@@ -1678,11 +1666,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
* seen for the case of TDM mode. The remaining cases have
* normal logic.
*/
- if (max98090->tdm_slots > 1)
+ if (tdm_regval)
regval ^= M98090_BCI_MASK;
- snd_soc_write(codec,
+ snd_soc_component_write(component,
M98090_REG_INTERFACE_FORMAT, regval);
+
+ regval = 0;
+ if (tdm_regval)
+ regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT |
+ max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT |
+ 0 << M98090_TDM_SLOTDLY_SHIFT;
+
+ snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval);
+ snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval);
}
return 0;
@@ -1691,43 +1688,32 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct max98090_cdata *cdata;
- cdata = &max98090->dai[0];
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
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_write(codec, M98090_REG_TDM_FORMAT,
- 0 << M98090_TDM_SLOTL_SHIFT |
- 1 << M98090_TDM_SLOTR_SHIFT |
- 0 << M98090_TDM_SLOTDLY_SHIFT);
-
- /* FSW TDM */
- snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL,
- M98090_TDM_MASK,
- M98090_TDM_MASK);
- }
+ if (slot_width != 16)
+ return -EINVAL;
- /*
- * Normally advisable to set TDM first, but this permits either order
- */
- cdata->fmt = 0;
- max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+ if (rx_mask != tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask)
+ return -EINVAL;
+
+ max98090->tdm_slots = slots;
+ max98090->tdm_lslot = ffs(rx_mask) - 1;
+ max98090->tdm_rslot = fls(rx_mask) - 1;
return 0;
}
-static int max98090_set_bias_level(struct snd_soc_codec *codec,
+static int max98090_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1745,7 +1731,7 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
if (IS_ERR(max98090->mclk))
break;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(max98090->mclk);
} else {
ret = clk_prepare_enable(max98090->mclk);
@@ -1755,10 +1741,10 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98090->regmap);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -1767,7 +1753,7 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
/* Set internal pull-up to lowest power mode */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
regcache_mark_dirty(max98090->regmap);
break;
@@ -1850,7 +1836,7 @@ static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
static int max98090_find_divisor(int target_freq, int pclk)
{
int current_diff = INT_MAX;
- int test_diff = INT_MAX;
+ int test_diff;
int divisor_index = 0;
int i;
@@ -1924,12 +1910,27 @@ static int max98090_configure_dmic(struct max98090_priv *max98090,
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_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -1941,7 +1942,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
+ snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
M98090_WS_MASK, 0);
break;
default:
@@ -1949,24 +1950,24 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
}
if (max98090->master)
- max98090_configure_bclk(codec);
+ max98090_configure_bclk(component);
cdata->rate = max98090->lrclk;
/* Update filter mode */
if (max98090->lrclk < 24000)
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_MODE_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ 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_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_DHF_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ 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,
@@ -1981,8 +1982,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1999,19 +2000,19 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq <= 20000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
max98090->pclk = freq;
} else if ((freq > 20000000) && (freq <= 40000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
max98090->pclk = freq >> 1;
} else if ((freq > 40000000) && (freq <= 60000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
max98090->pclk = freq >> 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -2020,13 +2021,14 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98090_dai_mute(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 regval;
regval = mute ? M98090_DVM_MASK : 0;
- snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL,
+ snd_soc_component_update_bits(component, M98090_REG_DAI_PLAYBACK_LEVEL,
M98090_DVM_MASK, regval);
return 0;
@@ -2035,14 +2037,14 @@ static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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 && dai->active == 1)
+ 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));
@@ -2050,7 +2052,7 @@ static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!max98090->master && dai->active == 1)
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
schedule_work(&max98090->pll_det_disable_work);
break;
default:
@@ -2065,7 +2067,7 @@ 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_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
unsigned int status, mask;
/*
@@ -2088,7 +2090,7 @@ static void max98090_pll_det_enable_work(struct work_struct *work)
msecs_to_jiffies(100));
/* Enable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK,
1 << M98090_IULK_SHIFT);
}
@@ -2097,35 +2099,49 @@ 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_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
/* Disable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK, 0);
}
-static void max98090_pll_work(struct work_struct *work)
+static void max98090_pll_work(struct max98090_priv *max98090)
{
- struct max98090_priv *max98090 =
- container_of(work, struct max98090_priv, pll_work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
+ unsigned int pll;
+ int i;
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
return;
- dev_info_ratelimited(codec->dev, "PLL unlocked\n");
+ 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_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, 0);
- msleep(10);
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
- /* Give PLL time to lock */
- msleep(10);
+ 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)
@@ -2133,7 +2149,7 @@ 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_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
int status = 0;
int reg;
@@ -2141,25 +2157,23 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
/* Strong pull up allows mic detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, 0);
msleep(50);
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ snd_soc_component_read(component, M98090_REG_JACK_STATUS);
/* Weak pull up allows only insertion detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
- } else {
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
}
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ 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(codec->dev, "No Headset Detected\n");
+ dev_dbg(component->dev, "No Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
@@ -2171,7 +2185,7 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state ==
M98090_JACK_STATE_HEADSET) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Headset Button Down Detected\n");
/*
@@ -2188,7 +2202,7 @@ static void max98090_jack_work(struct work_struct *work)
/* Line is reported as Headphone */
/* Nokia Headset is reported as Headphone */
/* Mono Headphone is reported as Headphone */
- dev_dbg(codec->dev, "Headphone Detected\n");
+ dev_dbg(component->dev, "Headphone Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
@@ -2197,7 +2211,7 @@ static void max98090_jack_work(struct work_struct *work)
break;
case M98090_JKSNS_MASK:
- dev_dbg(codec->dev, "Headset Detected\n");
+ dev_dbg(component->dev, "Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADSET;
@@ -2206,7 +2220,7 @@ static void max98090_jack_work(struct work_struct *work)
break;
default:
- dev_dbg(codec->dev, "Unrecognized Jack Status\n");
+ dev_dbg(component->dev, "Unrecognized Jack Status\n");
break;
}
@@ -2217,21 +2231,21 @@ static void max98090_jack_work(struct work_struct *work)
static irqreturn_t max98090_interrupt(int irq, void *data)
{
struct max98090_priv *max98090 = data;
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
int ret;
unsigned int mask;
unsigned int active;
/* Treat interrupt before codec is initialized as spurious */
- if (codec == NULL)
+ if (component == NULL)
return IRQ_NONE;
- dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
+ dev_dbg(component->dev, "***** max98090_interrupt *****\n");
ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_INTERRUPT_S: %d\n",
ret);
return IRQ_NONE;
@@ -2240,13 +2254,13 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_DEVICE_STATUS: %d\n",
ret);
return IRQ_NONE;
}
- dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
+ dev_dbg(component->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
active, mask, active & mask);
active &= mask;
@@ -2255,20 +2269,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
return IRQ_NONE;
if (active & M98090_CLD_MASK)
- dev_err(codec->dev, "M98090_CLD_MASK\n");
+ dev_err(component->dev, "M98090_CLD_MASK\n");
if (active & M98090_SLD_MASK)
- dev_dbg(codec->dev, "M98090_SLD_MASK\n");
+ dev_dbg(component->dev, "M98090_SLD_MASK\n");
if (active & M98090_ULK_MASK) {
- dev_dbg(codec->dev, "M98090_ULK_MASK\n");
- schedule_work(&max98090->pll_work);
+ dev_dbg(component->dev, "M98090_ULK_MASK\n");
+ max98090_pll_work(max98090);
}
if (active & M98090_JDET_MASK) {
- dev_dbg(codec->dev, "M98090_JDET_MASK\n");
+ dev_dbg(component->dev, "M98090_JDET_MASK\n");
- pm_wakeup_event(codec->dev, 100);
+ pm_wakeup_event(component->dev, 100);
queue_delayed_work(system_power_efficient_wq,
&max98090->jack_work,
@@ -2276,10 +2290,10 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
}
if (active & M98090_DRCACT_MASK)
- dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
+ dev_dbg(component->dev, "M98090_DRCACT_MASK\n");
if (active & M98090_DRCCLP_MASK)
- dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
+ dev_err(component->dev, "M98090_DRCCLP_MASK\n");
return IRQ_HANDLED;
}
@@ -2287,7 +2301,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
/**
* max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
*
- * @codec: MAX98090 codec
+ * @component: MAX98090 component
* @jack: jack to report detection events on
*
* Enable microphone detection via IRQ on the MAX98090. If GPIOs are
@@ -2297,20 +2311,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
*
* If no jack is supplied detection will be disabled.
*/
-int max98090_mic_detect(struct snd_soc_codec *codec,
+int max98090_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "max98090_mic_detect\n");
+ dev_dbg(component->dev, "max98090_mic_detect\n");
max98090->jack = jack;
if (jack) {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
1 << M98090_IJDET_SHIFT);
} else {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
0);
}
@@ -2331,16 +2345,17 @@ EXPORT_SYMBOL_GPL(max98090_mic_detect);
#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,
- .digital_mute = max98090_dai_digital_mute,
+ .mute_stream = max98090_dai_mute,
.trigger = max98090_dai_trigger,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_driver max98090_dai[] = {
-{
+static struct snd_soc_dai_driver max98090_dai = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
@@ -2357,25 +2372,24 @@ static struct snd_soc_dai_driver max98090_dai[] = {
.formats = MAX98090_FORMATS,
},
.ops = &max98090_dai_ops,
-}
};
-static int max98090_probe(struct snd_soc_codec *codec)
+static int max98090_probe(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "max98090_probe\n");
+ dev_dbg(component->dev, "max98090_probe\n");
- max98090->mclk = devm_clk_get(codec->dev, "mclk");
+ max98090->mclk = devm_clk_get(component->dev, "mclk");
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- max98090->codec = codec;
+ max98090->component = component;
/* Reset the codec, the DSP core, and disable all interrupts */
max98090_reset(max98090);
@@ -2394,26 +2408,29 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090->pa1en = 0;
max98090->pa2en = 0;
- ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
+ max98090->tdm_lslot = 0;
+ max98090->tdm_rslot = 1;
+
+ ret = snd_soc_component_read(component, M98090_REG_REVISION_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;
}
if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
devtype = MAX98090;
- dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
devtype = MAX98091;
- dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98091 REVID=0x%02x\n", ret);
} else {
devtype = MAX98090;
- dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
+ dev_err(component->dev, "Unrecognized revision 0x%02x\n", ret);
}
if (max98090->devtype != devtype) {
- dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+ dev_warn(component->dev, "Mismatch in DT specified CODEC type.\n");
max98090->devtype = devtype;
}
@@ -2424,10 +2441,9 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090_pll_det_enable_work);
INIT_WORK(&max98090->pll_det_disable_work,
max98090_pll_det_disable_work);
- INIT_WORK(&max98090->pll_work, max98090_pll_work);
/* Enable jack detection */
- snd_soc_write(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_write(component, M98090_REG_JACK_DETECT,
M98090_JDETEN_MASK | M98090_JDEB_25MS);
/*
@@ -2435,75 +2451,74 @@ static int max98090_probe(struct snd_soc_codec *codec)
* An old interrupt ocurring prior to installing the ISR
* can keep a new interrupt from generating a trigger.
*/
- snd_soc_read(codec, M98090_REG_DEVICE_STATUS);
+ snd_soc_component_read(component, M98090_REG_DEVICE_STATUS);
/* High Performance is default */
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_DACHP_MASK,
1 << M98090_DACHP_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_PERFMODE_MASK,
0 << M98090_PERFMODE_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_ADC_CONTROL,
M98090_ADCHP_MASK,
1 << M98090_ADCHP_SHIFT);
/* Turn on VCM bandgap reference */
- snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
+ snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
M98090_VCM_MODE_MASK);
- err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias);
+ err = device_property_read_u32(component->dev, "maxim,micbias", &micbias);
if (err) {
micbias = M98090_MBVSEL_2V8;
- dev_info(codec->dev, "use default 2.8v micbias\n");
+ dev_info(component->dev, "use default 2.8v micbias\n");
} else if (micbias > M98090_MBVSEL_2V8) {
- dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);
+ dev_err(component->dev, "micbias out of range 0x%x\n", micbias);
micbias = M98090_MBVSEL_2V8;
}
- snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
+ snd_soc_component_update_bits(component, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, micbias);
- max98090_add_widgets(codec);
+ max98090_add_widgets(component);
err_access:
return ret;
}
-static int max98090_remove(struct snd_soc_codec *codec)
+static void max98090_remove(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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);
- cancel_work_sync(&max98090->pll_work);
- max98090->codec = NULL;
-
- return 0;
+ max98090->component = NULL;
}
-static void max98090_seq_notifier(struct snd_soc_dapm_context *dapm,
+static void max98090_seq_notifier(struct snd_soc_component *component,
enum snd_soc_dapm_type event, int subseq)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
if (max98090->shdn_pending) {
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, 0);
msleep(40);
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ 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_codec_driver soc_codec_dev_max98090 = {
- .probe = max98090_probe,
- .remove = max98090_remove,
- .seq_notifier = max98090_seq_notifier,
- .set_bias_level = max98090_set_bias_level,
+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 = {
@@ -2518,12 +2533,16 @@ static const struct regmap_config max98090_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+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");
@@ -2533,19 +2552,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
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 if (i2c_id) {
- driver_data = i2c_id->driver_data;
- }
-
- max98090->devtype = driver_data;
+ max98090->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data;
@@ -2570,9 +2577,9 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98090, max98090_dai,
- ARRAY_SIZE(max98090_dai));
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98090,
+ &max98090_dai, 1);
err_enable:
return ret;
}
@@ -2592,14 +2599,11 @@ static void max98090_i2c_shutdown(struct i2c_client *i2c)
msleep(40);
}
-static int max98090_i2c_remove(struct i2c_client *client)
+static void max98090_i2c_remove(struct i2c_client *client)
{
max98090_i2c_shutdown(client);
- snd_soc_unregister_codec(&client->dev);
- return 0;
}
-#ifdef CONFIG_PM
static int max98090_runtime_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2621,9 +2625,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2641,31 +2643,19 @@ static int max98090_resume(struct device *dev)
return 0;
}
-static int max98090_suspend(struct device *dev)
-{
- 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(max98090_suspend, max98090_resume)
+ RUNTIME_PM_OPS(max98090_runtime_suspend, max98090_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
};
-static const struct i2c_device_id max98090_i2c_id[] = {
- { "max98090", MAX98090 },
- { "max98091", MAX98091 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
-
+#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[] = {
@@ -2678,11 +2668,11 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
static struct i2c_driver max98090_i2c_driver = {
.driver = {
.name = "max98090",
- .pm = &max98090_pm,
+ .pm = pm_ptr(&max98090_pm),
.of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
},
- .probe = max98090_i2c_probe,
+ .probe = max98090_i2c_probe,
.shutdown = max98090_i2c_shutdown,
.remove = max98090_i2c_remove,
.id_table = max98090_i2c_id,
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index bc610d9a9ecb..6ce8dd176e48 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98090.h -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 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 _MAX98090_H
@@ -1519,7 +1516,7 @@ struct max98090_cdata {
struct max98090_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
enum max98090_type devtype;
struct max98090_pdata *pdata;
struct clk *mclk;
@@ -1533,11 +1530,11 @@ struct max98090_priv {
struct delayed_work jack_work;
struct delayed_work pll_det_enable_work;
struct work_struct pll_det_disable_work;
- struct work_struct pll_work;
struct snd_soc_jack *jack;
unsigned int dai_fmt;
int tdm_slots;
- int tdm_width;
+ int tdm_lslot;
+ int tdm_rslot;
u8 lin_state;
unsigned int pa1en;
unsigned int pa2en;
@@ -1546,7 +1543,7 @@ struct max98090_priv {
bool shdn_pending;
};
-int max98090_mic_detect(struct snd_soc_codec *codec,
+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
index 5ead87d2ab1d..cfb63fe69267 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98095.c -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 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>
@@ -251,7 +248,7 @@ static const struct regmap_config max98095_regmap = {
/*
* Load equalizer DSP coefficient configurations registers
*/
-static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+static void m98095_eq_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int eq_reg;
@@ -269,15 +266,15 @@ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98095_BYTE0(coefs[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_codec *codec, unsigned int dai,
+static void m98095_biquad_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int bq_reg;
@@ -295,8 +292,8 @@ static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE0(coefs[i]));
}
}
@@ -353,12 +350,12 @@ static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -367,8 +364,8 @@ static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -377,12 +374,12 @@ static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -391,8 +388,8 @@ static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -598,21 +595,21 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
static int max98095_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
(1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ 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_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -628,8 +625,8 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
static int max98095_line_pga(struct snd_soc_dapm_widget *w,
int event, u8 channel)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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)))
@@ -640,13 +637,13 @@ static int max98095_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;
@@ -676,15 +673,15 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << (w->shift+2)), 0);
break;
default:
@@ -942,8 +939,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -955,11 +952,11 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -969,31 +966,31 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ 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_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
if (max98095->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)max98095->sysclk);
- snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1003,8 +1000,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1016,11 +1013,11 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1030,31 +1027,31 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ 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_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
if (max98095->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)max98095->sysclk);
- snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1064,8 +1061,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1077,11 +1074,11 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1091,31 +1088,31 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ 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_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
if (max98095->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)max98095->sysclk);
- snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1124,8 +1121,8 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1142,13 +1139,13 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq < 20000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x10);
} else if ((freq >= 20000000) && (freq < 40000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x20);
} else if ((freq >= 40000000) && (freq < 60000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x30);
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -1161,8 +1158,8 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1171,22 +1168,20 @@ static int max98095_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, M98095_028_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, M98095_028_DAI1_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_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 */
regval |= M98095_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;
}
@@ -1216,11 +1211,11 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ 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_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ snd_soc_component_write(component, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
}
return 0;
@@ -1229,8 +1224,8 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1239,22 +1234,20 @@ static int max98095_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, M98095_032_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, M98095_032_DAI2_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_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 */
regval |= M98095_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;
}
@@ -1284,11 +1277,11 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ 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_write(codec, M98095_035_DAI2_CLOCK,
+ snd_soc_component_write(component, M98095_035_DAI2_CLOCK,
M98095_DAI_BSEL64);
}
@@ -1298,8 +1291,8 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1308,22 +1301,20 @@ static int max98095_dai3_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, M98095_03C_DAI3_CLKCFG_HI,
+ 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_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_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;
}
@@ -1353,21 +1344,21 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ 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_write(codec, M98095_03F_DAI3_CLOCK,
+ snd_soc_component_write(component, M98095_03F_DAI3_CLOCK,
M98095_DAI_BSEL64);
}
return 0;
}
-static int max98095_set_bias_level(struct snd_soc_codec *codec,
+static int max98095_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1385,7 +1376,7 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
if (IS_ERR(max98095->mclk))
break;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(max98095->mclk);
} else {
ret = clk_prepare_enable(max98095->mclk);
@@ -1395,21 +1386,21 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98095->regmap);
if (ret != 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, M98095_MBEN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, 0);
regcache_mark_dirty(max98095->regmap);
break;
@@ -1494,8 +1485,8 @@ static int max98095_get_eq_channel(const char *name)
static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1528,7 +1519,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
}
}
- 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);
@@ -1537,29 +1528,29 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ 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_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_eq_band(codec, channel, 0, coef_set->band1);
- m98095_eq_band(codec, channel, 1, coef_set->band2);
- m98095_eq_band(codec, channel, 2, coef_set->band3);
- m98095_eq_band(codec, channel, 3, coef_set->band4);
- m98095_eq_band(codec, channel, 4, coef_set->band5);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ 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_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1569,9 +1560,9 @@ static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_eq_cfg *cfg;
unsigned int cfgcnt;
@@ -1624,34 +1615,31 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
max98095->eq_enum.texts = max98095->eq_texts;
max98095->eq_enum.items = max98095->eq_textcnt;
- ret = snd_soc_add_codec_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 const char *bq_mode_name[] = {"Biquad1 Mode", "Biquad2 Mode"};
-static int max98095_get_bq_channel(struct snd_soc_codec *codec,
+static int max98095_get_bq_channel(struct snd_soc_component *component,
const char *name)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bq_mode_name); i++)
- if (strcmp(name, bq_mode_name[i]) == 0)
- return i;
+ int ret;
- /* Shouldn't happen */
- dev_err(codec->dev, "Bad biquad channel name '%s'\n", name);
- return -EINVAL;
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, kcontrol->id.name);
+ 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;
@@ -1682,7 +1670,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
}
}
- 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->bq_cfg[best].name,
pdata->bq_cfg[best].rate, fs);
@@ -1691,27 +1679,27 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ 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_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_biquad_band(codec, channel, 0, coef_set->band1);
- m98095_biquad_band(codec, channel, 1, coef_set->band2);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ 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_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
+ 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)
@@ -1723,9 +1711,9 @@ static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_bq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_biquad_cfg *cfg;
unsigned int cfgcnt;
@@ -1779,19 +1767,19 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
max98095->bq_enum.texts = max98095->bq_texts;
max98095->bq_enum.items = max98095->bq_textcnt;
- ret = snd_soc_add_codec_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 Biquad control: %d\n", ret);
+ dev_err(component->dev, "Failed to add Biquad control: %d\n", ret);
}
-static void max98095_handle_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -1802,27 +1790,27 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec)
if (pdata->digmic_right_mode)
regval |= M98095_DIGMIC_R;
- snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+ snd_soc_component_write(component, M98095_087_CFG_MIC, regval);
/* Configure equalizers */
if (pdata->eq_cfgcnt)
- max98095_handle_eq_pdata(codec);
+ max98095_handle_eq_pdata(component);
/* Configure bi-quad filters */
if (pdata->bq_cfgcnt)
- max98095_handle_bq_pdata(codec);
+ max98095_handle_bq_pdata(component);
}
static irqreturn_t max98095_report_jack(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, M98095_007_JACK_AUTO_STS);
+ 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)
@@ -1853,9 +1841,9 @@ static irqreturn_t max98095_report_jack(int irq, void *data)
return IRQ_HANDLED;
}
-static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_enable(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1866,41 +1854,41 @@ static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
if (max98095->pdata->jack_detect_delay)
slew = max98095->pdata->jack_detect_delay;
- ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+ ret = snd_soc_component_write(component, M98095_08E_JACK_DC_SLEW, slew);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
/* configure auto detection to be enabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, detect_enable);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ 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_codec *codec)
+static int max98095_jack_detect_disable(struct snd_soc_component *component)
{
int ret = 0;
/* configure auto detection to be disabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, 0x0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
return ret;
}
-int max98095_jack_detect(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ 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;
@@ -1910,44 +1898,44 @@ int max98095_jack_detect(struct snd_soc_codec *codec,
if (!hp_jack && !mic_jack)
return -EINVAL;
- max98095_jack_detect_enable(codec);
+ max98095_jack_detect_enable(component);
/* enable interrupts for headphone jack detection */
- ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+ ret = snd_soc_component_update_bits(component, M98095_013_JACK_INT_EN,
M98095_IDDONE, M98095_IDDONE);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+ dev_err(component->dev, "Failed to cfg jack irqs %d\n", ret);
return ret;
}
- max98095_report_jack(client->irq, codec);
+ max98095_report_jack(client->irq, component);
return 0;
}
EXPORT_SYMBOL_GPL(max98095_jack_detect);
#ifdef CONFIG_PM
-static int max98095_suspend(struct snd_soc_codec *codec)
+static int max98095_suspend(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
if (max98095->headphone_jack || max98095->mic_jack)
- max98095_jack_detect_disable(codec);
+ max98095_jack_detect_disable(component);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
return 0;
}
-static int max98095_resume(struct snd_soc_codec *codec)
+static int max98095_resume(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
if (max98095->headphone_jack || max98095->mic_jack) {
- max98095_jack_detect_enable(codec);
- max98095_report_jack(client->irq, codec);
+ max98095_jack_detect_enable(component);
+ max98095_report_jack(client->irq, component);
}
return 0;
@@ -1957,30 +1945,30 @@ static int max98095_resume(struct snd_soc_codec *codec)
#define max98095_resume NULL
#endif
-static int max98095_reset(struct snd_soc_codec *codec)
+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_write(codec, M98095_00F_HOST_CFG, 0);
+ ret = snd_soc_component_write(component, M98095_00F_HOST_CFG, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ dev_err(component->dev, "Failed to reset DSP: %d\n", ret);
return ret;
}
- ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ ret = snd_soc_component_write(component, M98095_097_PWR_SYS, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ 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_write(codec, i, snd_soc_read(codec, i));
+ ret = snd_soc_component_write(component, i, snd_soc_component_read(component, i));
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ dev_err(component->dev, "Failed to reset: %d\n", ret);
return ret;
}
}
@@ -1988,21 +1976,21 @@ static int max98095_reset(struct snd_soc_codec *codec)
return ret;
}
-static int max98095_probe(struct snd_soc_codec *codec)
+static int max98095_probe(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "mclk");
+ 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(codec);
+ max98095_reset(component);
- client = to_i2c_client(codec->dev);
+ client = to_i2c_client(component->dev);
/* initialize private data */
@@ -2037,89 +2025,93 @@ static int max98095_probe(struct snd_soc_codec *codec)
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "max98095", codec);
+ IRQF_ONESHOT, "max98095", component);
if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;
}
}
- ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ ret = snd_soc_component_read(component, M98095_0FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failure reading hardware revision: %d\n",
+ dev_err(component->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_irq;
}
- dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
+ dev_info(component->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+ snd_soc_component_write(component, M98095_097_PWR_SYS, M98095_PWRSV);
- snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ snd_soc_component_write(component, M98095_048_MIX_DAC_LR,
M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
- snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ snd_soc_component_write(component, M98095_049_MIX_DAC_M,
M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
- snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
- snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
- snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+ 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_write(codec, M98095_02C_DAI1_IOCFG,
+ snd_soc_component_write(component, M98095_02C_DAI1_IOCFG,
M98095_S1NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ snd_soc_component_write(component, M98095_036_DAI2_IOCFG,
M98095_S2NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ snd_soc_component_write(component, M98095_040_DAI3_IOCFG,
M98095_S3NORMAL|M98095_SDATA);
- max98095_handle_pdata(codec);
+ max98095_handle_pdata(component);
/* take the codec out of the shut down */
- snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ 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, codec);
+ free_irq(client->irq, component);
err_access:
return ret;
}
-static int max98095_remove(struct snd_soc_codec *codec)
+static void max98095_remove(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ 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(codec);
+ max98095_jack_detect_disable(component);
if (client->irq)
- free_irq(client->irq, codec);
-
- return 0;
+ free_irq(client->irq, component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_max98095 = {
- .probe = max98095_probe,
- .remove = max98095_remove,
- .suspend = max98095_suspend,
- .resume = max98095_resume,
- .set_bias_level = max98095_set_bias_level,
- .component_driver = {
- .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),
- },
+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,
- const struct i2c_device_id *id)
+static int max98095_i2c_probe(struct i2c_client *i2c)
{
struct max98095_priv *max98095;
int ret;
@@ -2138,40 +2130,30 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
return ret;
}
- max98095->devtype = id->driver_data;
+ max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98095,
max98095_dai, ARRAY_SIZE(max98095_dai));
return ret;
}
-static int max98095_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id max98095_i2c_id[] = {
- { "max98095", MAX98095 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
-
+#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 = max98095_i2c_probe,
- .remove = max98095_i2c_remove,
+ .probe = max98095_i2c_probe,
.id_table = max98095_i2c_id,
};
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
index 2ebbe4e894bf..2af7e77021a2 100644
--- a/sound/soc/codecs/max98095.h
+++ b/sound/soc/codecs/max98095.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98095.h -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 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 _MAX98095_H
@@ -315,7 +312,7 @@
/* Default Delay used in Slew Rate Calculation for Jack detection */
#define M98095_DEFAULT_SLEW_DELAY 0x18
-extern int max98095_jack_detect(struct snd_soc_codec *codec,
+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
index 426ed2dae6ca..cc811f58c9d2 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -1,21 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. 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 and
- * only 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.
- *
* 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>
@@ -27,63 +19,83 @@
#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 gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = dai->component;
+ struct max98357a_priv *max98357a =
+ snd_soc_component_get_drvdata(component);
- if (!sdmode)
+ if (!max98357a->sdmode)
return 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- gpiod_set_value(sdmode, 1);
+ 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(sdmode, 0);
+ 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[] = {
- {"Speaker", NULL, "HiFi Playback"},
+ {"SD_MODE", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SD_MODE"},
};
-static int max98357a_codec_probe(struct snd_soc_codec *codec)
-{
- struct gpio_desc *sdmode;
-
- sdmode = devm_gpiod_get_optional(codec->dev, "sdmode", GPIOD_OUT_LOW);
- if (IS_ERR(sdmode))
- return PTR_ERR(sdmode);
-
- snd_soc_codec_set_drvdata(codec, sdmode);
-
- return 0;
-}
-
-static const struct snd_soc_codec_driver max98357a_codec_driver = {
- .probe = max98357a_codec_probe,
- .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),
- },
+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,
+ .trigger = max98357a_daiops_trigger,
};
static struct snd_soc_dai_driver max98357a_dai_driver = {
@@ -95,7 +107,10 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
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,
@@ -107,20 +122,38 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
static int max98357a_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
- &max98357a_dai_driver, 1);
-}
+ 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");
+ }
-static int max98357a_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, max98357a);
- return 0;
+ 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);
@@ -129,6 +162,7 @@ MODULE_DEVICE_TABLE(of, max98357a_device_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id max98357a_acpi_match[] = {
{ "MX98357A", 0 },
+ { "MX98360A", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
@@ -141,7 +175,6 @@ static struct platform_driver max98357a_platform_driver = {
.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
},
.probe = max98357a_platform_probe,
- .remove = max98357a_platform_remove,
};
module_platform_driver(max98357a_platform_driver);
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644
index 000000000000..25af78ab30d5
--- /dev/null
+++ b/sound/soc/codecs/max98363.c
@@ -0,0 +1,465 @@
+// 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 const struct reg_default max98363_reg[] = {
+ {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},
+};
+
+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;
+
+ regcache_cache_only(max98363->regmap, false);
+ if (max98363->first_hw_init)
+ regcache_cache_bypass(max98363->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!max98363->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(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);
+ else
+ goto out;
+
+ 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;
+
+out:
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000
+#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_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 = 1;
+
+ 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 const 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;
+
+ regcache_cache_only(max98363->regmap, true);
+
+ 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;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+ return 0;
+}
+
+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
index 7bc2a17c1e94..852db211ba1e 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98371.c -- ALSA SoC Stereo MAX98371 driver
*
* Copyright 2015-16 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/i2c.h>
@@ -157,10 +154,6 @@ static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
);
-static const DECLARE_TLV_DB_RANGE(max98371_noload_gain_tlv,
- 0, 11, TLV_DB_SCALE_ITEM(950, 100, 0),
-);
-
static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
static const struct snd_kcontrol_new max98371_snd_controls[] = {
@@ -187,15 +180,15 @@ static const struct snd_kcontrol_new max98371_snd_controls[] = {
static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
@@ -210,7 +203,7 @@ static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
val |= MAX98371_DAI_LEFT;
break;
default:
- dev_err(codec->dev, "DAI wrong mode unsupported");
+ dev_err(component->dev, "DAI wrong mode unsupported");
return -EINVAL;
}
regmap_update_bits(max98371->regmap, MAX98371_FMT,
@@ -222,8 +215,8 @@ 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_codec *codec = dai->codec;
- struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -348,15 +341,16 @@ static struct snd_soc_dai_driver max98371_dai[] = {
}
};
-static const struct snd_soc_codec_driver max98371_codec = {
- .component_driver = {
- .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),
- },
+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 = {
@@ -370,8 +364,7 @@ static const struct regmap_config max98371_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98371_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98371_i2c_probe(struct i2c_client *i2c)
{
struct max98371_priv *max98371;
int ret, reg;
@@ -397,42 +390,36 @@ static int max98371_i2c_probe(struct i2c_client *i2c,
}
dev_info(&i2c->dev, "device version %x\n", reg);
- ret = snd_soc_register_codec(&i2c->dev, &max98371_codec,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int max98371_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max98371_i2c_id[] = {
- { "max98371", 0 },
+ { "max98371" },
{ }
};
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",
- .pm = NULL,
.of_match_table = of_match_ptr(max98371_of_match),
},
- .probe = max98371_i2c_probe,
- .remove = max98371_i2c_remove,
+ .probe = max98371_i2c_probe,
.id_table = max98371_i2c_id,
};
diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h
index 9f6330964d98..63d9a9de3316 100644
--- a/sound/soc/codecs/max98371.h
+++ b/sound/soc/codecs/max98371.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98371.h -- MAX98371 ALSA SoC Audio driver
*
* Copyright 2011-2012 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 _MAX98371_H
@@ -62,6 +59,5 @@
struct max98371_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
};
#endif
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
new file mode 100644
index 000000000000..f58b8c8625a7
--- /dev/null
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -0,0 +1,616 @@
+// 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/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pm.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 const 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,
+ }
+};
+
+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;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+ 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);
+
+ /* 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"},
+ { },
+};
+
+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 = pm_ptr(&max98373_pm),
+ },
+ .probe = 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..88ff215f52b3
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -0,0 +1,888 @@
+// 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 const 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+ 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);
+
+ regcache_cache_only(max98373->regmap, false);
+ if (max98373->first_hw_init)
+ regcache_cache_bypass(max98373->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!max98373->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(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_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;
+
+ regcache_cache_only(max98373->regmap, true);
+
+ 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;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
+}
+
+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 const 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)
+{
+ 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",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = pm_ptr(&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
index 562e88765129..33eb4576da23 100644
--- a/sound/soc/codecs/max98373.c
+++ b/sound/soc/codecs/max98373.c
@@ -2,412 +2,40 @@
// 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_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
#include <sound/tlv.h>
#include "max98373.h"
-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_codec *codec = codec_dai->codec;
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
- unsigned int format = 0;
- unsigned int invert = 0;
-
- dev_dbg(codec->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(codec->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_codec *codec,
- struct snd_pcm_hw_params *params)
-{
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
- /* 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(codec->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_codec *codec = dai->codec;
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
- 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(codec->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(codec->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;
- default:
- dev_err(codec->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(codec, 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_codec *codec = dai->codec;
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
- 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(codec->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(codec->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 int max98373_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+ 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_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(max98373->regmap,
MAX98373_R20FF_GLOBAL_SHDN,
MAX98373_GLOBAL_EN_MASK, 0);
- max98373->tdm_mode = 0;
+ usleep_range(30000, 31000);
+ max98373->tdm_mode = false;
break;
default:
return 0;
@@ -435,7 +63,7 @@ static const struct snd_kcontrol_new max98373_spkfb_control =
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_POST_PMD),
+ 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"),
@@ -454,7 +82,7 @@ SND_SOC_DAPM_SIGGEN("IMON"),
SND_SOC_DAPM_SIGGEN("FBMON"),
};
-static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0);
+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),
@@ -470,65 +98,21 @@ 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(-50, -50, 0),
- 2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0),
- 8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0),
- 10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0),
- 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
- 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+ 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(0, -100, 0),
+ 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(0, -25, 0),
+ 0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0),
);
-static bool max98373_readable_register(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- 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_R21FF_REV_ID:
- return true;
- default:
- return false;
- }
-}
-
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"
@@ -586,6 +170,31 @@ 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),
@@ -595,6 +204,15 @@ 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,
@@ -602,7 +220,7 @@ SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
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, 0, max98373_digital_tlv),
+ 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,
@@ -614,7 +232,7 @@ SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
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, 0, max98373_dht_rotation_point_tlv),
+ 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,
@@ -627,8 +245,10 @@ 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("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0),
-SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 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,
@@ -644,36 +264,37 @@ 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("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 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, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3,
- 0, 0x3C, 0, max98373_bde_gain_tlv),
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1,
- 0, 0xF, 0, max98373_limiter_thresh_tlv),
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1,
- 0, 0xF, 0, max98373_limiter_thresh_tlv),
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1,
- 0, 0xF, 0, max98373_limiter_thresh_tlv),
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1,
- 0, 0xF, 0, max98373_limiter_thresh_tlv),
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
/* Limiter */
SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
MAX98373_LIMITER_EN_SHIFT, 1, 0),
@@ -700,36 +321,40 @@ static const struct snd_soc_dapm_route max98373_audio_map[] = {
{ "Speaker FB Sense", NULL, "SpkFB Sense" },
};
-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,
+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_codec *codec)
+static int max98373_probe(struct snd_soc_component *component)
{
- struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
-
- codec->control_data = max98373->regmap;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
/* Software Reset */
- regmap_write(max98373->regmap,
- MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
+ max98373_reset(max98373, component->dev);
/* IV default slot configuration */
regmap_write(max98373->regmap,
@@ -745,13 +370,6 @@ static int max98373_probe(struct snd_soc_codec *codec)
regmap_write(max98373->regmap,
MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
0x1);
- /* Set inital volume (0dB) */
- regmap_write(max98373->regmap,
- MAX98373_R203D_AMP_DIG_VOL_CTRL,
- 0x00);
- regmap_write(max98373->regmap,
- MAX98373_R203E_AMP_PATH_GAIN,
- 0x00);
/* Enable DC blocker */
regmap_write(max98373->regmap,
MAX98373_R203F_AMP_DSP_CFG,
@@ -783,6 +401,11 @@ static int max98373_probe(struct snd_soc_codec *codec)
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,
@@ -803,59 +426,47 @@ static int max98373_probe(struct snd_soc_codec *codec)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int max98373_suspend(struct device *dev)
-{
- struct max98373_priv *max98373 = dev_get_drvdata(dev);
+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);
- regcache_cache_only(max98373->regmap, true);
- regcache_mark_dirty(max98373->regmap);
- return 0;
-}
-static int max98373_resume(struct device *dev)
+static int max98373_sdw_probe(struct snd_soc_component *component)
{
- struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
- regmap_write(max98373->regmap,
- MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
- regcache_cache_only(max98373->regmap, false);
- 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 snd_soc_codec_driver soc_codec_dev_max98373 = {
- .probe = max98373_probe,
- .component_driver = {
- .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),
- },
+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);
-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 void max98373_slot_config(struct i2c_client *i2c,
- struct max98373_priv *max98373)
+void max98373_slot_config(struct device *dev,
+ struct max98373_priv *max98373)
{
int value;
- struct device *dev = &i2c->dev;
if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
max98373->v_slot = value & 0xF;
@@ -867,109 +478,31 @@ static void max98373_slot_config(struct i2c_client *i2c,
else
max98373->i_slot = 1;
- if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
- max98373->spkfb_slot = value & 0xF;
- else
- max98373->spkfb_slot = 2;
-}
-
-static int max98373_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
-
- int ret = 0;
- int reg = 0;
- struct max98373_priv *max98373 = NULL;
-
- max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
-
- if (!max98373) {
- ret = -ENOMEM;
- return ret;
+ /* This will assert RESET */
+ max98373->reset = devm_gpiod_get_optional(dev,
+ "maxim,reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max98373->reset)) {
+ dev_err(dev, "error %ld looking up RESET GPIO line\n",
+ PTR_ERR(max98373->reset));
+ return;
}
- i2c_set_clientdata(i2c, max98373);
-
- /* update interleave mode info */
- if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode"))
- max98373->interleave_mode = 1;
- else
- max98373->interleave_mode = 0;
-
- /* 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;
+ /* Cycle reset */
+ if (max98373->reset) {
+ gpiod_set_consumer_name(max98373->reset ,"MAX98373_RESET");
+ gpiod_direction_output(max98373->reset, 1);
+ msleep(50);
+ gpiod_direction_output(max98373->reset, 0);
+ 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);
-
- /* voltage/current slot configuration */
- max98373_slot_config(i2c, max98373);
-
- /* codec registeration */
- ret = snd_soc_register_codec(&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 int max98373_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+ max98373->spkfb_slot = value & 0xF;
+ else
+ max98373->spkfb_slot = 2;
}
-
-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 = max98373_i2c_probe,
- .remove = max98373_i2c_remove,
- .id_table = max98373_i2c_id,
-};
-
-module_i2c_driver(max98373_i2c_driver)
+EXPORT_SYMBOL_GPL(max98373_slot_config);
MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
index f6a37aa02f26..af3b62217497 100644
--- a/sound/soc/codecs/max98373.h
+++ b/sound/soc/codecs/max98373.h
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2017, Maxim Integrated
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2017 Maxim Integrated */
#ifndef _MAX98373_H
#define _MAX98373_H
@@ -130,6 +130,8 @@
#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)
@@ -193,6 +195,9 @@
#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 */
@@ -201,13 +206,35 @@
/* MAX98373_R2000_SW_RESET */
#define MAX98373_SOFT_RESET (0x1 << 0)
+struct max98373_cache {
+ u32 reg;
+ u32 val;
+};
+
struct max98373_priv {
struct regmap *regmap;
+ struct gpio_desc *reset;
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/max98388.c b/sound/soc/codecs/max98388.c
new file mode 100644
index 000000000000..076f15a9867e
--- /dev/null
+++ b/sound/soc/codecs/max98388.c
@@ -0,0 +1,1013 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.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 "max98388.h"
+
+static const struct reg_default max98388_reg[] = {
+ {MAX98388_R2000_SW_RESET, 0x00},
+ {MAX98388_R2001_INT_RAW1, 0x00},
+ {MAX98388_R2002_INT_RAW2, 0x00},
+ {MAX98388_R2004_INT_STATE1, 0x00},
+ {MAX98388_R2005_INT_STATE2, 0x00},
+ {MAX98388_R2020_THERM_WARN_THRESH, 0x0A},
+ {MAX98388_R2031_SPK_MON_THRESH, 0x58},
+ {MAX98388_R2032_SPK_MON_LD_SEL, 0x08},
+ {MAX98388_R2033_SPK_MON_DURATION, 0x02},
+ {MAX98388_R2037_ERR_MON_CTRL, 0x01},
+ {MAX98388_R2040_PCM_MODE_CFG, 0xC0},
+ {MAX98388_R2041_PCM_CLK_SETUP, 0x04},
+ {MAX98388_R2042_PCM_SR_SETUP, 0x88},
+ {MAX98388_R2044_PCM_TX_CTRL1, 0x00},
+ {MAX98388_R2045_PCM_TX_CTRL2, 0x00},
+ {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF},
+ {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF},
+ {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF},
+ {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF},
+ {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF},
+ {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF},
+ {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF},
+ {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF},
+ {MAX98388_R2058_PCM_RX_SRC1, 0x00},
+ {MAX98388_R2059_PCM_RX_SRC2, 0x01},
+ {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00},
+ {MAX98388_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98388_R205E_PCM_RX_EN, 0x00},
+ {MAX98388_R205F_PCM_TX_EN, 0x00},
+ {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00},
+ {MAX98388_R2091_SPK_CH_CFG, 0x02},
+ {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03},
+ {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01},
+ {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00},
+ {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00},
+ {MAX98388_R209F_SPK_CH_AMP_EN, 0x00},
+ {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10},
+ {MAX98388_R20A7_IV_DATA_EN, 0x00},
+ {MAX98388_R20E0_BP_ALC_THRESH, 0x04},
+ {MAX98388_R20E1_BP_ALC_RATES, 0x20},
+ {MAX98388_R20E2_BP_ALC_ATTEN, 0x06},
+ {MAX98388_R20E3_BP_ALC_REL, 0x02},
+ {MAX98388_R20E4_BP_ALC_MUTE, 0x33},
+ {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00},
+ {MAX98388_R20EF_BP_ALC_EN, 0x00},
+ {MAX98388_R210E_AUTO_RESTART, 0x00},
+ {MAX98388_R210F_GLOBAL_EN, 0x00},
+ {MAX98388_R22FF_REV_ID, 0x00},
+};
+
+static int max98388_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 max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 0);
+ usleep_range(30000, 31000);
+ max98388->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98388_monomix_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1,
+ MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98388_monomix_switch_text);
+
+static const struct snd_kcontrol_new max98388_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98388_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98388_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98388_R20A7_IV_DATA_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98388_R20A7_IV_DATA_EN, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Voltage", NULL,
+ MAX98388_R205D_PCM_TX_SRC_EN, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Current", NULL,
+ MAX98388_R205D_PCM_TX_SRC_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98388_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1);
+static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0);
+
+static const char * const max98388_alc_max_atten_text[] = {
+ "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS",
+ "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS",
+ "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum,
+ MAX98388_R20E2_BP_ALC_ATTEN,
+ MAX98388_ALC_MAX_ATTEN_SHIFT,
+ max98388_alc_max_atten_text);
+
+static const char * const max98388_thermal_warn_text[] = {
+ "95C", "105C", "115C", "125C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum,
+ MAX98388_R2020_THERM_WARN_THRESH,
+ MAX98388_THERM_WARN_THRESH_SHIFT,
+ max98388_thermal_warn_text);
+
+static const char * const max98388_thermal_shutdown_text[] = {
+ "135C", "145C", "155C", "165C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum,
+ MAX98388_R2020_THERM_WARN_THRESH,
+ MAX98388_THERM_SHDN_THRESH_SHIFT,
+ max98388_thermal_shutdown_text);
+
+static const char * const max98388_alc_thresh_single_text[] = {
+ "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V",
+ "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V",
+ "2.725V", "2.650V", "2.575V", "2.500V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum,
+ MAX98388_R20E0_BP_ALC_THRESH,
+ MAX98388_ALC_THRESH_SHIFT,
+ max98388_alc_thresh_single_text);
+
+static const char * const max98388_alc_attack_rate_text[] = {
+ "0", "10us", "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(max98388_alc_attack_rate_enum,
+ MAX98388_R20E1_BP_ALC_RATES,
+ MAX98388_ALC_ATTACK_RATE_SHIFT,
+ max98388_alc_attack_rate_text);
+
+static const char * const max98388_alc_release_rate_text[] = {
+ "20us", "40us", "80us", "160us", "320us", "640us",
+ "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms",
+ "81.92ms", "163.84ms", "327.68ms", "655.36ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum,
+ MAX98388_R20E1_BP_ALC_RATES,
+ MAX98388_ALC_RELEASE_RATE_SHIFT,
+ max98388_alc_release_rate_text);
+
+static const char * const max98388_alc_debounce_text[] = {
+ "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum,
+ MAX98388_R20E3_BP_ALC_REL,
+ MAX98388_ALC_DEBOUNCE_TIME_SHIFT,
+ max98388_alc_debounce_text);
+
+static const char * const max98388_alc_mute_delay_text[] = {
+ "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum,
+ MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_DELAY_SHIFT,
+ max98388_alc_mute_delay_text);
+
+static const char * const max98388_spkmon_duration_text[] = {
+ "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms",
+ "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum,
+ MAX98388_R2033_SPK_MON_DURATION,
+ MAX98388_SPKMON_DURATION_SHIFT,
+ max98388_spkmon_duration_text);
+
+static const char * const max98388_spkmon_thresh_text[] = {
+ "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V",
+ "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V",
+ "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V",
+ "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V",
+ "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V",
+ "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V",
+ "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V",
+ "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V",
+ "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V",
+ "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V",
+ "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V",
+ "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V",
+ "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V",
+ "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V",
+ "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V",
+ "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum,
+ MAX98388_R2031_SPK_MON_THRESH,
+ MAX98388_SPKMON_THRESH_SHIFT,
+ max98388_spkmon_thresh_text);
+
+static const char * const max98388_spkmon_load_text[] = {
+ "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm",
+ "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm",
+ "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm",
+ "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm",
+ "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm",
+ "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm",
+ "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm",
+ "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm",
+ "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm",
+ "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm",
+ "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm",
+ "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm",
+ "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm",
+ "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm",
+ "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm",
+ "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm",
+ "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm",
+ "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm",
+ "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm",
+ "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm",
+ "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm",
+ "33.50ohm", "33.75ohm"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum,
+ MAX98388_R2032_SPK_MON_LD_SEL,
+ MAX98388_SPKMON_LOAD_SHIFT,
+ max98388_spkmon_load_text);
+
+static const char * const max98388_edge_rate_text[] = {
+ "Normal", "Reduced", "Maximum", "Increased",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum,
+ MAX98388_R2094_SPK_AMP_ER_CTRL,
+ MAX98388_EDGE_RATE_FALL_SHIFT,
+ max98388_edge_rate_text);
+
+static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum,
+ MAX98388_R2094_SPK_AMP_ER_CTRL,
+ MAX98388_EDGE_RATE_RISE_SHIFT,
+ max98388_edge_rate_text);
+
+static const char * const max98388_ssm_mod_text[] = {
+ "1.5%", "3.0%", "4.5%", "6.0%",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum,
+ MAX98388_R2093_SPK_AMP_SSM_CFG,
+ MAX98388_SPK_AMP_SSM_MOD_SHIFT,
+ max98388_ssm_mod_text);
+
+static const struct snd_kcontrol_new max98388_snd_controls[] = {
+ SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0),
+ /* Two Cell Mode Enable */
+ SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG,
+ MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0),
+ /* Speaker Amplifier Overcurrent Automatic Restart Enable */
+ SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_OVC_AUTORESTART_SHIFT, 1, 0),
+ /* Thermal Shutdown Automatic Restart Enable */
+ SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_THERM_AUTORESTART_SHIFT, 1, 0),
+ /* PVDD UVLO Auto Restart */
+ SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0),
+ /* Clock Monitor Automatic Restart Enable */
+ SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_CMON_AUTORESTART_SHIFT, 1, 0),
+ SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL,
+ MAX98388_CLOCK_MON_SHIFT, 1, 0),
+ /* Pinknoise Generator Enable */
+ SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN,
+ MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0),
+ SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0),
+ SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0),
+ /* Digital Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL,
+ 0, 0x7F, 1, max98388_digital_tlv),
+ /* Speaker Volume */
+ SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG,
+ 0, 5, 0, max98388_amp_gain_tlv),
+ SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum),
+ SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum),
+ /* Brownout Protection Automatic Level Control */
+ SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0),
+ SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum),
+ SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum),
+ SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum),
+ SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum),
+ SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum),
+ SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_EN_SHIFT, 1, 0),
+ SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum),
+ /* Speaker Monitor */
+ SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL,
+ MAX98388_SPK_MON_SHIFT, 1, 0),
+ SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum),
+ SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum),
+ SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum),
+ /* General Parameters */
+ SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum),
+ SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum),
+ SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG,
+ MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0),
+ SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum),
+};
+
+static const struct snd_soc_dapm_route max98388_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 */
+ { "ADC Voltage", NULL, "VMON"},
+ { "ADC Current", NULL, "IMON"},
+ { "VI Sense", "Switch", "ADC Voltage"},
+ { "VI Sense", "Switch", "ADC Current"},
+ { "Voltage Sense", NULL, "VI Sense"},
+ { "Current Sense", NULL, "VI Sense"},
+};
+
+static void max98388_reset(struct max98388_priv *max98388, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_update_bits(max98388->regmap,
+ MAX98388_R2000_SW_RESET,
+ MAX98388_SOFT_RESET,
+ MAX98388_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(max98388->regmap,
+ MAX98388_R22FF_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);
+}
+
+static int max98388_probe(struct snd_soc_component *component)
+{
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98388_reset(max98388, component->dev);
+
+ /* General channel source configuration */
+ regmap_write(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ 0x10);
+
+ /* Enable DC blocker */
+ regmap_write(max98388->regmap,
+ MAX98388_R2091_SPK_CH_CFG,
+ 0x1);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98388->regmap,
+ MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ 0x3);
+ /* TX slot configuration */
+ regmap_write(max98388->regmap,
+ MAX98388_R2044_PCM_TX_CTRL1,
+ max98388->v_slot);
+
+ regmap_write(max98388->regmap,
+ MAX98388_R2045_PCM_TX_CTRL2,
+ max98388->i_slot);
+ /* Enable Auto-restart behavior by default */
+ regmap_write(max98388->regmap,
+ MAX98388_R210E_AUTO_RESTART, 0xF);
+ /* Set interleave mode */
+ if (max98388->interleave_mode)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98388_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker Amplifier Channel Enable */
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R209F_SPK_CH_AMP_EN,
+ MAX98388_SPK_EN_MASK, 1);
+
+ return 0;
+}
+
+static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98388_priv *max98388 = 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 = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98388_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98388_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98388_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98388_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98388_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 max98388_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 max98388_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98388->ch_size;
+ int value;
+
+ if (!max98388->tdm_mode) {
+ /* BCLK configuration */
+ value = max98388_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98388_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 max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+ int ret, reg;
+ int status = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98388->ch_size = snd_pcm_format_width(params_format(params));
+
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ goto err;
+
+ /* GLOBAL_EN OFF prior to the channel size re-configure */
+ if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) {
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ goto err;
+
+ if (status) {
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 0);
+ usleep_range(30000, 31000);
+ }
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_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 = MAX98388_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98388_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98388_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98388_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98388_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98388_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98388_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98388_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98388_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98388_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98388_PCM_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(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_MASK,
+ sampling_rate);
+
+ /* set sampling rate of IV */
+ if (max98388->interleave_mode &&
+ sampling_rate > MAX98388_PCM_SR_16000)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_IV_MASK,
+ (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT);
+ else
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_IV_MASK,
+ sampling_rate << MAX98388_PCM_SR_IV_SHIFT);
+
+ ret = max98388_set_clock(component, params);
+
+ if (status) {
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ }
+
+ return ret;
+
+err:
+ return -EINVAL;
+}
+
+#define MAX_NUM_SLOTS 16
+#define MAX_NUM_CH 2
+
+static int max98388_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 max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+ unsigned int mask;
+ int cnt, slot_found;
+ int addr, bits;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98388->tdm_mode = false;
+ else
+ max98388->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98388_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(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ slot_found = 0;
+ mask = rx_mask;
+ for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) {
+ if (mask & 0x1) {
+ if (slot_found == 0)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ MAX98388_RX_SRC_CH0_SHIFT,
+ cnt);
+ else
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ MAX98388_RX_SRC_CH1_SHIFT,
+ cnt);
+ slot_found++;
+ if (slot_found >= MAX_NUM_CH)
+ break;
+ }
+ }
+
+ /* speaker feedback slot configuration */
+ slot_found = 0;
+ mask = tx_mask;
+ for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) {
+ if (mask & 0x1) {
+ addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8);
+ bits = cnt % 8;
+ regmap_update_bits(max98388->regmap, addr, bits, bits);
+ slot_found++;
+ if (slot_found >= MAX_NUM_CH)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98388_dai_ops = {
+ .set_fmt = max98388_dai_set_fmt,
+ .hw_params = max98388_dai_hw_params,
+ .set_tdm_slot = max98388_dai_tdm_slot,
+};
+
+static bool max98388_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2:
+ case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2:
+ case MAX98388_R2020_THERM_WARN_THRESH:
+ case MAX98388_R2031_SPK_MON_THRESH
+ ... MAX98388_R2033_SPK_MON_DURATION:
+ case MAX98388_R2037_ERR_MON_CTRL:
+ case MAX98388_R2040_PCM_MODE_CFG
+ ... MAX98388_R2042_PCM_SR_SETUP:
+ case MAX98388_R2044_PCM_TX_CTRL1
+ ... MAX98388_R2045_PCM_TX_CTRL2:
+ case MAX98388_R2050_PCM_TX_HIZ_CTRL1
+ ... MAX98388_R2059_PCM_RX_SRC2:
+ case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH
+ ... MAX98388_R205F_PCM_TX_EN:
+ case MAX98388_R2090_SPK_CH_VOL_CTRL
+ ... MAX98388_R2094_SPK_AMP_ER_CTRL:
+ case MAX98388_R209E_SPK_CH_PINK_NOISE_EN
+ ... MAX98388_R209F_SPK_CH_AMP_EN:
+ case MAX98388_R20A0_IV_DATA_DSP_CTRL:
+ case MAX98388_R20A7_IV_DATA_EN:
+ case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE:
+ case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN:
+ case MAX98388_R210E_AUTO_RESTART:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98388_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct snd_soc_dai_driver max98388_dai[] = {
+ {
+ .name = "max98388-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98388_RATES,
+ .formats = MAX98388_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98388_RATES,
+ .formats = MAX98388_FORMATS,
+ },
+ .ops = &max98388_dai_ops,
+ }
+};
+
+static int max98388_suspend(struct device *dev)
+{
+ struct max98388_priv *max98388 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98388->regmap, true);
+ regcache_mark_dirty(max98388->regmap);
+
+ return 0;
+}
+
+static int max98388_resume(struct device *dev)
+{
+ struct max98388_priv *max98388 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98388->regmap, false);
+ max98388_reset(max98388, dev);
+ regcache_sync(max98388->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops max98388_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume)
+};
+
+static const struct regmap_config max98388_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98388_R22FF_REV_ID,
+ .reg_defaults = max98388_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98388_reg),
+ .readable_reg = max98388_readable_register,
+ .volatile_reg = max98388_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98388 = {
+ .probe = max98388_probe,
+ .controls = max98388_snd_controls,
+ .num_controls = ARRAY_SIZE(max98388_snd_controls),
+ .dapm_widgets = max98388_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets),
+ .dapm_routes = max98388_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98388_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static void max98388_read_deveice_property(struct device *dev,
+ struct max98388_priv *max98388)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+ max98388->v_slot = value & 0xF;
+ else
+ max98388->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+ max98388->i_slot = value & 0xF;
+ else
+ max98388->i_slot = 1;
+
+ if (device_property_read_bool(dev, "adi,interleave-mode"))
+ max98388->interleave_mode = true;
+ else
+ max98388->interleave_mode = false;
+}
+
+static int max98388_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+
+ struct max98388_priv *max98388 = NULL;
+
+ max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL);
+ if (!max98388)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98388);
+
+ /* regmap initialization */
+ max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap);
+ if (IS_ERR(max98388->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap),
+ "Failed to allocate register map.\n");
+
+ /* voltage/current slot & gpio configuration */
+ max98388_read_deveice_property(&i2c->dev, max98388);
+
+ /* Device Reset */
+ max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98388->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio),
+ "Unable to request GPIO\n");
+
+ if (max98388->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(max98388->reset_gpio, 0);
+ /* Wait for the hw reset done */
+ usleep_range(5000, 6000);
+ }
+
+ /* Read Revision ID */
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R22FF_REV_ID, &reg);
+ if (ret < 0)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to read the revision ID\n");
+
+ dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98388,
+ max98388_dai,
+ ARRAY_SIZE(max98388_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98388_i2c_id[] = {
+ { "max98388"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98388_i2c_id);
+
+static const struct of_device_id max98388_of_match[] = {
+ { .compatible = "adi,max98388", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98388_of_match);
+
+static const struct acpi_device_id max98388_acpi_match[] = {
+ { "ADS8388", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98388_acpi_match);
+
+static struct i2c_driver max98388_i2c_driver = {
+ .driver = {
+ .name = "max98388",
+ .of_match_table = max98388_of_match,
+ .acpi_match_table = max98388_acpi_match,
+ .pm = pm_sleep_ptr(&max98388_pm),
+ },
+ .probe = max98388_i2c_probe,
+ .id_table = max98388_i2c_id,
+};
+
+module_i2c_driver(max98388_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98388 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h
new file mode 100644
index 000000000000..77833d181913
--- /dev/null
+++ b/sound/soc/codecs/max98388.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98388.h -- MAX98388 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98388_H
+#define _MAX98388_H
+
+/* Device Status Registers */
+#define MAX98388_R2000_SW_RESET 0x2000
+#define MAX98388_R2001_INT_RAW1 0x2001
+#define MAX98388_R2002_INT_RAW2 0x2002
+#define MAX98388_R2004_INT_STATE1 0x2004
+#define MAX98388_R2005_INT_STATE2 0x2005
+/* Thermal Protection Registers */
+#define MAX98388_R2020_THERM_WARN_THRESH 0x2020
+/* Error Monitor */
+#define MAX98388_R2031_SPK_MON_THRESH 0x2031
+#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032
+#define MAX98388_R2033_SPK_MON_DURATION 0x2033
+#define MAX98388_R2037_ERR_MON_CTRL 0x2037
+/* PCM Registers */
+#define MAX98388_R2040_PCM_MODE_CFG 0x2040
+#define MAX98388_R2041_PCM_CLK_SETUP 0x2041
+#define MAX98388_R2042_PCM_SR_SETUP 0x2042
+#define MAX98388_R2044_PCM_TX_CTRL1 0x2044
+#define MAX98388_R2045_PCM_TX_CTRL2 0x2045
+#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050
+#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051
+#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052
+#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053
+#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054
+#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055
+#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056
+#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057
+#define MAX98388_R2058_PCM_RX_SRC1 0x2058
+#define MAX98388_R2059_PCM_RX_SRC2 0x2059
+#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C
+#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D
+#define MAX98388_R205E_PCM_RX_EN 0x205E
+#define MAX98388_R205F_PCM_TX_EN 0x205F
+/* Speaker Channel Control */
+#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090
+#define MAX98388_R2091_SPK_CH_CFG 0x2091
+#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092
+#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093
+#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094
+#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E
+#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F
+#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0
+#define MAX98388_R20A7_IV_DATA_EN 0x20A7
+#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0
+#define MAX98388_R20E1_BP_ALC_RATES 0x20E1
+#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2
+#define MAX98388_R20E3_BP_ALC_REL 0x20E3
+#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4
+#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE
+#define MAX98388_R20EF_BP_ALC_EN 0x20EF
+#define MAX98388_R210E_AUTO_RESTART 0x210E
+#define MAX98388_R210F_GLOBAL_EN 0x210F
+#define MAX98388_R22FF_REV_ID 0x22FF
+
+/* MAX98388_R2000_SW_RESET */
+#define MAX98388_SOFT_RESET (0x1 << 0)
+
+/* MAX98388_R2020_THERM_WARN_THRESH */
+#define MAX98388_THERM_SHDN_THRESH_SHIFT (0)
+#define MAX98388_THERM_WARN_THRESH_SHIFT (2)
+
+/* MAX98388_R2022_PCM_TX_SRC_1 */
+#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98388_R2024_PCM_DATA_FMT_CFG */
+#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98388_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98388_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98388_R2031_SPK_MON_THRESH */
+#define MAX98388_SPKMON_THRESH_SHIFT (0)
+
+/* MAX98388_R2032_SPK_MON_LD_SEL */
+#define MAX98388_SPKMON_LOAD_SHIFT (0)
+
+/* MAX98388_R2033_SPK_MON_DURATION */
+#define MAX98388_SPKMON_DURATION_SHIFT (0)
+
+/* MAX98388_R2037_ERR_MON_CTRL */
+#define MAX98388_CLOCK_MON_SHIFT (0)
+#define MAX98388_SPK_MON_SHIFT (1)
+
+/* MAX98388_R203E_AMP_PATH_GAIN */
+#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98388_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98388_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98388_R2041_PCM_CLK_SETUP */
+#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98388_R2042_PCM_SR_SETUP */
+#define MAX98388_PCM_SR_MASK (0xF << 0)
+#define MAX98388_PCM_SR_IV_MASK (0xF << 4)
+#define MAX98388_PCM_SR_IV_SHIFT (4)
+#define MAX98388_PCM_SR_8000 (0x0 << 0)
+#define MAX98388_PCM_SR_11025 (0x1 << 0)
+#define MAX98388_PCM_SR_12000 (0x2 << 0)
+#define MAX98388_PCM_SR_16000 (0x3 << 0)
+#define MAX98388_PCM_SR_22050 (0x4 << 0)
+#define MAX98388_PCM_SR_24000 (0x5 << 0)
+#define MAX98388_PCM_SR_32000 (0x6 << 0)
+#define MAX98388_PCM_SR_44100 (0x7 << 0)
+#define MAX98388_PCM_SR_48000 (0x8 << 0)
+#define MAX98388_PCM_SR_88200 (0x9 << 0)
+#define MAX98388_PCM_SR_96000 (0xA << 0)
+
+/* MAX98388_R2043_AMP_EN */
+#define MAX98388_SPK_EN_MASK (0x1 << 0)
+#define MAX98388_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98388_SPKFB_EN_SHIFT (1)
+
+/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98388_FLT_EN_SHIFT (4)
+
+/* MAX98388_R2058_PCM_RX_SRC1 */
+#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0)
+
+/* MAX98388_R2059_PCM_RX_SRC2 */
+#define MAX98388_RX_SRC_CH0_SHIFT (0)
+#define MAX98388_RX_SRC_CH1_SHIFT (4)
+
+/* MAX98388_R2091_SPK_CH_CFG */
+#define MAX98388_SPK_CFG_DCBLK_SHIFT (0)
+#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1)
+#define MAX98388_SPK_CFG_INV_SHIFT (2)
+#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3)
+#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4)
+
+/* MAX98388_R2092_SPK_AMP_OUT_CFG */
+#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0)
+#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3)
+
+/* MAX98388_R2093_SPK_AMP_SSM_CFG */
+#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0)
+#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1)
+
+/* MAX98388_R2094_SPK_AMP_ER_CTRL */
+#define MAX98388_EDGE_RATE_RISE_SHIFT (0)
+#define MAX98388_EDGE_RATE_FALL_SHIFT (2)
+
+/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */
+#define MAX98388_PINK_NOISE_GEN_SHIFT (0)
+
+/* MAX98388_R20A0_IV_DATA_DSP_CTRL */
+#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0)
+#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1)
+#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2)
+#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3)
+#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4)
+
+/* MAX98388_R20B2_BDE_L4_CFG_2 */
+#define MAX98388_LVL4_HOLD_EN_SHIFT (6)
+#define MAX98388_LVL4_MUTE_EN_SHIFT (7)
+
+/* MAX98388_R20B5_BDE_EN */
+#define MAX98388_BDE_EN_SHIFT (0)
+
+/* MAX98388_R20D1_DHT_CFG */
+#define MAX98388_DHT_ROT_PNT_SHIFT (0)
+#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4)
+
+/* MAX98388_R20D2_DHT_ATTACK_CFG */
+#define MAX98388_DHT_ATTACK_RATE_SHIFT (0)
+#define MAX98388_DHT_ATTACK_STEP_SHIFT (3)
+
+/* MAX98388_R20D3_DHT_RELEASE_CFG */
+#define MAX98388_DHT_RELEASE_RATE_SHIFT (0)
+#define MAX98388_DHT_RELEASE_STEP_SHIFT (3)
+
+/* MAX98388_R20D4_DHT_EN */
+#define MAX98388_DHT_EN_SHIFT (0)
+
+/* MAX98388_R20E0_BP_ALC_THRESH */
+#define MAX98388_ALC_THRESH_SHIFT (0)
+
+/* MAX98388_R20E1_BP_ALC_RATES */
+#define MAX98388_ALC_RELEASE_RATE_SHIFT (0)
+#define MAX98388_ALC_ATTACK_RATE_SHIFT (4)
+
+/* MAX98388_R20E2_BP_ALC_ATTEN */
+#define MAX98388_ALC_MAX_ATTEN_SHIFT (0)
+
+/* MAX98388_R20E3_BP_ALC_REL */
+#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0)
+
+/* MAX98388_R20E4_BP_ALC_MUTE */
+#define MAX98388_ALC_MUTE_EN_SHIFT (0)
+#define MAX98388_ALC_MUTE_DELAY_SHIFT (1)
+#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4)
+#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5)
+
+/* MAX98388_R210E_AUTO_RESTART */
+#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0)
+#define MAX98388_THERM_AUTORESTART_SHIFT (1)
+#define MAX98388_OVC_AUTORESTART_SHIFT (2)
+#define MAX98388_CMON_AUTORESTART_SHIFT (3)
+
+/* MAX98388_R210F_GLOBAL_EN */
+#define MAX98388_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98388_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+
+#endif
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
new file mode 100644
index 000000000000..a8a282ff9fc5
--- /dev/null
+++ b/sound/soc/codecs/max98390.c
@@ -0,0 +1,1141 @@
+// 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/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 const 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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops max98390_pm = {
+ 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"},
+ {},
+};
+
+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 = pm_ptr(&max98390_pm),
+ },
+ .probe = 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..4b4e1fc98a6d
--- /dev/null
+++ b/sound/soc/codecs/max98396.c
@@ -0,0 +1,1915 @@
+// 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 <sound/tlv.h>
+#include "max98396.h"
+
+static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
+ "avdd",
+ "dvdd",
+ "dvddio",
+};
+
+static const 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 const 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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops max98396_pm = {
+ 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 = pm_ptr(&max98396_pm),
+ },
+ .probe = 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
index a3dfc918c278..c395132689b4 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9850.c -- codec driver for max9850
*
@@ -6,13 +7,7 @@
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
*
* Initial development of this code was funded by
- * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.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.
- *
+ * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/
*/
#include <linux/module.h>
@@ -32,19 +27,6 @@ struct max9850_priv {
unsigned int sysclk;
};
-/* max9850 register cache */
-static const struct reg_default max9850_reg[] = {
- { 2, 0x0c },
- { 3, 0x00 },
- { 4, 0x00 },
- { 5, 0x00 },
- { 6, 0x00 },
- { 7, 0x00 },
- { 8, 0x00 },
- { 9, 0x00 },
- { 10, 0x00 },
-};
-
/* 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)
@@ -52,9 +34,9 @@ static bool max9850_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case MAX9850_STATUSA:
case MAX9850_STATUSB:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -130,8 +112,8 @@ static int max9850_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 max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
u64 lrclk_div;
u8 sf, da;
@@ -139,14 +121,14 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
- sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
+ 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_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
- snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+ 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:
@@ -161,7 +143,7 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+ snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da);
return 0;
}
@@ -169,16 +151,16 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
static int max9850_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 max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, MAX9850_CLOCK, 0x0);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x0);
else if (freq <= 26000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x4);
else if (freq <= 40000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x8);
else
return -EINVAL;
@@ -188,15 +170,15 @@ static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int max9850_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 da = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -234,15 +216,15 @@ static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* set da */
- snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+ snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da);
return 0;
}
-static int max9850_set_bias_level(struct snd_soc_codec *codec,
+static int max9850_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -251,10 +233,10 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max9850->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -289,35 +271,34 @@ static struct snd_soc_dai_driver max9850_dai = {
.ops = &max9850_dai_ops,
};
-static int max9850_probe(struct snd_soc_codec *codec)
+static int max9850_probe(struct snd_soc_component *component)
{
/* enable zero-detect */
- snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+ snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1);
/* enable slew-rate control */
- snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+ snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40);
/* set slew-rate 125ms */
- snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+ snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_max9850 = {
- .probe = max9850_probe,
- .set_bias_level = max9850_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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,
- const struct i2c_device_id *id)
+static int max9850_i2c_probe(struct i2c_client *i2c)
{
struct max9850_priv *max9850;
int ret;
@@ -333,19 +314,13 @@ static int max9850_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max9850);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max9850, &max9850_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max9850, &max9850_dai, 1);
return ret;
}
-static int max9850_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max9850_i2c_id[] = {
- { "max9850", 0 },
+ { "max9850" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
@@ -355,7 +330,6 @@ static struct i2c_driver max9850_i2c_driver = {
.name = "max9850",
},
.probe = max9850_i2c_probe,
- .remove = max9850_i2c_remove,
.id_table = max9850_i2c_id,
};
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
index 72b1ddb04b0d..da313640b4bf 100644
--- a/sound/soc/codecs/max9850.h
+++ b/sound/soc/codecs/max9850.h
@@ -1,14 +1,9 @@
+/* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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 _MAX9850_H
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
index a7320e709890..c94142768c81 100644
--- a/sound/soc/codecs/max98504.c
+++ b/sound/soc/codecs/max98504.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX98504 ALSA SoC Audio driver
*
* Copyright 2013 - 2014 Maxim Integrated Products
* Copyright 2016 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
@@ -38,7 +35,7 @@ struct max98504_priv {
unsigned int brownout_release_rate;
};
-static struct reg_default max98504_reg_defaults[] = {
+static const struct reg_default max98504_reg_defaults[] = {
{ 0x01, 0},
{ 0x02, 0},
{ 0x03, 0},
@@ -223,8 +220,10 @@ static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
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)
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
{
struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
struct regmap *map = max98504->regmap;
@@ -294,6 +293,7 @@ static const struct snd_soc_component_driver max98504_component_driver = {
.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 = {
@@ -307,8 +307,7 @@ static const struct regmap_config max98504_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98504_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max98504_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h
index afbefad2d5ce..8b2a113b7118 100644
--- a/sound/soc/codecs/max98504.h
+++ b/sound/soc/codecs/max98504.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* MAX98504 ALSA SoC Audio driver
*
* Copyright 2011 - 2012 Maxim Integrated Products
* Copyright 2016 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef MAX98504_H_
#define MAX98504_H_
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
new file mode 100644
index 000000000000..2bf8976c1828
--- /dev/null
+++ b/sound/soc/codecs/max98520.c
@@ -0,0 +1,766 @@
+// 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/consumer.h>
+#include <linux/of.h>
+#include <sound/tlv.h>
+#include "max98520.h"
+
+static const 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 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 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 = {
+ 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"},
+ { },
+};
+
+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 = pm_ptr(&max98520_pm),
+ },
+ .probe = 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
index a2dc6a47f466..716d16daf7d7 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -1,23 +1,14 @@
-/*
- * 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
- *
- * 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.
- */
+// 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>
@@ -261,8 +252,8 @@ static int max9860_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 max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -270,18 +261,18 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
unsigned long n;
int ret;
- dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (max9860->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = MAX9860_MASTER;
break;
default:
@@ -306,7 +297,7 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
break;
case SND_SOC_DAIFMT_DSP_A:
if (params_width(params) != 16) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"DSP_A works for 16 bits per sample only.\n");
return -EINVAL;
}
@@ -315,7 +306,7 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
break;
case SND_SOC_DAIFMT_DSP_B:
if (params_width(params) != 16) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"DSP_B works for 16 bits per sample only.\n");
return -EINVAL;
}
@@ -343,7 +334,7 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
ifc1a ^= MAX9860_WCI;
- /* fall through */
+ fallthrough;
case SND_SOC_DAIFMT_IB_NF:
ifc1a ^= MAX9860_DBCI;
ifc1b ^= MAX9860_ABCI;
@@ -352,16 +343,16 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dev_dbg(codec->dev, "IFC1A %02x\n", ifc1a);
+ dev_dbg(component->dev, "IFC1A %02x\n", ifc1a);
ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
if (ret) {
- dev_err(codec->dev, "Failed to set IFC1A: %d\n", ret);
+ dev_err(component->dev, "Failed to set IFC1A: %d\n", ret);
return ret;
}
- dev_dbg(codec->dev, "IFC1B %02x\n", ifc1b);
+ dev_dbg(component->dev, "IFC1B %02x\n", ifc1b);
ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
if (ret) {
- dev_err(codec->dev, "Failed to set IFC1B: %d\n", ret);
+ dev_err(component->dev, "Failed to set IFC1B: %d\n", ret);
return ret;
}
@@ -417,33 +408,34 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
}
sysclk |= max9860->psclk;
- dev_dbg(codec->dev, "SYSCLK %02x\n", sysclk);
+ dev_dbg(component->dev, "SYSCLK %02x\n", sysclk);
ret = regmap_write(max9860->regmap,
MAX9860_SYSCLK, sysclk);
if (ret) {
- dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
return ret;
}
- dev_dbg(codec->dev, "N %lu\n", n);
+ dev_dbg(component->dev, "N %lu\n", n);
ret = regmap_write(max9860->regmap,
MAX9860_AUDIOCLKHIGH, n >> 8);
if (ret) {
- dev_err(codec->dev, "Failed to set NHI: %d\n", 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(codec->dev, "Failed to set NLO: %d\n", ret);
+ dev_err(component->dev, "Failed to set NLO: %d\n", ret);
return ret;
}
if (!master) {
- dev_dbg(codec->dev, "Enable PLL\n");
+ dev_dbg(component->dev, "Enable PLL\n");
ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
MAX9860_PLL, MAX9860_PLL);
if (ret) {
- dev_err(codec->dev, "Failed to enable PLL: %d\n", ret);
+ dev_err(component->dev, "Failed to enable PLL: %d\n",
+ ret);
return ret;
}
}
@@ -453,12 +445,12 @@ static int max9860_hw_params(struct snd_pcm_substream *substream,
static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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;
@@ -497,13 +489,13 @@ static struct snd_soc_dai_driver max9860_dai = {
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &max9860_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int max9860_set_bias_level(struct snd_soc_codec *codec,
+static int max9860_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max9860_priv *max9860 = dev_get_drvdata(codec->dev);
+ struct max9860_priv *max9860 = dev_get_drvdata(component->dev);
int ret;
switch (level) {
@@ -515,7 +507,8 @@ static int max9860_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
MAX9860_SHDN, MAX9860_SHDN);
if (ret) {
- dev_err(codec->dev, "Failed to remove SHDN: %d\n", ret);
+ dev_err(component->dev, "Failed to remove SHDN: %d\n",
+ ret);
return ret;
}
break;
@@ -524,7 +517,7 @@ static int max9860_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
MAX9860_SHDN, 0);
if (ret) {
- dev_err(codec->dev, "Failed to request SHDN: %d\n",
+ dev_err(component->dev, "Failed to request SHDN: %d\n",
ret);
return ret;
}
@@ -534,21 +527,18 @@ static int max9860_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static const struct snd_soc_codec_driver max9860_codec_driver = {
- .set_bias_level = max9860_set_bias_level,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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);
@@ -593,14 +583,12 @@ static int max9860_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops max9860_pm_ops = {
- SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+ RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
};
-static int max9860_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max9860_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct max9860_priv *max9860;
@@ -615,16 +603,14 @@ static int max9860_probe(struct i2c_client *i2c,
return -ENOMEM;
max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
- if (IS_ERR(max9860->dvddio)) {
- ret = PTR_ERR(max9860->dvddio);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret);
- return ret;
- }
+ 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 = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb);
+ ret = devm_regulator_register_notifier(max9860->dvddio,
+ &max9860->dvddio_nb);
if (ret)
dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret);
@@ -651,8 +637,7 @@ static int max9860_probe(struct i2c_client *i2c,
if (IS_ERR(mclk)) {
ret = PTR_ERR(mclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get MCLK: %d\n", ret);
+ dev_err_probe(dev, ret, "Failed to get MCLK\n");
goto err_regulator;
}
@@ -698,8 +683,8 @@ static int max9860_probe(struct i2c_client *i2c,
pm_runtime_enable(dev);
pm_runtime_idle(dev);
- ret = snd_soc_register_codec(dev, &max9860_codec_driver,
- &max9860_dai, 1);
+ 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;
@@ -714,15 +699,13 @@ err_regulator:
return ret;
}
-static int max9860_remove(struct i2c_client *i2c)
+static void max9860_remove(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct max9860_priv *max9860 = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
pm_runtime_disable(dev);
regulator_disable(max9860->dvddio);
- return 0;
}
static const struct i2c_device_id max9860_i2c_id[] = {
@@ -738,13 +721,13 @@ static const struct of_device_id max9860_of_match[] = {
MODULE_DEVICE_TABLE(of, max9860_of_match);
static struct i2c_driver max9860_i2c_driver = {
- .probe = max9860_probe,
+ .probe = max9860_probe,
.remove = max9860_remove,
.id_table = max9860_i2c_id,
.driver = {
.name = "max9860",
.of_match_table = max9860_of_match,
- .pm = &max9860_pm_ops,
+ .pm = pm_ptr(&max9860_pm_ops),
},
};
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
index 22041bd67a7d..e07b905eaf50 100644
--- a/sound/soc/codecs/max9860.h
+++ b/sound/soc/codecs/max9860.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the MAX9860 Mono Audio Voice Codec
*
* Author: Peter Rosin <peda@axentia.s>
* Copyright 2016 Axentia Technologies
- *
- * 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.
*/
#ifndef _SND_SOC_MAX9860
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 2f60924fe919..50db88fce904 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -1,13 +1,12 @@
-/*
- * max9867.c -- max9867 ALSA SoC Audio driver
- *
- * Copyright 2013-15 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.
- */
-
+// 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>
@@ -17,287 +16,449 @@
#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_sidetone_text[] = {
- "None", "Left", "Right", "LeftRight", "LeftRightDiv2",
-};
static const char *const max9867_filter_text[] = {"IIR", "FIR"};
-static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
- max9867_filter_text);
+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 (!snd_soc_dapm_widget_name_cmp(w, "ADCL"))
+ adc_dac = MAX9867_ADC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "ADCR"))
+ adc_dac = MAX9867_ADC_RIGHT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "DACL"))
+ adc_dac = MAX9867_DAC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "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 SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6,
- max9867_sidetone_text);
-static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0);
-static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1);
-static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1);
-static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1);
-static const SNDRV_CTL_TLVD_DECLARE_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 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("Master Playback Volume", MAX9867_LEFTVOL,
- MAX9867_RIGHTVOL, 0, 63, 1),
- SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN,
- MAX9867_RIGHTMICGAIN,
- 0, 15, 1, max9860_capture_tlv),
- SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN,
- MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv),
- SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN,
- MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv),
- SOC_ENUM("Digital Sidetone Src", max9867_sidetone),
- SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1),
- SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0),
- SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1),
- SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL,
- 4, 15, 1, max9860_adc_left_tlv),
- SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL,
- 0, 15, 1, max9860_adc_right_tlv),
+ 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("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0),
- SOC_ENUM("DSP Filter", max9867_filter),
+ 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),
};
-static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"};
+/* 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),
+};
-static SOC_ENUM_SINGLE_DECL(max9867_mux_enum,
- MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT,
- max9867_mux);
+/* Sidetone mixer */
+static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0),
+};
-static const struct snd_kcontrol_new max9867_dapm_mux_controls =
- SOC_DAPM_ENUM("Route", max9867_mux_enum);
+/* 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);
-static const struct snd_kcontrol_new max9867_left_dapm_control =
- SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0);
-static const struct snd_kcontrol_new max9867_right_dapm_control =
- SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0);
-static const struct snd_kcontrol_new max9867_line_dapm_control =
- SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 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_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0),
- SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0),
- SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_OUTPUT("HPOUT"),
-
- SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0),
- SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0),
- SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
- &max9867_dapm_mux_controls),
-
- SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1,
- &max9867_left_dapm_control),
- SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1,
- &max9867_right_dapm_control),
- SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0,
- &max9867_line_dapm_control),
- SND_SOC_DAPM_INPUT("LINE_IN"),
+ 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 DAC", NULL, "DAI_OUT"},
- {"Right DAC", NULL, "DAI_OUT"},
- {"Output Mixer", NULL, "Left DAC"},
- {"Output Mixer", NULL, "Right DAC"},
- {"HPOUT", NULL, "Output Mixer"},
-
- {"Left ADC", NULL, "DAI_IN"},
- {"Right ADC", NULL, "DAI_IN"},
- {"Input Mixer", NULL, "Left ADC"},
- {"Input Mixer", NULL, "Right ADC"},
- {"Input Mux", "Line", "Input Mixer"},
- {"Input Mux", "Mic", "Input Mixer"},
- {"Input Mux", "Mic_Line", "Input Mixer"},
- {"Right Line", "Switch", "Input Mux"},
- {"Left Line", "Switch", "Input Mux"},
- {"LINE_IN", NULL, "Left Line"},
- {"LINE_IN", NULL, "Right Line"},
+ {"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"},
};
-enum rates {
- pcm_rate_8, pcm_rate_16, pcm_rate_24,
- pcm_rate_32, pcm_rate_44,
- pcm_rate_48, max_pcm_rate,
+static const unsigned int max9867_rates_44k1[] = {
+ 11025, 22050, 44100,
};
-static const struct ni_div_rates {
- u32 mclk;
- u16 ni[max_pcm_rate];
-} ni_div[] = {
- {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
- {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
- {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
- {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
- {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
- {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
- {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
- {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
+static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
+ .list = max9867_rates_44k1,
+ .count = ARRAY_SIZE(max9867_rates_44k1),
};
-static inline int get_ni_value(int mclk, int rate)
+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)
{
- int i, ret = 0;
+ struct max9867_priv *max9867 =
+ snd_soc_component_get_drvdata(dai->component);
- /* find the closest rate index*/
- for (i = 0; i < ARRAY_SIZE(ni_div); i++) {
- if (ni_div[i].mclk >= mclk)
- break;
- }
- if (i == ARRAY_SIZE(ni_div))
- return -EINVAL;
+ if (max9867->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
- switch (rate) {
- case 8000:
- return ni_div[i].ni[pcm_rate_8];
- case 16000:
- return ni_div[i].ni[pcm_rate_16];
- case 32000:
- return ni_div[i].ni[pcm_rate_32];
- case 44100:
- return ni_div[i].ni[pcm_rate_44];
- case 48000:
- return ni_div[i].ni[pcm_rate_48];
- default:
- pr_err("%s wrong rate %d\n", __func__, rate);
- ret = -EINVAL;
- }
- return ret;
+ return 0;
}
static int max9867_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;
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
- unsigned int ni_h, ni_l;
- int value;
+ 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);
- value = get_ni_value(max9867->sysclk, params_rate(params));
- if (value < 0)
- return value;
-
- ni_h = (0xFF00 & value) >> 8;
- ni_l = 0x00FF & value;
/* set up the ni value */
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
- MAX9867_NI_HIGH_MASK, ni_h);
+ MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
- MAX9867_NI_LOW_MASK, ni_l);
- if (!max9867->master) {
- /*
- * 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);
- } else {
- unsigned long int bclk_rate, pclk_bclk_ratio;
- int bclk_value;
-
- bclk_rate = params_rate(params) * 2 * params_width(params);
- pclk_bclk_ratio = max9867->pclk/bclk_rate;
- switch (params_width(params)) {
- case 8:
- case 16:
- switch (pclk_bclk_ratio) {
- case 2:
- bclk_value = MAX9867_IFC1B_PCLK_2;
- break;
- case 4:
- bclk_value = MAX9867_IFC1B_PCLK_4;
- break;
+ 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:
- bclk_value = MAX9867_IFC1B_PCLK_8;
- break;
case 16:
- bclk_value = MAX9867_IFC1B_PCLK_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:
- dev_err(codec->dev,
- "unsupported sampling rate\n");
return -EINVAL;
}
- break;
- case 24:
- bclk_value = MAX9867_IFC1B_24BIT;
- break;
- case 32:
- bclk_value = MAX9867_IFC1B_32BIT;
- break;
- default:
- dev_err(codec->dev, "unsupported sampling rate\n");
- return -EINVAL;
}
regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
- MAX9867_IFC1B_BCLK_MASK, bclk_value);
- }
- return 0;
-}
+ MAX9867_IFC1B_BCLK_MASK, value);
-static int max9867_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ /* 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++;
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
+ /* 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)
+static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
- if (mute)
- regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
- MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK);
- else
- regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
- MAX9867_DAC_MUTE_MASK, 0);
- return 0;
+ 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_codec *codec = codec_dai->codec;
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ max9867->pclk = freq;
} else if (freq >= 20000000 && freq <= 40000000) {
value |= MAX9867_PSCLK_20_40;
- max9867->pclk = freq/2;
+ max9867->pclk = freq / 2;
} else if (freq >= 40000000 && freq <= 60000000) {
value |= MAX9867_PSCLK_40_60;
- max9867->pclk = freq/4;
+ max9867->pclk = freq / 4;
} else {
- pr_err("bad clock frequency %d", freq);
+ dev_err(component->dev,
+ "Invalid clock frequency %uHz (required 10-60MHz)\n",
+ freq);
return -EINVAL;
}
- value = value << MAX9867_PSCLK_SHIFT;
+ 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;
- /* exact integer mode is not supported */
- value &= ~MAX9867_FREQ_MASK;
+ value = value << MAX9867_PSCLK_SHIFT;
regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
MAX9867_PSCLK_MASK, value);
return 0;
@@ -306,27 +467,36 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
- u8 iface1A = 0, iface1B = 0;
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- max9867->master = 1;
- iface1A |= MAX9867_MASTER;
+ 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_CBS_CFS:
- max9867->master = 0;
- iface1A &= ~MAX9867_MASTER;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max9867->provider = false;
+ iface1A = iface1B = 0;
break;
default:
return -EINVAL;
}
- /* for i2s compatible mode */
- iface1A |= MAX9867_I2S_DLY;
- /* SDOUT goes to hiz state after all data is transferred */
- iface1A |= MAX9867_SDOUT_HIZ;
+ 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) {
@@ -346,83 +516,114 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
}
regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
- regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
+ 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_fmt = max9867_dai_set_fmt,
.set_sysclk = max9867_set_dai_sysclk,
- .prepare = max9867_prepare,
- .digital_mute = max9867_mute,
- .hw_params = max9867_dai_hw_params,
+ .set_fmt = max9867_dai_set_fmt,
+ .mute_stream = max9867_mute,
+ .startup = max9867_startup,
+ .hw_params = max9867_dai_hw_params,
+ .no_capture_mute = 1,
};
-#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
-
static struct snd_soc_dai_driver max9867_dai[] = {
{
.name = "max9867-aif1",
.playback = {
.stream_name = "HiFi Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
- .rates = MAX9867_RATES,
- .formats = MAX9867_FORMATS,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "HiFi Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
- .rates = MAX9867_RATES,
- .formats = MAX9867_FORMATS,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &max9867_dai_ops,
+ .symmetric_rate = 1,
}
};
-#ifdef CONFIG_PM_SLEEP
-static int max9867_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int max9867_suspend(struct snd_soc_component *component)
{
- struct max9867_priv *max9867 = dev_get_drvdata(dev);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- /* Drop down to power saving mode when system is suspended */
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK);
return 0;
}
-static int max9867_resume(struct device *dev)
+static int max9867_resume(struct snd_soc_component *component)
{
- struct max9867_priv *max9867 = dev_get_drvdata(dev);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
return 0;
}
+#else
+#define max9867_suspend NULL
+#define max9867_resume NULL
#endif
-static int max9867_probe(struct snd_soc_codec *codec)
+static int max9867_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
{
- struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ }
- dev_dbg(codec->dev, "max98090_probe\n");
- max9867->codec = codec;
return 0;
}
-static const struct snd_soc_codec_driver max9867_codec = {
- .probe = max9867_probe,
- .component_driver = {
- .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),
- },
+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)
@@ -438,47 +639,20 @@ static bool max9867_volatile_register(struct device *dev, unsigned int reg)
}
}
-static const struct reg_default max9867_reg[] = {
- { 0x04, 0x00 },
- { 0x05, 0x00 },
- { 0x06, 0x00 },
- { 0x07, 0x00 },
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0A, 0x00 },
- { 0x0B, 0x00 },
- { 0x0C, 0x00 },
- { 0x0D, 0x00 },
- { 0x0E, 0x00 },
- { 0x0F, 0x00 },
- { 0x10, 0x00 },
- { 0x11, 0x00 },
- { 0x12, 0x00 },
- { 0x13, 0x00 },
- { 0x14, 0x00 },
- { 0x15, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x00 },
-};
-
static const struct regmap_config max9867_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX9867_REVISION,
- .reg_defaults = max9867_reg,
- .num_reg_defaults = ARRAY_SIZE(max9867_reg),
.volatile_reg = max9867_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
-static int max9867_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max9867_i2c_probe(struct i2c_client *i2c)
{
struct max9867_priv *max9867;
- int ret = 0, reg;
+ int ret, reg;
- max9867 = devm_kzalloc(&i2c->dev,
- sizeof(*max9867), GFP_KERNEL);
+ max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL);
if (!max9867)
return -ENOMEM;
@@ -486,61 +660,54 @@ static int max9867_i2c_probe(struct i2c_client *i2c,
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);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
- ret = regmap_read(max9867->regmap,
- MAX9867_REVISION, &reg);
+ 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 = snd_soc_register_codec(&i2c->dev, &max9867_codec,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
- return ret;
-}
-static int max9867_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
+ 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 },
+ { "max9867" },
{ }
};
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);
-
-static const struct dev_pm_ops max9867_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
-};
+#endif
static struct i2c_driver max9867_i2c_driver = {
.driver = {
.name = "max9867",
.of_match_table = of_match_ptr(max9867_of_match),
- .pm = &max9867_pm_ops,
},
- .probe = max9867_i2c_probe,
- .remove = max9867_i2c_remove,
+ .probe = max9867_i2c_probe,
.id_table = max9867_i2c_id,
};
module_i2c_driver(max9867_i2c_driver);
-MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
-MODULE_DESCRIPTION("ALSA SoC MAX9867 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
index 65590b4ad62a..b6b880631b13 100644
--- a/sound/soc/codecs/max9867.h
+++ b/sound/soc/codecs/max9867.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max9867.h -- MAX9867 ALSA SoC Audio driver
*
* Copyright 2013-2015 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 _MAX9867_H
@@ -26,13 +23,11 @@
#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_WIDTH 0x7
-#define MAX9867_NI_HIGH_MASK 0x7F
-#define MAX9867_NI_LOW_MASK 0x7F
-#define MAX9867_NI_LOW_SHIFT 0x1
-#define MAX9867_PLL (1<<7)
-#define MAX9867_AUDIOCLKLOW 0x07
+#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)
@@ -43,41 +38,30 @@
#define MAX9867_BCI_MODE (1<<5)
#define MAX9867_IFC1B 0x09
#define MAX9867_IFC1B_BCLK_MASK 7
-#define MAX9867_IFC1B_32BIT 0x01
-#define MAX9867_IFC1B_24BIT 0x02
-#define MAX9867_IFC1B_PCLK_2 4
-#define MAX9867_IFC1B_PCLK_4 5
-#define MAX9867_IFC1B_PCLK_8 6
-#define MAX9867_IFC1B_PCLK_16 7
-#define MAX9867_CODECFLTR 0x0a
-#define MAX9867_DACGAIN 0x0b
+#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_DAC_MUTE_SHIFT 0x6
-#define MAX9867_DAC_MUTE_WIDTH 0x1
-#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT)
#define MAX9867_ADCLEVEL 0x0d
#define MAX9867_LEFTLINELVL 0x0e
-#define MAX9867_RIGTHLINELVL 0x0f
+#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_INPUT_SHIFT 0x6
#define MAX9867_MICCONFIG 0x15
#define MAX9867_MODECONFIG 0x16
#define MAX9867_PWRMAN 0x17
-#define MAX9867_SHTDOWN_MASK (1<<7)
+#define MAX9867_PWRMAN_SHDN (1<<7)
#define MAX9867_REVISION 0xff
#define MAX9867_CACHEREGNUM 10
-/* codec private data */
-struct max9867_priv {
- struct regmap *regmap;
- struct snd_soc_codec *codec;
- unsigned int sysclk;
- unsigned int pclk;
- unsigned int master;
-};
#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 61cc18e35efb..1bd0d4761ca6 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -1,14 +1,9 @@
+// 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>
@@ -138,8 +133,7 @@ static const struct regmap_config max9877_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9877_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9877_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int i;
@@ -157,7 +151,7 @@ static int max9877_i2c_probe(struct i2c_client *client,
}
static const struct i2c_device_id max9877_i2c_id[] = {
- { "max9877", 0 },
+ { "max9877" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
index 368343f29dd0..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
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 921f95fc396d..66c78051bd09 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -1,9 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98925.c -- ALSA SoC Stereo MAX98925 driver
* Copyright 2013-15 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/delay.h>
#include <linux/i2c.h>
@@ -99,8 +97,8 @@ static const struct snd_kcontrol_new max98925_dai_sel_mux =
static int max98925_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -256,7 +254,7 @@ static const struct {
},
};
-static inline int max98925_rate_value(struct snd_soc_codec *codec,
+static inline int max98925_rate_value(struct snd_soc_component *component,
int rate, int clock, int *value, int *n, int *m)
{
int ret = -EINVAL;
@@ -297,32 +295,29 @@ static void max98925_set_sense_data(struct max98925_priv *max98925)
static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
unsigned int invert = 0;
- dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* set DAI to slave mode */
+ 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_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/*
- * set left channel DAI to master mode,
- * right channel always slave
+ * 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;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
@@ -339,7 +334,7 @@ static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(component->dev, "DAI invert mode unsupported");
return -EINVAL;
}
@@ -352,7 +347,7 @@ 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_codec *codec = max98925->codec;
+ 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;
@@ -395,12 +390,12 @@ static int max98925_set_clock(struct max98925_priv *max98925,
mdll = M98925_MDLL_MULT_MCLKx8;
break;
default:
- dev_info(max98925->codec->dev, "unsupported sysclk %d\n",
+ dev_info(max98925->component->dev, "unsupported sysclk %d\n",
max98925->sysclk);
return -EINVAL;
}
- if (max98925_rate_value(codec, rate, clock, &dai_sr, &n, &m))
+ if (max98925_rate_value(component, rate, clock, &dai_sr, &n, &m))
return -EINVAL;
/* set DAI_SR to correct LRCLK frequency */
@@ -427,8 +422,8 @@ 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_codec *codec = dai->codec;
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
switch (params_width(params)) {
case 16:
@@ -454,7 +449,7 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
__func__, params_format(params));
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: format supported %d",
+ dev_dbg(component->dev, "%s: format supported %d",
__func__, params_format(params));
return max98925_set_clock(max98925, params);
}
@@ -462,8 +457,8 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
static int max98925_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case 0:
@@ -516,11 +511,11 @@ static struct snd_soc_dai_driver max98925_dai[] = {
}
};
-static int max98925_probe(struct snd_soc_codec *codec)
+static int max98925_probe(struct snd_soc_component *component)
{
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
- max98925->codec = codec;
+ 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,
@@ -538,16 +533,17 @@ static int max98925_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_max98925 = {
- .probe = max98925_probe,
- .component_driver = {
- .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),
- },
+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 = {
@@ -561,8 +557,7 @@ static const struct regmap_config max98925_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98925_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98925_i2c_probe(struct i2c_client *i2c)
{
int ret, reg;
u32 value;
@@ -612,40 +607,35 @@ static int max98925_i2c_probe(struct i2c_client *i2c,
dev_info(&i2c->dev, "device version 0x%02X\n", reg);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925,
+ 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 codec: %d\n", ret);
+ "Failed to register component: %d\n", ret);
return ret;
}
-static int max98925_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max98925_i2c_id[] = {
- { "max98925", 0 },
+ { "max98925" },
{ }
};
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),
- .pm = NULL,
},
- .probe = max98925_i2c_probe,
- .remove = max98925_i2c_remove,
+ .probe = max98925_i2c_probe,
.id_table = max98925_i2c_id,
};
diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h
index 3783248f2780..6d55ccad27f9 100644
--- a/sound/soc/codecs/max98925.h
+++ b/sound/soc/codecs/max98925.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98925.h -- MAX98925 ALSA SoC Audio driver
*
* Copyright 2013-2015 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 _MAX98925_H
@@ -821,7 +818,7 @@
struct max98925_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct max98925_pdata *pdata;
unsigned int sysclk;
unsigned int v_slot;
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index 7b1d1b0fa879..ae962bda163e 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -1,9 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98926.c -- ALSA SoC MAX98926 driver
* Copyright 2013-15 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/delay.h>
#include <linux/i2c.h>
@@ -22,15 +20,6 @@ static const char * const max98926_boost_voltage_txt[] = {
"6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
};
-static const char * const max98926_boost_current_txt[] = {
- "0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0",
- "2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4"
-};
-
-static const char *const max98926_dai_txt[] = {
- "Left", "Right", "LeftRight", "LeftRightDiv2",
-};
-
static const char *const max98926_pdm_ch_text[] = {
"Current", "Voltage",
};
@@ -336,18 +325,18 @@ static void max98926_set_sense_data(struct max98926_priv *max98926)
static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
unsigned int invert = 0;
- dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
max98926_set_sense_data(max98926);
break;
default:
- dev_err(codec->dev, "DAI clock mode unsupported\n");
+ dev_err(component->dev, "DAI clock mode unsupported\n");
return -EINVAL;
}
@@ -364,7 +353,7 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported\n");
+ dev_err(component->dev, "DAI invert mode unsupported\n");
return -EINVAL;
}
@@ -381,8 +370,8 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
{
int dai_sr = -EINVAL;
int rate = params_rate(params), i;
- struct snd_soc_codec *codec = dai->codec;
- struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+ 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)) {
@@ -408,7 +397,7 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
max98926->ch_size = 32;
break;
default:
- dev_dbg(codec->dev, "format unsupported %d\n",
+ dev_dbg(component->dev, "format unsupported %d\n",
params_format(params));
return -EINVAL;
}
@@ -485,27 +474,28 @@ static struct snd_soc_dai_driver max98926_dai[] = {
}
};
-static int max98926_probe(struct snd_soc_codec *codec)
+static int max98926_probe(struct snd_soc_component *component)
{
- struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
- max98926->codec = codec;
+ max98926->component = component;
/* Hi-Z all the slots */
regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_max98926 = {
- .probe = max98926_probe,
- .component_driver = {
- .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),
- },
+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 = {
@@ -519,8 +509,7 @@ static const struct regmap_config max98926_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98926_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98926_i2c_probe(struct i2c_client *i2c)
{
int ret, reg;
u32 value;
@@ -539,7 +528,8 @@ static int max98926_i2c_probe(struct i2c_client *i2c,
"Failed to allocate regmap: %d\n", ret);
goto err_out;
}
- if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") ||
+ 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)) {
@@ -563,42 +553,37 @@ static int max98926_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926,
+ 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 codec: %d\n", ret);
+ "Failed to register component: %d\n", ret);
dev_info(&i2c->dev, "device version: %x\n", reg);
err_out:
return ret;
}
-static int max98926_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max98926_i2c_id[] = {
- { "max98926", 0 },
+ { "max98926" },
{ }
};
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),
- .pm = NULL,
},
- .probe = max98926_i2c_probe,
- .remove = max98926_i2c_remove,
+ .probe = max98926_i2c_probe,
.id_table = max98926_i2c_id,
};
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
index 9d7ab6df79ca..d622d5f4384c 100644
--- a/sound/soc/codecs/max98926.h
+++ b/sound/soc/codecs/max98926.h
@@ -1,9 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98926.h -- MAX98926 ALSA SoC Audio driver
* Copyright 2013-2015 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 _MAX98926_H
@@ -838,7 +836,7 @@
struct max98926_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
unsigned int sysclk;
unsigned int v_slot;
unsigned int i_slot;
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index f701fdc81175..0e9b8970997c 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -1,13 +1,9 @@
+// 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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/acpi.h>
@@ -19,12 +15,11 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <sound/tlv.h>
#include "max98927.h"
-static struct reg_default max98927_reg[] = {
+static const struct reg_default max98927_reg[] = {
{MAX98927_R0001_INT_RAW1, 0x00},
{MAX98927_R0002_INT_RAW2, 0x00},
{MAX98927_R0003_INT_RAW3, 0x00},
@@ -142,32 +137,31 @@ static struct reg_default max98927_reg[] = {
static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ 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_CBM_CFM:
- max98927->master = true;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max98927->provider = true;
mode = MAX98927_PCM_MASTER_MODE_MASTER;
break;
default:
- dev_err(codec->dev, "DAI clock mode unsupported\n");
+ 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);
+ 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:
@@ -176,14 +170,12 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported\n");
+ 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);
+ 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) {
@@ -209,36 +201,31 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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_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);
+ 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_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);
+ 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_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_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);
+ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ 0);
}
return 0;
}
@@ -268,12 +255,12 @@ static int max98927_get_bclk_sel(int bclk)
static int max98927_set_clock(struct max98927_priv *max98927,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_codec *codec = max98927->codec;
+ 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->master) {
+ if (max98927->provider) {
int i;
/* match rate to closest value */
for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
@@ -281,28 +268,27 @@ static int max98927_set_clock(struct max98927_priv *max98927,
break;
}
if (i == ARRAY_SIZE(rate_table)) {
- dev_err(codec->dev, "failed to find proper clock rate.\n");
+ 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);
+ 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(codec->dev, "format unsupported %d\n",
+ 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);
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, value);
}
return 0;
}
@@ -311,8 +297,8 @@ 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_codec *codec = dai->codec;
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -328,18 +314,17 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
break;
default:
- dev_err(codec->dev, "format unsupported %d\n",
+ 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);
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
- dev_dbg(codec->dev, "format supported %d",
+ dev_dbg(component->dev, "format supported %d",
params_format(params));
/* sampling rate configuration */
@@ -372,32 +357,29 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
sampling_rate = MAX98927_PCM_SR_SET1_SR_48000;
break;
default:
- dev_err(codec->dev, "rate %d not supported\n",
+ 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);
+ 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);
+ 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);
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
return max98927_set_clock(max98927, params);
err:
return -EINVAL;
@@ -407,8 +389,8 @@ 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_codec *codec = dai->codec;
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -417,15 +399,13 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
/* BCLK configuration */
bsel = max98927_get_bclk_sel(slots * slot_width);
if (bsel == 0) {
- dev_err(codec->dev, "BCLK %d not supported\n",
+ 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);
+ regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel);
/* Channel size configuration */
switch (slot_width) {
@@ -439,38 +419,31 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
break;
default:
- dev_err(codec->dev, "format unsupported %d\n",
+ 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);
+ 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);
+ 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);
+ 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);
+ 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;
}
@@ -483,8 +456,8 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
max98927->sysclk = freq;
return 0;
@@ -500,28 +473,24 @@ static const struct snd_soc_dai_ops max98927_dai_ops = {
static int max98927_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ 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 = 0;
+ 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);
+ 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);
+ 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;
@@ -534,8 +503,8 @@ static const char * const max98927_switch_text[] = {
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);
+ 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);
@@ -545,17 +514,17 @@ static const struct snd_kcontrol_new max98927_vi_control =
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),
+ 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),
+ &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),
+ MAX98927_R003E_MEAS_EN, 0, 0),
SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
- MAX98927_R003E_MEAS_EN, 1, 0),
+ MAX98927_R003E_MEAS_EN, 1, 0),
SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
- &max98927_vi_control),
+ &max98927_vi_control),
SND_SOC_DAPM_SIGGEN("VMON"),
SND_SOC_DAPM_SIGGEN("IMON"),
};
@@ -625,20 +594,19 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
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("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),
+ 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),
+ 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),
+ 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),
+ MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
SOC_ENUM("Current Limit", max98927_current_limit),
};
@@ -677,128 +645,92 @@ static struct snd_soc_dai_driver max98927_dai[] = {
}
};
-static int max98927_probe(struct snd_soc_codec *codec)
+static int max98927_probe(struct snd_soc_component *component)
{
- struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
- max98927->codec = codec;
+ max98927->component = component;
/* Software Reset */
- regmap_write(max98927->regmap,
- MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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));
+ 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);
+ 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));
+ 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);
+ 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);
@@ -811,28 +743,28 @@ 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);
+ 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)
+ SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume)
};
-static const struct snd_soc_codec_driver soc_codec_dev_max98927 = {
- .probe = max98927_probe,
- .component_driver = {
- .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),
- },
+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 = {
@@ -863,17 +795,14 @@ static void max98927_slot_config(struct i2c_client *i2c,
max98927->i_l_slot = 1;
}
-static int max98927_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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);
-
+ max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL);
if (!max98927) {
ret = -ENOMEM;
return ret;
@@ -881,14 +810,14 @@ static int max98927_i2c_probe(struct i2c_client *i2c,
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 = 1;
- else
- max98927->interleave_mode = 0;
- } else
- max98927->interleave_mode = 0;
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) {
+ max98927->interleave_mode = true;
+ } else {
+ if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode",
+ &value))
+ if (value > 0)
+ max98927->interleave_mode = true;
+ }
/* regmap initialization */
max98927->regmap
@@ -900,9 +829,21 @@ static int max98927_i2c_probe(struct i2c_client *i2c,
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);
+ 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);
@@ -914,22 +855,25 @@ static int max98927_i2c_probe(struct i2c_client *i2c,
max98927_slot_config(i2c, max98927);
/* codec registeration */
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int max98927_i2c_remove(struct i2c_client *client)
+static void max98927_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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},
+ { "max98927"},
{ },
};
@@ -956,9 +900,9 @@ static struct i2c_driver max98927_i2c_driver = {
.name = "max98927",
.of_match_table = of_match_ptr(max98927_of_match),
.acpi_match_table = ACPI_PTR(max98927_acpi_match),
- .pm = &max98927_pm,
+ .pm = pm_ptr(&max98927_pm),
},
- .probe = max98927_i2c_probe,
+ .probe = max98927_i2c_probe,
.remove = max98927_i2c_remove,
.id_table = max98927_i2c_id,
};
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
index 9ea839735433..2353910f5f17 100644
--- a/sound/soc/codecs/max98927.h
+++ b/sound/soc/codecs/max98927.h
@@ -1,14 +1,9 @@
+/* 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>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the 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 _MAX98927_H
#define _MAX98927_H
@@ -258,8 +253,9 @@
struct max98927_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ 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;
@@ -268,7 +264,7 @@ struct max98927_priv {
unsigned int ch_size;
unsigned int rate;
unsigned int iface;
- unsigned int master;
+ unsigned int provider;
unsigned int digital_gain;
bool tdm_mode;
};
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index be7a45f05bbf..086ac97e8386 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -1,24 +1,11 @@
+// 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, http://www.phytec.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 Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
+ * Phytec Messtechnik GmbH, https://www.phytec.de
*/
#include <linux/module.h>
#include <linux/device.h>
@@ -107,13 +94,13 @@ 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_codec *codec = dai->codec;
+ 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_update_bits(codec, MC13783_AUDIO_DAC,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_DAC,
0xf << 17, i << 17);
return 0;
}
@@ -126,7 +113,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int rate = params_rate(params);
unsigned int val;
@@ -141,7 +128,7 @@ static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
val);
return 0;
@@ -160,7 +147,7 @@ static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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;
@@ -194,21 +181,20 @@ static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
}
/* DAI clock master masks */
- 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:
val |= AUDIO_C_CLK_EN;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
val |= AUDIO_CSM;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ default:
return -EINVAL;
}
val |= AUDIO_C_RESET;
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -230,11 +216,11 @@ static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
return ret;
/*
- * In synchronous mode force the voice codec into slave mode
+ * 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_MASTER_MASK;
- fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBC_CFC;
ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
return ret;
@@ -255,7 +241,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int clk;
unsigned int val = 0;
unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
@@ -275,7 +261,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
val |= AUDIO_CLK(clk);
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -308,7 +294,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
SSI_NETWORK_DAC_RXSLOT_MASK;
@@ -344,7 +330,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -353,7 +339,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = 0x3f;
@@ -366,7 +352,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
val |= (0x01 << 4); /* secondary timeslot TX is 1 */
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -606,12 +592,12 @@ static struct snd_kcontrol_new mc13783_control_list[] = {
SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
};
-static int mc13783_probe(struct snd_soc_codec *codec)
+static int mc13783_probe(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
- snd_soc_codec_init_regmap(codec,
- dev_get_regmap(codec->dev->parent, NULL));
+ 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);
@@ -638,14 +624,12 @@ static int mc13783_probe(struct snd_soc_codec *codec)
return 0;
}
-static int mc13783_remove(struct snd_soc_codec *codec)
+static void mc13783_remove(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
-
- return 0;
}
#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
@@ -727,21 +711,22 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
.formats = MC13783_FORMATS,
},
.ops = &mc13783_ops_sync,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static const struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
- .probe = mc13783_probe,
- .remove = mc13783_remove,
- .component_driver = {
- .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),
- },
+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)
@@ -782,27 +767,19 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
if (priv->adc_ssi_port == priv->dac_ssi_port)
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
else
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
return ret;
}
-static int mc13783_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver mc13783_codec_driver = {
.driver = {
.name = "mc13783-codec",
},
- .remove = mc13783_codec_remove,
};
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
index 3a6d1993a217..8992d3ab57e6 100644
--- a/sound/soc/codecs/mc13783.h
+++ b/sound/soc/codecs/mc13783.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2008 Juergen Beisert, kernel@pengutronix.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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MC13783_MIXER_H
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 5cc960d8211e..c6585e8143a5 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., 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; 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.
*/
#include <linux/module.h>
@@ -68,7 +56,6 @@ 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 DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
"A-law"};
@@ -338,8 +325,8 @@ 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_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -351,23 +338,23 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
if (priv->clk_in) {
switch (priv->mclk / params_rate(hw_params)) {
case 256:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 1);
break;
case 512:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 2);
break;
case 1024:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 3);
break;
default:
- dev_err(codec->dev, "Unsupported MCLKI\n");
+ dev_err(component->dev, "Unsupported MCLKI\n");
break;
}
} else {
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 0);
}
@@ -375,35 +362,35 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
if (srate < 0)
return srate;
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate);
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv);
+ 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)
+static int ml26124_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
break;
case SNDRV_PCM_STREAM_PLAYBACK:
- snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
break;
}
if (mute)
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_ON);
else
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_OFF);
return 0;
@@ -413,20 +400,19 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
unsigned char mode;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
- /* set master/slave audio interface */
- 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 = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mode = 0;
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+ snd_soc_component_update_bits(component, ML26124_SAI_MODE_SEL, BIT(0), mode);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -450,8 +436,8 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ml26124_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 ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -469,17 +455,17 @@ static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+static int ml26124_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
msleep(100);
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK,
ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
break;
@@ -487,8 +473,8 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* VMID ON */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ 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);
@@ -496,7 +482,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
/* VMID OFF */
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
ML26124_VMID, 0);
break;
}
@@ -505,9 +491,10 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ml26124_dai_ops = {
.hw_params = ml26124_hw_params,
- .digital_mute = ml26124_mute,
+ .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 = {
@@ -525,30 +512,31 @@ static struct snd_soc_dai_driver ml26124_dai = {
.rates = ML26124_RATES,
.formats = ML26124_FORMATS,},
.ops = &ml26124_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ml26124_probe(struct snd_soc_codec *codec)
+static int ml26124_probe(struct snd_soc_component *component)
{
/* Software Reset */
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+ 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_codec_driver soc_codec_dev_ml26124 = {
- .probe = ml26124_probe,
- .set_bias_level = ml26124_set_bias_level,
- .suspend_bias_off = true,
- .component_driver = {
- .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),
- },
+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 = {
@@ -561,8 +549,7 @@ static const struct regmap_config ml26124_i2c_regmap = {
.write_flag_mask = 0x01,
};
-static int ml26124_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ml26124_i2c_probe(struct i2c_client *i2c)
{
struct ml26124_priv *priv;
int ret;
@@ -580,18 +567,12 @@ static int ml26124_i2c_probe(struct i2c_client *i2c,
return ret;
}
- return snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ml26124, &ml26124_dai, 1);
-}
-
-static int ml26124_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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 },
+ { "ml26124" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
@@ -601,7 +582,6 @@ static struct i2c_driver ml26124_i2c_driver = {
.name = "ml26124",
},
.probe = ml26124_i2c_probe,
- .remove = ml26124_i2c_remove,
.id_table = ml26124_i2c_id,
};
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
index 5ea0cbb8c46c..080a6232f41f 100644
--- a/sound/soc/codecs/ml26124.h
+++ b/sound/soc/codecs/ml26124.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., 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; 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.
*/
#ifndef ML26124_H
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 44062bb7bf2f..9ca381812975 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -1,10 +1,12 @@
+// 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>
@@ -16,8 +18,8 @@
#define CDC_D_REVISION1 (0xf000)
#define CDC_D_PERPH_SUBTYPE (0xf005)
-#define CDC_D_INT_EN_SET (0x015)
-#define CDC_D_INT_EN_CLR (0x016)
+#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)
@@ -225,6 +227,10 @@
#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)
@@ -288,7 +294,7 @@ struct pm8916_wcd_analog_priv {
int mbhc_btn0_released;
bool detect_accessory_type;
struct clk *mclk;
- struct snd_soc_codec *codec;
+ 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;
@@ -303,12 +309,13 @@ struct pm8916_wcd_analog_priv {
};
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
-static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" };
+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);
@@ -318,7 +325,7 @@ static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
/* RDAC2 MUX */
static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
- CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text);
+ 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)
@@ -338,18 +345,18 @@ static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = {
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_codec *codec)
+static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component)
{
- struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
- snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
+ 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_update_bits(codec, CDC_A_MICB_1_VAL,
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_VAL,
MICB_1_VAL_MICB_OUT_VAL_MASK,
MICB_VOLTAGE_REGVAL(wcd->micbias_mv));
/*
@@ -360,20 +367,19 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec)
msleep(50);
}
- snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
+ 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_ext(struct snd_soc_codec
- *codec, int event,
- int reg, unsigned int cap_mode)
+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(codec);
- snd_soc_update_bits(codec, CDC_A_MICB_1_EN,
+ 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;
}
@@ -381,91 +387,68 @@ static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_codec
return 0;
}
-static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_codec
- *codec, int event,
- int reg, u32 cap_mode)
+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_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
- MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
- MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
- snd_soc_update_bits(codec, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0);
- snd_soc_update_bits(codec, CDC_A_MICB_1_EN,
+ 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;
- case SND_SOC_DAPM_POST_PMU:
- pm8916_wcd_analog_micbias_enable(codec);
- snd_soc_update_bits(codec, CDC_A_MICB_1_EN,
- MICB_1_EN_BYP_CAP_MASK, cap_mode);
break;
}
return 0;
}
-static int pm8916_wcd_analog_enable_micbias_ext1(struct
- snd_soc_dapm_widget
- *w, struct snd_kcontrol
- *kcontrol, int event)
+static int pm8916_wcd_analog_enable_micbias1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+ 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_ext(codec, event, w->reg,
- wcd->micbias1_cap_mode);
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias1_cap_mode);
}
-static int pm8916_wcd_analog_enable_micbias_ext2(struct
- snd_soc_dapm_widget
- *w, struct snd_kcontrol
- *kcontrol, int event)
+static int pm8916_wcd_analog_enable_micbias2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+ 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_ext(codec, event, w->reg,
- wcd->micbias2_cap_mode);
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias2_cap_mode);
}
-static int pm8916_wcd_analog_enable_micbias_int1(struct
- snd_soc_dapm_widget
- *w, struct snd_kcontrol
- *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
-
- return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg,
- wcd->micbias1_cap_mode);
-}
-
static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
bool micbias2_enabled)
{
- struct snd_soc_codec *codec = priv->codec;
+ 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_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
+ 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_update_bits(codec, CDC_A_MICB_2_EN,
+ 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_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+ 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_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+ 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);
@@ -482,7 +465,7 @@ static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
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_update_bits(codec, reg_addr,
+ snd_soc_component_update_bits(component, reg_addr,
CDC_A_MBHC_BTN_VREF_MASK,
reg_val);
reg_addr++;
@@ -493,12 +476,12 @@ static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
{
- struct snd_soc_codec *codec = wcd->codec;
+ struct snd_soc_component *component = wcd->component;
bool micbias_enabled = false;
u32 plug_type = 0;
u32 int_en_mask;
- snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1,
+ 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 |
@@ -510,23 +493,23 @@ static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
if (wcd->gnd_jack_type_normally_open)
plug_type |= CDC_A_GND_PLUG_TYPE_NO;
- snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2,
+ 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_write(codec, CDC_A_MBHC_DBNC_TIMER,
+ 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_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL,
+ 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_read(codec, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE)
+ 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);
@@ -535,8 +518,8 @@ static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
if (wcd->mbhc_btn_enabled)
int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET;
- snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0);
- snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask);
+ 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;
}
@@ -546,10 +529,14 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
*w, struct snd_kcontrol
*kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -558,15 +545,14 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
break;
}
- return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg,
- wcd->micbias2_cap_mode);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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;
@@ -578,7 +564,7 @@ static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (w->reg == CDC_A_TX_2_EN)
- snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
+ 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);
/*
@@ -587,17 +573,17 @@ static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
* happen when the input voltage is changing too much.
*/
usleep_range(10000, 10010);
- snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift,
+ 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_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL,
+ 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_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL,
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
CONN_TX2_SERIAL_TX2_MUX,
CONN_TX2_SERIAL_TX2_ADC_2);
break;
@@ -609,21 +595,21 @@ static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
* to reduce the tx pop
*/
usleep_range(12000, 12010);
- snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
+ 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_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL,
+ 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_update_bits(codec, CDC_A_MICB_1_CTL,
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
MICB_1_CTL_CFILT_REF_SEL_MASK, 0);
- /* fall through */
+ fallthrough;
case CDC_A_TX_3_EN:
- snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL,
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
CONN_TX2_SERIAL_TX2_MUX,
CONN_TX2_SERIAL_TX2_ZERO);
break;
@@ -639,11 +625,11 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL,
+ 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 |
@@ -653,35 +639,63 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
SPKR_PWRSTG_CTL_HBRDGE_EN |
SPKR_PWRSTG_CTL_CLAMP_EN);
- snd_soc_update_bits(codec, CDC_A_RX_EAR_CTL,
+ 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_update_bits(codec, CDC_A_SPKR_DRV_CTL,
+ snd_soc_component_update_bits(component, CDC_A_SPKR_DRV_CTL,
SPKR_DRV_CTL_DEF_MASK,
SPKR_DRV_CTL_DEF_VAL);
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL,
+ 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_update_bits(codec, CDC_A_SPKR_DAC_CTL,
+ 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_update_bits(codec, CDC_A_RX_EAR_CTL,
+ 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},
@@ -701,36 +715,36 @@ static const struct reg_default wcd_reg_defaults_2_0[] = {
{CDC_A_MASTER_BIAS_CTL, 0x30},
};
-static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
+static int pm8916_wcd_analog_probe(struct snd_soc_component *component)
{
- struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev);
+ 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(codec->dev, "failed to enable regulators (%d)\n", err);
+ dev_err(component->dev, "failed to enable regulators (%d)\n", err);
return err;
}
- snd_soc_codec_init_regmap(codec,
- dev_get_regmap(codec->dev->parent, NULL));
- snd_soc_codec_set_drvdata(codec, priv);
- priv->pmic_rev = snd_soc_read(codec, CDC_D_REVISION1);
- priv->codec_version = snd_soc_read(codec, CDC_D_PERPH_SUBTYPE);
+ 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(codec->dev, "PMIC REV: %d\t CODEC Version: %d\n",
+ dev_info(component->dev, "PMIC REV: %d\t CODEC Version: %d\n",
priv->pmic_rev, priv->codec_version);
- snd_soc_write(codec, CDC_D_PERPH_RESET_CTL4, 0x01);
- snd_soc_write(codec, CDC_A_PERPH_RESET_CTL4, 0x01);
+ 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_write(codec, wcd_reg_defaults_2_0[reg].reg,
+ snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg,
wcd_reg_defaults_2_0[reg].def);
- priv->codec = codec;
+ priv->component = component;
- snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+ 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);
@@ -739,14 +753,14 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
return 0;
}
-static int pm8916_wcd_analog_remove(struct snd_soc_codec *codec)
+static void pm8916_wcd_analog_remove(struct snd_soc_component *component)
{
- struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev);
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev);
- snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+ snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL,
RST_CTL_DIG_SW_RST_N_MASK, 0);
- return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
priv->supplies);
}
@@ -798,12 +812,20 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
{"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) */
- {"HEADPHONE", NULL, "HPHL PA"},
- {"HEADPHONE", NULL, "HPHR PA"},
+ {"HPH_L", NULL, "HPHL PA"},
+ {"HPH_R", NULL, "HPHR PA"},
- {"HPHL PA", NULL, "EAR_HPHL_CLK"},
- {"HPHR PA", NULL, "EAR_HPHR_CLK"},
+ {"HPHL DAC", NULL, "EAR_HPHL_CLK"},
+ {"HPHR DAC", NULL, "EAR_HPHR_CLK"},
{"CP", NULL, "NCP_CLK"},
@@ -824,14 +846,16 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
{"SPK PA", NULL, "SPK DAC"},
{"SPK DAC", "Switch", "PDM_RX3"},
- {"MIC BIAS Internal1", NULL, "INT_LDO_H"},
- {"MIC BIAS Internal2", NULL, "INT_LDO_H"},
- {"MIC BIAS External1", NULL, "INT_LDO_H"},
- {"MIC BIAS External2", NULL, "INT_LDO_H"},
- {"MIC BIAS Internal1", NULL, "vdd-micbias"},
- {"MIC BIAS Internal2", NULL, "vdd-micbias"},
- {"MIC BIAS External1", NULL, "vdd-micbias"},
- {"MIC BIAS External2", NULL, "vdd-micbias"},
+ {"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[] = {
@@ -844,11 +868,21 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AMIC1"),
SND_SOC_DAPM_INPUT("AMIC3"),
SND_SOC_DAPM_INPUT("AMIC2"),
- SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+ 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,
@@ -874,21 +908,26 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0),
/* TX */
- SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_EN, 7, 0,
- pm8916_wcd_analog_enable_micbias_int1,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_2_EN, 7, 0,
+ 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 External1", CDC_A_MICB_1_EN, 7, 0,
- pm8916_wcd_analog_enable_micbias_ext1,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0,
- pm8916_wcd_analog_enable_micbias_ext2,
- 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,
@@ -934,11 +973,11 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
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_codec *codec,
+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_codec_get_drvdata(codec);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
wcd->jack = jack;
@@ -950,8 +989,8 @@ 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_codec *codec = priv->codec;
- u32 val = snd_soc_read(codec, CDC_A_MBHC_RESULT_1);
+ 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))
@@ -967,10 +1006,10 @@ static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
{
struct pm8916_wcd_analog_priv *priv = arg;
- struct snd_soc_codec *codec = priv->codec;
+ struct snd_soc_component *component = priv->component;
u32 btn_result;
- btn_result = snd_soc_read(codec, CDC_A_MBHC_RESULT_1) &
+ btn_result = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1) &
CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
switch (btn_result) {
@@ -993,7 +1032,7 @@ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
SND_JACK_BTN_0, btn_mask);
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Unexpected button press result (%x)", btn_result);
break;
}
@@ -1004,15 +1043,15 @@ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
{
struct pm8916_wcd_analog_priv *priv = arg;
- struct snd_soc_codec *codec = priv->codec;
+ struct snd_soc_component *component = priv->component;
bool ins = false;
- if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
+ 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_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
+ 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));
@@ -1020,7 +1059,7 @@ static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
if (ins) { /* hs insertion */
bool micbias_enabled = false;
- if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
+ if (snd_soc_component_read(component, CDC_A_MICB_2_EN) &
CDC_A_MICB_2_EN_ENABLE)
micbias_enabled = true;
@@ -1075,18 +1114,19 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
},
};
-static const struct snd_soc_codec_driver pm8916_wcd_analog = {
- .probe = pm8916_wcd_analog_probe,
- .remove = pm8916_wcd_analog_remove,
- .set_jack = pm8916_wcd_analog_set_jack,
- .component_driver = {
- .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),
- },
+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,
@@ -1157,12 +1197,6 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
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];
@@ -1173,71 +1207,57 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
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) {
- dev_err(dev, "failed to get mbhc switch irq\n");
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(dev, irq, pm8916_mbhc_switch_irq_handler,
+ 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)
+ if (ret) {
dev_err(dev, "cannot request mbhc switch irq\n");
+ return ret;
+ }
if (priv->mbhc_btn_enabled) {
irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
- if (irq < 0) {
- dev_err(dev, "failed to get button press irq\n");
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(dev, irq, mbhc_btn_press_irq_handler,
+ 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)
+ if (ret) {
dev_err(dev, "cannot request mbhc button press irq\n");
+ return ret;
+ }
irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
- if (irq < 0) {
- dev_err(dev, "failed to get button release irq\n");
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(dev, irq, mbhc_btn_release_irq_handler,
+ 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)
+ if (ret) {
dev_err(dev, "cannot request mbhc button release irq\n");
-
+ return ret;
+ }
}
dev_set_drvdata(dev, priv);
- return snd_soc_register_codec(dev, &pm8916_wcd_analog,
+ return devm_snd_soc_register_component(dev, &pm8916_wcd_analog,
pm8916_wcd_analog_dai,
ARRAY_SIZE(pm8916_wcd_analog_dai));
}
-static int pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
-{
- struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev);
-
- snd_soc_unregister_codec(&pdev->dev);
- clk_disable_unprepare(priv->mclk);
-
- return 0;
-}
-
static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
{ .compatible = "qcom,pm8916-wcd-analog-codec", },
{ }
@@ -1251,7 +1271,6 @@ static struct platform_driver pm8916_wcd_analog_spmi_driver = {
.of_match_table = pm8916_wcd_analog_spmi_match_table,
},
.probe = pm8916_wcd_analog_spmi_probe,
- .remove = pm8916_wcd_analog_spmi_remove,
};
module_platform_driver(pm8916_wcd_analog_spmi_driver);
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c
index 13354d6304a8..ebb6f2e84818 100644
--- a/sound/soc/codecs/msm8916-wcd-digital.c
+++ b/sound/soc/codecs/msm8916-wcd-digital.c
@@ -1,14 +1,5 @@
-/* Copyright (c) 2016, The Linux Foundation. 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 and
- * only 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016, The Linux Foundation. All rights reserved.
#include <linux/module.h>
#include <linux/err.h>
@@ -196,6 +187,43 @@
#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;
};
@@ -215,13 +243,15 @@ 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" };
-static const char *const rx_mix2_text[] = { "ZERO", "IIR1", "IIR2" };
-static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
/* RX1 MIX1 */
static const struct soc_enum rx_mix1_inp_enum[] = {
@@ -230,10 +260,6 @@ static const struct soc_enum rx_mix1_inp_enum[] = {
SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_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 MIX1 */
static const struct soc_enum rx2_mix1_inp_enum[] = {
SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text),
@@ -241,10 +267,6 @@ static const struct soc_enum rx2_mix1_inp_enum[] = {
SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_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);
-
/* 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),
@@ -252,6 +274,16 @@ static const struct soc_enum rx3_mix1_inp_enum[] = {
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);
@@ -291,9 +323,13 @@ 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 -38.4 dB to +38.4 dB in 0.3 dB steps */
-static const DECLARE_TLV_DB_SCALE(digital_gain, -3840, 30, 0);
+/* 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[] = {
@@ -317,17 +353,172 @@ static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0,
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,
- -128, 127, digital_gain),
+ -84, 40, digital_gain),
SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL,
- -128, 127, digital_gain),
+ -84, 40, digital_gain),
SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL,
- -128, 127, digital_gain),
+ -84, 40, digital_gain),
SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN,
- -128, 127, digital_gain),
+ -84, 40, digital_gain),
SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN,
- -128, 127, digital_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),
@@ -341,6 +532,44 @@ static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = {
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(
@@ -348,14 +577,20 @@ static int msm8916_wcd_digital_enable_interpolator(
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_write(codec, rx_gain_reg[w->shift],
- snd_soc_read(codec, rx_gain_reg[w->shift]));
+ 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;
@@ -365,7 +600,7 @@ static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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;
@@ -377,46 +612,46 @@ static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable TX digital mute */
- snd_soc_update_bits(codec, tx_vol_ctl_reg,
+ 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_read(codec, tx_mux_ctl_reg) &
+ 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_update_bits(codec, tx_mux_ctl_reg,
+ 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_update_bits(codec, tx_mux_ctl_reg,
+ 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_write(codec, tx_gain_reg[w->shift],
- snd_soc_read(codec, tx_gain_reg[w->shift]));
- snd_soc_update_bits(codec, tx_vol_ctl_reg,
+ 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_update_bits(codec, tx_vol_ctl_reg,
+ 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_update_bits(codec, tx_mux_ctl_reg,
+ 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_update_bits(codec, dec_reset_reg, 1 << w->shift,
+ snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift,
1 << w->shift);
- snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0);
- snd_soc_update_bits(codec, tx_mux_ctl_reg,
+ 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_update_bits(codec, tx_vol_ctl_reg,
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
TX_VOL_CTL_CFG_MUTE_EN_MASK, 0);
break;
}
@@ -428,35 +663,35 @@ static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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(codec->dev, "Invalid DMIC\n");
+ dev_err(component->dev, "Invalid DMIC\n");
return -EINVAL;
}
ret = kstrtouint(dmic_num, 10, &dmic);
if (ret < 0 || dmic > 2) {
- dev_err(codec->dev, "Invalid DMIC line on the codec\n");
+ dev_err(component->dev, "Invalid DMIC line on the component\n");
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, LPASS_CDC_CLK_DMIC_B1_CTL,
+ 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_update_bits(codec, LPASS_CDC_TX1_DMIC_CTL,
+ 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_update_bits(codec, LPASS_CDC_TX2_DMIC_CTL,
+ snd_soc_component_update_bits(component, LPASS_CDC_TX2_DMIC_CTL,
TXN_DMIC_CTL_CLK_SEL_MASK,
TXN_DMIC_CTL_CLK_SEL_DIV3);
break;
@@ -467,6 +702,24 @@ static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w,
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),
@@ -511,6 +764,10 @@ static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = {
&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),
@@ -553,6 +810,15 @@ static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = {
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,
@@ -575,20 +841,20 @@ static int msm8916_wcd_digital_get_clks(struct platform_device *pdev,
return 0;
}
-static int msm8916_wcd_digital_codec_probe(struct snd_soc_codec *codec)
+static int msm8916_wcd_digital_component_probe(struct snd_soc_component *component)
{
- struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(codec->dev);
+ struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(component->dev);
- snd_soc_codec_set_drvdata(codec, priv);
+ snd_soc_component_set_drvdata(component, priv);
return 0;
}
-static int msm8916_wcd_digital_codec_set_sysclk(struct snd_soc_codec *codec,
+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(codec->dev);
+ struct msm8916_wcd_digital_priv *p = dev_get_drvdata(component->dev);
return clk_set_rate(p->mclk, freq);
}
@@ -618,18 +884,18 @@ static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream,
rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ;
break;
default:
- dev_err(dai->codec->dev, "Invalid sampling rate %d\n",
+ 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_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL,
+ 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_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL,
+ 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:
@@ -638,18 +904,19 @@ static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL,
+ 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_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL,
+ 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_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL,
+ 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_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL,
+ 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;
@@ -726,10 +993,14 @@ static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
{"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"},
@@ -746,10 +1017,14 @@ static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
{"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"},
@@ -766,10 +1041,27 @@ static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
{"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"},
@@ -780,32 +1072,32 @@ static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct msm8916_wcd_digital_priv *msm8916_wcd;
unsigned long mclk_rate;
- msm8916_wcd = snd_soc_codec_get_drvdata(codec);
- snd_soc_update_bits(codec, LPASS_CDC_CLK_MCLK_CTL,
+ 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_update_bits(codec, LPASS_CDC_CLK_PDM_CTL,
+ 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_update_bits(codec, LPASS_CDC_TOP_CTL,
+ 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_update_bits(codec, LPASS_CDC_TOP_CTL,
+ 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(codec->dev, "Invalid mclk rate %ld\n", mclk_rate);
+ dev_err(component->dev, "Invalid mclk rate %ld\n", mclk_rate);
break;
}
return 0;
@@ -814,7 +1106,7 @@ static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream,
static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_PDM_CTL,
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_PDM_CTL,
LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0);
}
@@ -851,18 +1143,18 @@ static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = {
},
};
-static const struct snd_soc_codec_driver msm8916_wcd_digital = {
- .probe = msm8916_wcd_digital_codec_probe,
- .set_sysclk = msm8916_wcd_digital_codec_set_sysclk,
- .component_driver = {
- .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),
- },
+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 = {
@@ -878,7 +1170,6 @@ 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 resource *mem_res;
struct regmap *digital_map;
int ret;
@@ -886,8 +1177,7 @@ static int msm8916_wcd_digital_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem_res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -910,25 +1200,32 @@ static int msm8916_wcd_digital_probe(struct platform_device *pdev)
ret = clk_prepare_enable(priv->mclk);
if (ret < 0) {
dev_err(dev, "failed to enable mclk %d\n", ret);
- return ret;
+ goto err_clk;
}
dev_set_drvdata(dev, priv);
- return snd_soc_register_codec(dev, &msm8916_wcd_digital,
+ 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 int msm8916_wcd_digital_remove(struct platform_device *pdev)
+static void msm8916_wcd_digital_remove(struct platform_device *pdev)
{
struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_codec(&pdev->dev);
clk_disable_unprepare(priv->mclk);
clk_disable_unprepare(priv->ahbclk);
-
- return 0;
}
static const struct of_device_id msm8916_wcd_digital_match_table[] = {
diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c
new file mode 100644
index 000000000000..2a5e963fb2b5
--- /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/mod_devicetable.h>
+#include <linux/module.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/mt6357.c b/sound/soc/codecs/mt6357.c
new file mode 100644
index 000000000000..988728df15e4
--- /dev/null
+++ b/sound/soc/codecs/mt6357.c
@@ -0,0 +1,1855 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MT6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include "mt6357.h"
+
+static void set_playback_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_CLR, MT6357_GPIO_MODE2_CLEAR_ALL);
+ if (enable) {
+ /* set gpio mosi mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_SET,
+ MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI |
+ MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 |
+ MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 |
+ MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6357_GPIO_DIR0,
+ MT6357_GPIO8_DIR_MASK |
+ MT6357_GPIO9_DIR_MASK |
+ MT6357_GPIO10_DIR_MASK |
+ MT6357_GPIO11_DIR_MASK,
+ MT6357_GPIO8_DIR_INPUT |
+ MT6357_GPIO9_DIR_INPUT |
+ MT6357_GPIO10_DIR_INPUT |
+ MT6357_GPIO11_DIR_INPUT);
+ }
+}
+
+static void set_capture_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_CLR, MT6357_GPIO_MODE3_CLEAR_ALL);
+ if (enable) {
+ /* set gpio miso mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_SET,
+ MT6357_GPIO12_MODE_SET_AUD_CLK_MISO |
+ MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 |
+ MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 |
+ MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to 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, MT6357_GPIO_DIR0,
+ MT6357_GPIO12_DIR_MASK |
+ MT6357_GPIO13_DIR_MASK |
+ MT6357_GPIO14_DIR_MASK |
+ MT6357_GPIO15_DIR_MASK,
+ MT6357_GPIO12_DIR_INPUT |
+ MT6357_GPIO13_DIR_INPUT |
+ MT6357_GPIO14_DIR_INPUT |
+ MT6357_GPIO15_DIR_INPUT);
+ }
+}
+
+static void hp_main_output_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX; i++) {
+ stage = up ? i : MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT);
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPROUT_STG_CTRL_VAUDP15_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= MT6357_HP_AUX_LOOP_GAIN_MAX; i++) {
+ stage = up ? i : MT6357_HP_AUX_LOOP_GAIN_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_AUX_LOOP_GAIN_MASK,
+ stage << MT6357_HP_AUX_LOOP_GAIN_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_pull_down(struct mt6357_priv *priv, bool enable)
+{
+ if (enable)
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE);
+ else
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE);
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_12DB) || reg_idx == DL_GAIN_N_40DB;
+}
+
+static void volume_ramp(struct mt6357_priv *priv, int lfrom, int lto,
+ int rfrom, int rto, unsigned int reg_addr)
+{
+ int lcount, rcount, sleep = 0;
+
+ if (!is_valid_hp_pga_idx(lfrom) || !is_valid_hp_pga_idx(lto))
+ pr_debug("%s(), invalid left volume index, from %d, to %d\n",
+ __func__, lfrom, lto);
+
+ if (!is_valid_hp_pga_idx(rfrom) || !is_valid_hp_pga_idx(rto))
+ pr_debug("%s(), invalid right volume index, from %d, to %d\n",
+ __func__, rfrom, rto);
+
+ if (lto > lfrom)
+ lcount = 1;
+ else
+ lcount = -1;
+
+ if (rto > rfrom)
+ rcount = 1;
+ else
+ rcount = -1;
+
+ while ((lto != lfrom) || (rto != rfrom)) {
+ if (lto != lfrom) {
+ lfrom += lcount;
+ if (is_valid_hp_pga_idx(lfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_LEFT_MASK,
+ lfrom << MT6357_DL_GAIN_REG_LEFT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (rto != rfrom) {
+ rfrom += rcount;
+ if (is_valid_hp_pga_idx(rfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_RIGHT_MASK,
+ rfrom << MT6357_DL_GAIN_REG_RIGHT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (sleep)
+ usleep_range(200, 300);
+ }
+}
+
+static void lo_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON1);
+}
+
+static void hp_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON2);
+}
+
+static void hs_volume_ramp(struct mt6357_priv *priv, int from, int to)
+{
+ volume_ramp(priv, from, to, 0, 0, MT6357_ZCD_CON3);
+}
+
+/* Volume and channel swap controls */
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(hp_degain_tlv, -1200, 1200, 0);
+
+static const struct snd_kcontrol_new mt6357_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_TLV("Headphone Volume",
+ MT6357_ZCD_CON2, MT6357_AUD_HPL_GAIN_SFT,
+ MT6357_AUD_HPR_GAIN_SFT, MT6357_AUD_HP_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Headphone Vin Volume",
+ MT6357_AUDDEC_ANA_CON7, MT6357_HP_IVBUF_DEGAIN_SFT,
+ MT6357_HP_IVBUF_DEGAIN_MAX, 1, hp_degain_tlv),
+ SOC_DOUBLE_TLV("Lineout Volume",
+ MT6357_ZCD_CON1, MT6357_AUD_LOL_GAIN_SFT,
+ MT6357_AUD_LOR_GAIN_SFT, MT6357_AUD_LO_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Handset Volume",
+ MT6357_ZCD_CON3, MT6357_AUD_HS_GAIN_SFT,
+ MT6357_AUD_HS_GAIN_MAX, 1, playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ MT6357_AUDENC_ANA_CON0, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPLGAIN_SFT, MT6357_AUDPREAMPLGAIN_MAX,
+ 0, capture_tlv),
+};
+
+/* Uplink controls */
+
+enum {
+ 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,
+ MIC_TYPE_MUX_LPBK,
+ MIC_TYPE_MUX_SGEN,
+};
+
+#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",
+ "Loopback",
+ "Sine Generator",
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_type_mux_map_enum, SND_SOC_NOPM,
+ 0, mic_type_mux_map);
+
+static const struct snd_kcontrol_new mic_type_mux_control =
+ SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum);
+
+static const char * const pga_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+/* Downlink controls */
+static const char * const hslo_mux_map[] = {
+ "Open", "DACR", "Playback", "Test mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new lo_mux_control =
+ SOC_DAPM_ENUM("Line out source", lo_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hs_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new hs_mux_control =
+ SOC_DAPM_ENUM("Handset source", hs_mux_map_enum);
+
+static const char * const hplr_mux_map[] = {
+ "Open", "Line Out", "DAC", "Handset"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpr_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpr_mux_control =
+ SOC_DAPM_ENUM("Headphone Right source", hpr_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpl_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpl_mux_control =
+ SOC_DAPM_ENUM("Headphone Left source", hpl_mux_map_enum);
+
+static const char * const dac_mux_map[] = {
+ "Normal Path", "Sine Generator"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_mux_map_enum,
+ MT6357_AFE_TOP_CON0,
+ MT6357_DL_SINE_ON_SFT,
+ dac_mux_map);
+
+static const struct snd_kcontrol_new dac_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_mux_map_enum);
+
+static int mt6357_set_dmic(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* DMIC enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_DEFAULT_VALUE | MT6357_AUDDIGMICEN_ENABLE);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_ENABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_SELECT);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_DESELECT);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_DISABLE);
+ /* DMIC disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_OFF | MT6357_AUDDIGMICEN_DISABLE);
+ }
+ return 0;
+}
+
+static int mt6357_set_amic(struct mt6357_priv *priv, bool enable, unsigned int mic_type)
+{
+ if (enable) {
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_RUN_VALUE);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_OUTPUT);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_ON);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG1,
+ MT6357_DCCLK_RESYNC_BYPASS_MASK,
+ MT6357_DCCLK_RESYNC_BYPASS);
+
+ /* mic bias 0: set the correct DC couple*/
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_ALL);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_P1);
+ break;
+ default:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ break;
+ }
+
+ /* mic bias 1: set the correct DC couple */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_ENABLE);
+
+ /* Audio L/R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_ENABLE);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_ENABLE);
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_DC);
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_DC);
+ } else {
+ /* Audio L preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* Audio R preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_AC);
+ }
+ } else {
+ /* disable any Mic Bias 0 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ /* disable any Mic Bias 1 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_DISABLE);
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_OFF);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_PDN);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_STOP_VALUE);
+ }
+ }
+
+ return 0;
+}
+
+static int mt6357_set_loopback(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* enable aud_pad lpk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE);
+ /* Set UL Part: enable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_DISABLE);
+ /* disable aud_pad lpbk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ return 0;
+}
+
+static int mt6357_set_ul_sine_gen(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ 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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_capture_gpio(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ set_capture_gpio(priv, false);
+ 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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK, MT6357_RSTB_ENCODER_VA28_ENABLE);
+ /* Enable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK, MT6357_LCLDO_ENC_EN_VA28_ENABLE);
+ /* LCLDO_ENC remote sense */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_ENABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* LCLDO_ENC remote sense off */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_DISABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE);
+ /* disable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK,
+ MT6357_LCLDO_ENC_EN_VA28_DISABLE);
+ /* disable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK,
+ MT6357_RSTB_ENCODER_VA28_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, true);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, true);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, true);
+ break;
+ default:
+ mt6357_set_amic(priv, true, mic_type);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, false);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, false);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, false);
+ break;
+ default:
+ mt6357_set_amic(priv, false, mic_type);
+ break;
+ }
+ 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);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* L preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_ENABLE);
+ /* L ADC input sel : L PGA. Enable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRUP);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio L ADC input sel : off, disable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_IDLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* L preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_DISABLE);
+ /* disable Audio L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ 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);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* R preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_ENABLE);
+ /* R ADC input sel : R PGA. Enable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRINPUTSEL_MASK,
+ MT6357_AUDADCRINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRUP);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio R ADC input sel : off, disable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCRINPUTSEL_MASK, MT6357_AUDADCRINPUTSEL_IDLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK, MT6357_AUDPREAMPRDCCEN_AC);
+ /* R preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_DISABLE);
+ /* disable Audio R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_enable_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON0, &lgain);
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON1, &rgain);
+ /* L PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPLGAIN_SFT);
+ /* R PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPRGAIN_SFT);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ /* set the mic gains to the stored values */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK, lgain);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK, rgain);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void configure_downlinks(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ELR_0,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE);
+ /* Disable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK | MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* Disable lineout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_AUD_REFN_DERES_VAUDP15_MASK,
+ MT6357_AUD_REFN_DERES_VAUDP15_ENABLE);
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON1, MT6357_DIVCKS_ON);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON0, MT6357_DIVCKS_CHG);
+ /* Set NCP soft start mode as default mode: 150us */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON4,
+ MT6357_DIVCKS_PWD_NCP_ST_150US);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_ENABLE);
+ usleep_range(250, 270);
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_ENABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_LCLDO_EN_VA18_ENABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_HCLDO_EN_VA18_ENABLE);
+ /* Enable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK, MT6357_NVREG_EN_VAUDP15_ENABLE);
+ usleep_range(100, 120);
+ /* Enable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE);
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_ENABLE);
+ /* Enable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_ENABLE);
+ usleep_range(100, 120);
+ } else {
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_DISABLE);
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_DISABLE);
+ /* Enable linout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_ENABLE);
+ /* Enable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_ENABLE);
+ /* Enable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_ENABLE);
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE);
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK,
+ MT6357_NVREG_EN_VAUDP15_DISABLE);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_DISABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_LCLDO_EN_VA18_DISABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_HCLDO_EN_VA18_DISABLE);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_MASK, MT6357_DIVCKS_PWD_NCP_DISABLE);
+ }
+}
+
+static int mt_audio_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_playback_gpio(priv, true);
+
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+
+ /* Disable HP main CMFB Switch */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Audio system digital clock power down release */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_NORMAL_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_ENABLE);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_ENABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+
+ configure_downlinks(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ configure_downlinks(priv, false);
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_DOWN |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_DISABLE);
+
+ set_playback_gpio(priv, false);
+
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ 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;
+}
+
+static int lo_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON1, &lgain);
+ rgain = (lgain & MT6357_AUD_LOR_GAIN_MASK) >> MT6357_AUD_LOR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_LOL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE);
+ /* Set LOL gain to normal gain step by step */
+ lo_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease LOL gain to minimum gain step by step */
+
+ lo_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE);
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE);
+ /* Clear LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ lgain << MT6357_AUD_LOL_GAIN_SFT |
+ rgain << MT6357_AUD_LOR_GAIN_SFT);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hs_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int gain; /* HS register has only one gain slot */
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON3, &gain);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK,
+ DL_GAIN_N_40DB);
+
+ /* Set HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE);
+ /* Set HS gain to normal gain step by step */
+ hs_volume_ramp(priv, DL_GAIN_N_40DB, gain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease HS gain to minimum gain step by step */
+ hs_volume_ramp(priv, gain, DL_GAIN_N_40DB);
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE);
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Clear HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK, gain);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hp_main_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON2, &lgain);
+ rgain = (lgain & MT6357_AUD_HPR_GAIN_MASK) >> MT6357_AUD_HPR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_HPL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ priv->hp_channel_number++;
+ if (priv->hp_channel_number > 1)
+ break;
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set HPP/N STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPROUT_STB_ENH_VAUDP15_MASK |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_NORMAL |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Enable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE);
+ /* Enable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE);
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE);
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Enable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+ usleep_range(1000, 1200);
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* apply volume setting */
+ hp_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ usleep_range(100, 120);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_channel_number--;
+ if (priv->hp_channel_number > 0)
+ break;
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* decrease HPL/R gain to normal gain step by step */
+ hp_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* 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, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_RESET |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Disable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE);
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE);
+ /* Disable HP aux CMFB loop,
+ * Enable HP main CMFB for HP off state
+ */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ lgain << MT6357_AUD_HPL_GAIN_SFT |
+ rgain << MT6357_AUD_HPR_GAIN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int right_dac_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int left_dac_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Supply widgets subsequence */
+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,
+};
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6357_dapm_widgets[] = {
+ /* Analog Clocks */
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6357_AUDDEC_ANA_CON11,
+ MT6357_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6357_AUDENC_ANA_CON6,
+ MT6357_CLKSQ_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_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,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+
+ /* Digital Clocks */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_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,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_RESERVED_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_LPBK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_LPBK_CTL_SFT, 1, NULL, 0),
+
+ /* General */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6357_AFE_UL_DL_CON0,
+ MT6357_AFE_ON_SFT, 0, NULL, 0),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "MT6357 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),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, adc_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_left_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_right_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ 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_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_SUPPLY("MICBIAS0", MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_PWD_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_PWD_SFT, 0, NULL, 0),
+
+ /* UL inputs */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("LPBK"),
+ SND_SOC_DAPM_INPUT("SGEN UL"),
+
+ /* Downlinks */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "MT6357 Playback", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &dac_mux_control),
+
+ SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM, 0, 0, right_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM, 0, 0, left_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Analog Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL SRC", MT6357_AFE_DL_SRC2_CON0_L,
+ MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("Line Out Source", SND_SOC_NOPM, 0, 0, &lo_mux_control,
+ lo_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Handset Source", SND_SOC_NOPM, 0, 0, &hs_mux_control,
+ hs_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Right Source", SND_SOC_NOPM, 0, 0, &hpr_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Left Source", SND_SOC_NOPM, 0, 0, &hpl_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ /* DL outputs */
+ SND_SOC_DAPM_OUTPUT("Headphones"),
+ SND_SOC_DAPM_OUTPUT("Hansdet"),
+ SND_SOC_DAPM_OUTPUT("Line out"),
+
+ /* Sine generator */
+ SND_SOC_DAPM_SUPPLY("SGEN UL Enable",
+ MT6357_AFE_TOP_CON0, MT6357_UL_SINE_ON_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN Enable",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_DAC_EN_CTL_SFT, 0, mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_MUTE_SW_CTL_SFT, 1, NULL, 0)
+};
+
+static const struct snd_soc_dapm_route mt6357_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "Mic Type 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"},
+
+ {"Mic Type Mux", "ACC", "ADC"},
+ {"Mic Type Mux", "DCC", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC"},
+ {"Mic Type Mux", "DMIC", "AIN0"},
+ {"Mic Type Mux", "DMIC", "AIN2"},
+ {"Mic Type Mux", "Loopback", "LPBK"},
+ {"Mic Type Mux", "Sine Generator", "SGEN UL"},
+
+ {"SGEN UL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+ {"SGEN UL", NULL, "SGEN UL Enable"},
+ {"SGEN UL", NULL, "SGEN MUTE"},
+ {"SGEN UL", NULL, "SGEN Enable"},
+
+ {"ADC", NULL, "PGA L Mux"},
+ {"ADC", NULL, "PGA R Mux"},
+ {"ADC", NULL, "ADC Supply"},
+
+ {"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"},
+
+ {"AIN0", NULL, "MICBIAS0"},
+ {"AIN1", NULL, "MICBIAS1"},
+ {"AIN2", NULL, "MICBIAS0"},
+ {"LPBK", NULL, "AUDIO_TOP_LPBK"},
+
+ /* Playback */
+ {"DAC Mux", "Normal Path", "AIF_RX"},
+ {"DAC Mux", "Sine Generator", "SGEN DL"},
+
+ {"AIF_RX", NULL, "DL SRC"},
+
+ {"SGEN DL", NULL, "DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN Enable"},
+ {"SGEN DL", NULL, "DL Digital Supply"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC Mux"},
+ {"DACR", NULL, "DAC Mux"},
+
+ {"DL Analog Supply", NULL, "CLK_BUF"},
+ {"DL Analog Supply", NULL, "AUDGLB"},
+ {"DL Analog Supply", NULL, "CLKSQ Audio"},
+ {"DL Analog Supply", NULL, "AUDNCP_CK"},
+ {"DL Analog Supply", NULL, "ZCD13M_CK"},
+ {"DL Analog Supply", NULL, "AUD_CK"},
+ {"DL Analog Supply", NULL, "AUDIF_CK"},
+
+ {"DL Digital Supply", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Supply", NULL, "AFE_ON"},
+
+ {"DACR", NULL, "DL Digital Supply"},
+ {"DACR", NULL, "DL Analog Supply"},
+ {"DACL", NULL, "DL Digital Supply"},
+ {"DACL", NULL, "DL Analog Supply"},
+
+ {"Line Out Source", "DACR", "DACR"},
+ {"Line Out Source", "Playback", "DACL"},
+ {"Line Out Source", "Test mode", "DACL"},
+
+ {"Handset Source", "DACR", "DACR"},
+ {"Handset Source", "Playback", "DACL"},
+ {"Handset Source", "Test mode", "DACL"},
+
+ {"Headphone Right Source", "DAC", "DACR"},
+ {"Headphone Right Source", "Line Out", "Line Out Source"},
+ {"Headphone Right Source", "Handset", "Handset Source"},
+
+ {"Headphone Left Source", "DAC", "DACL"},
+ {"Headphone Left Source", "Line Out", "Line Out Source"},
+ {"Headphone Left Source", "Handset", "Handset Source"},
+
+ {"Line out", NULL, "Line Out Source"},
+ {"Hansdet", NULL, "Handset Source"},
+
+ {"Headphones", NULL, "Headphone Right Source"},
+ {"Headphones", NULL, "Headphone Left Source"},
+};
+
+static struct snd_soc_dai_driver mtk_6357_dai_codecs[] = {
+ {
+ .name = "mt6357-snd-codec-aif1",
+ .playback = {
+ .stream_name = "MT6357 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ .capture = {
+ .stream_name = "MT6357 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MT6357_SOC_HIGH_USE_RATE,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ },
+};
+
+static int mt6357_codec_probe(struct snd_soc_component *codec)
+{
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ snd_soc_component_init_regmap(codec, priv->regmap);
+
+ /* Enable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK, MT6357_XO_AUDIO_EN_M_ENABLE);
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* set gpio */
+ set_playback_gpio(priv, false);
+ set_capture_gpio(priv, false);
+ /* Disable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK,
+ MT6357_XO_AUDIO_EN_M_DISABLE);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6357_soc_component_driver = {
+ .probe = mt6357_codec_probe,
+ .read = snd_soc_component_read,
+ .write = snd_soc_component_write,
+ .controls = mt6357_controls,
+ .num_controls = ARRAY_SIZE(mt6357_controls),
+ .dapm_widgets = mt6357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6357_dapm_widgets),
+ .dapm_routes = mt6357_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6357_dapm_routes),
+};
+
+static const u32 micbias_values[] = {
+ 1700000, 1800000, 1900000, 2000000,
+ 2100000, 2500000, 2600000, 2700000
+};
+
+static u32 mt6357_get_micbias_idx(struct device_node *np, const char *micbias)
+{
+ int err;
+ u32 idx, val;
+
+ err = of_property_read_u32(np, micbias, &val);
+ if (err)
+ return 0;
+
+ for (idx = 0; idx < ARRAY_SIZE(micbias_values); idx++) {
+ if (val == micbias_values[idx])
+ return idx;
+ }
+ return 0;
+}
+
+static int mt6357_parse_dt(struct mt6357_priv *priv)
+{
+ u32 micbias_voltage_index = 0;
+ struct device_node *np = priv->dev->parent->of_node;
+
+ if (!np)
+ return -EINVAL;
+
+ priv->pull_down_needed = false;
+ if (of_property_read_bool(np, "mediatek,hp-pull-down"))
+ priv->pull_down_needed = true;
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias0-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS0_VREF_SFT);
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias1-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS1_VREF_SFT);
+
+ return 0;
+}
+
+static int mt6357_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ struct mt6357_priv *priv;
+ int ret;
+
+ ret = devm_regulator_get_enable(&pdev->dev, "vaud28");
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to enable vaud28 regulator\n");
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*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);
+
+ ret = mt6357_parse_dt(priv);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to parse dts\n");
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6357_soc_component_driver,
+ mtk_6357_dai_codecs,
+ ARRAY_SIZE(mtk_6357_dai_codecs));
+}
+
+static const struct platform_device_id mt6357_platform_ids[] = {
+ {"mt6357-sound", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6357_platform_ids);
+
+static struct platform_driver mt6357_platform_driver = {
+ .driver = {
+ .name = "mt6357-sound",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = mt6357_platform_driver_probe,
+ .id_table = mt6357_platform_ids,
+};
+
+module_platform_driver(mt6357_platform_driver)
+
+MODULE_DESCRIPTION("MT6357 ALSA SoC codec driver");
+MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mt6357.h b/sound/soc/codecs/mt6357.h
new file mode 100644
index 000000000000..7f6fccada6a2
--- /dev/null
+++ b/sound/soc/codecs/mt6357.h
@@ -0,0 +1,660 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6357.h -- mt6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#ifndef __MT6357_H__
+#define __MT6357_H__
+
+#include <linux/types.h>
+
+/* Reg bit defines */
+/* MT6357_GPIO_DIR0 */
+#define MT6357_GPIO8_DIR_MASK BIT(8)
+#define MT6357_GPIO8_DIR_INPUT 0
+#define MT6357_GPIO8_DIR_OUTPUT BIT(8)
+#define MT6357_GPIO9_DIR_MASK BIT(9)
+#define MT6357_GPIO9_DIR_INPUT 0
+#define MT6357_GPIO9_DIR_OUTPUT BIT(9)
+#define MT6357_GPIO10_DIR_MASK BIT(10)
+#define MT6357_GPIO10_DIR_INPUT 0
+#define MT6357_GPIO10_DIR_OUTPUT BIT(10)
+#define MT6357_GPIO11_DIR_MASK BIT(11)
+#define MT6357_GPIO11_DIR_INPUT 0
+#define MT6357_GPIO11_DIR_OUTPUT BIT(11)
+#define MT6357_GPIO12_DIR_MASK BIT(12)
+#define MT6357_GPIO12_DIR_INPUT 0
+#define MT6357_GPIO12_DIR_OUTPUT BIT(12)
+#define MT6357_GPIO13_DIR_MASK BIT(13)
+#define MT6357_GPIO13_DIR_INPUT 0
+#define MT6357_GPIO13_DIR_OUTPUT BIT(13)
+#define MT6357_GPIO14_DIR_MASK BIT(14)
+#define MT6357_GPIO14_DIR_INPUT 0
+#define MT6357_GPIO14_DIR_OUTPUT BIT(14)
+#define MT6357_GPIO15_DIR_MASK BIT(15)
+#define MT6357_GPIO15_DIR_INPUT 0
+#define MT6357_GPIO15_DIR_OUTPUT BIT(15)
+
+/* MT6357_GPIO_MODE2 */
+#define MT6357_GPIO8_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO8_MODE_GPIO 0
+#define MT6357_GPIO9_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO9_MODE_GPIO 0
+#define MT6357_GPIO10_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO10_MODE_GPIO 0
+#define MT6357_GPIO11_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_AUD_SYNC_MOSI BIT(9)
+#define MT6357_GPIO11_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE2_SET */
+#define MT6357_GPIO8_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO9_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO10_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO11_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI BIT(9)
+
+/* MT6357_GPIO_MODE2_CLR */
+#define MT6357_GPIO_MODE2_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_GPIO_MODE3 */
+#define MT6357_GPIO12_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO12_MODE_GPIO 0
+#define MT6357_GPIO13_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO13_MODE_GPIO 0
+#define MT6357_GPIO14_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO14_MODE_GPIO 0
+#define MT6357_GPIO15_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_AUD_SYNC_MISO BIT(9)
+#define MT6357_GPIO15_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE3_SET */
+#define MT6357_GPIO12_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_SET_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO13_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO14_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO15_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO BIT(9)
+
+/* MT6357_GPIO_MODE3_CLR */
+#define MT6357_GPIO_MODE3_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_DCXO_CW14 */
+#define MT6357_XO_AUDIO_EN_M_SFT 13
+#define MT6357_XO_AUDIO_EN_M_MASK BIT(13)
+#define MT6357_XO_AUDIO_EN_M_ENABLE BIT(13)
+#define MT6357_XO_AUDIO_EN_M_DISABLE 0
+
+/* MT6357_AUD_TOP_CKPDN_CON0 */
+#define MT6357_AUDNCP_CK_PDN_SFT 6
+#define MT6357_ZCD13M_CK_PDN_SFT 5
+#define MT6357_AUDIF_CK_PDN_SFT 2
+#define MT6357_AUD_CK_PDN_SFT 1
+
+/* MT6357_AUDNCP_CLKDIV_CON0 */
+#define MT6357_DIVCKS_CHG BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON1 */
+#define MT6357_DIVCKS_ON BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON3 */
+#define MT6357_DIVCKS_PWD_NCP_MASK BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_DISABLE BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_ENABLE 0
+
+/* MT6357_AUDNCP_CLKDIV_CON4 */
+#define MT6357_DIVCKS_PWD_NCP_ST_SEL_MASK GENMASK(1, 0)
+#define MT6357_DIVCKS_PWD_NCP_ST_50US 0
+#define MT6357_DIVCKS_PWD_NCP_ST_100US 1
+#define MT6357_DIVCKS_PWD_NCP_ST_150US 2
+#define MT6357_DIVCKS_PWD_NCP_ST_200US 3
+
+/* MT6357_AFE_UL_DL_CON0 */
+#define MT6357_AFE_UL_LR_SWAP_SFT 15
+#define MT6357_AFE_ON_SFT 0
+
+/* MT6357_AFE_DL_SRC2_CON0_L */
+#define MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+
+/* MT6357_AFE_UL_SRC_CON0_H */
+#define MT6357_C_TWO_DIGITAL_MIC_CTL_MASK BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_ENABLE BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_DISABLE 0
+
+/* MT6357_AFE_UL_SRC_CON0_L */
+#define MT6357_UL_SDM_3_LEVEL_CTL_MASK BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_SELECT BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_DESELECT 0
+#define MT6357_UL_SRC_ON_TMP_CTL_MASK BIT(0)
+#define MT6357_UL_SRC_ENABLE BIT(0)
+#define MT6357_UL_SRC_DISABLE 0
+
+/* MT6357_AFE_TOP_CON0 */
+#define MT6357_UL_SINE_ON_SFT 1
+#define MT6357_UL_SINE_ON_MASK BIT(1)
+#define MT6357_DL_SINE_ON_SFT 0
+#define MT6357_DL_SINE_ON_MASK BIT(0)
+
+/* MT6357_AUDIO_TOP_CON0 */
+#define MT6357_PDN_LPBK_CTL_SFT 15
+#define MT6357_PDN_AFE_CTL_SFT 7
+#define MT6357_PDN_DAC_CTL_SFT 6
+#define MT6357_PDN_ADC_CTL_SFT 5
+#define MT6357_PDN_I2S_DL_CTL_SFT 3
+#define MT6357_PWR_CLK_DIS_CTL_SFT 2
+#define MT6357_PDN_AFE_TESTMODEL_CTL_SFT 1
+#define MT6357_PDN_RESERVED_SFT 0
+
+/* MT6357_AFUNC_AUD_CON0 */
+#define MT6357_CCI_AUD_ANACK_INVERT BIT(15)
+#define MT6357_CCI_AUD_ANACK_NORMAL 0
+#define MT6357_CCI_AUDIO_FIFO_WPTR_SFT 12
+#define MT6357_CCI_SCRAMBLER_CG_ENABLE BIT(11)
+#define MT6357_CCI_SCRAMBLER_CG_DISABLE 0
+#define MT6357_CCI_LCK_INV_OUT_OF_PHASE BIT(10)
+#define MT6357_CCI_LCK_INV_IN_PHASE 0
+#define MT6357_CCI_RAND_ENABLE BIT(9)
+#define MT6357_CCI_RAND_DISABLE 0
+#define MT6357_CCI_SPLT_SCRMB_CLK_ON BIT(8)
+#define MT6357_CCI_SPLT_SCRMB_CLK_OFF 0
+#define MT6357_CCI_SPLT_SCRMB_ON BIT(7)
+#define MT6357_CCI_SPLT_SCRMB_OFF 0
+#define MT6357_CCI_AUD_IDAC_TEST_EN_FROM_TEST_IN BIT(6)
+#define MT6357_CCI_AUD_IDAC_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_ZERO_PADDING_DISABLE BIT(5)
+#define MT6357_CCI_ZERO_PADDING_ENABLE 0
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_FROM_TEST_IN BIT(4)
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_AUD_SDM_MUTE_L_REG_CTL BIT(3)
+#define MT6357_CCI_AUD_SDM_MUTE_L_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_MUTE_R_REG_CTL BIT(2)
+#define MT6357_CCI_AUD_SDM_MUTE_R_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER3 BIT(1)
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER1 0
+#define MT6357_CCI_SCRAMBLER_ENABLE BIT(0)
+#define MT6357_CCI_SCRAMBLER_DISABLE 0
+
+/* MT6357_AFUNC_AUD_CON2 */
+#define MT6357_CCI_AUDIO_FIFO_ENABLE BIT(3)
+#define MT6357_CCI_AUDIO_FIFO_DISABLE 0
+#define MT6357_CCI_ACD_MODE_NORMAL_PATH BIT(2)
+#define MT6357_CCI_ACD_MODE_TEST_PATH 0
+#define MT6357_CCI_AFIFO_CLK_PWDB_ON BIT(1)
+#define MT6357_CCI_AFIFO_CLK_PWDB_DOWN 0
+#define MT6357_CCI_ACD_FUNC_RSTB_RELEASE BIT(0)
+#define MT6357_CCI_ACD_FUNC_RSTB_RESET 0
+
+/* MT6357_AFE_ADDA_MTKAIF_CFG0 */
+#define MT6357_ADDA_MTKAIF_LPBK_CTL_MASK BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_ENABLE BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_DISABLE 0
+
+/* MT6357_AFE_SGEN_CFG0 */
+#define MT6357_SGEN_DAC_EN_CTL_SFT 7
+#define MT6357_SGEN_DAC_ENABLE BIT(7)
+#define MT6357_SGEN_MUTE_SW_CTL_SFT 6
+#define MT6357_SGEN_MUTE_SW_DISABLE 0
+
+/* MT6357_AFE_DCCLK_CFG0 */
+#define MT6357_DCCLK_DIV_MASK GENMASK(15, 5)
+#define MT6357_DCCLK_DIV_SFT 5
+#define MT6357_DCCLK_DIV_RUN_VALUE (32 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_DIV_STOP_VALUE (259 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_PDN_MASK BIT(1)
+#define MT6357_DCCLK_PDN BIT(1)
+#define MT6357_DCCLK_OUTPUT 0
+#define MT6357_DCCLK_GEN_ON_MASK BIT(0)
+#define MT6357_DCCLK_GEN_ON BIT(0)
+#define MT6357_DCCLK_GEN_OFF 0
+
+/* MT6357_AFE_DCCLK_CFG1 */
+#define MT6357_DCCLK_RESYNC_BYPASS_MASK BIT(8)
+#define MT6357_DCCLK_RESYNC_BYPASS BIT(8)
+
+/* MT6357_AFE_AUD_PAD_TOP */
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK GENMASK(15, 8)
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE (BIT(13) | BIT(12) | BIT(8))
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE (BIT(13) | BIT(12))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_MASK GENMASK(7, 0)
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE (BIT(5) | BIT(4) | BIT(0))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON0 */
+#define MT6357_AUDADCLINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCLINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCLINPUTSEL_IDLE 0
+#define MT6357_AUDADCLPWRUP_SFT 12
+#define MT6357_AUDADCLPWRUP_MASK BIT(12)
+#define MT6357_AUDADCLPWRUP BIT(12)
+#define MT6357_AUDADCLPWRDOWN 0
+#define MT6357_AUDPREAMPLGAIN_SFT 8
+#define MT6357_AUDPREAMPLGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPLGAIN_MAX 4
+#define MT6357_AUDPREAMPLINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPLINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPLDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPLDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_AC 0
+#define MT6357_AUDPREAMPLON_MASK BIT(0)
+#define MT6357_AUDPREAMPLON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPLON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON1 */
+#define MT6357_AUDADCRINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCRINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCRINPUTSEL_IDLE 0
+#define MT6357_AUDADCRPWRUP_SFT 12
+#define MT6357_AUDADCRPWRUP_MASK BIT(12)
+#define MT6357_AUDADCRPWRUP BIT(12)
+#define MT6357_AUDADCRPWRDOWN 0
+#define MT6357_AUDPREAMPRGAIN_SFT 8
+#define MT6357_AUDPREAMPRGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPRGAIN_MAX 4
+#define MT6357_AUDPREAMPRINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPRINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPRDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPRDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_AC 0
+#define MT6357_AUDPREAMPRON_MASK BIT(0)
+#define MT6357_AUDPREAMPRON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPRON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON6 */
+#define MT6357_CLKSQ_EN_SFT 0
+
+/* MT6357_AUDENC_ANA_CON7 */
+#define MT6357_AUDDIGMICBIAS_MASK GENMASK(2, 1)
+#define MT6357_AUDDIGMICBIAS_DEFAULT_VALUE BIT(2)
+#define MT6357_AUDDIGMICBIAS_OFF 0
+#define MT6357_AUDDIGMICEN_MASK BIT(0)
+#define MT6357_AUDDIGMICEN_ENABLE BIT(0)
+#define MT6357_AUDDIGMICEN_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON8 */
+#define MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_ENABLE BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSW0N_ENABLE BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSWN_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS0_VREF_SFT 4
+#define MT6357_AUD_MICBIAS0_PWD_SFT 0
+
+#define MT6357_AUD_MICBIAS0_DC_MASK (MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_ALL (MT6357_AUD_MICBIAS0_DCSW2N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_P1 (MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_DISABLE_ALL 0
+
+/* MT6357_AUDENC_ANA_CON9 */
+#define MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_DISABLE 0
+#define MT6357_AUD_MICBIAS1_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS1_VREF_SFT 4
+#define MT6357_AUD_MICBIAS1_PWD_SFT 0
+
+/* MT6357_AUDDEC_ANA_CON0 */
+#define MT6357_AUD_HPR_SC_VAUDP15_MASK BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_DISABLE BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPL_SC_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_DISABLE BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT 10
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT 8
+#define MT6357_AUD_HPR_BIAS_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_BIAS_VAUDP15_MASK BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_MASK BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VA28_MASK BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_ENABLE BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VA28_MASK BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_ENABLE BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON1 */
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_MASK GENMASK(14, 12)
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_SFT 12
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK GENMASK(10, 8)
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT 8
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX 7
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_MASK BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_MASK BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON2 */
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_MASK BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE 0
+#define MT6357_AUD_REFN_DERES_VAUDP15_MASK BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_ENABLE BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_MASK GENMASK(6, 4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_POPEN BIT(5)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 (BIT(4) | BIT(5))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(4) | BIT(6))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P470 (BIT(4) | BIT(5) | BIT(6))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_MASK GENMASK(2, 0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_POPEN BIT(1)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250 (BIT(0) | BIT(1))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(0) | BIT(2))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P470 (BIT(0) | BIT(1) | BIT(2))
+
+/* MT6357_AUDDEC_ANA_CON3 */
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON4 */
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON6 */
+#define MT6357_HP_AUX_LOOP_GAIN_MASK GENMASK(15, 12)
+#define MT6357_HP_AUX_LOOP_GAIN_SFT 12
+#define MT6357_HP_AUX_LOOP_GAIN_MAX 0x0f
+#define MT6357_HPR_AUX_CMFB_LOOP_MASK BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_ENABLE BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPL_AUX_CMFB_LOOP_MASK BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_ENABLE BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPRL_MAIN_CMFB_LOOP_MASK BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE 0
+#define MT6357_HP_CMFB_RST_MASK BIT(7)
+#define MT6357_HP_CMFB_RST_NORMAL BIT(7)
+#define MT6357_HP_CMFB_RST_RESET 0
+#define MT6357_DAC_LOW_NOISE_MODE_MASK BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_ENABLE BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON7 */
+#define MT6357_HP_IVBUF_DEGAIN_SFT 2
+#define MT6357_HP_IVBUF_DEGAIN_MAX 1
+
+/* MT6357_AUDDEC_ANA_CON10 */
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE 0
+
+/* MT6357_AUDDEC_ANA_CON11 */
+#define MT6357_RSTB_ENCODER_VA28_MASK BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_ENABLE BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_DISABLE 0
+#define MT6357_AUDGLB_PWRDN_VA28_SFT 4
+#define MT6357_RSTB_DECODER_VA28_MASK BIT(0)
+#define MT6357_RSTB_DECODER_VA28_ENABLE BIT(0)
+#define MT6357_RSTB_DECODER_VA28_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON12 */
+#define MT6357_VA28REFGEN_EN_VA28_MASK BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_ENABLE BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_DISABLE 0
+#define MT6357_VA33REFGEN_EN_VA18_MASK BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_ENABLE BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_DISABLE 0
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE 0
+#define MT6357_LCLDO_ENC_EN_VA28_MASK BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_ENABLE BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_DISABLE 0
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_MASK BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_LCLDO_EN_VA18_MASK BIT(4)
+#define MT6357_LCLDO_EN_VA18_ENABLE BIT(4)
+#define MT6357_LCLDO_EN_VA18_DISABLE 0
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_MASK BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_HCLDO_EN_VA18_MASK BIT(0)
+#define MT6357_HCLDO_EN_VA18_ENABLE BIT(0)
+#define MT6357_HCLDO_EN_VA18_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON13 */
+#define MT6357_NVREG_EN_VAUDP15_MASK BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_ENABLE BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ELR_0 */
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_DISABLE 0
+
+/* MT6357_ZCD_CON1 */
+#define MT6357_AUD_LOL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_LOL_GAIN_SFT 0
+#define MT6357_AUD_LOR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_LOR_GAIN_SFT 7
+#define MT6357_AUD_LO_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON2 */
+#define MT6357_AUD_HPL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HPL_GAIN_SFT 0
+#define MT6357_AUD_HPR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_HPR_GAIN_SFT 7
+#define MT6357_AUD_HP_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON3 */
+#define MT6357_AUD_HS_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HS_GAIN_SFT 0
+#define MT6357_AUD_HS_GAIN_MAX 0x12
+
+/* Registers list */
+/* gpio direction */
+#define MT6357_GPIO_DIR0 0x0088
+/* mosi */
+#define MT6357_GPIO_MODE2 0x00B6
+#define MT6357_GPIO_MODE2_SET 0x00B8
+#define MT6357_GPIO_MODE2_CLR 0x00BA
+/* miso */
+#define MT6357_GPIO_MODE3 0x00BC
+#define MT6357_GPIO_MODE3_SET 0x00BE
+#define MT6357_GPIO_MODE3_CLR 0x00C0
+
+#define MT6357_DCXO_CW14 0x07AC
+
+#define MT6357_AUD_TOP_CKPDN_CON0 0x208C
+#define MT6357_AUDNCP_CLKDIV_CON0 0x20B4
+#define MT6357_AUDNCP_CLKDIV_CON1 0x20B6
+#define MT6357_AUDNCP_CLKDIV_CON2 0x20B8
+#define MT6357_AUDNCP_CLKDIV_CON3 0x20BA
+#define MT6357_AUDNCP_CLKDIV_CON4 0x20BC
+#define MT6357_AFE_UL_DL_CON0 0x2108
+#define MT6357_AFE_DL_SRC2_CON0_L 0x210A
+#define MT6357_AFE_UL_SRC_CON0_H 0x210C
+#define MT6357_AFE_UL_SRC_CON0_L 0x210E
+#define MT6357_AFE_TOP_CON0 0x2110
+#define MT6357_AUDIO_TOP_CON0 0x2112
+#define MT6357_AFUNC_AUD_CON0 0x2116
+#define MT6357_AFUNC_AUD_CON2 0x211A
+#define MT6357_AFE_ADDA_MTKAIF_CFG0 0x2134
+#define MT6357_AFE_SGEN_CFG0 0x2140
+#define MT6357_AFE_DCCLK_CFG0 0x2146
+#define MT6357_AFE_DCCLK_CFG1 0x2148
+#define MT6357_AFE_AUD_PAD_TOP 0x214C
+#define MT6357_AUDENC_ANA_CON0 0x2188
+#define MT6357_AUDENC_ANA_CON1 0x218A
+#define MT6357_AUDENC_ANA_CON6 0x2194
+#define MT6357_AUDENC_ANA_CON7 0x2196
+#define MT6357_AUDENC_ANA_CON8 0x2198
+#define MT6357_AUDENC_ANA_CON9 0x219A
+#define MT6357_AUDDEC_ANA_CON0 0x2208
+#define MT6357_AUDDEC_ANA_CON1 0x220A
+#define MT6357_AUDDEC_ANA_CON2 0x220C
+#define MT6357_AUDDEC_ANA_CON3 0x220E
+#define MT6357_AUDDEC_ANA_CON4 0x2210
+#define MT6357_AUDDEC_ANA_CON6 0x2214
+#define MT6357_AUDDEC_ANA_CON7 0x2216
+#define MT6357_AUDDEC_ANA_CON10 0x221C
+#define MT6357_AUDDEC_ANA_CON11 0x221E
+#define MT6357_AUDDEC_ANA_CON12 0x2220
+#define MT6357_AUDDEC_ANA_CON13 0x2222
+#define MT6357_AUDDEC_ELR_0 0x2226
+#define MT6357_ZCD_CON1 0x228A
+#define MT6357_ZCD_CON2 0x228C
+#define MT6357_ZCD_CON3 0x228E
+
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_12DB = 20,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+enum {
+ UL_GAIN_0DB = 0,
+ UL_GAIN_6DB,
+ UL_GAIN_12DB,
+ UL_GAIN_18DB,
+ UL_GAIN_24DB,
+};
+
+#define MT6357_DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define MT6357_DL_GAIN_REG_LEFT_MASK 0x001f
+#define MT6357_DL_GAIN_REG_LEFT_SHIFT 0
+#define MT6357_DL_GAIN_REG_RIGHT_MASK 0x0f80
+#define MT6357_DL_GAIN_REG_RIGHT_SHIFT 7
+#define MT6357_DL_GAIN_REG_MASK 0x0f9f
+
+#define MT6357_SND_SOC_ADV_MT_FMTS (\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_U16_BE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_BE |\
+ SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_U24_BE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_S32_BE |\
+ SNDRV_PCM_FMTBIT_U32_LE |\
+ SNDRV_PCM_FMTBIT_U32_BE)
+
+#define MT6357_SOC_HIGH_USE_RATE (\
+ SNDRV_PCM_RATE_CONTINUOUS |\
+ SNDRV_PCM_RATE_8000_192000)
+
+/* codec private structure */
+struct mt6357_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ bool pull_down_needed;
+ int hp_channel_number;
+};
+#endif
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
new file mode 100644
index 000000000000..e033027fd4c7
--- /dev/null
+++ b/sound/soc/codecs/mt6358.c
@@ -0,0 +1,2431 @@
+// 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/mod_devicetable.h>
+#include <linux/module.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;
+
+ 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);
+}
+
+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;
+}
+
+/* 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 int mt6358_dmic_mode_get(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->dmic_one_wire_mode;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 0;
+}
+
+static int mt6358_dmic_mode_set(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->dmic_one_wire_mode != enabled) {
+ priv->dmic_one_wire_mode = enabled;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 1;
+ }
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ 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),
+
+ SOC_SINGLE_BOOL_EXT("Dmic Mode Switch", 0,
+ mt6358_dmic_mode_get, mt6358_dmic_mode_set),
+};
+
+/* 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..b729c3899b7e
--- /dev/null
+++ b/sound/soc/codecs/mt6358.h
@@ -0,0 +1,2310 @@
+/* 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);
+#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..ed34cc15b80e
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -0,0 +1,1062 @@
+// 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.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..78ada3a5bfae
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.h
@@ -0,0 +1,137 @@
+/* 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;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_MT6359_ACCDET)
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+#else
+static inline int
+mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+#endif
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
new file mode 100644
index 000000000000..f73120c6a6ce
--- /dev/null
+++ b/sound/soc/codecs/mt6359.c
@@ -0,0 +1,2962 @@
+// 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.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_gpio_smt(struct mt6359_priv *priv)
+{
+ /* set gpio SMT mode */
+ regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0);
+}
+
+static void mt6359_set_gpio_driving(struct mt6359_priv *priv)
+{
+ /* 8:4mA(default), a:8mA, c:12mA, e:16mA */
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888);
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888);
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88);
+}
+
+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 orig_gain[2], new_gain[2];
+ int ret;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ break;
+ case MT6359_ZCD_CON1:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ break;
+ case MT6359_ZCD_CON3:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ break;
+ case MT6359_AUDENC_ANA_CON0:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ break;
+ case MT6359_AUDENC_ANA_CON1:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ break;
+ case MT6359_AUDENC_ANA_CON2:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ 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;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ break;
+ }
+
+ ret = 0;
+ if (orig_gain[0] != new_gain[0]) {
+ ret = 1;
+ } else if (snd_soc_volsw_is_stereo(mc)) {
+ if (orig_gain[1] != new_gain[1])
+ ret = 1;
+ }
+
+ 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;
+}
+
+static int mt6359_get_playback_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;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ ucontrol->value.integer.value[1] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ break;
+ case MT6359_ZCD_CON1:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ ucontrol->value.integer.value[1] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ break;
+ case MT6359_ZCD_CON3:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* 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);
+ 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_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);
+
+ /* Switch LOL MUX to audio DAC */
+ if (mux == LO_MUX_L_DAC) {
+ if (priv->dev_counter[DEVICE_HP] > 0) {
+ dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n",
+ __func__, priv->dev_counter[DEVICE_HP]);
+ break;
+ }
+ /* Enable DACL and switch HP MUX to open*/
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009);
+ /* Disable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200);
+ usleep_range(100, 120);
+ /* Switch LOL MUX to DACL */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117);
+ } else if (mux == LO_MUX_3RD_DAC) {
+ /* 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);
+
+ if (mux == LO_MUX_L_DAC) {
+ /* 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 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"},
+
+ {"MISO0_MUX", NULL, "UL_SRC"},
+ {"MISO1_MUX", NULL, "UL_SRC"},
+ {"MISO2_MUX", NULL, "UL_SRC_34"},
+
+ {"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"},
+ {"LOL Mux", "Playback_L_DAC", "DACL"},
+ {"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_set_gpio_smt(priv);
+ mt6359_set_gpio_driving(priv);
+ 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(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, 0x12, 0,
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
+ SOC_DOUBLE_EXT_TLV("Lineout Volume",
+ MT6359_ZCD_CON1, 0, 7, 0x12, 0,
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
+ SOC_SINGLE_EXT_TLV("Handset Volume",
+ MT6359_ZCD_CON3, 0, 0x12, 0,
+ mt6359_get_playback_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, "audio-codec");
+ if (!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..d16bccebae52
--- /dev/null
+++ b/sound/soc/codecs/mt6660.c
@@ -0,0 +1,581 @@
+// 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 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 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 = {
+ 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" },
+ {},
+};
+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 = pm_ptr(&mt6660_dev_pm_ops),
+ },
+ .probe = 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/nau8325.c b/sound/soc/codecs/nau8325.c
new file mode 100644
index 000000000000..2266f320a8f2
--- /dev/null
+++ b/sound/soc/codecs/nau8325.c
@@ -0,0 +1,900 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8325.c -- Nuvoton NAU8325 audio codec driver
+//
+// Copyright 2023 Nuvoton Technology Crop.
+// Author: Seven Lee <WTLI@nuvoton.com>
+// David Lin <CTLIN0@nuvoton.com>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.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 "nau8325.h"
+
+/* Range of Master Clock MCLK (Hz) */
+#define MASTER_CLK_MAX 49152000
+#define MASTER_CLK_MIN 2048000
+
+/* scaling for MCLK source */
+#define CLK_PROC_BYPASS (-1)
+
+/* the maximum CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* from MCLK input */
+#define MCLK_SRC 4
+
+static const struct nau8325_src_attr mclk_n1_div[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 3, 0x2 },
+};
+
+/* over sampling rate */
+static const struct nau8325_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 nau8325_src_attr mclk_n2_div[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+ { 4, 0x4 },
+};
+
+static const struct nau8325_src_attr mclk_n3_mult[] = {
+ { 0, 0x1 },
+ { 1, 0x2 },
+ { 2, 0x3 },
+ { 3, 0x4 },
+};
+
+/* Sample Rate and MCLK_SRC selections */
+static const struct nau8325_srate_attr target_srate_table[] = {
+ /* { FS, range, max, { MCLK source }} */
+ { 48000, 2, true, { 12288000, 19200000, 24000000 } },
+ { 16000, 1, false, { 4096000, 6400000, 8000000 } },
+ { 8000, 0, false, { 2048000, 3200000, 4000000 }},
+ { 44100, 2, true, { 11289600, 17640000, 22050000 }},
+ { 64000, 3, false, { 16384000, 25600000, 32000000 } },
+ { 96000, 3, true, { 24576000, 38400000, 48000000 } },
+ { 12000, 0, true, { 3072000, 4800000, 6000000 } },
+ { 24000, 1, true, { 6144000, 9600000, 12000000 } },
+ { 32000, 2, false, { 8192000, 12800000, 16000000 } },
+};
+
+static const struct reg_default nau8325_reg_defaults[] = {
+ { NAU8325_R00_HARDWARE_RST, 0x0000 },
+ { NAU8325_R01_SOFTWARE_RST, 0x0000 },
+ { NAU8325_R03_CLK_CTRL, 0x0000 },
+ { NAU8325_R04_ENA_CTRL, 0x0000 },
+ { NAU8325_R05_INTERRUPT_CTRL, 0x007f },
+ { NAU8325_R09_IRQOUT, 0x0000 },
+ { NAU8325_R0A_IO_CTRL, 0x0000 },
+ { NAU8325_R0B_PDM_CTRL, 0x0000 },
+ { NAU8325_R0C_TDM_CTRL, 0x0000 },
+ { NAU8325_R0D_I2S_PCM_CTRL1, 0x000a },
+ { NAU8325_R0E_I2S_PCM_CTRL2, 0x0000 },
+ { NAU8325_R0F_L_TIME_SLOT, 0x0000 },
+ { NAU8325_R10_R_TIME_SLOT, 0x0000 },
+ { NAU8325_R11_HPF_CTRL, 0x0000 },
+ { NAU8325_R12_MUTE_CTRL, 0x0000 },
+ { NAU8325_R13_DAC_VOLUME, 0xf3f3 },
+ { NAU8325_R29_DAC_CTRL1, 0x0081 },
+ { NAU8325_R2A_DAC_CTRL2, 0x0000 },
+ { NAU8325_R2C_ALC_CTRL1, 0x000e },
+ { NAU8325_R2D_ALC_CTRL2, 0x8400 },
+ { NAU8325_R2E_ALC_CTRL3, 0x0000 },
+ { NAU8325_R2F_ALC_CTRL4, 0x003f },
+ { NAU8325_R40_CLK_DET_CTRL, 0xa801 },
+ { NAU8325_R50_MIXER_CTRL, 0x0000 },
+ { NAU8325_R55_MISC_CTRL, 0x0000 },
+ { NAU8325_R60_BIAS_ADJ, 0x0000 },
+ { NAU8325_R61_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8325_R62_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8325_R63_ANALOG_CONTROL_3, 0x0000 },
+ { NAU8325_R64_ANALOG_CONTROL_4, 0x0000 },
+ { NAU8325_R65_ANALOG_CONTROL_5, 0x0000 },
+ { NAU8325_R66_ANALOG_CONTROL_6, 0x0000 },
+ { NAU8325_R69_CLIP_CTRL, 0x0000 },
+ { NAU8325_R73_RDAC, 0x0008 },
+};
+
+static bool nau8325_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R02_DEVICE_ID ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R49_TEST_STATUS ... NAU8325_R4A_ANALOG_READ:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST:
+ case NAU8325_R03_CLK_CTRL ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST ... NAU8325_R02_DEVICE_ID:
+ case NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R4A_ANALOG_READ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8325_dac_oversampl_texts[] = {
+ "64", "256", "128", "32",
+};
+
+static const unsigned int nau8325_dac_oversampl_values[] = {
+ 0, 1, 2, 4,
+};
+
+static const struct soc_enum nau8325_dac_oversampl_enum =
+ SOC_VALUE_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_SFT, 0x7,
+ ARRAY_SIZE(nau8325_dac_oversampl_texts),
+ nau8325_dac_oversampl_texts,
+ nau8325_dac_oversampl_values);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -8000, 600);
+
+static const struct snd_kcontrol_new nau8325_snd_controls[] = {
+ SOC_ENUM("DAC Oversampling Rate", nau8325_dac_oversampl_enum),
+ SOC_DOUBLE_TLV("Speaker Volume", NAU8325_R13_DAC_VOLUME,
+ NAU8325_DAC_VOLUME_L_SFT, NAU8325_DAC_VOLUME_R_SFT,
+ NAU8325_DAC_VOLUME_R_EN, 0, dac_vol_tlv),
+ SOC_SINGLE("ALC Max Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_SFT, NAU8325_ALC_MAXGAIN_MAX, 0),
+ SOC_SINGLE("ALC Min Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MINGAIN_SFT, NAU8325_ALC_MINGAIN_MAX, 0),
+ SOC_SINGLE("ALC Decay Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_SFT, NAU8325_ALC_DCY_MAX, 0),
+ SOC_SINGLE("ALC Attack Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_ATK_SFT, NAU8325_ALC_ATK_MAX, 0),
+ SOC_SINGLE("ALC Hold Time", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_HLD_SFT, NAU8325_ALC_HLD_MAX, 0),
+ SOC_SINGLE("ALC Target Level", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_LVL_SFT, NAU8325_ALC_LVL_MAX, 0),
+ SOC_SINGLE("ALC Enable Switch", NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN_SFT, 1, 0),
+};
+
+static int nau8325_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, 0);
+ msleep(30);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Soft mute the output to prevent the pop noise. */
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, NAU8325_SOFT_MUTE);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8325_powerup_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (nau8325->clock_detection)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, NAU8325_PWRUP_DFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8325_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Power Up", SND_SOC_NOPM, 0, 0,
+ nau8325_powerup_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_LEFT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_RIGHT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route nau8325_dapm_routes[] = {
+ { "DACL", NULL, "Power Up" },
+ { "DACR", NULL, "Power Up" },
+
+ { "DACL", NULL, "AIFRX" },
+ { "DACR", NULL, "AIFRX" },
+ { "SPKL", NULL, "DACL" },
+ { "SPKR", NULL, "DACR" },
+};
+
+static int nau8325_srate_clk_apply(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int n1_sel, int mclk_mult_sel, int n2_sel)
+{
+ if (!srate_table || n2_sel < 0 || n2_sel >= ARRAY_SIZE(mclk_n2_div) ||
+ n1_sel < 0 || n1_sel >= ARRAY_SIZE(mclk_n1_div)) {
+ dev_dbg(nau8325->dev, "The CLK isn't supported.");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX,
+ (srate_table->range << NAU8325_REG_SRATE_SFT) |
+ (srate_table->max ? NAU8325_REG_DIV_MAX : 0));
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SRC_MASK, mclk_n2_div[n2_sel].val);
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_MUL_SRC_MASK,
+ mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT);
+
+ if (mclk_mult_sel != CLK_PROC_BYPASS) {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK,
+ mclk_n3_mult[mclk_mult_sel].val <<
+ NAU8325_MCLK_SEL_SFT);
+ } else {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK, 0);
+ }
+
+ switch (mclk_mult_sel) {
+ case 2:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN, NAU8325_MCLK4XEN_EN);
+ break;
+ case 3:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN);
+ break;
+ default:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8325_clksrc_n2(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int mclk, int *n2_sel)
+{
+ int i, mclk_src, ratio;
+
+ ratio = NAU8325_MCLK_FS_RATIO_NUM;
+ for (i = 0; i < ARRAY_SIZE(mclk_n2_div); i++) {
+ mclk_src = mclk >> mclk_n2_div[i].param;
+ if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_256;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_400;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_500;
+ break;
+ }
+ }
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM)
+ *n2_sel = i;
+
+ return ratio;
+}
+
+static const struct nau8325_srate_attr *target_srate_attribute(int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(target_srate_table); i++)
+ if (target_srate_table[i].fs == srate)
+ break;
+
+ if (i == ARRAY_SIZE(target_srate_table))
+ goto proc_err;
+
+ return &target_srate_table[i];
+
+proc_err:
+ return NULL;
+}
+
+static int nau8325_clksrc_choose(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr **srate_table,
+ int *n1_sel, int *mult_sel, int *n2_sel)
+{
+ int i, j, mclk, mclk_max, ratio, ratio_sel, n2_max;
+
+ if (!nau8325->mclk || !nau8325->fs)
+ goto proc_err;
+
+ /* select sampling rate and MCLK_SRC */
+ *srate_table = target_srate_attribute(nau8325->fs);
+ if (!*srate_table)
+ goto proc_err;
+
+ /* First check clock from MCLK directly, decide N2 for MCLK_SRC.
+ * If not good, consider 1/N1 and Multiplier.
+ */
+ ratio = nau8325_clksrc_n2(nau8325, *srate_table, nau8325->mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM) {
+ *n1_sel = 0;
+ *mult_sel = CLK_PROC_BYPASS;
+ *n2_sel = MCLK_SRC;
+ goto proc_done;
+ }
+
+ /* Get MCLK_SRC through 1/N, Multiplier, and then 1/N2. */
+ mclk_max = 0;
+ for (i = 0; i < ARRAY_SIZE(mclk_n1_div); i++) {
+ for (j = 0; j < ARRAY_SIZE(mclk_n3_mult); j++) {
+ mclk = nau8325->mclk << mclk_n3_mult[j].param;
+ mclk = mclk / mclk_n1_div[i].param;
+ ratio = nau8325_clksrc_n2(nau8325,
+ *srate_table, mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM &&
+ (mclk_max < mclk || i > *n1_sel)) {
+ mclk_max = mclk;
+ n2_max = *n2_sel;
+ *n1_sel = i;
+ *mult_sel = j;
+ ratio_sel = ratio;
+ goto proc_done;
+ }
+ }
+ }
+ if (mclk_max) {
+ *n2_sel = n2_max;
+ ratio = ratio_sel;
+ goto proc_done;
+ }
+
+proc_err:
+ dev_dbg(nau8325->dev, "The MCLK %d is invalid. It can't get MCLK_SRC of 256/400/500 FS (%d)",
+ nau8325->mclk, nau8325->fs);
+ return -EINVAL;
+proc_done:
+ dev_dbg(nau8325->dev, "nau8325->fs=%d,range=0x%x, %s, (n1,mu,n2,dmu):(%d,%d,%d), MCLK_SRC=%uHz (%d)",
+ nau8325->fs, (*srate_table)->range,
+ (*srate_table)->max ? "MAX" : "MIN",
+ *n1_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : mclk_n1_div[*n1_sel].param,
+ *mult_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : 1 << mclk_n3_mult[*mult_sel].param,
+ 1 << mclk_n2_div[*n2_sel].param,
+ (*srate_table)->mclk_src[ratio],
+ (*srate_table)->mclk_src[ratio] / nau8325->fs);
+
+ return 0;
+}
+
+static int nau8325_clock_config(struct nau8325 *nau8325)
+{
+ const struct nau8325_srate_attr *srate_table;
+ int ret, n1_sel, mult_sel, n2_sel;
+
+ ret = nau8325_clksrc_choose(nau8325, &srate_table,
+ &n1_sel, &mult_sel, &n2_sel);
+ if (ret)
+ goto err;
+
+ ret = nau8325_srate_clk_apply(nau8325, srate_table,
+ n1_sel, mult_sel, n2_sel);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct nau8325_osr_attr *nau8325_get_osr(struct nau8325 *nau8325)
+{
+ unsigned int osr;
+
+ regmap_read(nau8325->regmap, NAU8325_R29_DAC_CTRL1, &osr);
+ osr &= NAU8325_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+
+ return &osr_dac_sel[osr];
+}
+
+static int nau8325_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ const struct nau8325_osr_attr *osr;
+
+ osr = nau8325_get_osr(nau8325);
+ 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 nau8325_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ const struct nau8325_osr_attr *osr;
+ int ret;
+
+ nau8325->fs = params_rate(params);
+ osr = nau8325_get_osr(nau8325);
+ if (!osr || !osr->osr || nau8325->fs * osr->osr > CLK_DA_AD_MAX) {
+ ret = -EINVAL;
+ goto err;
+ }
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8325_CLK_DAC_SRC_SFT);
+
+ ret = nau8325_clock_config(nau8325);
+ if (ret)
+ goto err;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8325_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8325_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8325_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8325_I2S_DL_32;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DL_MASK, val_len);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int nau8325_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_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;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8325_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8325_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8325_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8325_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8325_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK |
+ NAU8325_I2S_PCMB_EN, ctrl1_val);
+
+ return 0;
+}
+
+static int nau8325_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) {
+ dev_dbg(nau8325->dev, "MCLK exceeds the range, MCLK:%d", freq);
+ return -EINVAL;
+ }
+
+ nau8325->mclk = freq;
+ dev_dbg(nau8325->dev, "MCLK %dHz", nau8325->mclk);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8325_component_driver = {
+ .set_sysclk = nau8325_set_sysclk,
+ .suspend_bias_off = true,
+ .controls = nau8325_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8325_snd_controls),
+ .dapm_widgets = nau8325_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8325_dapm_widgets),
+ .dapm_routes = nau8325_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8325_dapm_routes),
+};
+
+static const struct snd_soc_dai_ops nau8325_dai_ops = {
+ .startup = nau8325_dai_startup,
+ .hw_params = nau8325_hw_params,
+ .set_fmt = nau8325_set_fmt,
+};
+
+#define NAU8325_RATES SNDRV_PCM_RATE_8000_96000
+#define NAU8325_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver nau8325_dai = {
+ .name = NAU8325_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8325_RATES,
+ .formats = NAU8325_FORMATS,
+ },
+ .ops = &nau8325_dai_ops,
+};
+
+static const struct regmap_config nau8325_regmap_config = {
+ .reg_bits = NAU8325_REG_ADDR_LEN,
+ .val_bits = NAU8325_REG_DATA_LEN,
+
+ .max_register = NAU8325_REG_MAX,
+ .readable_reg = nau8325_readable_reg,
+ .writeable_reg = nau8325_writeable_reg,
+ .volatile_reg = nau8325_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8325_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8325_reg_defaults),
+};
+
+static void nau8325_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0001);
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0000);
+}
+
+static void nau8325_init_regs(struct nau8325 *nau8325)
+{
+ struct regmap *regmap = nau8325->regmap;
+ struct device *dev = nau8325->dev;
+
+ /* set ALC parameters */
+ regmap_update_bits(regmap, NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_MASK,
+ 0x7 << NAU8325_ALC_MAXGAIN_SFT);
+ regmap_update_bits(regmap, NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK |
+ NAU8325_ALC_HLD_MASK, (0x5 << NAU8325_ALC_DCY_SFT) |
+ (0x3 << NAU8325_ALC_ATK_SFT) |
+ (0x5 << NAU8325_ALC_HLD_SFT));
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+
+ /* DAC Reference Voltage Setting */
+ switch (nau8325->dac_vref_microvolt) {
+ case 1800000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 0 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2700000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 1 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2880000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 2 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 3060000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 3 << NAU8325_DACVREFSEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid dac-vref-microvolt %d", nau8325->dac_vref_microvolt);
+
+ }
+
+ /* DAC Reference Voltage Decoupling Capacitors. */
+ regmap_update_bits(regmap, NAU8325_R63_ANALOG_CONTROL_3,
+ NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4);
+ /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */
+ regmap_update_bits(regmap, NAU8325_R64_ANALOG_CONTROL_4,
+ NAU8325_CLASSD_SLEWN_MASK, 0x7);
+
+ /* VMID Tieoff (VMID Resistor Selection) */
+ switch (nau8325->vref_impedance_ohms) {
+ case 0:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 0 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 25000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 1 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 125000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 2 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 2500:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 3 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid vref-impedance-ohms %d", nau8325->vref_impedance_ohms);
+ }
+
+
+ /* enable VMID, BIAS, DAC, DCA CLOCK, Voltage/Current Amps
+ */
+ regmap_update_bits(regmap, NAU8325_R61_ANALOG_CONTROL_1,
+ NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK |
+ NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK |
+ NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK |
+ NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK,
+ (0x1 << NAU8325_DACEN_SFT) |
+ (0x1 << NAU8325_DACCLKEN_SFT) |
+ (0x1 << NAU8325_DACEN_R_SFT) |
+ (0x1 << NAU8325_DACCLKEN_R_SFT) |
+ (0x1 << NAU8325_CLASSDEN_SFT) |
+ (0x1 << NAU8325_VMDFSTENB_SFT) |
+ (0x1 << NAU8325_BIASEN_SFT) | 0x3);
+
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ regmap_update_bits(regmap, NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_MASK,
+ NAU8325_DAC_OVERSAMPLE_128);
+}
+
+static void nau8325_print_device_properties(struct nau8325 *nau8325)
+{
+ struct device *dev = nau8325->dev;
+
+ dev_dbg(dev, "vref-impedance-ohms: %d", nau8325->vref_impedance_ohms);
+ dev_dbg(dev, "dac-vref-microvolt: %d", nau8325->dac_vref_microvolt);
+ dev_dbg(dev, "alc-enable: %d", nau8325->alc_enable);
+ dev_dbg(dev, "clock-det-data: %d", nau8325->clock_det_data);
+ dev_dbg(dev, "clock-detection-disable: %d", nau8325->clock_detection);
+}
+
+static int nau8325_read_device_properties(struct device *dev,
+ struct nau8325 *nau8325)
+{
+ int ret;
+
+ nau8325->alc_enable =
+ device_property_read_bool(dev, "nuvoton,alc-enable");
+ nau8325->clock_det_data =
+ device_property_read_bool(dev, "nuvoton,clock-det-data");
+ nau8325->clock_detection =
+ !device_property_read_bool(dev, "nuvoton,clock-detection-disable");
+
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance-ohms",
+ &nau8325->vref_impedance_ohms);
+ if (ret)
+ nau8325->vref_impedance_ohms = 125000;
+ ret = device_property_read_u32(dev, "nuvoton,dac-vref-microvolt",
+ &nau8325->dac_vref_microvolt);
+ if (ret)
+ nau8325->dac_vref_microvolt = 2880000;
+
+ return 0;
+}
+
+static int nau8325_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8325 *nau8325 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8325) {
+ nau8325 = devm_kzalloc(dev, sizeof(*nau8325), GFP_KERNEL);
+ if (!nau8325) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = nau8325_read_device_properties(dev, nau8325);
+ if (ret)
+ goto err;
+ }
+ i2c_set_clientdata(i2c, nau8325);
+
+ nau8325->regmap = devm_regmap_init_i2c(i2c, &nau8325_regmap_config);
+ if (IS_ERR(nau8325->regmap)) {
+ ret = PTR_ERR(nau8325->regmap);
+ goto err;
+ }
+ nau8325->dev = dev;
+ nau8325_print_device_properties(nau8325);
+
+ nau8325_reset_chip(nau8325->regmap);
+ ret = regmap_read(nau8325->regmap, NAU8325_R02_DEVICE_ID, &value);
+ if (ret) {
+ dev_dbg(dev, "Failed to read device id (%d)", ret);
+ goto err;
+ }
+ nau8325_init_regs(nau8325);
+
+ ret = devm_snd_soc_register_component(dev, &nau8325_component_driver,
+ &nau8325_dai, 1);
+err:
+ return ret;
+}
+
+static const struct i2c_device_id nau8325_i2c_ids[] = {
+ { "nau8325" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8325_of_ids[] = {
+ { .compatible = "nuvoton,nau8325", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8325_of_ids);
+#endif
+
+static struct i2c_driver nau8325_i2c_driver = {
+ .driver = {
+ .name = "nau8325",
+ .of_match_table = of_match_ptr(nau8325_of_ids),
+ },
+ .probe = nau8325_i2c_probe,
+ .id_table = nau8325_i2c_ids,
+};
+module_i2c_driver(nau8325_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8325 driver");
+MODULE_AUTHOR("Seven Lee <WTLI@nuvoton.com>");
+MODULE_AUTHOR("David Lin <CTLIN0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8325.h b/sound/soc/codecs/nau8325.h
new file mode 100644
index 000000000000..0d173b66a4d4
--- /dev/null
+++ b/sound/soc/codecs/nau8325.h
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nau8325.h -- Nuvoton NAU8325 audio codec driver
+ *
+ * Copyright 2023 Nuvoton Technology Crop.
+ * Author: Seven Lee <WTLI@nuvoton.com>
+ * David Lin <CTLIN0@nuvoton.com>
+ */
+
+#ifndef __NAU8325_H__
+#define __NAU8325_H__
+
+#define NAU8325_R00_HARDWARE_RST 0x00
+#define NAU8325_R01_SOFTWARE_RST 0x01
+#define NAU8325_R02_DEVICE_ID 0x02
+#define NAU8325_R03_CLK_CTRL 0x03
+#define NAU8325_R04_ENA_CTRL 0x04
+#define NAU8325_R05_INTERRUPT_CTRL 0x05
+#define NAU8325_R06_INT_CLR_STATUS 0x06
+#define NAU8325_R09_IRQOUT 0x09
+#define NAU8325_R0A_IO_CTRL 0x0a
+#define NAU8325_R0B_PDM_CTRL 0x0b
+#define NAU8325_R0C_TDM_CTRL 0x0c
+#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d
+#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e
+#define NAU8325_R0F_L_TIME_SLOT 0x0f
+#define NAU8325_R10_R_TIME_SLOT 0x10
+#define NAU8325_R11_HPF_CTRL 0x11
+#define NAU8325_R12_MUTE_CTRL 0x12
+#define NAU8325_R13_DAC_VOLUME 0x13
+#define NAU8325_R1D_DEBUG_READ1 0x1d
+#define NAU8325_R1F_DEBUG_READ2 0x1f
+#define NAU8325_R22_DEBUG_READ3 0x22
+#define NAU8325_R29_DAC_CTRL1 0x29
+#define NAU8325_R2A_DAC_CTRL2 0x2a
+#define NAU8325_R2C_ALC_CTRL1 0x2c
+#define NAU8325_R2D_ALC_CTRL2 0x2d
+#define NAU8325_R2E_ALC_CTRL3 0x2e
+#define NAU8325_R2F_ALC_CTRL4 0x2f
+#define NAU8325_R40_CLK_DET_CTRL 0x40
+#define NAU8325_R49_TEST_STATUS 0x49
+#define NAU8325_R4A_ANALOG_READ 0x4a
+#define NAU8325_R50_MIXER_CTRL 0x50
+#define NAU8325_R55_MISC_CTRL 0x55
+#define NAU8325_R60_BIAS_ADJ 0x60
+#define NAU8325_R61_ANALOG_CONTROL_1 0x61
+#define NAU8325_R62_ANALOG_CONTROL_2 0x62
+#define NAU8325_R63_ANALOG_CONTROL_3 0x63
+#define NAU8325_R64_ANALOG_CONTROL_4 0x64
+#define NAU8325_R65_ANALOG_CONTROL_5 0x65
+#define NAU8325_R66_ANALOG_CONTROL_6 0x66
+#define NAU8325_R69_CLIP_CTRL 0x69
+#define NAU8325_R73_RDAC 0x73
+#define NAU8325_REG_MAX NAU8325_R73_RDAC
+
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8325_REG_ADDR_LEN 16
+#define NAU8325_REG_DATA_LEN 16
+
+/* CLK_CTRL (0x03) */
+#define NAU8325_CLK_DAC_SRC_SFT 12
+#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT)
+#define NAU8325_CLK_MUL_SRC_SFT 6
+#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT)
+#define NAU8325_MCLK_SEL_SFT 3
+#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT)
+#define NAU8325_MCLK_SRC_MASK 0x7
+
+/* ENA_CTRL (0x04) */
+#define NAU8325_DAC_LEFT_CH_EN_SFT 3
+#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT)
+#define NAU8325_DAC_RIGHT_CH_EN_SFT 2
+#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT)
+
+/* INTERRUPT_CTRL (0x05) */
+#define NAU8325_ARP_DWN_INT_SFT 12
+#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT)
+#define NAU8325_CLIP_INT_SFT 11
+#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT)
+#define NAU8325_LVD_INT_SFT 10
+#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT)
+#define NAU8325_PWR_INT_DIS_SFT 8
+#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT)
+#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4
+#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT)
+#define NAU8325_CLIP_INT_DIS_SFT 3
+#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT)
+#define NAU8325_LVD_INT_DIS_SFT 2
+#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT)
+#define NAU8325_PWR_INT_MASK 0x1
+
+/* INT_CLR_STATUS (0x06) */
+#define NAU8325_INT_STATUS_MASK 0x7f
+
+/* IRQOUT (0x9) */
+#define NAU8325_IRQOUT_SEL_SEF 12
+#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF)
+#define NAU8325_DEM_DITH_SFT 7
+#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT)
+#define NAU8325_GAINZI3_SFT 5
+#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT)
+#define NAU8325_GAINZI2_MASK 0x1f
+
+/* IO_CTRL (0x0a) */
+#define NAU8325_IRQ_PL_SFT 15
+#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT)
+#define NAU8325_IRQ_PS_SFT 14
+#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT)
+#define NAU8325_IRQ_PE_SFT 13
+#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT)
+#define NAU8325_IRQ_DS_SFT 12
+#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT)
+#define NAU8325_IRQ_OUTPUT_SFT 11
+#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT)
+#define NAU8325_IRQ_PIN_DEBUG_SFT 10
+#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT)
+
+/* PDM_CTRL (0x0b) */
+#define NAU8325_PDM_LCH_EDGE_SFT 1
+#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT)
+#define NAU8325_PDM_MODE_EN 0x1
+
+/* TDM_CTRL (0x0c) */
+#define NAU8325_TDM_SFT 15
+#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT)
+#define NAU8325_PCM_OFFSET_CTRL_SFT 14
+#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT)
+#define NAU8325_DAC_LEFT_SFT 6
+#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT)
+#define NAU8325_DAC_RIGHT_SFT 3
+#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT)
+
+/* I2S_PCM_CTRL1 (0x0d) */
+#define NAU8325_DACCM_CTL_SFT 14
+#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT)
+#define NAU8325_CMB8_0_SFT 10
+#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT)
+#define NAU8325_UA_OFFSET_SFT 9
+#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT)
+#define NAU8325_I2S_BP_SFT 7
+#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_PCMB_SFT 6
+#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT)
+#define NAU8325_I2S_DACPSHS0_SFT 5
+#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT)
+#define NAU8325_I2S_DL_SFT 2
+#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DF_MASK 0x3
+#define NAU8325_I2S_DF_RIGTH 0x0
+#define NAU8325_I2S_DF_LEFT 0x1
+#define NAU8325_I2S_DF_I2S 0x2
+#define NAU8325_I2S_DF_PCM_AB 0x3
+
+/* I2S_PCM_CTRL2 (0x0e) */
+#define NAU8325_PCM_TS_SFT 10
+#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT)
+#define NAU8325_PCM8BIT0_SFT 8
+#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT)
+
+/* L_TIME_SLOT (0x0f)*/
+#define NAU8325_SHORT_FS_DET_SFT 13
+#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT)
+#define NAU8325_TSLOT_L0_MASK 0x3ff
+
+/* R_TIME_SLOT (0x10)*/
+#define NAU8325_TSLOT_R0_MASK 0x3ff
+
+/* HPF_CTRL (0x11)*/
+#define NAU8325_DAC_HPF_SFT 15
+#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT)
+#define NAU8325_DAC_HPF_APP_SFT 14
+#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT)
+#define NAU8325_DAC_HPF_FCUT_SFT 11
+#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT)
+
+/* MUTE_CTRL (0x12)*/
+#define NAU8325_SOFT_MUTE_SFT 15
+#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT)
+#define NAU8325_DAC_ZC_SFT 8
+#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT)
+#define NAU8325_UNMUTE_CTL_SFT 6
+#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT)
+#define NAU8325_ANA_MUTE_SFT 4
+#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT)
+#define NAU8325_AUTO_MUTE_SFT 3
+#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT)
+
+/* DAC_VOLUME (0x13) */
+#define NAU8325_DAC_VOLUME_L_SFT 8
+#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT)
+#define NAU8325_DAC_VOLUME_R_SFT 0
+#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT)
+#define NAU8325_DAC_VOL_MAX 0xff
+
+/* DEBUG_READ1 (0x1d)*/
+#define NAU8325_OSR100_MASK (0x1 << 6)
+#define NAU8325_MIPS500_MASK (0x1 << 5)
+#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4)
+#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3)
+#define NAU8325_MUTEB_MASK (0x1 << 2)
+#define NAU8325_PDOSCB_MASK (0x1 << 1)
+#define NAU8325_POWERDOWN1B_D_MASK 0x1
+
+/* DEBUG_READ2 (0x1f)*/
+#define NAU8325_R_CHANNEL_Vol_SFT 8
+#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT)
+#define NAU8325_L_CHANNEL_Vol_MASK 0xff
+
+/* DEBUG_READ3(0x22)*/
+#define NAU8325_PGAL_GAIN_MASK (0x3f << 7)
+#define NAU8325_CLIP_MASK (0x1 << 6)
+#define NAU8325_SCAN_MODE_MASK (0x1 << 5)
+#define NAU8325_SDB_MASK (0x1 << 4)
+#define NAU8325_TALARM_MASK (0x1 << 3)
+#define NAU8325_SHORTR_MASK (0x1 << 2)
+#define NAU8325_SHORTL_MASK (0x1 << 1)
+#define NAU8325_TMDET_MASK 0x1
+
+/* DAC_CTRL1 (0x29) */
+#define NAU8325_DAC_OVERSAMPLE_SFT 0
+#define NAU8325_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8325_DAC_OVERSAMPLE_256 1
+#define NAU8325_DAC_OVERSAMPLE_128 2
+#define NAU8325_DAC_OVERSAMPLE_64 0
+#define NAU8325_DAC_OVERSAMPLE_32 4
+
+/* ALC_CTRL1 (0x2c) */
+#define NAU8325_ALC_MAXGAIN_SFT 5
+#define NAU8325_ALC_MAXGAIN_MAX 0x7
+#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT)
+#define NAU8325_ALC_MINGAIN_MAX 4
+#define NAU8325_ALC_MINGAIN_SFT 1
+#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT)
+
+/* ALC_CTRL2 (0x2d) */
+#define NAU8325_ALC_DCY_SFT 12
+#define NAU8325_ALC_DCY_MAX 0xb
+#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT)
+#define NAU8325_ALC_ATK_SFT 8
+#define NAU8325_ALC_ATK_MAX 0xb
+#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT)
+#define NAU8325_ALC_HLD_SFT 4
+#define NAU8325_ALC_HLD_MAX 0xa
+#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT)
+#define NAU8325_ALC_LVL_SFT 0
+#define NAU8325_ALC_LVL_MAX 0xf
+#define NAU8325_ALC_LVL_MASK 0xf
+
+/* ALC_CTRL3 (0x2e) */
+#define NAU8325_ALC_EN_SFT 15
+#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT)
+
+/* TEMP_COMP_CTRL (0x30) */
+#define NAU8325_TEMP_COMP_ACT2_MASK 0xff
+
+/* LPF_CTRL (0x33) */
+#define NAU8325_LPF_IN1_EN_SFT 15
+#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT)
+#define NAU8325_LPF_IN1_TC_SFT 11
+#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT)
+#define NAU8325_LPF_IN2_EN_SFT 10
+#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT)
+#define NAU8325_LPF_IN2_TC_SFT 6
+#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT)
+
+/* CLK_DET_CTRL (0x40) */
+#define NAU8325_APWRUP_SFT 15
+#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT)
+#define NAU8325_CLKPWRUP_SFT 14
+#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT)
+#define NAU8325_PWRUP_DFT_SFT 13
+#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT)
+#define NAU8325_REG_SRATE_SFT 10
+#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT)
+#define NAU8325_REG_ALT_SRATE_SFT 9
+#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT)
+#define NAU8325_REG_DIV_MAX 0x1
+
+/* BIAS_ADJ (0x60) */
+#define NAU8325_BIAS_VMID_SEL_SFT 4
+#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x61) */
+#define NAU8325_VMDFSTENB_SFT 14
+#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT)
+#define NAU8325_CLASSDEN_SFT 12
+#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT)
+#define NAU8325_DACCLKEN_R_SFT 10
+#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT)
+#define NAU8325_DACEN_R_SFT 8
+#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT)
+#define NAU8325_DACCLKEN_SFT 6
+#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT)
+#define NAU8325_DACEN_SFT 4
+#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT)
+#define NAU8325_BIASEN_SFT 2
+#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT)
+#define NAU8325_VMIDEN_MASK 0x3
+
+/* ANALOG_CONTROL_2 (0x62) */
+#define NAU8325_PWMMOD_SFT 14
+#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT)
+#define NAU8325_DACTEST_SFT 6
+#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT)
+#define NAU8325_DACREFCAP_SFT 4
+#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+
+/* ANALOG_CONTROL_3 (0x63) */
+#define NAU8325_POWER_DOWN_L_SFT 12
+#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT)
+#define NAU8325_POWER_DOWN_R_SFT 11
+#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+#define NAU8325_CLASSD_FINE_SFT 5
+#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT)
+#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf
+
+/* ANALOG_CONTROL_4 (0x64) */
+#define NAU8325_CLASSD_OCPN_SFT 12
+#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT)
+#define NAU8325_CLASSD_OCPP_SFT 8
+#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT)
+#define NAU8325_CLASSD_SLEWN_MASK 0xff
+
+/* ANALOG_CONTROL_5 (0x65) */
+#define NAU8325_MCLK_RANGE_SFT 2
+#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT)
+#define NAU8325_MCLK8XEN_SFT 1
+#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT)
+#define NAU8325_MCLK4XEN_EN 0x1
+
+/* ANALOG_CONTROL_6 (0x66) */
+#define NAU8325_VBATLOW_SFT 4
+#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT)
+#define NAU8325_VDDSPK_LIM_SFT 3
+#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT)
+#define NAU8325_VDDSPK_LIM_MASK 0x7
+
+/* CLIP_CTRL (0x69)*/
+#define NAU8325_ANTI_CLIP_SFT 4
+#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT)
+
+/* RDAC (0x73) */
+#define NAU8325_CLK_DAC_DELAY_SFT 4
+#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT)
+#define NAU8325_DACVREFSEL_SFT 2
+#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT)
+
+#define NAU8325_CODEC_DAI "nau8325-hifi"
+
+struct nau8325 {
+ struct device *dev;
+ struct regmap *regmap;
+ int mclk;
+ int fs;
+ int vref_impedance_ohms;
+ int dac_vref_microvolt;
+ int clock_detection;
+ int clock_det_data;
+ int alc_enable;
+};
+
+struct nau8325_src_attr {
+ int param;
+ unsigned int val;
+};
+
+enum {
+ NAU8325_MCLK_FS_RATIO_256,
+ NAU8325_MCLK_FS_RATIO_400,
+ NAU8325_MCLK_FS_RATIO_500,
+ NAU8325_MCLK_FS_RATIO_NUM,
+};
+
+struct nau8325_srate_attr {
+ int fs;
+ int range;
+ bool max;
+ unsigned int mclk_src[NAU8325_MCLK_FS_RATIO_NUM];
+};
+
+struct nau8325_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+#endif /* __NAU8325_H__ */
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index b08fb7e243c3..caf2edb23088 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* NAU85L40 ALSA SoC audio driver
*
* Copyright 2016 Nuvoton Technology Corp.
* Author: John Hsu <KCHSU0@nuvoton.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -19,7 +16,7 @@
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -29,7 +26,6 @@
#include <sound/tlv.h>
#include "nau8540.h"
-
#define NAU_FREF_MAX 13500000
#define NAU_FVCO_MAX 100000000
#define NAU_FVCO_MIN 90000000
@@ -233,15 +229,60 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new digital_ch1_mux =
SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+static int nau8540_fepga_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 nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF |
+ NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF |
+ NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF |
+ NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF |
+ NAU8540_ACDC_CTL_MIC4N_VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int nau8540_precharge_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 nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, 0);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
static int adc_power_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ msleep(160);
/* DO12 and DO34 pad output enable */
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN);
regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
NAU8540_I2S_DO12_TRI, 0);
regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
@@ -251,6 +292,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w,
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);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, 0);
}
return 0;
}
@@ -258,8 +301,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w,
static int aiftx_power_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -277,28 +320,26 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
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_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0,
+ nau8540_precharge_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MUX("Digital CH4 Mux",
SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
@@ -319,20 +360,20 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
{"Frontend PGA3", NULL, "MIC3"},
{"Frontend PGA4", NULL, "MIC4"},
- {"ADC1", NULL, "Frontend PGA1"},
- {"ADC2", NULL, "Frontend PGA2"},
- {"ADC3", NULL, "Frontend PGA3"},
- {"ADC4", NULL, "Frontend PGA4"},
+ {"Precharge", NULL, "Frontend PGA1"},
+ {"Precharge", NULL, "Frontend PGA2"},
+ {"Precharge", NULL, "Frontend PGA3"},
+ {"Precharge", NULL, "Frontend PGA4"},
- {"ADC CH1", NULL, "ADC1"},
- {"ADC CH2", NULL, "ADC2"},
- {"ADC CH3", NULL, "ADC3"},
- {"ADC CH4", NULL, "ADC4"},
+ {"ADC CH1", NULL, "Precharge"},
+ {"ADC CH2", NULL, "Precharge"},
+ {"ADC CH3", NULL, "Precharge"},
+ {"ADC CH4", NULL, "Precharge"},
- {"ADC1", NULL, "MICBIAS1"},
- {"ADC2", NULL, "MICBIAS1"},
- {"ADC3", NULL, "MICBIAS2"},
- {"ADC4", NULL, "MICBIAS2"},
+ {"ADC CH1", NULL, "MICBIAS1"},
+ {"ADC CH2", NULL, "MICBIAS1"},
+ {"ADC CH3", NULL, "MICBIAS2"},
+ {"ADC CH4", NULL, "MICBIAS2"},
{"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
{"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
@@ -360,28 +401,41 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
{"AIFTX", NULL, "Digital CH4 Mux"},
};
-static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr)
+static const struct nau8540_osr_attr *
+nau8540_get_osr(struct nau8540 *nau8540)
{
- int osrate;
+ 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 -EINVAL;
- osrate = osr_adc_sel[osr].osr;
+ 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;
- if (rate * osr > CLK_ADC_MAX) {
- dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
+ osr = nau8540_get_osr(nau8540);
+ if (!osr || !osr->osr)
return -EINVAL;
- }
- return 0;
+ 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_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, osr;
+ 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)
@@ -389,13 +443,14 @@ static int nau8540_hw_params(struct snd_pcm_substream *substream,
* values must be selected such that the maximum frequency is less
* than 6.144 MHz.
*/
- regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
- osr &= NAU8540_ADC_OSR_MASK;
- if (nau8540_clock_check(nau8540, params_rate(params), osr))
+ 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_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
+ osr->clk_src << NAU8540_CLK_ADC_SRC_SFT);
switch (params_width(params)) {
case 16:
@@ -422,15 +477,15 @@ static int nau8540_hw_params(struct snd_pcm_substream *substream,
static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl2_val |= NAU8540_I2S_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -493,8 +548,8 @@ static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ 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)))
@@ -519,11 +574,61 @@ static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static int nau8540_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8540->regmap;
+ unsigned int val;
+ int ret = 0;
+
+ /* Reading the peak data to detect abnormal data in the ADC channel.
+ * If abnormal data happens, the driver takes recovery actions to
+ * refresh the ADC channel.
+ */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, NAU8540_CLK_AGC_EN);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, NAU8540_ALC_CH_ALL_EN);
+
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "1.ADC CH1 peak data %x", val);
+ if (!val) {
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, NAU8540_PGA_CH_ALL_MUTE);
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, 0);
+ regmap_write(regmap, NAU8540_REG_RST, 0x1);
+ regmap_write(regmap, NAU8540_REG_RST, 0);
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "2.ADC CH1 peak data %x", val);
+ if (!val) {
+ dev_err(nau8540->dev, "Channel recovery failed!!");
+ ret = -EIO;
+ }
+ }
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, 0);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
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,
+ .trigger = nau8540_dai_trigger,
};
#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
@@ -588,7 +693,7 @@ static int nau8540_calc_fll_param(unsigned int fll_in,
fvco_max = 0;
fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
fvco_max < fvco) {
fvco_max = fvco;
@@ -649,10 +754,10 @@ static void nau8540_fll_apply(struct regmap *regmap,
}
/* freq_out must be 256*Fs in order to achieve the best performance */
-static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+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_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
struct nau8540_fll fll_param;
int ret, fs;
@@ -702,10 +807,10 @@ static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
return 0;
}
-static int nau8540_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case NAU8540_CLK_DIS:
@@ -777,9 +882,9 @@ static void nau8540_init_regs(struct nau8540 *nau8540)
NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
}
-static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec)
+static int __maybe_unused nau8540_suspend(struct snd_soc_component *component)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
regcache_cache_only(nau8540->regmap, true);
regcache_mark_dirty(nau8540->regmap);
@@ -787,9 +892,9 @@ static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8540_resume(struct snd_soc_component *component)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
regcache_cache_only(nau8540->regmap, false);
regcache_sync(nau8540->regmap);
@@ -797,21 +902,21 @@ static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver nau8540_codec_driver = {
- .set_sysclk = nau8540_set_sysclk,
- .set_pll = nau8540_set_pll,
- .suspend = nau8540_suspend,
- .resume = nau8540_resume,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -828,8 +933,7 @@ static const struct regmap_config nau8540_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
};
-static int nau8540_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8540_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8540 *nau8540 = dev_get_platdata(dev);
@@ -856,19 +960,12 @@ static int nau8540_i2c_probe(struct i2c_client *i2c,
nau8540_reset_chip(nau8540->regmap);
nau8540_init_regs(nau8540);
- return snd_soc_register_codec(dev,
- &nau8540_codec_driver, &nau8540_dai, 1);
+ return devm_snd_soc_register_component(dev,
+ &nau8540_component_driver, &nau8540_dai, 1);
}
-static int nau8540_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-
static const struct i2c_device_id nau8540_i2c_ids[] = {
- { "nau8540", 0 },
+ { "nau8540" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
@@ -887,7 +984,6 @@ static struct i2c_driver nau8540_i2c_driver = {
.of_match_table = of_match_ptr(nau8540_of_ids),
},
.probe = nau8540_i2c_probe,
- .remove = nau8540_i2c_remove,
.id_table = nau8540_i2c_ids,
};
module_i2c_driver(nau8540_i2c_driver);
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
index 732b490edf81..762bb93b06fd 100644
--- a/sound/soc/codecs/nau8540.h
+++ b/sound/soc/codecs/nau8540.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NAU85L40 ALSA SoC audio driver
*
* Copyright 2016 Nuvoton Technology Corp.
* Author: John Hsu <KCHSU0@nuvoton.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 __NAU8540_H__
@@ -81,6 +78,7 @@
/* POWER_MANAGEMENT (0x01) */
+#define NAU8540_ADC_ALL_EN 0xf
#define NAU8540_ADC4_EN (0x1 << 3)
#define NAU8540_ADC3_EN (0x1 << 2)
#define NAU8540_ADC2_EN (0x1 << 1)
@@ -88,6 +86,7 @@
/* CLOCK_CTRL (0x02) */
#define NAU8540_CLK_ADC_EN (0x1 << 15)
+#define NAU8540_CLK_AGC_EN (0x1 << 3)
#define NAU8540_CLK_I2S_EN (0x1 << 1)
/* CLOCK_SRC (0x03) */
@@ -171,6 +170,13 @@
#define NAU8540_TDM_OFFSET_EN (0x1 << 14)
#define NAU8540_TDM_TX_MASK 0xf
+/* ALC_CONTROL_3 (0x22) */
+#define NAU8540_ALC_CH1_EN (0x1 << 12)
+#define NAU8540_ALC_CH2_EN (0x1 << 13)
+#define NAU8540_ALC_CH3_EN (0x1 << 14)
+#define NAU8540_ALC_CH4_EN (0x1 << 15)
+#define NAU8540_ALC_CH_ALL_EN (0xf << 12)
+
/* ADC_SAMPLE_RATE (0x3A) */
#define NAU8540_CH_SYNC (0x1 << 14)
#define NAU8540_ADC_OSR_MASK 0x3
@@ -184,12 +190,20 @@
#define NAU8540_VMID_SEL_SFT 4
#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT)
+/* MUTE (0x61) */
+#define NAU8540_PGA_CH1_MUTE 0x1
+#define NAU8540_PGA_CH2_MUTE 0x2
+#define NAU8540_PGA_CH3_MUTE 0x4
+#define NAU8540_PGA_CH4_MUTE 0x8
+#define NAU8540_PGA_CH_ALL_MUTE 0xf
+
/* MIC_BIAS (0x67) */
#define NAU8540_PU_PRE (0x1 << 8)
/* REFERENCE (0x68) */
#define NAU8540_PRECHARGE_DIS (0x1 << 13)
#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_DISCHRG_EN (0x1 << 11)
/* FEPGA1 (0x69) */
#define NAU8540_FEPGA1_MODCH2_SHT_SFT 7
@@ -202,7 +216,16 @@
#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)
-
+#define NAU8540_ACDC_CTL_SFT 8
+#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT)
+#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15)
+#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14)
+#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13)
+#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12)
+#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11)
+#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10)
+#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9)
+#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8)
/* System Clock Source */
enum {
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index c8e2451ae0a3..6f432b992941 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* nau8810.c -- NAU8810 ALSA Soc Audio driver
*
@@ -6,10 +7,6 @@
* Author: David Lin <ctlin0@nuvoton.com>
*
* Based on WM8974.c
- *
- * 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>
@@ -167,11 +164,12 @@ static bool nau8810_volatile_reg(struct device *dev, unsigned int reg)
static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ __be16 tmp;
val = (u16 *)ucontrol->value.bytes.data;
reg = NAU8810_REG_EQ1;
@@ -180,8 +178,8 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
/* 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));
+ tmp = cpu_to_be16(reg_val);
+ memcpy(val + i, &tmp, sizeof(tmp));
}
return 0;
@@ -198,12 +196,13 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ __be16 *tmp;
data = kmemdup(ucontrol->value.bytes.data,
params->max, GFP_KERNEL | GFP_DMA);
@@ -216,10 +215,11 @@ static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
/* conversion of 16-bit integers between native CPU format
* and big endian format
*/
- value = be16_to_cpu(*(val + i));
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
ret = regmap_write(nau8810->regmap, reg + i, value);
if (ret) {
- dev_err(codec->dev, "EQ configuration fail, register: %x ret: %d\n",
+ dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n",
reg + i, ret);
kfree(data);
return ret;
@@ -358,6 +358,8 @@ static const struct snd_kcontrol_new nau8810_snd_controls[] = {
/* 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,
@@ -366,6 +368,8 @@ static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = {
/* 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,
@@ -373,24 +377,25 @@ static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = {
};
/* PGA Mute */
-static const struct snd_kcontrol_new nau8810_inpga_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, 0),
+ 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),
};
-/* Mic Input boost vol */
-static const struct snd_kcontrol_new nau8810_mic_boost_controls =
- SOC_DAPM_SINGLE("Mic Volume", NAU8810_REG_ADCBOOST,
- NAU8810_PMICBSTGAIN_SFT, 0x7, 0);
-
/* Loopback Switch */
static const struct snd_kcontrol_new nau8810_loopback =
SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP,
@@ -399,14 +404,31 @@ static const struct snd_kcontrol_new nau8810_loopback =
static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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],
@@ -414,9 +436,9 @@ static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
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", "HiFi Playback", NAU8810_REG_POWER3,
+ SND_SOC_DAPM_DAC("DAC", "Playback", NAU8810_REG_POWER3,
NAU8810_DAC_EN_SFT, 0),
- SND_SOC_DAPM_ADC("ADC", "HiFi Capture", NAU8810_REG_POWER2,
+ 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),
@@ -429,8 +451,10 @@ static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
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_inpga_mute,
- ARRAY_SIZE(nau8810_inpga_mute)),
+ 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),
@@ -440,6 +464,7 @@ static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
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"),
@@ -451,10 +476,12 @@ 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"},
@@ -469,13 +496,16 @@ static const struct snd_soc_dapm_route nau8810_dapm_routes[] = {
/* Input Boost Stage */
{"ADC", NULL, "Input Boost Stage"},
{"ADC", NULL, "PLL", check_mclk_select_pll},
- {"Input Boost Stage", NULL, "Input PGA"},
- {"Input Boost Stage", NULL, "MICP"},
+ {"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"},
+ {"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"},
@@ -485,8 +515,8 @@ static const struct snd_soc_dapm_route nau8810_dapm_routes[] = {
static int nau8810_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
nau8810->clk_id = clk_id;
nau8810->sysclk = freq;
@@ -496,7 +526,7 @@ static int nau8810_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int nau88l0_calc_pll(unsigned int pll_in,
+static int nau8810_calc_pll(unsigned int pll_in,
unsigned int fs, struct nau8810_pll *pll_param)
{
u64 f2, f2_max, pll_ratio;
@@ -508,7 +538,8 @@ static int nau88l0_calc_pll(unsigned int pll_in,
f2_max = 0;
scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
- f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10;
+ 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;
@@ -538,14 +569,14 @@ static int nau88l0_calc_pll(unsigned int pll_in,
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_codec *codec = codec_dai->codec;
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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 = nau88l0_calc_pll(freq_in, fs, pll_param);
+ 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;
@@ -577,15 +608,15 @@ static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl2_val |= NAU8810_CLKIO_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -667,9 +698,27 @@ static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate)
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_codec *codec = dai->codec;
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -723,10 +772,10 @@ static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static int nau8810_set_bias_level(struct snd_soc_codec *codec,
+static int nau8810_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
struct regmap *map = nau8810->regmap;
switch (level) {
@@ -741,7 +790,7 @@ static int nau8810_set_bias_level(struct snd_soc_codec *codec,
NAU8810_IOBUF_EN | NAU8810_ABIAS_EN,
NAU8810_IOBUF_EN | NAU8810_ABIAS_EN);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ 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);
@@ -791,7 +840,7 @@ static struct snd_soc_dai_driver nau8810_dai = {
.formats = NAU8810_FORMATS,
},
.ops = &nau8810_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static const struct regmap_config nau8810_regmap_config = {
@@ -808,22 +857,21 @@ static const struct regmap_config nau8810_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults),
};
-static const struct snd_soc_codec_driver nau8810_codec_driver = {
- .set_bias_level = nau8810_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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,
- const struct i2c_device_id *id)
+static int nau8810_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8810 *nau8810 = dev_get_platdata(dev);
@@ -842,19 +890,14 @@ static int nau8810_i2c_probe(struct i2c_client *i2c,
regmap_write(nau8810->regmap, NAU8810_REG_RESET, 0x00);
- return snd_soc_register_codec(dev,
- &nau8810_codec_driver, &nau8810_dai, 1);
-}
-
-static int nau8810_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &nau8810_component_driver, &nau8810_dai, 1);
}
static const struct i2c_device_id nau8810_i2c_id[] = {
- { "nau8810", 0 },
+ { "nau8810" },
+ { "nau8812" },
+ { "nau8814" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
@@ -862,6 +905,8 @@ 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);
@@ -872,8 +917,7 @@ static struct i2c_driver nau8810_i2c_driver = {
.name = "nau8810",
.of_match_table = of_match_ptr(nau8810_of_match),
},
- .probe = nau8810_i2c_probe,
- .remove = nau8810_i2c_remove,
+ .probe = nau8810_i2c_probe,
.id_table = nau8810_i2c_id,
};
diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h
index df882658ca91..6a7cacbe044a 100644
--- a/sound/soc/codecs/nau8810.h
+++ b/sound/soc/codecs/nau8810.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NAU8810 ALSA SoC audio driver
*
* Copyright 2016 Nuvoton Technology Corp.
* Author: David Lin <ctlin0@nuvoton.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 __NAU8810_H__
@@ -72,6 +69,7 @@
/* 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)
@@ -231,7 +229,10 @@
/* 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
@@ -239,12 +240,15 @@
#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
@@ -254,6 +258,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
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
new file mode 100644
index 000000000000..edb95f869a4a
--- /dev/null
+++ b/sound/soc/codecs/nau8821.c
@@ -0,0 +1,1959 @@
+// 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/dmi.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 NAU8821_JD_ACTIVE_HIGH BIT(0)
+
+static int nau8821_quirk;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+#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;
+
+ return regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1,
+ ucontrol->value.bytes.data, params->max);
+}
+
+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;
+ int ret;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1,
+ data, params->max);
+
+ kfree(data);
+
+ return ret;
+}
+
+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(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ 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(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ 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 int nau8821_left_fepga_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);
+
+ if (!nau8821->left_input_single_end)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK,
+ NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, 0);
+ break;
+ default:
+ break;
+ }
+
+ 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),
+ /* single-ended design only on the left */
+ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ 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 {
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ }
+ } 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);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8821->adc_delay);
+}
+
+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");
+ nau8821->left_input_single_end = device_property_read_bool(dev,
+ "nuvoton,left-input-single-end");
+ 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;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-slew-rate",
+ &nau8821->dmic_slew_rate);
+ if (ret)
+ nau8821->dmic_slew_rate = 0;
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms",
+ &nau8821->adc_delay);
+ if (ret)
+ nau8821->adc_delay = 125;
+ if (nau8821->adc_delay < 125 || nau8821->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
+
+ 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);
+ regmap_update_bits(regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SLEW_MASK, nau8821->dmic_slew_rate <<
+ NAU8821_DMIC_SLEW_SFT);
+ if (nau8821->left_input_single_end) {
+ regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE,
+ NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN);
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN);
+ }
+}
+
+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;
+}
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id nau8821_quirk_table[] = {
+ {
+ /* Positivo CW14Q01P-V2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P-V2"),
+ },
+ .driver_data = (void *)(NAU8821_JD_ACTIVE_HIGH),
+ },
+ {}
+};
+
+static void nau8821_check_quirks(void)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (quirk_override != -1) {
+ nau8821_quirk = quirk_override;
+ return;
+ }
+
+ dmi_id = dmi_first_match(nau8821_quirk_table);
+ if (dmi_id)
+ nau8821_quirk = (unsigned long)dmi_id->driver_data;
+}
+
+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_check_quirks();
+
+ if (nau8821_quirk & NAU8821_JD_ACTIVE_HIGH)
+ nau8821->jkdet_polarity = 0;
+
+ 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" },
+ { }
+};
+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 = 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..f0935ffafcbe
--- /dev/null
+++ b/sound/soc/codecs/nau8821.h
@@ -0,0 +1,586 @@
+/* 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
+#define NAU8821_DMIC_SLEW_SFT 8
+#define NAU8821_DMIC_SLEW_MASK (0x7 << NAU8821_DMIC_SLEW_SFT)
+
+/* 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
+
+/* MUTE_MIC_L_N (0x6b) */
+#define NAU8821_MUTE_MICNL_SFT 5
+#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT)
+#define NAU8821_MUTE_MICNR_SFT 4
+#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT)
+#define NAU8821_MUTE_MICRP_SFT 2
+#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT)
+
+/* 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_LOWNOISE_SFT 10
+#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT)
+#define NAU8821_MICBIAS_POWERUP_SFT 8
+#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT)
+#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_DISCHRG_SFT 11
+#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT)
+#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_ACDC_CTRL_SFT 14
+#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_FEPGA_MODEL_SFT 4
+#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODER_SFT 0
+#define NAU8821_FEPGA_MODER_MASK 0xf
+#define NAU8821_FEPGA_MODER_AAF 0x1
+#define NAU8821_FEPGA_MODER_DIS 0x2
+#define NAU8821_FEPGA_MODER_IMP12K 0x8
+
+
+/* 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;
+ bool left_input_single_end;
+ int jkdet_polarity;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+ int fs;
+ int dmic_clk_threshold;
+ int dmic_slew_rate;
+ int key_enable;
+ int adc_delay;
+};
+
+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..15d6f8d01f78
--- /dev/null
+++ b/sound/soc/codecs/nau8822.c
@@ -0,0 +1,1221 @@
+// 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/clk.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;
+ __be16 tmp;
+
+ 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
+ */
+ tmp = cpu_to_be16(reg_val);
+ memcpy(val + i, &tmp, sizeof(tmp));
+ }
+
+ 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;
+ __be16 *tmp;
+
+ 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
+ */
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
+ 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_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_dbg(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_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);
+ unsigned long mclk_freq;
+
+ nau8822->div_id = clk_id;
+ nau8822->sysclk = freq;
+
+ if (nau8822->mclk) {
+ mclk_freq = clk_get_rate(nau8822->mclk);
+ if (mclk_freq != freq) {
+ int ret = nau8822_set_pll(dai, NAU8822_CLK_MCLK,
+ NAU8822_CLK_MCLK, mclk_freq, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set PLL\n");
+ return ret;
+ }
+ nau8822->div_id = NAU8822_CLK_PLL;
+ }
+ }
+
+ dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq,
+ nau8822->div_id == NAU8822_CLK_PLL ? "PLL" : "MCLK");
+
+ 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_CBP_CFP:
+ ctrl2_val |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 div = 0, 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)
+ div = nau8822->pll.mclk_scaler;
+
+ nau8822_config_clkdiv(dai, div, 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)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (nau8822->mclk &&
+ snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) {
+ int ret = clk_prepare_enable(nau8822->mclk);
+
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (nau8822->mclk &&
+ snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_OFF)
+ clk_disable_unprepare(nau8822->mclk);
+
+ 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->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(nau8822->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk),
+ "Error getting mclk\n");
+
+ 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" },
+ { }
+};
+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 = 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..13fe0a091e9e
--- /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;
+ struct clk *mclk;
+ 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
index 088e0cef4cb8..542bd22e6180 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -1,16 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* NAU88L24 ALSA SoC audio driver
*
* Copyright 2016 Nuvoton Technology Corp.
* Author: John Hsu <KCHSU0@nuvoton.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -30,6 +28,13 @@
#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);
@@ -205,11 +210,11 @@ static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout)
if (timeout) {
ret = down_timeout(&nau8824->jd_sem, timeout);
if (ret < 0)
- dev_warn(nau8824->dev, "Acquire semaphone timeout\n");
+ dev_warn(nau8824->dev, "Acquire semaphore timeout\n");
} else {
ret = down_interruptible(&nau8824->jd_sem);
if (ret < 0)
- dev_warn(nau8824->dev, "Acquire semaphone fail\n");
+ dev_warn(nau8824->dev, "Acquire semaphore fail\n");
}
return ret;
@@ -363,13 +368,13 @@ static const struct snd_kcontrol_new nau8824_snd_controls[] = {
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),
+ NAU8824_REG_CLASSD_GAIN_1, 8, 0x19, 0, spk_vol_tlv),
SOC_SINGLE_TLV("Speaker Left DACL Volume",
- NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv),
+ NAU8824_REG_CLASSD_GAIN_2, 0, 0x19, 0, spk_vol_tlv),
SOC_SINGLE_TLV("Speaker Left DACR Volume",
- NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv),
+ NAU8824_REG_CLASSD_GAIN_1, 0, 0x19, 0, spk_vol_tlv),
SOC_SINGLE_TLV("Speaker Right DACL Volume",
- NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv),
+ NAU8824_REG_CLASSD_GAIN_2, 8, 0x19, 0, spk_vol_tlv),
SOC_SINGLE_TLV("Headphone Right DACR Volume",
NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv),
@@ -409,13 +414,22 @@ static const struct snd_kcontrol_new nau8824_snd_controls[] = {
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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -437,8 +451,8 @@ static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w,
static int nau8824_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -461,8 +475,8 @@ static int nau8824_spk_event(struct snd_soc_dapm_widget *w,
static int nau8824_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -487,10 +501,15 @@ static int nau8824_pump_event(struct snd_soc_dapm_widget *w,
static int system_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ int ret;
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,
@@ -502,26 +521,71 @@ static int system_clock_control(struct snd_soc_dapm_widget *w,
} else {
nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
}
+
+ clk_disable_unprepare(nau8824->mclk);
+ } else {
+ dev_dbg(nau8824->dev, "system clock control : POWER ON\n");
+
+ ret = clk_prepare_enable(nau8824->mclk);
+ if (ret)
+ return ret;
+
+ /* 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
int src;
+ unsigned int freq;
+
+ freq = clk_get_rate(nau8824->mclk);
+ if (!freq)
+ freq = nau8824->fs * 256;
/* 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)
+ if (freq / (0x1 << src) <= DMIC_CLK)
break;
}
- dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256);
+ dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, freq);
regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT));
@@ -591,7 +655,8 @@ static const struct snd_kcontrol_new nau8824_dacr_mux =
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),
+ system_clock_control, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_INPUT("HSMIC1"),
SND_SOC_DAPM_INPUT("HSMIC2"),
@@ -634,8 +699,8 @@ static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
NAU8824_ADCR_EN_SFT, 0),
- SND_SOC_DAPM_AIF_OUT("AIFTX", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("AIFRX", "HiFi Playback", 0, SND_SOC_NOPM, 0, 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),
@@ -760,7 +825,7 @@ static const struct snd_soc_dapm_route nau8824_dapm_routes[] = {
static bool nau8824_is_jack_inserted(struct nau8824 *nau8824)
{
struct snd_soc_jack *jack = nau8824->jack;
- bool insert = FALSE;
+ bool insert = false;
if (nau8824->irq && jack)
insert = jack->status & SND_JACK_HEADPHONE;
@@ -849,7 +914,10 @@ static void nau8824_jdet_work(struct work_struct *work)
NAU8824_IRQ_KEY_RELEASE_DIS |
NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0);
- nau8824_sema_release(nau8824);
+ if (nau8824->resume_lock) {
+ nau8824_sema_release(nau8824);
+ nau8824->resume_lock = false;
+ }
}
static void nau8824_setup_auto_irq(struct nau8824 *nau8824)
@@ -914,7 +982,10 @@ static irqreturn_t nau8824_interrupt(int irq, void *data)
/* release semaphore held after resume,
* and cancel jack detection
*/
- nau8824_sema_release(nau8824);
+ 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;
@@ -962,35 +1033,52 @@ static irqreturn_t nau8824_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
-static int nau8824_clock_check(struct nau8824 *nau8824,
- int stream, int rate, int osr)
+static const struct nau8824_osr_attr *
+nau8824_get_osr(struct nau8824 *nau8824, int stream)
{
- int osrate;
+ 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 -EINVAL;
- osrate = osr_dac_sel[osr].osr;
+ 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 -EINVAL;
- osrate = osr_adc_sel[osr].osr;
+ 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;
- if (!osrate || rate * osr > CLK_DA_AD_MAX) {
- dev_err(nau8824->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
+ osr = nau8824_get_osr(nau8824, substream->stream);
+ if (!osr || !osr->osr)
return -EINVAL;
- }
- return 0;
+ 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_codec *codec = dai->codec;
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
+ 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);
@@ -1001,27 +1089,19 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
* than 6.144 MHz.
*/
nau8824->fs = params_rate(params);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- regmap_read(nau8824->regmap,
- NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
- osr &= NAU8824_DAC_OVERSAMPLE_MASK;
- if (nau8824_clock_check(nau8824, substream->stream,
- nau8824->fs, osr))
- return -EINVAL;
+ 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_dac_sel[osr].clk_src << NAU8824_CLK_DAC_SRC_SFT);
- } else {
- regmap_read(nau8824->regmap,
- NAU8824_REG_ADC_FILTER_CTRL, &osr);
- osr &= NAU8824_ADC_SYNC_DOWN_MASK;
- if (nau8824_clock_check(nau8824, substream->stream,
- nau8824->fs, osr))
- return -EINVAL;
+ osr->clk_src << NAU8824_CLK_DAC_SRC_SFT);
+ else
regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
NAU8824_CLK_ADC_SRC_MASK,
- osr_adc_sel[osr].clk_src << NAU8824_CLK_ADC_SRC_SFT);
- }
+ osr->clk_src << NAU8824_CLK_ADC_SRC_SFT);
/* make BCLK and LRC divde configuration if the codec as master. */
regmap_read(nau8824->regmap,
@@ -1038,7 +1118,7 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
else if (bclk_fs <= 256)
bclk_div = 0;
else
- return -EINVAL;
+ goto error;
regmap_update_bits(nau8824->regmap,
NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
@@ -1059,30 +1139,30 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
val_len |= NAU8824_I2S_DL_32;
break;
default:
- return -EINVAL;
+ 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 0;
+ return err;
}
static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
unsigned int ctrl1_val = 0, ctrl2_val = 0;
- nau8824_sema_acquire(nau8824, HZ);
-
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl2_val |= NAU8824_I2S_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -1119,6 +1199,8 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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);
@@ -1149,8 +1231,8 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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_codec *codec = dai->codec;
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ 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)) ||
@@ -1227,7 +1309,7 @@ static int nau8824_calc_fll_param(unsigned int fll_in,
fvco_max = 0;
fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
fvco_max < fvco) {
fvco_max = fvco;
@@ -1288,10 +1370,10 @@ static void nau8824_fll_apply(struct regmap *regmap,
}
/* freq_out must be 256*Fs in order to achieve the best performance */
-static int nau8824_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+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_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
struct nau8824_fll fll_param;
int ret, fs;
@@ -1374,10 +1456,10 @@ static int nau8824_config_sysclk(struct nau8824 *nau8824,
return 0;
}
-static int nau8824_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
return nau8824_config_sysclk(nau8824, clk_id, freq);
}
@@ -1403,10 +1485,10 @@ static void nau8824_resume_setup(struct nau8824 *nau8824)
}
}
-static int nau8824_set_bias_level(struct snd_soc_codec *codec,
+static int nau8824_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1416,7 +1498,7 @@ static int nau8824_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Setup codec configuration after resume */
nau8824_resume_setup(nau8824);
}
@@ -1434,23 +1516,23 @@ static int nau8824_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int nau8824_codec_probe(struct snd_soc_codec *codec)
+static int nau8824_component_probe(struct snd_soc_component *component)
{
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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_codec *codec)
+static int __maybe_unused nau8824_suspend(struct snd_soc_component *component)
{
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
if (nau8824->irq) {
disable_irq(nau8824->irq);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
}
regcache_cache_only(nau8824->regmap, true);
regcache_mark_dirty(nau8824->regmap);
@@ -1458,9 +1540,10 @@ static int __maybe_unused nau8824_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int __maybe_unused nau8824_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8824_resume(struct snd_soc_component *component)
{
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int ret;
regcache_cache_only(nau8824->regmap, false);
regcache_sync(nau8824->regmap);
@@ -1468,33 +1551,37 @@ static int __maybe_unused nau8824_resume(struct snd_soc_codec *codec)
/* Hold semaphore to postpone playback happening
* until jack detection done.
*/
- nau8824_sema_acquire(nau8824, 0);
+ 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_codec_driver nau8824_codec_driver = {
- .probe = nau8824_codec_probe,
- .set_sysclk = nau8824_set_sysclk,
- .set_pll = nau8824_set_pll,
- .set_bias_level = nau8824_set_bias_level,
- .suspend = nau8824_suspend,
- .resume = nau8824_resume,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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,
@@ -1547,10 +1634,10 @@ static const struct regmap_config nau8824_regmap_config = {
* events will be routed to the given jack. Jack can be null to stop
* reporting.
*/
-int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+int nau8824_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
int ret;
nau8824->jack = jack;
@@ -1797,11 +1884,95 @@ static int nau8824_read_device_properties(struct device *dev,
if (ret)
nau8824->jack_eject_debounce = 1;
+ nau8824->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(nau8824->mclk))
+ return PTR_ERR(nau8824->mclk);
+
return 0;
}
-static int nau8824_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/* 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),
+ },
+ {
+ /* Positivo CW14Q01P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {
+ /* Positivo K1424G */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "K1424G"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {
+ /* Positivo N14ZP74G */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "N14ZP74G"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {}
+};
+
+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);
@@ -1820,10 +1991,16 @@ static int nau8824_i2c_probe(struct i2c_client *i2c,
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);
@@ -1838,19 +2015,12 @@ static int nau8824_i2c_probe(struct i2c_client *i2c,
if (i2c->irq)
nau8824_setup_irq(nau8824);
- return snd_soc_register_codec(dev,
- &nau8824_codec_driver, &nau8824_dai, 1);
-}
-
-
-static int nau8824_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &nau8824_component_driver, &nau8824_dai, 1);
}
static const struct i2c_device_id nau8824_i2c_ids[] = {
- { "nau8824", 0 },
+ { "nau8824" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
@@ -1878,7 +2048,6 @@ static struct i2c_driver nau8824_i2c_driver = {
.acpi_match_table = ACPI_PTR(nau8824_acpi_match),
},
.probe = nau8824_i2c_probe,
- .remove = nau8824_i2c_remove,
.id_table = nau8824_i2c_ids,
};
module_i2c_driver(nau8824_i2c_driver);
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
index 21eae2431c83..d8e19515133c 100644
--- a/sound/soc/codecs/nau8824.h
+++ b/sound/soc/codecs/nau8824.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NAU88L24 ALSA SoC audio driver
*
* Copyright 2016 Nuvoton Technology Corp.
* Author: John Hsu <KCHSU0@nuvoton.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 __NAU8824_H__
@@ -200,7 +197,7 @@
/* 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
+#define NAU8824_JACK_LOGIC (0x1 << 1)
/* INTERRUPT_SETTING_1 (0x0F) */
@@ -437,8 +434,10 @@ struct nau8824 {
struct snd_soc_jack *jack;
struct work_struct jdet_work;
struct semaphore jd_sem;
+ struct clk *mclk;
int fs;
int irq;
+ int resume_lock;
int micbias_voltage;
int vref_impedance;
int jkdet_polarity;
@@ -471,8 +470,9 @@ struct nau8824_osr_attr {
};
-int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+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
index a1b697b6fb64..25b8b19e27ec 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Nuvoton NAU8825 audio codec driver
*
@@ -5,13 +6,12 @@
* Author: Anatol Pomozov <anatol@chromium.org>
* Copyright 2015 Nuvoton Technology Corp.
* Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/int_log.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -39,7 +39,6 @@
#define NAU_FVCO_MIN 90000000
/* cross talk suppression detection */
-#define LOG10_MAGIC 646456993
#define GAIN_AUGMENT 22500
#define SIDETONE_BASE 207000
@@ -48,11 +47,13 @@
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_frac_num;
int fll_int;
int clk_ref_div;
};
@@ -178,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_CLASSG_CTRL, 0x0 },
{ NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
{ NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_FLL2_LOWER, 0x0 },
+ { NAU8825_REG_FLL2_UPPER, 0x0 },
{ NAU8825_REG_BIAS_ADJ, 0x0 },
{ NAU8825_REG_TRIM_SETTINGS, 0x0 },
{ NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
@@ -200,39 +203,20 @@ static struct reg_default nau8825_xtalk_baktab[] = {
{ 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
+/* The regmap patch for Rev C */
+static const struct reg_sequence nau8825_regmap_patch[] = {
+ { NAU8825_REG_FLL2, 0x0000 },
+ { NAU8825_REG_FLL4, 0x8010 },
+ { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0800 },
+ { NAU8825_REG_DACL_CTRL, 0x00cf },
+ { NAU8825_REG_DACR_CTRL, 0x02cf },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 },
+ { NAU8825_REG_FLL2_LOWER, 0x26e9 },
+ { NAU8825_REG_FLL2_UPPER, 0x0031 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0220 },
+ { NAU8825_REG_MIC_BIAS, 0x0046 },
};
/**
@@ -252,7 +236,7 @@ static const unsigned short logtable[256] = {
*
* Acquires the semaphore without jiffies. Try to acquire the semaphore
* atomically. Returns 0 if the semaphore has been acquired successfully
- * or 1 if it it cannot be acquired.
+ * or 1 if it cannot be acquired.
*/
static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
{
@@ -296,7 +280,7 @@ static inline void nau8825_sema_reset(struct nau8825 *nau8825)
}
/**
- * Ramp up the headphone volume change gradually to target level.
+ * 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
@@ -348,67 +332,18 @@ static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
}
/**
- * Computes log10 of a value; the result is round off to 3 decimal. This func-
- * tion takes reference to dvb-math. The source code locates as the following.
- * Linux/drivers/media/dvb-core/dvb_math.c
+ * nau8825_intlog10_dec3 - Computes log10 of a value, rounding the result to 3 decimal places.
+ * @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);
+ return intlog10(value) / ((1 << 24) / 1000);
}
/**
- * computes cross talk suppression sidetone gain.
+ * nau8825_xtalk_sidetone - computes cross talk suppression sidetone gain.
*
* @sig_org: orignal signal level
* @sig_cros: cross talk signal level
@@ -424,10 +359,8 @@ static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros)
{
u32 gain, sidetone;
- if (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) {
- WARN_ON(1);
+ 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);
@@ -608,8 +541,13 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
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);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ 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);
}
static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
@@ -622,9 +560,14 @@ static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
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);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ 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);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
/* 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 |
@@ -855,7 +798,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
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_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER:
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:
@@ -881,6 +824,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_IMM_MODE_CTRL:
case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER:
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:
@@ -911,15 +855,41 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
}
}
+static int nau8825_fepga_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:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK,
+ NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ 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(125);
+ msleep(nau8825->adc_delay);
regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
break;
@@ -938,8 +908,8 @@ static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -962,18 +932,33 @@ static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ 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);
break;
case SND_SOC_DAPM_POST_PMD:
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ 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);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
break;
default:
return -EINVAL;
@@ -982,6 +967,31 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
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)
{
@@ -1095,12 +1105,15 @@ static const struct snd_kcontrol_new nau8825_dacr_mux =
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_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
@@ -1153,12 +1166,13 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
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_NOPM, 0, 0, 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_NOPM, 0, 0, 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),
@@ -1183,9 +1197,11 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"ADC", NULL, "ADC Clock"},
{"ADC", NULL, "ADC Power"},
{"AIFTX", NULL, "ADC"},
+ {"AIFTX", NULL, "System Clock"},
- {"DDACL", NULL, "Playback"},
- {"DDACR", NULL, "Playback"},
+ {"AIFRX", NULL, "System Clock"},
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
{"DDACL", NULL, "DDAC Clock"},
{"DDACR", NULL, "DDAC Clock"},
{"DACL Mux", "DACL", "DDACL"},
@@ -1217,36 +1233,53 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"HPOR", NULL, "Class G"},
};
-static int nau8825_clock_check(struct nau8825 *nau8825,
- int stream, int rate, int osr)
+static const struct nau8825_osr_attr *
+nau8825_get_osr(struct nau8825 *nau8825, int stream)
{
- int osrate;
+ 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 -EINVAL;
- osrate = osr_dac_sel[osr].osr;
+ 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 -EINVAL;
- osrate = osr_adc_sel[osr].osr;
+ return NULL;
+ return &osr_adc_sel[osr];
}
+}
- if (!osrate || rate * osr > CLK_DA_AD_MAX) {
- dev_err(nau8825->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
+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 0;
+ 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_codec *codec = dai->codec;
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
+ 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);
@@ -1256,29 +1289,19 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
* values must be selected such that the maximum frequency is less
* than 6.144 MHz.
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- regmap_read(nau8825->regmap, NAU8825_REG_DAC_CTRL1, &osr);
- osr &= NAU8825_DAC_OVERSAMPLE_MASK;
- if (nau8825_clock_check(nau8825, substream->stream,
- params_rate(params), osr)) {
- nau8825_sema_release(nau8825);
- return -EINVAL;
- }
+ 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_dac_sel[osr].clk_src << NAU8825_CLK_DAC_SRC_SFT);
- } else {
- regmap_read(nau8825->regmap, NAU8825_REG_ADC_RATE, &osr);
- osr &= NAU8825_ADC_SYNC_DOWN_MASK;
- if (nau8825_clock_check(nau8825, substream->stream,
- params_rate(params), osr)) {
- nau8825_sema_release(nau8825);
- return -EINVAL;
- }
+ osr->clk_src << NAU8825_CLK_DAC_SRC_SFT);
+ else
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
NAU8825_CLK_ADC_SRC_MASK,
- osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT);
- }
+ 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);
@@ -1291,10 +1314,8 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
bclk_div = 1;
else if (bclk_fs <= 128)
bclk_div = 0;
- else {
- nau8825_sema_release(nau8825);
- return -EINVAL;
- }
+ 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);
@@ -1314,30 +1335,31 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
val_len |= NAU8825_I2S_DL_32;
break;
default:
- nau8825_sema_release(nau8825);
- return -EINVAL;
+ 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 0;
+ return err;
}
static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl2_val |= NAU8825_I2S_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -1389,9 +1411,107 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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
@@ -1410,7 +1530,7 @@ static struct snd_soc_dai_driver nau8825_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2, /* Only 1 channel of data */
.rates = NAU8825_RATES,
.formats = NAU8825_FORMATS,
},
@@ -1427,14 +1547,20 @@ static struct snd_soc_dai_driver nau8825_dai = {
* events will be routed to the given jack. Jack can be null to stop
* reporting.
*/
-int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ 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]
*/
@@ -1538,6 +1664,10 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
{
struct regmap *regmap = nau8825->regmap;
+ /* Enable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE);
+
/* Enable headset jack type detection complete interruption and
* jack ejection interruption.
*/
@@ -1546,6 +1676,9 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
/* 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,
@@ -1593,6 +1726,121 @@ static int nau8825_button_decode(int value)
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;
@@ -1654,12 +1902,26 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
snd_soc_dapm_sync(dapm);
break;
case 3:
- /* detect error case */
- dev_err(nau8825->dev, "detection error; disable mic function\n");
- type = SND_JACK_HEADPHONE;
+ /* 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);
+
+ /* Disable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0);
+
/* 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.
@@ -1882,6 +2144,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
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);
@@ -1900,9 +2166,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
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);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ 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.
@@ -1936,6 +2203,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
/* 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 = {
@@ -1952,24 +2223,22 @@ static const struct regmap_config nau8825_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
};
-static int nau8825_codec_probe(struct snd_soc_codec *codec)
+static int nau8825_component_probe(struct snd_soc_component *component)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ 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 int nau8825_codec_remove(struct snd_soc_codec *codec)
+static void nau8825_component_remove(struct snd_soc_component *component)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
/* Cancel and reset cross tak suppresstion detection funciton */
nau8825_xtalk_cancel(nau8825);
-
- return 0;
}
/**
@@ -2018,7 +2287,7 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
fvco_max = 0;
fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
fvco_max < fvco) {
fvco_max = fvco;
@@ -2032,9 +2301,12 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
/* 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;
+ fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF;
+ if (fll_param->fll_frac_num == 16)
+ fll_param->fll_frac = fvco & 0xFFFF;
+ else
+ fll_param->fll_frac = fvco & 0xFFFFFF;
return 0;
}
@@ -2048,8 +2320,16 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
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 16/24 bit fractional input */
+ if (fll_param->fll_frac_num == 16)
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2,
+ fll_param->fll_frac);
+ else {
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER,
+ fll_param->fll_frac & 0xffff);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER,
+ (fll_param->fll_frac >> 16) & 0xff);
+ }
/* FLL 10-bit integer input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
@@ -2084,20 +2364,25 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
}
/* freq_out must be 256*Fs in order to achieve the best performance */
-static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+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_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
struct nau8825_fll fll_param;
int ret, fs;
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ fll_param.fll_frac_num = 16;
+ else
+ fll_param.fll_frac_num = 24;
+
fs = freq_out / 256;
ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
if (ret < 0) {
- dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ 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);
@@ -2110,7 +2395,7 @@ static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
{
- int ret = 0;
+ int ret;
nau8825->mclk = devm_clk_get(nau8825->dev, "mclk");
if (IS_ERR(nau8825->mclk)) {
@@ -2298,10 +2583,10 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
return 0;
}
-static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
return nau8825_configure_sysclk(nau8825, clk_id, freq);
}
@@ -2331,10 +2616,10 @@ static int nau8825_resume_setup(struct nau8825 *nau8825)
return 0;
}
-static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+static int nau8825_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -2345,11 +2630,11 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ 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 codec mclk\n");
+ dev_err(nau8825->dev, "Unable to prepare component mclk\n");
return ret;
}
}
@@ -2383,12 +2668,12 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_suspend(struct snd_soc_component *component)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
disable_irq(nau8825->irq);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ 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");
@@ -2399,9 +2684,9 @@ static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_resume(struct snd_soc_component *component)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
int ret;
regcache_cache_only(nau8825->regmap, false);
@@ -2415,24 +2700,31 @@ static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver nau8825_codec_driver = {
- .probe = nau8825_codec_probe,
- .remove = nau8825_codec_remove,
- .set_sysclk = nau8825_set_sysclk,
- .set_pll = nau8825_set_pll,
- .set_bias_level = nau8825_set_bias_level,
- .suspend_bias_off = true,
- .suspend = nau8825_suspend,
- .resume = nau8825_resume,
-
- .component_driver = {
- .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),
- },
+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)
@@ -2469,6 +2761,8 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
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,
@@ -2535,17 +2829,19 @@ static int nau8825_read_device_properties(struct device *dev,
nau8825->jack_eject_debounce = 0;
nau8825->xtalk_enable = device_property_read_bool(dev,
"nuvoton,crosstalk-enable");
-
- nau8825->mclk = devm_clk_get(dev, "mclk");
- if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ 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_optional(dev, "mclk");
+ if (IS_ERR(nau8825->mclk))
+ return PTR_ERR(nau8825->mclk);
+ if (!nau8825->mclk)
/* 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;
}
@@ -2567,8 +2863,7 @@ static int nau8825_setup_irq(struct nau8825 *nau8825)
return 0;
}
-static int nau8825_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8825_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
@@ -2608,8 +2903,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
ret);
return ret;
}
- if ((value & NAU8825_SOFTWARE_ID_MASK) !=
- NAU8825_SOFTWARE_ID_NAU8825) {
+ nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK;
+ switch (nau8825->sw_id) {
+ case NAU8825_SOFTWARE_ID_NAU8825:
+ break;
+ case NAU8825_SOFTWARE_ID_NAU8825C:
+ ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch,
+ ARRAY_SIZE(nau8825_regmap_patch));
+ if (ret) {
+ dev_err(dev, "Failed to register Rev C patch: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
dev_err(dev, "Not a NAU8825 chip\n");
return -ENODEV;
}
@@ -2619,18 +2925,16 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
if (i2c->irq)
nau8825_setup_irq(nau8825);
- return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &nau8825_component_driver,
&nau8825_dai, 1);
}
-static int nau8825_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+static void nau8825_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id nau8825_i2c_ids[] = {
- { "nau8825", 0 },
+ { "nau8825" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index f7e732125882..2abfbb5184da 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NAU8825 ALSA SoC audio driver
*
* Copyright 2015 Google Inc.
* Author: Anatol Pomozov <anatol.pomozov@chrominium.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __NAU8825_H__
@@ -78,6 +75,8 @@
#define NAU8825_REG_MISC_CTRL 0x55
#define NAU8825_REG_I2C_DEVICE_ID 0x58
#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_FLL2_LOWER 0x5a
+#define NAU8825_REG_FLL2_UPPER 0x5b
#define NAU8825_REG_BIAS_ADJ 0x66
#define NAU8825_REG_TRIM_SETTINGS 0x68
#define NAU8825_REG_ANALOG_CONTROL_1 0x69
@@ -158,6 +157,8 @@
/* 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)
@@ -171,6 +172,8 @@
#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)
@@ -208,6 +211,17 @@
#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
@@ -226,6 +240,15 @@
#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)
@@ -250,6 +273,9 @@
#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)
@@ -260,6 +286,8 @@
#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
@@ -360,6 +388,7 @@
#define NAU8825_GPIO2JD1 (1 << 7)
#define NAU8825_SOFTWARE_ID_MASK 0x3
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+#define NAU8825_SOFTWARE_ID_NAU8825C 0x1
/* BIAS_ADJ (0x66) */
#define NAU8825_BIAS_HPR_IMP (1 << 15)
@@ -372,6 +401,13 @@
#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)
@@ -399,6 +435,9 @@
/* 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
@@ -406,10 +445,17 @@
/* BOOST (0x76) */
#define NAU8825_PRECHARGE_DIS (1 << 13)
#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_DISCHRG_EN (1 << 11)
#define NAU8825_HP_BOOST_DIS (1 << 9)
#define NAU8825_HP_BOOST_G_DIS (1 << 8)
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+/* FEPGA (0x77) */
+#define NAU8825_ACDC_CTRL_SFT 14
+#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT)
+#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT)
+#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT)
+
/* POWER_UP_CONTROL (0x7f) */
#define NAU8825_POWERUP_INTEGR_R (1 << 5)
#define NAU8825_POWERUP_INTEGR_L (1 << 4)
@@ -419,6 +465,8 @@
#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)
@@ -452,6 +500,7 @@ struct nau8825 {
struct clk *mclk;
struct work_struct xtalk_work;
struct semaphore xtalk_sem;
+ int sw_id;
int irq;
int mclk_freq; /* 0 - mclk is disabled */
int button_pressed;
@@ -478,9 +527,11 @@ struct nau8825 {
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_codec *codec,
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c
new file mode 100644
index 000000000000..2cc4c6395f55
--- /dev/null
+++ b/sound/soc/codecs/ntp8835.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8835/NTP8835C Audio Amplifiers
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/bits.h>
+#include <linux/reset.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8835_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8835_INPUT_FMT 0x0
+#define NTP8835_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8835_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8835_GSA_FMT 0x1
+#define NTP8835_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8835_GSA_BS(x) ((x) << 2)
+#define NTP8835_GSA_RIGHT_J BIT(0)
+#define NTP8835_GSA_LSB BIT(1)
+#define NTP8835_MCLK_FREQ_CTRL 0x2
+#define NTP8835_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8835_SOFT_MUTE 0x26
+#define NTP8835_SOFT_MUTE_SM1 BIT(0)
+#define NTP8835_SOFT_MUTE_SM2 BIT(1)
+#define NTP8835_SOFT_MUTE_SM3 BIT(2)
+#define NTP8835_PWM_SWITCH 0x27
+#define NTP8835_PWM_SWITCH_POF1 BIT(0)
+#define NTP8835_PWM_SWITCH_POF2 BIT(1)
+#define NTP8835_PWM_SWITCH_POF3 BIT(2)
+#define NTP8835_PWM_MASK_CTRL0 0x28
+#define NTP8835_PWM_MASK_CTRL0_OUT_LOW BIT(1)
+#define NTP8835_PWM_MASK_CTRL0_FPMLD BIT(2)
+#define NTP8835_MASTER_VOL 0x2e
+#define NTP8835_CHNL_A_VOL 0x2f
+#define NTP8835_CHNL_B_VOL 0x30
+#define NTP8835_CHNL_C_VOL 0x31
+#define REG_MAX NTP8835_CHNL_C_VOL
+
+#define NTP8835_FW_NAME "eq_8835.bin"
+#define NTP8835_FW_MAGIC 0x38383335 /* "8835" */
+
+struct ntp8835_priv {
+ struct i2c_client *i2c;
+ struct reset_control *reset;
+ unsigned int format;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+};
+
+static const DECLARE_TLV_DB_RANGE(ntp8835_vol_scale,
+ 0, 1, TLV_DB_SCALE_ITEM(-15000, 0, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-15000, 1000, 0),
+ 7, 0xff, TLV_DB_SCALE_ITEM(-10000, 50, 0),
+);
+
+static int ntp8835_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->access =
+ (SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
+ uinfo->count = 1;
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ntp8835_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, NTP8835_SOFT_MUTE);
+
+ ucontrol->value.integer.value[0] = val ? 0 : 1;
+ return 0;
+}
+
+static int ntp8835_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ unsigned int val;
+
+ val = ucontrol->value.integer.value[0] ? 0 : 7;
+
+ snd_soc_component_write(component, NTP8835_SOFT_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new ntp8835_vol_control[] = {
+ SOC_SINGLE_TLV("Playback Volume", NTP8835_MASTER_VOL, 0,
+ 0xff, 0, ntp8835_vol_scale),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Playback Switch",
+ .info = ntp8835_mute_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = ntp8835_mute_get,
+ .put = ntp8835_mute_put,
+ },
+};
+
+static void ntp8835_reset_gpio(struct ntp8835_priv *ntp8835)
+{
+ /*
+ * Proper initialization sequence for NTP835 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8835->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8835_sound_on[] = {
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_FPMLD },
+ { NTP8835_PWM_SWITCH, 0x00 },
+ { NTP8835_SOFT_MUTE, 0x00 },
+};
+
+static const struct reg_sequence ntp8835_sound_off[] = {
+ { NTP8835_SOFT_MUTE, NTP8835_SOFT_MUTE_SM1 |
+ NTP8835_SOFT_MUTE_SM2 |
+ NTP8835_SOFT_MUTE_SM3 },
+
+ { NTP8835_PWM_SWITCH, NTP8835_PWM_SWITCH_POF1 |
+ NTP8835_PWM_SWITCH_POF2 |
+ NTP8835_PWM_SWITCH_POF3 },
+
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_OUT_LOW |
+ NTP8835_PWM_MASK_CTRL0_FPMLD },
+};
+
+static int ntp8835_load_firmware(struct ntp8835_priv *ntp8835)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8835->i2c, NTP8835_FW_NAME, NTP8835_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8835->i2c->dev,
+ "Could not find firmware %s\n", NTP8835_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8835_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_off,
+ ARRAY_SIZE(ntp8835_sound_off));
+
+ /*
+ * According to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8835->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8835->mclk);
+
+ return 0;
+}
+
+static int ntp8835_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ntp8835_reset_gpio(ntp8835);
+ ret = clk_prepare_enable(ntp8835->mclk);
+ if (ret)
+ return ret;
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_on,
+ ARRAY_SIZE(ntp8835_sound_on));
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret) {
+ dev_err(&ntp8835->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8835_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8835_vol_control,
+ ARRAY_SIZE(ntp8835_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8835_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route ntp8835_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+ { "OUT3", NULL, "AIFIN" },
+};
+
+static int ntp8835_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 12288000:
+ case 24576000:
+ case 18432000:
+ ntp8835->mclk_rate = freq;
+ break;
+ default:
+ ntp8835->mclk_rate = 0;
+ dev_err(component->dev, "Unsupported MCLK value: %u", freq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_ntp8835 = {
+ .probe = ntp8835_probe,
+ .suspend = ntp8835_snd_suspend,
+ .resume = ntp8835_snd_resume,
+ .dapm_widgets = ntp8835_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8835_dapm_widgets),
+ .dapm_routes = ntp8835_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8835_dapm_routes),
+ .set_sysclk = ntp8835_set_component_sysclk,
+};
+
+static int ntp8835_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 ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int ret;
+
+ switch (ntp8835->mclk_rate) {
+ case 12288000:
+ mcf = 0;
+ break;
+ case 24576000:
+ mcf = 1;
+ break;
+ case 18432000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_MCLK_FREQ_CTRL,
+ NTP8835_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8835->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8835_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_INPUT_FMT,
+ NTP8835_INPUT_FMT_MASTER_MODE |
+ NTP8835_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8835_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8835_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8835_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8835_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8835_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8835_GSA_BS_MASK |
+ NTP8835_GSA_RIGHT_J |
+ NTP8835_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8835_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8835_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8835->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+};
+
+static const struct snd_soc_dai_ops ntp8835_dai_ops = {
+ .hw_params = ntp8835_hw_params,
+ .set_fmt = ntp8835_set_fmt,
+};
+
+static struct snd_soc_dai_driver ntp8835_dai = {
+ .name = "ntp8835-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 3,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = NTP8835_FORMATS,
+ },
+ .ops = &ntp8835_dai_ops,
+};
+
+static const struct regmap_config ntp8835_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8835_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8835_priv *ntp8835;
+ struct regmap *regmap;
+ int ret;
+
+ ntp8835 = devm_kzalloc(&i2c->dev, sizeof(*ntp8835), GFP_KERNEL);
+ if (!ntp8835)
+ return -ENOMEM;
+
+ ntp8835->i2c = i2c;
+
+ ntp8835->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8835->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->reset),
+ "Failed to get reset\n");
+
+ ret = reset_control_deassert(ntp8835->reset);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to deassert reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8835);
+
+ ntp8835_reset_gpio(ntp8835);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8835_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8835,
+ &ntp8835_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8835->mclk = devm_clk_get_enabled(&i2c->dev, "mclk");
+ if (IS_ERR(ntp8835->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->mclk), "failed to get mclk\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8835_i2c_id[] = {
+ { "ntp8835" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);
+
+static const struct of_device_id ntp8835_of_match[] = {
+ {.compatible = "neofidelity,ntp8835",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8835_of_match);
+
+static struct i2c_driver ntp8835_i2c_driver = {
+ .probe = ntp8835_i2c_probe,
+ .id_table = ntp8835_i2c_id,
+ .driver = {
+ .name = "ntp8835",
+ .of_match_table = ntp8835_of_match,
+ },
+};
+module_i2c_driver(ntp8835_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8835 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c
new file mode 100644
index 000000000000..5593d48ef696
--- /dev/null
+++ b/sound/soc/codecs/ntp8918.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8918 Audio Amplifier
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8918_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define NTP8918_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8918_INPUT_FMT 0x0
+#define NTP8918_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8918_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8918_GSA_FMT 0x1
+#define NTP8918_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8918_GSA_BS(x) ((x) << 2)
+#define NTP8918_GSA_RIGHT_J BIT(0)
+#define NTP8918_GSA_LSB BIT(1)
+#define NTP8918_MCLK_FREQ_CTRL 0x2
+#define NTP8918_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8918_MASTER_VOL 0x0C
+#define NTP8918_CHNL_A_VOL 0x17
+#define NTP8918_CHNL_B_VOL 0x18
+#define NTP8918_SOFT_MUTE 0x33
+#define NTP8918_SOFT_MUTE_SM1 BIT(0)
+#define NTP8918_SOFT_MUTE_SM2 BIT(1)
+#define NTP8918_PWM_SWITCH 0x34
+#define NTP8918_PWM_MASK_CTRL0 0x35
+#define REG_MAX NTP8918_PWM_MASK_CTRL0
+
+#define NTP8918_FW_NAME "eq_8918.bin"
+#define NTP8918_FW_MAGIC 0x38393138 /* "8918" */
+
+struct ntp8918_priv {
+ struct i2c_client *i2c;
+ struct clk *bck;
+ struct reset_control *reset;
+ unsigned int format;
+};
+
+static const DECLARE_TLV_DB_SCALE(ntp8918_master_vol_scale, -12550, 50, 0);
+
+static const struct snd_kcontrol_new ntp8918_vol_control[] = {
+ SOC_SINGLE_RANGE_TLV("Playback Volume", NTP8918_MASTER_VOL, 0,
+ 0x04, 0xff, 0, ntp8918_master_vol_scale),
+ SOC_SINGLE("Playback Switch", NTP8918_PWM_MASK_CTRL0, 1, 1, 1),
+};
+
+static void ntp8918_reset_gpio(struct ntp8918_priv *ntp8918)
+{
+ /*
+ * Proper initialization sequence for NTP8918 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8918->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8918_sound_off[] = {
+ { NTP8918_MASTER_VOL, 0 },
+};
+
+static const struct reg_sequence ntp8918_sound_on[] = {
+ { NTP8918_MASTER_VOL, 0b11 },
+};
+
+static int ntp8918_load_firmware(struct ntp8918_priv *ntp8918)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8918->i2c, NTP8918_FW_NAME, NTP8918_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8918->i2c->dev, "Could not find firmware %s\n",
+ NTP8918_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8918_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_off,
+ ARRAY_SIZE(ntp8918_sound_off));
+
+ /*
+ * According to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8918->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8918->bck);
+
+ return 0;
+}
+
+static int ntp8918_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = clk_prepare_enable(ntp8918->bck);
+ if (ret)
+ return ret;
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_on,
+ ARRAY_SIZE(ntp8918_sound_on));
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret) {
+ dev_err(&ntp8918->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8918_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8918_vol_control,
+ ARRAY_SIZE(ntp8918_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8918_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+};
+
+static const struct snd_soc_dapm_route ntp8918_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+};
+
+static const struct snd_soc_component_driver soc_component_ntp8918 = {
+ .probe = ntp8918_probe,
+ .suspend = ntp8918_snd_suspend,
+ .resume = ntp8918_snd_resume,
+ .dapm_widgets = ntp8918_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8918_dapm_widgets),
+ .dapm_routes = ntp8918_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8918_dapm_routes),
+};
+
+static int ntp8918_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 ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int bclk;
+ int ret;
+
+ bclk = snd_soc_params_to_bclk(params);
+ switch (bclk) {
+ case 3072000:
+ case 2822400:
+ mcf = 0;
+ break;
+ case 6144000:
+ mcf = 1;
+ break;
+ case 2048000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_MCLK_FREQ_CTRL,
+ NTP8918_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8918->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8918_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_INPUT_FMT,
+ NTP8918_INPUT_FMT_MASTER_MODE |
+ NTP8918_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8918_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8918_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8918_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8918_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8918_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8918_GSA_BS_MASK |
+ NTP8918_GSA_RIGHT_J |
+ NTP8918_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8918_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8918_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8918->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ unsigned int mute_mask = NTP8918_SOFT_MUTE_SM1 |
+ NTP8918_SOFT_MUTE_SM2;
+
+ return snd_soc_component_update_bits(dai->component, NTP8918_SOFT_MUTE,
+ mute_mask, mute ? mute_mask : 0);
+}
+
+static const struct snd_soc_dai_ops ntp8918_dai_ops = {
+ .hw_params = ntp8918_hw_params,
+ .set_fmt = ntp8918_set_fmt,
+ .mute_stream = ntp8918_digital_mute,
+};
+
+static struct snd_soc_dai_driver ntp8918_dai = {
+ .name = "ntp8918-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NTP8918_RATES,
+ .formats = NTP8918_FORMATS,
+ },
+ .ops = &ntp8918_dai_ops,
+};
+
+static const struct regmap_config ntp8918_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8918_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8918_priv *ntp8918;
+ int ret;
+ struct regmap *regmap;
+
+ ntp8918 = devm_kzalloc(&i2c->dev, sizeof(*ntp8918), GFP_KERNEL);
+ if (!ntp8918)
+ return -ENOMEM;
+
+ ntp8918->i2c = i2c;
+
+ ntp8918->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8918->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->reset), "Failed to get reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8918);
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8918_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8918,
+ &ntp8918_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8918->bck = devm_clk_get_enabled(&i2c->dev, "bck");
+ if (IS_ERR(ntp8918->bck))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->bck), "failed to get bck clock\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8918_i2c_id[] = {
+ { "ntp8918" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
+
+static const struct of_device_id ntp8918_of_match[] = {
+ {.compatible = "neofidelity,ntp8918"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8918_of_match);
+
+static struct i2c_driver ntp8918_i2c_driver = {
+ .probe = ntp8918_i2c_probe,
+ .id_table = ntp8918_i2c_id,
+ .driver = {
+ .name = "ntp8918",
+ .of_match_table = ntp8918_of_match,
+ },
+};
+module_i2c_driver(ntp8918_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8918 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c
new file mode 100644
index 000000000000..5ced2e966ab7
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ntpfw.c - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "ntpfw.h"
+
+struct ntpfw_chunk {
+ __be16 length;
+ u8 step;
+ u8 data[];
+} __packed;
+
+struct ntpfw_header {
+ __be32 magic;
+} __packed;
+
+static bool ntpfw_verify(struct device *dev, const u8 *buf, size_t buf_size, u32 magic)
+{
+ const struct ntpfw_header *header = (struct ntpfw_header *)buf;
+ u32 buf_magic;
+
+ if (buf_size <= sizeof(*header)) {
+ dev_err(dev, "Failed to load firmware: image too small\n");
+ return false;
+ }
+
+ buf_magic = be32_to_cpu(header->magic);
+ if (buf_magic != magic) {
+ dev_err(dev, "Failed to load firmware: invalid magic 0x%x:\n", buf_magic);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ntpfw_verify_chunk(struct device *dev, const struct ntpfw_chunk *chunk, size_t buf_size)
+{
+ size_t chunk_size;
+
+ if (buf_size <= sizeof(*chunk)) {
+ dev_err(dev, "Failed to load firmware: chunk size too big\n");
+ return false;
+ }
+
+ if (chunk->step != 2 && chunk->step != 5) {
+ dev_err(dev, "Failed to load firmware: invalid chunk step: %d\n", chunk->step);
+ return false;
+ }
+
+ chunk_size = be16_to_cpu(chunk->length);
+ if (chunk_size > buf_size) {
+ dev_err(dev, "Failed to load firmware: invalid chunk length\n");
+ return false;
+ }
+
+ if (chunk_size % chunk->step) {
+ dev_err(dev, "Failed to load firmware: chunk length and step mismatch\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int ntpfw_send_chunk(struct i2c_client *i2c, const struct ntpfw_chunk *chunk)
+{
+ int ret;
+ size_t i;
+ size_t length = be16_to_cpu(chunk->length);
+
+ for (i = 0; i < length; i += chunk->step) {
+ ret = i2c_master_send(i2c, &chunk->data[i], chunk->step);
+ if (ret != chunk->step) {
+ dev_err(&i2c->dev, "I2C send failed: %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic)
+{
+ struct device *dev = &i2c->dev;
+ const struct ntpfw_chunk *chunk;
+ const struct firmware *fw;
+ const u8 *data;
+ size_t leftover;
+ int ret;
+
+ ret = request_firmware(&fw, name, dev);
+ if (ret) {
+ dev_warn(dev, "request_firmware '%s' failed with %d\n",
+ name, ret);
+ return ret;
+ }
+
+ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data = fw->data + sizeof(struct ntpfw_header);
+ leftover = fw->size - sizeof(struct ntpfw_header);
+
+ while (leftover) {
+ chunk = (struct ntpfw_chunk *)data;
+
+ if (!ntpfw_verify_chunk(dev, chunk, leftover)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = ntpfw_send_chunk(i2c, chunk);
+ if (ret)
+ goto done;
+
+ data += be16_to_cpu(chunk->length) + sizeof(*chunk);
+ leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk);
+ }
+
+done:
+ release_firmware(fw);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ntpfw_load);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h
new file mode 100644
index 000000000000..1cf10d5480ee
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/**
+ * ntpfw.h - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#ifndef __NTPFW_H__
+#define __NTPFW_H__
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+
+/**
+ * ntpfw_load - load firmware to amplifier over i2c interface.
+ *
+ * @i2c Pointer to amplifier's I2C client.
+ * @name Firmware file name.
+ * @magic Magic number to validate firmware.
+ * @return 0 or error code upon error.
+ */
+int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic);
+
+#endif /* __NTPFW_H__ */
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index c7e28dd2e815..a1ec881d7084 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM1681 ASoC codec driver
*
* Copyright (c) StreamUnlimited GmbH 2013
* Marek Belisko <marek.belisko@streamunlimited.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.
*/
#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>
@@ -90,10 +78,10 @@ struct pcm1681_private {
static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
-static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+static int pcm1681_set_deemph(struct snd_soc_component *component)
{
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
- int i = 0, val = -1, enable = 0;
+ 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++) {
@@ -120,8 +108,8 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec)
static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -131,23 +119,23 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ return pcm1681_set_deemph(component);
}
static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
- /* The PCM1681 can only be slave to all clocks */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Invalid clocking mode\n");
+ /* 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;
}
@@ -156,10 +144,10 @@ static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
int val;
if (mute)
@@ -174,8 +162,8 @@ static int pcm1681_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 pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -200,7 +188,7 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
val = 0x05;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -208,13 +196,14 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return pcm1681_set_deemph(codec);
+ 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,
- .digital_mute = pcm1681_digital_mute,
+ .mute_stream = pcm1681_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
@@ -288,25 +277,25 @@ static const struct regmap_config pcm1681_regmap = {
.readable_reg = pcm1681_accessible_reg,
};
-static const struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
- .component_driver = {
- .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),
- },
+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},
+ {"pcm1681"},
{}
};
MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
-static int pcm1681_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm1681_i2c_probe(struct i2c_client *client)
{
int ret;
struct pcm1681_private *priv;
@@ -324,16 +313,11 @@ static int pcm1681_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, priv);
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_pcm1681,
&pcm1681_dai, 1);
}
-static int pcm1681_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver pcm1681_i2c_driver = {
.driver = {
.name = "pcm1681",
@@ -341,7 +325,6 @@ static struct i2c_driver pcm1681_i2c_driver = {
},
.id_table = pcm1681_i2c_id,
.probe = pcm1681_i2c_probe,
- .remove = pcm1681_i2c_remove,
};
module_i2c_driver(pcm1681_i2c_driver);
diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c
new file mode 100644
index 000000000000..b68a528000be
--- /dev/null
+++ b/sound/soc/codecs/pcm1754.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1754 DAC ASoC codec driver
+ *
+ * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
+ * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct pcm1754_priv {
+ unsigned int format;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_desc *gpiod_format;
+};
+
+static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1754_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 pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+ int format;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ format = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ fallthrough;
+ case 24:
+ format = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_format, format);
+
+ return 0;
+}
+
+static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, mute);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1754_dai_ops = {
+ .set_fmt = pcm1754_set_dai_fmt,
+ .hw_params = pcm1754_hw_params,
+ .mute_stream = pcm1754_mute_stream,
+};
+
+static const struct snd_soc_dai_driver pcm1754_dai = {
+ .name = "pcm1754",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 200000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &pcm1754_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+
+ { "DAC1", NULL, "VCC" },
+ { "DAC2", NULL, "VCC" },
+
+ { "VOUTL", NULL, "DAC1" },
+ { "VOUTR", NULL, "DAC2" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1754 = {
+ .dapm_widgets = pcm1754_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets),
+ .dapm_routes = pcm1754_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes),
+};
+
+static int pcm1754_probe(struct platform_device *pdev)
+{
+ struct pcm1754_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+ int ret;
+
+ dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpiod_mute = devm_gpiod_get_optional(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->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_format))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_format),
+ "failed to get format gpio");
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(
+ &pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1754_of_match[] = {
+ { .compatible = "ti,pcm1754" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1754_of_match);
+#endif
+
+static struct platform_driver pcm1754_codec_driver = {
+ .driver = {
+ .name = "pcm1754-codec",
+ .of_match_table = of_match_ptr(pcm1754_of_match),
+ },
+ .probe = pcm1754_probe,
+};
+
+module_platform_driver(pcm1754_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1754 driver");
+MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>");
+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..abadf4f8ed5e
--- /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" },
+ { }
+};
+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 = 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
index 4118106abb8d..effc1dd6df22 100644
--- a/sound/soc/codecs/pcm179x-i2c.c
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM179X ASoC I2C driver
*
* Copyright (c) Teenage Engineering AB 2016
*
* Jacob Siverskog <jacob@teenage.engineering>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -23,8 +14,7 @@
#include "pcm179x.h"
-static int pcm179x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm179x_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -39,19 +29,16 @@ static int pcm179x_i2c_probe(struct i2c_client *client,
return pcm179x_common_init(&client->dev, regmap);
}
-static int pcm179x_i2c_remove(struct i2c_client *client)
-{
- return pcm179x_common_exit(&client->dev);
-}
-
+#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 },
+ { "pcm179x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
@@ -63,7 +50,6 @@ static struct i2c_driver pcm179x_i2c_driver = {
},
.id_table = pcm179x_i2c_ids,
.probe = pcm179x_i2c_probe,
- .remove = pcm179x_i2c_remove,
};
module_i2c_driver(pcm179x_i2c_driver);
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
index da924d444083..192fee90c971 100644
--- a/sound/soc/codecs/pcm179x-spi.c
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM179X ASoC SPI driver
*
* Copyright (c) Amarula Solutions B.V. 2013
*
* Michael Trimarchi <michael@amarulasolutions.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.
*/
#include <linux/module.h>
@@ -38,18 +29,14 @@ static int pcm179x_spi_probe(struct spi_device *spi)
return pcm179x_common_init(&spi->dev, regmap);
}
-static int pcm179x_spi_remove(struct spi_device *spi)
-{
- return pcm179x_common_exit(&spi->dev);
-}
-
-static const struct of_device_id pcm179x_of_match[] = {
+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 },
{ },
};
@@ -62,7 +49,6 @@ static struct spi_driver pcm179x_spi_driver = {
},
.id_table = pcm179x_spi_ids,
.probe = pcm179x_spi_probe,
- .remove = pcm179x_spi_remove,
};
module_spi_driver(pcm179x_spi_driver);
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index 82a3d9db32cb..f52ff66b6e64 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM179X ASoC codec driver
*
* Copyright (c) Amarula Solutions B.V. 2013
*
* Michael Trimarchi <michael@amarulasolutions.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.
*/
#include <linux/module.h>
@@ -77,18 +68,18 @@ struct pcm179x_private {
static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -103,8 +94,8 @@ static int pcm179x_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 pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -137,7 +128,7 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
}
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -154,7 +145,8 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops pcm179x_dai_ops = {
.set_fmt = pcm179x_set_dai_fmt,
.hw_params = pcm179x_hw_params,
- .digital_mute = pcm179x_digital_mute,
+ .mute_stream = pcm179x_mute,
+ .no_capture_mute = 1,
};
static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
@@ -205,15 +197,16 @@ const struct regmap_config pcm179x_regmap_config = {
};
EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
-static const struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
- .component_driver = {
- .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),
- },
+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)
@@ -228,18 +221,11 @@ int pcm179x_common_init(struct device *dev, struct regmap *regmap)
pcm179x->regmap = regmap;
dev_set_drvdata(dev, pcm179x);
- return snd_soc_register_codec(dev,
- &soc_codec_dev_pcm179x, &pcm179x_dai, 1);
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_pcm179x, &pcm179x_dai, 1);
}
EXPORT_SYMBOL_GPL(pcm179x_common_init);
-int pcm179x_common_exit(struct device *dev)
-{
- snd_soc_unregister_codec(dev);
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcm179x_common_exit);
-
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
index 11e331268aae..0039ca8ee742 100644
--- a/sound/soc/codecs/pcm179x.h
+++ b/sound/soc/codecs/pcm179x.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* definitions for PCM179X
*
* Copyright 2013 Amarula Solutions
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT 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 __PCM179X_H__
@@ -23,6 +14,5 @@
extern const struct regmap_config pcm179x_regmap_config;
int pcm179x_common_init(struct device *dev, struct regmap *regmap);
-int pcm179x_common_exit(struct device *dev);
#endif
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
index 543621232d60..a50f9f6e39c1 100644
--- a/sound/soc/codecs/pcm186x-i2c.c
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -2,7 +2,7 @@
/*
* Texas Instruments PCM186x Universal Audio ADC - I2C
*
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
* Andreas Dannenberg <dannenberg@ti.com>
* Andrew F. Davis <afd@ti.com>
*/
@@ -22,10 +22,18 @@ static const struct of_device_id pcm186x_of_match[] = {
};
MODULE_DEVICE_TABLE(of, pcm186x_of_match);
-static int pcm186x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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 enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
+ const enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c);
int irq = i2c->irq;
struct regmap *regmap;
@@ -36,25 +44,8 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c,
return pcm186x_probe(&i2c->dev, type, irq, regmap);
}
-static int pcm186x_i2c_remove(struct i2c_client *i2c)
-{
- pcm186x_remove(&i2c->dev);
-
- return 0;
-}
-
-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 struct i2c_driver pcm186x_i2c_driver = {
.probe = pcm186x_i2c_probe,
- .remove = pcm186x_i2c_remove,
.id_table = pcm186x_i2c_id,
.driver = {
.name = "pcm186x",
diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c
index 2366f8e4d4d4..bc1b0f0698ed 100644
--- a/sound/soc/codecs/pcm186x-spi.c
+++ b/sound/soc/codecs/pcm186x-spi.c
@@ -2,7 +2,7 @@
/*
* Texas Instruments PCM186x Universal Audio ADC - SPI
*
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
* Andreas Dannenberg <dannenberg@ti.com>
* Andrew F. Davis <afd@ti.com>
*/
@@ -36,13 +36,6 @@ static int pcm186x_spi_probe(struct spi_device *spi)
return pcm186x_probe(&spi->dev, type, irq, regmap);
}
-static int pcm186x_spi_remove(struct spi_device *spi)
-{
- pcm186x_remove(&spi->dev);
-
- return 0;
-}
-
static const struct spi_device_id pcm186x_spi_id[] = {
{ "pcm1862", PCM1862 },
{ "pcm1863", PCM1863 },
@@ -54,7 +47,6 @@ MODULE_DEVICE_TABLE(spi, pcm186x_spi_id);
static struct spi_driver pcm186x_spi_driver = {
.probe = pcm186x_spi_probe,
- .remove = pcm186x_spi_remove,
.id_table = pcm186x_spi_id,
.driver = {
.name = "pcm186x",
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
index cdb51427facc..13443f569ddb 100644
--- a/sound/soc/codecs/pcm186x.c
+++ b/sound/soc/codecs/pcm186x.c
@@ -2,7 +2,7 @@
/*
* Texas Instruments PCM186x Universal Audio ADC
*
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
* Andreas Dannenberg <dannenberg@ti.com>
* Andrew F. Davis <afd@ti.com>
*/
@@ -12,7 +12,6 @@
#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>
@@ -39,10 +38,10 @@ struct pcm186x_priv {
unsigned int sysclk;
unsigned int tdm_offset;
bool is_tdm_mode;
- bool is_master_mode;
+ bool is_provider_mode;
};
-static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 4000, 50);
+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,
@@ -158,7 +157,7 @@ static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {
* 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, 0),
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 1),
};
static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
@@ -184,8 +183,8 @@ static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
* 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, 0),
- SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 0),
+ 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[] = {
@@ -262,11 +261,10 @@ static int pcm186x_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 pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
- unsigned int format = params_format(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;
@@ -274,7 +272,7 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
u8 tdm_tx_sel = 0;
u8 pcm_cfg = 0;
- dev_dbg(codec->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
+ dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
__func__, rate, format, width, channels);
switch (width) {
@@ -306,7 +304,7 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, PCM186X_PCM_CFG,
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
PCM186X_PCM_CFG_RX_WLEN_MASK |
PCM186X_PCM_CFG_TX_WLEN_MASK,
pcm_cfg);
@@ -329,28 +327,28 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, PCM186X_TDM_TX_SEL,
+ 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_update_bits(codec, PCM186X_PCM_CFG,
+ 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 master mode. */
- if (priv->is_master_mode) {
+ /* Only configure clock dividers in provider mode. */
+ if (priv->is_provider_mode) {
div_bck = priv->sysclk / (div_lrck * rate);
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"%s() master_clk=%u div_bck=%u div_lrck=%u\n",
__func__, priv->sysclk, div_bck, div_lrck);
- snd_soc_write(codec, PCM186X_BCK_DIV, div_bck - 1);
- snd_soc_write(codec, PCM186X_LRK_DIV, div_lrck - 1);
+ snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1);
+ snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1);
}
return 0;
@@ -358,28 +356,27 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "%s() format=0x%x\n", __func__, format);
+ dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format);
- /* set master/slave audio interface */
- switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
if (!priv->sysclk) {
- dev_err(codec->dev, "operating in master mode requires sysclock to be configured\n");
+ 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_master_mode = true;
+ priv->is_provider_mode = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- priv->is_master_mode = false;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->is_provider_mode = false;
break;
default:
- dev_err(codec->dev, "Invalid DAI master/slave interface\n");
+ dev_err(component->dev, "Invalid DAI master/slave interface\n");
return -EINVAL;
}
@@ -388,7 +385,7 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
case SND_SOC_DAIFMT_NB_NF:
break;
default:
- dev_err(codec->dev, "Inverted DAI clocks not supported\n");
+ dev_err(component->dev, "Inverted DAI clocks not supported\n");
return -EINVAL;
}
@@ -402,7 +399,8 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
break;
case SND_SOC_DAIFMT_DSP_A:
priv->tdm_offset += 1;
- /* Fall through... DSP_A uses the same basic config as DSP_B
+ 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:
@@ -410,16 +408,16 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, PCM186X_CLK_CTRL,
+ snd_soc_component_update_bits(component, PCM186X_CLK_CTRL,
PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);
- snd_soc_write(codec, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
+ snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
- snd_soc_update_bits(codec, PCM186X_PCM_CFG,
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);
return 0;
@@ -428,16 +426,16 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
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_codec *codec = dai->codec;
- struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev,
+ 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(codec->dev, "tdm tx mask must not be 0\n");
+ dev_err(component->dev, "tdm tx mask must not be 0\n");
return -EINVAL;
}
@@ -445,14 +443,14 @@ static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
last_slot = __fls(tx_mask);
if (last_slot - first_slot != hweight32(tx_mask) - 1) {
- dev_err(codec->dev, "tdm tx mask must be contiguous\n");
+ 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(codec->dev, "tdm tx slot selection out of bounds\n");
+ dev_err(component->dev, "tdm tx slot selection out of bounds\n");
return -EINVAL;
}
@@ -464,10 +462,10 @@ static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n",
__func__, clk_id, freq, dir);
priv->sysclk = freq;
@@ -506,9 +504,9 @@ static struct snd_soc_dai_driver pcm1865_dai = {
.ops = &pcm186x_dai_ops,
};
-static int pcm186x_power_on(struct snd_soc_codec *codec)
+static int pcm186x_power_on(struct snd_soc_component *component)
{
- struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
@@ -519,42 +517,37 @@ static int pcm186x_power_on(struct snd_soc_codec *codec)
regcache_cache_only(priv->regmap, false);
ret = regcache_sync(priv->regmap);
if (ret) {
- dev_err(codec->dev, "Failed to restore cache\n");
+ 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_update_bits(codec, PCM186X_POWER_CTRL,
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
PCM186X_PWR_CTRL_PWRDN, 0);
return 0;
}
-static int pcm186x_power_off(struct snd_soc_codec *codec)
+static int pcm186x_power_off(struct snd_soc_component *component)
{
- struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
- snd_soc_update_bits(codec, PCM186X_POWER_CTRL,
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
regcache_cache_only(priv->regmap, true);
- ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
priv->supplies);
- if (ret)
- return ret;
-
- return 0;
}
-static int pcm186x_set_bias_level(struct snd_soc_codec *codec,
+static int pcm186x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
- snd_soc_codec_get_bias_level(codec), 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:
@@ -562,42 +555,42 @@ static int pcm186x_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- pcm186x_power_on(codec);
+ 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(codec);
+ pcm186x_power_off(component);
break;
}
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_pcm1863 = {
- .set_bias_level = pcm186x_set_bias_level,
-
- .component_driver = {
- .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),
- },
+static const 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_codec_driver soc_codec_dev_pcm1865 = {
- .set_bias_level = pcm186x_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .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),
- },
+static const 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)
@@ -687,13 +680,13 @@ int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
switch (type) {
case PCM1865:
case PCM1864:
- ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1865,
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865,
&pcm1865_dai, 1);
break;
case PCM1863:
case PCM1862:
default:
- ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1863,
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863,
&pcm1863_dai, 1);
}
if (ret) {
@@ -705,14 +698,6 @@ int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
}
EXPORT_SYMBOL_GPL(pcm186x_probe);
-int pcm186x_remove(struct device *dev)
-{
- snd_soc_unregister_codec(dev);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcm186x_remove);
-
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");
diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h
index b630111bb3c4..4d493754a3e2 100644
--- a/sound/soc/codecs/pcm186x.h
+++ b/sound/soc/codecs/pcm186x.h
@@ -2,7 +2,7 @@
/*
* Texas Instruments PCM186x Universal Audio ADC
*
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
* Andreas Dannenberg <dannenberg@ti.com>
* Andrew F. Davis <afd@ti.com>
*/
@@ -139,7 +139,7 @@ enum pcm186x_type {
#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL
/* PCM186X_PAGE */
-#define PCM186X_RESET 0xff
+#define PCM186X_RESET 0xfe
/* PCM186X_ADCX_INPUT_SEL_X */
#define PCM186X_ADC_INPUT_SEL_POL BIT(7)
@@ -215,6 +215,5 @@ extern const struct regmap_config pcm186x_regmap;
int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
struct regmap *regmap);
-int pcm186x_remove(struct device *dev);
#endif /* _PCM186X_H_ */
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index e59d8ffb93bd..d3d2e7f40170 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,18 +8,13 @@
* 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.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -26,17 +22,22 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include "pcm3008.h"
+struct pcm3008 {
+ struct gpio_desc *dem0_pin;
+ struct gpio_desc *dem1_pin;
+ struct gpio_desc *pdad_pin;
+ struct gpio_desc *pdda_pin;
+};
static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdda_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -45,11 +46,11 @@ static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdad_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -98,22 +99,25 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
- .component_driver = {
- .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),
- },
+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_codec_probe(struct platform_device *pdev)
{
- struct pcm3008_setup_data *setup = pdev->dev.platform_data;
- int ret;
+ struct device *dev = &pdev->dev;
+ struct pcm3008 *pcm;
- if (!setup)
- return -EINVAL;
+ pcm = devm_kzalloc(dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pcm);
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -123,45 +127,33 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
*/
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
- ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
- GPIOF_OUT_INIT_HIGH, "codec_dem0");
- if (ret != 0)
- return ret;
+ pcm->dem0_pin = devm_gpiod_get(dev, "dem0", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcm->dem0_pin))
+ return PTR_ERR(pcm->dem0_pin);
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
- ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
- GPIOF_OUT_INIT_LOW, "codec_dem1");
- if (ret != 0)
- return ret;
+ pcm->dem1_pin = devm_gpiod_get(dev, "dem1", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->dem1_pin))
+ return PTR_ERR(pcm->dem1_pin);
/* Configure PDAD GPIO. */
- ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
- GPIOF_OUT_INIT_LOW, "codec_pdad");
- if (ret != 0)
- return ret;
+ pcm->pdad_pin = devm_gpiod_get(dev, "pdad", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdad_pin))
+ return PTR_ERR(pcm->pdad_pin);
/* Configure PDDA GPIO. */
- ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
- GPIOF_OUT_INIT_LOW, "codec_pdda");
- if (ret != 0)
- return ret;
-
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
-}
-
-static int pcm3008_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
+ pcm->pdda_pin = devm_gpiod_get(dev, "pdda", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdda_pin))
+ return PTR_ERR(pcm->pdda_pin);
- return 0;
+ return devm_snd_soc_register_component(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 = pcm3008_codec_remove,
.driver = {
.name = "pcm3008-codec",
},
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
deleted file mode 100644
index 7e5489ab4812..000000000000
--- a/sound/soc/codecs/pcm3008.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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
-#define __LINUX_SND_SOC_PCM3008_H
-
-struct pcm3008_setup_data {
- unsigned dem0_pin;
- unsigned dem1_pin;
- unsigned pdad_pin;
- unsigned pdda_pin;
-};
-
-#endif
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
new file mode 100644
index 000000000000..3816b25a8ead
--- /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 <k.marinushkin@gmail.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 = pcm3060_i2c_probe,
+};
+
+module_i2c_driver(pcm3060_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3060 I2C driver");
+MODULE_AUTHOR("Kirill Marinushkin <k.marinushkin@gmail.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..6095841f2f56
--- /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 <k.marinushkin@gmail.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 <k.marinushkin@gmail.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..8974200652e7
--- /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 <k.marinushkin@gmail.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 <k.marinushkin@gmail.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..1b96835600b4
--- /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 <k.marinushkin@gmail.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
index 6feb0901dfeb..ff18c74b616c 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -1,25 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec i2c driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <sound/soc.h>
#include "pcm3168a.h"
-static int pcm3168a_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm3168a_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -30,11 +27,9 @@ static int pcm3168a_i2c_probe(struct i2c_client *i2c,
return pcm3168a_probe(&i2c->dev, regmap);
}
-static int pcm3168a_i2c_remove(struct i2c_client *i2c)
+static void pcm3168a_i2c_remove(struct i2c_client *i2c)
{
pcm3168a_remove(&i2c->dev);
-
- return 0;
}
static const struct i2c_device_id pcm3168a_i2c_id[] = {
@@ -43,6 +38,13 @@ static const struct i2c_device_id pcm3168a_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+static const struct acpi_device_id pcm3168a_acpi_match[] = {
+ { "PCM3168A" },
+ { "104C3168" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match);
+
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
@@ -55,8 +57,9 @@ static struct i2c_driver pcm3168a_i2c_driver = {
.id_table = pcm3168a_i2c_id,
.driver = {
.name = "pcm3168a",
+ .acpi_match_table = pcm3168a_acpi_match,
.of_match_table = pcm3168a_of_match,
- .pm = &pcm3168a_pm_ops,
+ .pm = pm_ptr(&pcm3168a_pm_ops),
},
};
module_i2c_driver(pcm3168a_i2c_driver);
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
index 03945a27ae40..0871338eacba 100644
--- a/sound/soc/codecs/pcm3168a-spi.c
+++ b/sound/soc/codecs/pcm3168a-spi.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec spi driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/init.h>
@@ -29,11 +26,9 @@ static int pcm3168a_spi_probe(struct spi_device *spi)
return pcm3168a_probe(&spi->dev, regmap);
}
-static int pcm3168a_spi_remove(struct spi_device *spi)
+static void pcm3168a_spi_remove(struct spi_device *spi)
{
pcm3168a_remove(&spi->dev);
-
- return 0;
}
static const struct spi_device_id pcm3168a_spi_id[] = {
@@ -55,7 +50,7 @@ static struct spi_driver pcm3168a_spi_driver = {
.driver = {
.name = "pcm3168a",
.of_match_table = pcm3168a_of_match,
- .pm = &pcm3168a_pm_ops,
+ .pm = pm_ptr(&pcm3168a_pm_ops),
},
};
module_spi_driver(pcm3168a_spi_driver);
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index b9d1207ccef2..c8617a488b11 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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/gpio/consumer.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -24,8 +22,7 @@
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S24_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
@@ -33,10 +30,10 @@
#define PCM3168A_FMT_RIGHT_J_16 0x3
#define PCM3168A_FMT_DSP_A 0x4
#define PCM3168A_FMT_DSP_B 0x5
-#define PCM3168A_FMT_DSP_MASK 0x4
+#define PCM3168A_FMT_I2S_TDM 0x6
+#define PCM3168A_FMT_LEFT_J_TDM 0x7
-#define PCM3168A_NUM_SUPPLIES 6
-static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+static const char *const pcm3168a_supply_names[] = {
"VDD1",
"VDD2",
"VCCAD1",
@@ -45,15 +42,27 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
"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[PCM3168A_NUM_SUPPLIES];
+ struct regulator_bulk_data supplies[ARRAY_SIZE(pcm3168a_supply_names)];
struct regmap *regmap;
struct clk *scki;
- bool adc_master_mode;
- bool dac_master_mode;
+ struct gpio_desc *gpio_rst;
unsigned long sysclk;
- unsigned int adc_fmt;
- unsigned int dac_fmt;
+
+ struct pcm3168a_io_params io_params[2];
+ struct snd_soc_dai_driver dai_drv[2];
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
@@ -131,10 +140,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
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_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
- SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
- SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
- SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 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),
@@ -174,9 +179,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
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_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
- SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
- SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 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),
@@ -268,7 +270,7 @@ static unsigned int pcm3168a_scki_ratios[] = {
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
-#define PCM1368A_MAX_SYSCLK 36864000
+#define PCM3168A_MAX_SYSCLK 36864000
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
{
@@ -285,10 +287,10 @@ static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
}
-static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm3168a_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -298,10 +300,17 @@ static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
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_codec_get_drvdata(dai->codec);
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(dai->component);
int ret;
- if (freq > PCM1368A_MAX_SYSCLK)
+ /*
+ * 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);
@@ -313,44 +322,63 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
- unsigned int format, bool dac)
+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_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
- u32 fmt, reg, mask, shift;
- bool master_mode;
+ 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:
- fmt = PCM3168A_FMT_LEFT_J;
- break;
case SND_SOC_DAIFMT_I2S:
- fmt = PCM3168A_FMT_I2S;
- break;
case SND_SOC_DAIFMT_RIGHT_J:
- fmt = PCM3168A_FMT_RIGHT_J;
- break;
case SND_SOC_DAIFMT_DSP_A:
- fmt = PCM3168A_FMT_DSP_A;
- break;
case SND_SOC_DAIFMT_DSP_B:
- fmt = PCM3168A_FMT_DSP_B;
break;
default:
- dev_err(codec->dev, "unsupported dai format\n");
+ dev_err(component->dev, "unsupported dai format\n");
return -EINVAL;
}
- switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- master_mode = false;
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ provider_mode = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- master_mode = true;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ provider_mode = true;
break;
default:
- dev_err(codec->dev, "unsupported master/slave mode\n");
+ dev_err(component->dev, "unsupported provider mode\n");
return -EINVAL;
}
@@ -361,137 +389,218 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- if (dac) {
- reg = PCM3168A_DAC_PWR_MST_FMT;
- mask = PCM3168A_DAC_FMT_MASK;
- shift = PCM3168A_DAC_FMT_SHIFT;
- pcm3168a->dac_master_mode = master_mode;
- pcm3168a->dac_fmt = fmt;
- } else {
- reg = PCM3168A_ADC_MST_FMT;
- mask = PCM3168A_ADC_FMTAD_MASK;
- shift = PCM3168A_ADC_FMTAD_SHIFT;
- pcm3168a->adc_master_mode = master_mode;
- pcm3168a->adc_fmt = fmt;
- }
+ io_params->provider_mode = provider_mode;
+ io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+ pcm3168a_update_fixup_pcm_stream(dai);
return 0;
}
-static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
- unsigned int format)
+static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
{
- return pcm3168a_set_dai_fmt(dai, format, true);
-}
+ 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;
+ }
-static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
- unsigned int format)
-{
- return pcm3168a_set_dai_fmt(dai, format, false);
+ 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_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
- bool tx, master_mode;
- u32 val, mask, shift, reg;
- unsigned int rate, fmt, ratio, max_ratio;
- int i, min_frame_size;
-
- rate = params_rate(params);
-
- ratio = pcm3168a->sysclk / rate;
-
- tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- if (tx) {
- max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+ 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;
- shift = PCM3168A_DAC_MSDA_SHIFT;
- master_mode = pcm3168a->dac_master_mode;
- fmt = pcm3168a->dac_fmt;
+ mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK;
+ ms_shift = PCM3168A_DAC_MSDA_SHIFT;
+ fmt_shift = PCM3168A_DAC_FMT_SHIFT;
} else {
- max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+ num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_ADC;
reg = PCM3168A_ADC_MST_FMT;
- mask = PCM3168A_ADC_MSAD_MASK;
- shift = PCM3168A_ADC_MSAD_SHIFT;
- master_mode = pcm3168a->adc_master_mode;
- fmt = pcm3168a->adc_fmt;
+ mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK;
+ ms_shift = PCM3168A_ADC_MSAD_SHIFT;
+ fmt_shift = PCM3168A_ADC_FMTAD_SHIFT;
}
- for (i = 0; i < max_ratio; i++) {
- if (pcm3168a_scki_ratios[i] == ratio)
- break;
- }
+ provider_mode = io_params->provider_mode;
- if (i == max_ratio) {
- dev_err(codec->dev, "unsupported sysclk ratio\n");
- return -EINVAL;
+ 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;
}
- min_frame_size = params_width(params) * 2;
- switch (min_frame_size) {
- case 32:
- if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
- dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
+ 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;
}
- fmt = PCM3168A_FMT_RIGHT_J_16;
break;
- case 48:
- if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
- dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+ 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 consumer mode using DSP\n");
return -EINVAL;
}
break;
- case 64:
+ case 32:
break;
default:
- dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+ dev_err(component->dev, "unsupported frame size: %d\n", slot_width);
return -EINVAL;
}
- if (master_mode)
- val = ((i + 1) << shift);
+ if (io_params->tdm_slots)
+ tdm_slots = io_params->tdm_slots;
else
- val = 0;
-
- regmap_update_bits(pcm3168a->regmap, reg, mask, val);
+ 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;
+ }
+ }
- if (tx) {
- mask = PCM3168A_DAC_FMT_MASK;
- shift = PCM3168A_DAC_FMT_SHIFT;
- } else {
- mask = PCM3168A_ADC_FMTAD_MASK;
- shift = PCM3168A_ADC_FMTAD_SHIFT;
+ 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, fmt << shift);
+ regmap_update_bits(pcm3168a->regmap, reg, mask,
+ (ms << ms_shift) | (fmt << fmt_shift));
return 0;
}
-static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
- .set_fmt = pcm3168a_set_dai_fmt_dac,
- .set_sysclk = pcm3168a_set_dai_sysclk,
- .hw_params = pcm3168a_hw_params,
- .digital_mute = pcm3168a_digital_mute
+static const 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_adc_dai_ops = {
- .set_fmt = pcm3168a_set_dai_fmt_adc,
+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
+ .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,
@@ -499,10 +608,11 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_dac_dai_ops
+ .ops = &pcm3168a_dai_ops
},
{
.name = "pcm3168a-adc",
+ .id = PCM3168A_DAI_ADC,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -510,7 +620,7 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_adc_dai_ops
+ .ops = &pcm3168a_dai_ops
},
};
@@ -559,6 +669,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
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;
@@ -595,16 +706,15 @@ const struct regmap_config pcm3168a_regmap = {
};
EXPORT_SYMBOL_GPL(pcm3168a_regmap);
-static const struct snd_soc_codec_driver pcm3168a_driver = {
- .idle_bias_off = true,
- .component_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)
- },
+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)
@@ -618,13 +728,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
- pcm3168a->scki = devm_clk_get(dev, "scki");
- if (IS_ERR(pcm3168a->scki)) {
- ret = PTR_ERR(pcm3168a->scki);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
- return ret;
- }
+ /*
+ * 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_optional(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) {
@@ -633,6 +755,9 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
}
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+ /* Fallback to the default if no clk entry available. */
+ if (!pcm3168a->sysclk)
+ pcm3168a->sysclk = 24576000;
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
@@ -640,8 +765,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
ret = devm_regulator_bulk_get(dev,
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to request supplies: %d\n", ret);
+ dev_err_probe(dev, ret, "failed to request supplies\n");
goto err_clk;
}
@@ -659,20 +783,30 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
- ret = pcm3168a_reset(pcm3168a);
- if (ret) {
- dev_err(dev, "Failed to reset device: %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);
- ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
- ARRAY_SIZE(pcm3168a_dais));
+ 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 codec: %d\n", ret);
+ dev_err(dev, "failed to register component: %d\n", ret);
goto err_regulator;
}
@@ -688,19 +822,33 @@ err_clk:
}
EXPORT_SYMBOL_GPL(pcm3168a_probe);
-void pcm3168a_remove(struct device *dev)
+static void pcm3168a_disable(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
- pm_runtime_disable(dev);
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
- 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);
@@ -752,19 +900,14 @@ static int pcm3168a_rt_suspend(struct device *dev)
regcache_cache_only(pcm3168a->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
- pcm3168a->supplies);
-
- clk_disable_unprepare(pcm3168a->scki);
+ 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_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = {
+ 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>");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
index 56c8332d82fb..c4b7140dce39 100644
--- a/sound/soc/codecs/pcm3168a.h
+++ b/sound/soc/codecs/pcm3168a.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* PCM3168A codec driver header
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#ifndef __PCM3168A_H__
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index 8ba322a00363..9bca53de2475 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM5102A codec
*
* Author: Florian Meier <florian.meier@koalo.de>
* Copyright 2013
- *
- * 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.
*/
#include <linux/init.h>
@@ -25,27 +17,25 @@ static struct snd_soc_dai_driver pcm5102a_dai = {
.playback = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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_codec_driver soc_codec_dev_pcm5102a;
+static const 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 snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a,
+ return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a,
&pcm5102a_dai, 1);
}
-static int pcm5102a_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static const struct of_device_id pcm5102a_of_match[] = {
{ .compatible = "ti,pcm5102a", },
{ }
@@ -54,7 +44,6 @@ MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
static struct platform_driver pcm5102a_codec_driver = {
.probe = pcm5102a_probe,
- .remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
.of_match_table = pcm5102a_of_match,
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
index 5f9c069569d5..a1d849b0c50f 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -1,27 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#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,
- const struct i2c_device_id *id)
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config = pcm512x_regmap;
@@ -37,10 +29,9 @@ static int pcm512x_i2c_probe(struct i2c_client *i2c,
return pcm512x_probe(&i2c->dev, regmap);
}
-static int pcm512x_i2c_remove(struct i2c_client *i2c)
+static void pcm512x_i2c_remove(struct i2c_client *i2c)
{
pcm512x_remove(&i2c->dev);
- return 0;
}
static const struct i2c_device_id pcm512x_i2c_id[] = {
@@ -48,27 +39,47 @@ static const struct i2c_device_id pcm512x_i2c_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
+ { "tas5754", },
+ { "tas5756", },
{ }
};
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", },
+ { .compatible = "ti,pcm5242", },
+ { .compatible = "ti,tas5754", },
+ { .compatible = "ti,tas5756", },
{ }
};
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 = pcm512x_i2c_probe,
+ .probe = pcm512x_i2c_probe,
.remove = pcm512x_i2c_remove,
.id_table = pcm512x_i2c_id,
.driver = {
.name = "pcm512x",
- .of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .of_match_table = of_match_ptr(pcm512x_of_match),
+ .acpi_match_table = ACPI_PTR(pcm512x_acpi_match),
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
index 7cdd2dc4fd79..92f7f78a4e20 100644
--- a/sound/soc/codecs/pcm512x-spi.c
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
@@ -34,10 +26,9 @@ static int pcm512x_spi_probe(struct spi_device *spi)
return pcm512x_probe(&spi->dev, regmap);
}
-static int pcm512x_spi_remove(struct spi_device *spi)
+static void pcm512x_spi_remove(struct spi_device *spi)
{
pcm512x_remove(&spi->dev);
- return 0;
}
static const struct spi_device_id pcm512x_spi_id[] = {
@@ -45,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
{ },
};
MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
@@ -54,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = {
{ .compatible = "ti,pcm5122", },
{ .compatible = "ti,pcm5141", },
{ .compatible = "ti,pcm5142", },
+ { .compatible = "ti,pcm5242", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm512x_of_match);
@@ -65,7 +58,7 @@ static struct spi_driver pcm512x_spi_driver = {
.driver = {
.name = "pcm512x",
.of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index e0f3556d3872..007dfc0fa224 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
@@ -53,6 +45,10 @@ struct pcm512x_priv {
unsigned long overclock_pll;
unsigned long overclock_dac;
unsigned long overclock_dsp;
+ int mute;
+ struct mutex mutex;
+ unsigned int bclk_ratio;
+ int force_pll_on;
};
/*
@@ -121,6 +117,8 @@ static const struct reg_default pcm512x_reg_defaults[] = {
{ 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)
@@ -226,8 +224,8 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg)
static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -236,10 +234,10 @@ static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_component_get_bias_level(component)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -254,8 +252,8 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -264,10 +262,10 @@ static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol,
static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_component_get_bias_level(component)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -282,8 +280,8 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -292,10 +290,10 @@ static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol,
static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_component_get_bias_level(component)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -384,6 +382,61 @@ 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),
@@ -391,8 +444,15 @@ 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),
-SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
- PCM512x_RQMR_SHIFT, 1, 1),
+{
+ .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),
@@ -522,8 +582,8 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -564,8 +624,8 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -590,15 +650,15 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
return pcm512x_dai_startup_slave(substream, dai);
default:
@@ -606,10 +666,10 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
}
}
-static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+static int pcm512x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(component->dev);
int ret;
switch (level) {
@@ -621,7 +681,7 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to remove standby: %d\n",
+ dev_err(component->dev, "Failed to remove standby: %d\n",
ret);
return ret;
}
@@ -631,7 +691,7 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, PCM512x_RQST);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request standby: %d\n",
+ dev_err(component->dev, "Failed to request standby: %d\n",
ret);
return ret;
}
@@ -645,8 +705,8 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai,
unsigned long bclk_rate)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
unsigned long sck_rate;
int pow2;
@@ -691,8 +751,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
unsigned long pll_rate)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -798,8 +858,8 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
unsigned long osr_rate,
unsigned long pllin_rate)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -829,8 +889,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -851,16 +911,21 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
int fssp;
int gpio;
- lrclk_div = snd_soc_params_to_frame_size(params);
- if (lrclk_div == 0) {
- dev_err(dev, "No LRCLK?\n");
- return -EINVAL;
+ 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_div = params->rate_den * 64 / lrclk_div;
- bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div);
+ bclk_rate = params_rate(params) * lrclk_div;
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
mck_rate = sck_rate;
} else {
@@ -949,7 +1014,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as dacref: %d\n", ret);
return ret;
}
@@ -958,7 +1023,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as dacin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -987,7 +1052,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_SCK);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set sck as dacref: %d\n", ret);
return ret;
}
@@ -1082,18 +1147,18 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE,
PCM512x_FSSP, fssp);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set fs speed: %d\n", ret);
+ dev_err(component->dev, "Failed to set fs speed: %d\n", ret);
return ret;
}
- dev_dbg(codec->dev, "DSP divider %d\n", dsp_div);
- dev_dbg(codec->dev, "DAC divider %d\n", dac_div);
- dev_dbg(codec->dev, "NCP divider %d\n", ncp_div);
- dev_dbg(codec->dev, "OSR divider %d\n", osr_div);
- dev_dbg(codec->dev, "BCK divider %d\n", bclk_div);
- dev_dbg(codec->dev, "LRCK divider %d\n", lrclk_div);
- dev_dbg(codec->dev, "IDAC %d\n", idac);
- dev_dbg(codec->dev, "1<<FSSP %d\n", 1 << fssp);
+ 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;
}
@@ -1102,15 +1167,13 @@ static int pcm512x_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 pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
int alen;
int gpio;
- int clock_output;
- int master_mode;
int ret;
- dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
params_rate(params),
params_channels(params));
@@ -1128,62 +1191,41 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
alen = PCM512x_ALEN_32;
break;
default:
- dev_err(codec->dev, "Bad frame size: %d\n",
+ dev_err(component->dev, "Bad frame size: %d\n",
params_width(params));
return -EINVAL;
}
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- ret = regmap_update_bits(pcm512x->regmap,
- PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP
- | PCM512x_BCKO | PCM512x_LRKO,
- 0);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to enable slave mode: %d\n", ret);
- return ret;
- }
+ 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(codec->dev,
+ dev_err(component->dev,
"Failed to enable clock divider autoset: %d\n",
ret);
return ret;
}
- return 0;
- case SND_SOC_DAIFMT_CBM_CFM:
- clock_output = PCM512x_BCKO | PCM512x_LRKO;
- master_mode = PCM512x_RLRK | PCM512x_RBCK;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- clock_output = PCM512x_BCKO;
- master_mode = PCM512x_RBCK;
- break;
- default:
- return -EINVAL;
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
- PCM512x_ALEN, alen);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to set frame size: %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(codec->dev, "Failed to set FLEX_A: %d\n", ret);
+ 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(codec->dev, "Failed to set FLEX_B: %d\n", ret);
+ dev_err(component->dev, "Failed to set FLEX_B: %d\n", ret);
return ret;
}
@@ -1196,7 +1238,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to ignore auto-clock failures: %d\n",
ret);
return ret;
@@ -1211,16 +1253,40 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS | PCM512x_IPLK);
if (ret != 0) {
- dev_err(codec->dev,
+ 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 (!pcm512x->force_pll_on) {
+ ret = regmap_update_bits(pcm512x->regmap,
+ PCM512x_PLL_EN, PCM512x_PLLE, 0);
+ } else {
+ /* provide minimum PLL config for TAS575x clocking
+ * and leave PLL enabled
+ */
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_0, 0x01);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_1, 0x04);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_EN, 0x01);
+ dev_dbg(component->dev, "Enabling PLL for TAS575x\n");
+ }
+
if (ret != 0) {
- dev_err(codec->dev, "Failed to disable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to set pll mode: %d\n", ret);
return ret;
}
}
@@ -1233,7 +1299,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
PCM512x_SREF, PCM512x_SREF_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as pllref: %d\n", ret);
return ret;
}
@@ -1242,7 +1308,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as pllin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -1251,33 +1317,15 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
PCM512x_PLLE, PCM512x_PLLE);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to enable pll: %d\n", ret);
return ret;
}
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO,
- clock_output);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable clock output: %d\n", ret);
- return ret;
- }
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE,
- PCM512x_RLRK | PCM512x_RBCK,
- master_mode);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable master mode: %d\n", ret);
- return ret;
- }
-
- if (pcm512x->pll_out) {
gpio = PCM512x_G1OE << (pcm512x->pll_out - 1);
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
gpio, gpio);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable gpio %d: %d\n",
+ dev_err(component->dev, "Failed to enable gpio %d: %d\n",
pcm512x->pll_out, ret);
return ret;
}
@@ -1286,7 +1334,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, gpio,
PCM512x_GxSL, PCM512x_GxSL_PLLCK);
if (ret != 0) {
- dev_err(codec->dev, "Failed to output pll on %d: %d\n",
+ dev_err(component->dev, "Failed to output pll on %d: %d\n",
ret, pcm512x->pll_out);
return ret;
}
@@ -1295,34 +1343,172 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
PCM512x_RQSY, PCM512x_RQSY_HALT);
if (ret != 0) {
- dev_err(codec->dev, "Failed to halt clocks: %d\n", ret);
+ 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(codec->dev, "Failed to resume clocks: %d\n", ret);
+ 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_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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 = {
@@ -1341,18 +1527,16 @@ static struct snd_soc_dai_driver pcm512x_dai = {
.ops = &pcm512x_dai_ops,
};
-static const struct snd_soc_codec_driver pcm512x_codec_driver = {
- .set_bias_level = pcm512x_set_bias_level,
- .idle_bias_off = true,
-
- .component_driver = {
- .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),
- },
+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 = {
@@ -1389,6 +1573,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (!pcm512x)
return -ENOMEM;
+ mutex_init(&pcm512x->mutex);
+
dev_set_drvdata(dev, pcm512x);
pcm512x->regmap = regmap;
@@ -1407,8 +1593,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
- ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
- &pcm512x->supply_nb[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",
@@ -1438,13 +1625,15 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
}
pcm512x->sclk = devm_clk_get(dev, NULL);
- if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ 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);
- return ret;
+ goto err;
}
}
@@ -1470,7 +1659,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-in\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_in = val;
}
@@ -1479,7 +1668,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-out\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_out = val;
}
@@ -1488,17 +1677,22 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
dev_err(dev,
"Error: both pll-in and pll-out, or none\n");
ret = -EINVAL;
- goto err_clk;
+ 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_clk;
+ goto err_pm;
}
+
+ if (!strcmp(np->name, "tas5756") ||
+ !strcmp(np->name, "tas5754"))
+ pcm512x->force_pll_on = 1;
+ dev_dbg(dev, "Device ID: %s\n", np->name);
}
#endif
- ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+ 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);
@@ -1523,7 +1717,6 @@ void pcm512x_remove(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
pm_runtime_disable(dev);
if (!IS_ERR(pcm512x->sclk))
clk_disable_unprepare(pcm512x->sclk);
@@ -1532,7 +1725,6 @@ void pcm512x_remove(struct device *dev)
}
EXPORT_SYMBOL_GPL(pcm512x_remove);
-#ifdef CONFIG_PM
static int pcm512x_suspend(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
@@ -1594,12 +1786,10 @@ static int pcm512x_resume(struct device *dev)
return 0;
}
-#endif
-const struct dev_pm_ops pcm512x_pm_ops = {
- SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(pcm512x_pm_ops) = {
+ 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>");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
index d70d9c0c2088..08d04f539805 100644
--- a/sound/soc/codecs/pcm512x.h
+++ b/sound/soc/codecs/pcm512x.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT 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 _SND_SOC_PCM512X
@@ -112,7 +104,9 @@
#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 */
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
new file mode 100644
index 000000000000..08cc52b374a9
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.c
@@ -0,0 +1,2170 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC Device
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#include <linux/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm6240.h"
+
+static const struct i2c_device_id pcmdevice_i2c_id[] = {
+ { "adc3120", ADC3120 },
+ { "adc5120", ADC5120 },
+ { "adc6120", ADC6120 },
+ { "dix4192", DIX4192 },
+ { "pcm1690", PCM1690 },
+ { "pcm3120", PCM3120 },
+ { "pcm3140", PCM3140 },
+ { "pcm5120", PCM5120 },
+ { "pcm5140", PCM5140 },
+ { "pcm6120", PCM6120 },
+ { "pcm6140", PCM6140 },
+ { "pcm6240", PCM6240 },
+ { "pcm6260", PCM6260 },
+ { "pcm9211", PCM9211 },
+ { "pcmd3140", PCMD3140 },
+ { "pcmd3180", PCMD3180 },
+ { "pcmd512x", PCMD512X },
+ { "taa5212", TAA5212 },
+ { "taa5412", TAA5412 },
+ { "tad5212", TAD5212 },
+ { "tad5412", TAD5412 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id);
+
+static const char *const pcmdev_ctrl_name[] = {
+ "%s i2c%d Dev%d Ch%d Ana Volume",
+ "%s i2c%d Dev%d Ch%d Digi Volume",
+ "%s i2c%d Dev%d Ch%d Fine Volume",
+};
+
+static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = {
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH1_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ },
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH2_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control adc5120_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm1690_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH5_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH6_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm9211_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH5_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH6_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH7_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH8_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_digi_vol_ctl[] = {
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH1_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH2_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH3_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ }
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv,
+ -12750, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_dig_gain_tlv,
+ -25500, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm9211_dig_gain_tlv,
+ -11450, 2000);
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc5120_fgain_tlv,
+ -10050, 2700);
+static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv,
+ -8050, 4700);
+static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv,
+ -80, 70);
+
+static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv,
+ unsigned short dev_no)
+{
+ struct i2c_client *client = (struct i2c_client *)pcm_priv->client;
+ struct regmap *map = pcm_priv->regmap;
+ int ret;
+
+ if (client->addr == pcm_priv->addr[dev_no])
+ return 0;
+
+ client->addr = pcm_priv->addr[dev_no];
+ /* All pcmdevices share the same regmap, clear the page
+ * inside regmap once switching to another pcmdevice.
+ * Register 0 at any pages inside pcmdevice is the same
+ * one for page-switching.
+ */
+ ret = regmap_write(map, PCMDEVICE_PAGE_SELECT, 0);
+ if (ret < 0)
+ dev_err(pcm_priv->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_read(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int *val)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(map, reg, val);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_update_bits(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int mask,
+ unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(map, reg, mask, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err=%d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, ret;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int reg = mc->reg;
+ unsigned int val;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ if (pcm_dev->chip_id == PCM1690) {
+ ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL,
+ &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read mode err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ val &= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ /* Set to wide-range mode, before using vol ctrl. */
+ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -25500;
+ goto out;
+ }
+ /* Set to fine mode, before using fine vol ctrl. */
+ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -12750;
+ goto out;
+ }
+ }
+
+ ret = pcmdev_dev_read(pcm_dev, dev_no, reg, &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ val = (val >> shift) & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ ucontrol->value.integer.value[0] = val;
+out:
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_get_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, rc;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int val, val_mask;
+ unsigned int reg = mc->reg;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ val = ucontrol->value.integer.value[0] & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ val_mask = mask << shift;
+ val = val << shift;
+
+ switch (vol_ctrl_type) {
+ case PCMDEV_PCM1690_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE;
+ break;
+ case PCMDEV_PCM1690_FINE_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP;
+ break;
+ }
+
+ rc = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, val_mask, val);
+ if (rc < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err = %d\n",
+ __func__, rc);
+ else
+ rc = 1;
+ mutex_unlock(&pcm_dev->codec_lock);
+ return rc;
+}
+
+static int pcmdevice_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_put_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
+ // ADC3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // DIX4192
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // PCM1690
+ {
+ {
+ .gain = pcm1690_fine_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_volsw,
+ .put = pcm1690_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ {
+ .gain = pcm1690_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_finevolsw,
+ .put = pcm1690_put_finevolsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ },
+ // PCM3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM3140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6240
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6260
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6260_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6260_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM9211
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .gain = pcm9211_dig_gain_tlv,
+ .pcmdev_ctrl = pcm9211_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm9211_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+
+ },
+ // PCMD3140
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD3180
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD512X
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAA5212
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAA5412
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAD5212
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAD5412
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+};
+
+static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(map, reg, data, len);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: bulk_write err = %d\n", __func__,
+ ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(map, reg, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdevice_info_profile(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max(0, pcm_dev->regbin.ncfgs - 1);
+
+ return 0;
+}
+
+static int pcmdevice_get_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm_dev->cur_conf;
+
+ return 0;
+}
+
+static int pcmdevice_set_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+ int nr_profile = ucontrol->value.integer.value[0];
+ int max = pcm_dev->regbin.ncfgs - 1;
+ int ret = 0;
+
+ nr_profile = clamp(nr_profile, 0, max);
+
+ if (pcm_dev->cur_conf != nr_profile) {
+ pcm_dev->cur_conf = nr_profile;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int pcmdevice_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->max;
+ return 0;
+}
+
+static void pcm9211_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_update_bits(pcm_dev, i,
+ PCM9211_REG_SW_CTRL, PCM9211_REG_SW_CTRL_MRST_MSK,
+ PCM9211_REG_SW_CTRL_MRST);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static void pcmdevice_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_write(pcm_dev, i, PCMDEVICE_REG_SWRESET,
+ PCMDEVICE_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
+ const unsigned char *config_data, unsigned int config_size,
+ int *status)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_config_info *cfg_info;
+ struct pcmdevice_block_data **bk_da;
+ unsigned int config_offset = 0, i;
+
+ cfg_info = kzalloc(sizeof(struct pcmdevice_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: cfg_name out of boundary\n", __func__);
+ goto out;
+ }
+ memcpy(cfg_info->cfg_name, &config_data[config_offset], 64);
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev, "%s: nblocks out of boundary\n",
+ __func__);
+ goto out;
+ }
+ cfg_info->nblocks =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(struct pcmdevice_block_data *), GFP_KERNEL);
+ if (!bk_da) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: out of boundary i = %d nblocks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ bk_da[i] = kzalloc(sizeof(struct pcmdevice_block_data),
+ GFP_KERNEL);
+ if (!bk_da[i]) {
+ *status = -ENOMEM;
+ break;
+ }
+ bk_da[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (bk_da[i]->block_type == PCMDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (bk_da[i]->dev_idx == 0)
+ cfg_info->active_dev =
+ (1 << pcm_dev->ndev) - 1;
+ else
+ cfg_info->active_dev =
+ 1 << (bk_da[i]->dev_idx - 1);
+ }
+
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: out of boundary: i = %d blks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+
+ bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+ bk_da[i]->block_size, GFP_KERNEL);
+ if (!bk_da[i]->regdata) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ config_offset += bk_da[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+out:
+ return cfg_info;
+}
+
+static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev,
+ int dev_no, int ctl_id)
+{
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct pcmdevice_mixer_control *pcmdev_ctrl;
+ struct snd_kcontrol_new *pcmdev_controls;
+ int ret, mix_index = 0, name_id, chn;
+ unsigned int id = pcm_dev->chip_id;
+ const int nr_chn =
+ pcmdev_gain_ctl_info[id][ctl_id].ctrl_array_size;
+ const char *ctrl_name;
+ char *name;
+
+ if (!nr_chn) {
+ dev_dbg(pcm_dev->dev, "%s: no gain ctrl for %s\n", __func__,
+ pcm_dev->dev_name);
+ return 0;
+ }
+
+ pcmdev_controls = devm_kcalloc(pcm_dev->dev, nr_chn,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_controls)
+ return -ENOMEM;
+
+ name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id;
+
+ ctrl_name = pcmdev_ctrl_name[name_id];
+
+ for (chn = 1; chn <= nr_chn; chn++) {
+ name = devm_kzalloc(pcm_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ ctrl_name, pcm_dev->upper_dev_name, adap->nr,
+ dev_no, chn);
+ pcmdev_controls[mix_index].tlv.p =
+ pcmdev_gain_ctl_info[id][ctl_id].gain;
+ pcmdev_ctrl = devm_kmemdup(pcm_dev->dev,
+ &pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl[chn - 1],
+ sizeof(*pcmdev_ctrl), GFP_KERNEL);
+ if (!pcmdev_ctrl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pcmdev_ctrl->dev_no = dev_no;
+ pcmdev_controls[mix_index].private_value =
+ (unsigned long)pcmdev_ctrl;
+ pcmdev_controls[mix_index].name = name;
+ pcmdev_controls[mix_index].access =
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ pcmdev_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_controls[mix_index].info = pcmdevice_info_volsw;
+ pcmdev_controls[mix_index].get =
+ pcmdev_gain_ctl_info[id][ctl_id].get;
+ pcmdev_controls[mix_index].put =
+ pcmdev_gain_ctl_info[id][ctl_id].put;
+ mix_index++;
+ }
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_controls, mix_index);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+out:
+ return ret;
+}
+
+static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev)
+{
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_kcontrol_new *pcmdev_ctrl;
+ char *name;
+ int ret;
+
+ pcmdev_ctrl = devm_kzalloc(pcm_dev->dev,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_ctrl)
+ return -ENOMEM;
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr);
+ pcmdev_ctrl->name = name;
+ pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_ctrl->info = pcmdevice_info_profile;
+ pcmdev_ctrl->get = pcmdevice_get_profile_id;
+ pcmdev_ctrl->put = pcmdevice_set_profile_id;
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_ctrl, 1);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static void pcmdevice_config_info_remove(void *ctxt)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *) ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ int i, j;
+
+ if (!cfg_info)
+ return;
+ for (i = 0; i < regbin->ncfgs; i++) {
+ if (!cfg_info[i])
+ continue;
+ if (cfg_info[i]->blk_data) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ if (!cfg_info[i]->blk_data[j])
+ continue;
+ kfree(cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ }
+ kfree(cfg_info[i]);
+ }
+ kfree(cfg_info);
+}
+
+static int pcmdev_regbin_ready(const struct firmware *fmw, void *ctxt)
+{
+ struct pcmdevice_config_info **cfg_info;
+ struct pcmdevice_priv *pcm_dev = ctxt;
+ struct pcmdevice_regbin_hdr *fw_hdr;
+ struct pcmdevice_regbin *regbin;
+ unsigned int total_config_sz = 0;
+ int offset = 0, ret = 0, i;
+ unsigned char *buf;
+
+ regbin = &(pcm_dev->regbin);
+ fw_hdr = &(regbin->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(pcm_dev->dev, "%s: failed to read %s\n",
+ __func__, pcm_dev->bin_name);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(pcm_dev->dev, "%s: file size(%d) not match %u",
+ __func__, (int)fmw->size, fw_hdr->img_sz);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(pcm_dev->dev, "%s: bin version 0x%04x is out of date",
+ __func__, fw_hdr->binary_version_num);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: invalid ndev(%u)\n", __func__,
+ fw_hdr->ndev);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (offset + PCMDEVICE_MAX_REGBIN_DEVICES > fw_hdr->img_sz) {
+ dev_err(pcm_dev->dev, "%s: devs out of boundary!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < PCMDEVICE_MAX_REGBIN_DEVICES; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < PCMDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(pcm_dev->dev, "%s: bin file error!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -ENOMEM;
+ goto out;
+ }
+ regbin->cfg_info = cfg_info;
+ regbin->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = pcmdevice_add_config(ctxt, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ /* In case the bin file is partially destroyed. */
+ if (regbin->ncfgs == 0)
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ regbin->ncfgs += 1;
+ }
+
+out:
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev,
+ "%s: remove config due to fw load error!\n", __func__);
+ pcmdevice_config_info_remove(pcm_dev);
+ }
+
+ return ret;
+}
+
+static int pcmdevice_comp_probe(struct snd_soc_component *comp)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp);
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ const struct firmware *fw_entry = NULL;
+ int ret, i, j;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ pcm_dev->component = comp;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ for (j = 0; j < 2; j++) {
+ ret = pcmdev_gain_ctrl_add(pcm_dev, i, j);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ if (comp->name_prefix) {
+ /* There's name_prefix defined in DTS. Bin file name will be
+ * name_prefix.bin stores the firmware including register
+ * setting and params for different filters inside chips, it
+ * must be copied into firmware folder. The same types of
+ * pcmdevices sitting on the same i2c bus will be aggregated as
+ * one single codec, all of them share the same bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s.bin", comp->name_prefix);
+ } else {
+ /* There's NO name_prefix defined in DTS. Bin file name will be
+ * device-name[defined in pcmdevice_i2c_id]-i2c-bus_id
+ * [0,1,...,N]-sum[1,...,4]dev.bin stores the firmware
+ * including register setting and params for different filters
+ * inside chips, it must be copied into firmware folder. The
+ * same types of pcmdevices sitting on the same i2c bus will be
+ * aggregated as one single codec, all of them share the same
+ * bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s-i2c-%d-%udev.bin", pcm_dev->dev_name, adap->nr,
+ pcm_dev->ndev);
+ }
+
+ ret = request_firmware(&fw_entry, pcm_dev->bin_name, pcm_dev->dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+
+ ret = pcmdev_regbin_ready(fw_entry, pcm_dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+ ret = pcmdev_profile_ctrl_add(pcm_dev);
+out:
+ release_firmware(fw_entry);
+
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+
+static void pcmdevice_comp_remove(struct snd_soc_component *codec)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+
+ if (!pcm_dev)
+ return;
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_config_info_remove(pcm_dev);
+ mutex_unlock(&pcm_dev->codec_lock);
+}
+
+static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI1 OUT", "ASI1 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route pcmdevice_audio_map[] = {
+ {"OUT", NULL, "ASI"},
+ {"ASI1 OUT", NULL, "MIC"},
+};
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_pcmdevice = {
+ .probe = pcmdevice_comp_probe,
+ .remove = pcmdevice_comp_remove,
+ .dapm_widgets = pcmdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcmdevice_dapm_widgets),
+ .dapm_routes = pcmdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pcmdevice_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pcmdev_single_byte_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int i, ret;
+
+ offset += 2;
+ if (offset + 4 * len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d byt wr out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = pcmdev_dev_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ data[offset + 3]);
+ /* skip this error for next operation or next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d single write err\n",
+ __func__, devn);
+
+ offset += 4;
+ }
+
+ return offset;
+}
+
+static int pcmdev_burst_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int ret;
+
+ offset += 2;
+ if (offset + 4 + len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d burst Out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ if (len % 4) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bst-len(%u) not div by 4\n",
+ __func__, devn, len);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_bulk_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ &(data[offset + 4]), len);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d bulk_write err = %d\n",
+ __func__, devn, ret);
+
+ offset += (len + 4);
+
+ return offset;
+}
+
+static int pcmdev_delay(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned int delay_time = 0;
+ int offset = 2;
+
+ if (offset + 2 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d delay out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ delay_time = get_unaligned_be16(&data[2]) * 1000;
+ usleep_range(delay_time, delay_time + 50);
+ offset += 2;
+
+ return offset;
+}
+
+static int pcmdev_bits_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ int offset = 2;
+ int ret;
+
+ if (offset + 6 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bit write out of memory\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_update_bits(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 3], data[offset + 4]),
+ data[offset + 1], data[offset + 5]);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d update_bits err = %d\n",
+ __func__, devn, ret);
+
+ offset += 6;
+
+ return offset;
+}
+
+static int pcmdevice_process_block(void *ctxt, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ int devn, dev_end, ret = 0;
+ unsigned char subblk_typ = data[1];
+
+ if (dev_idx) {
+ devn = dev_idx - 1;
+ dev_end = dev_idx;
+ } else {
+ devn = 0;
+ dev_end = pcm_dev->ndev;
+ }
+
+ /* loop in case of several devices sharing the same sub-block */
+ for (; devn < dev_end; devn++) {
+ switch (subblk_typ) {
+ case PCMDEVICE_CMD_SING_W:
+ ret = pcmdev_single_byte_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_BURST:
+ ret = pcmdev_burst_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_DELAY:
+ ret = pcmdev_delay(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_FIELD_W:
+ ret = pcmdev_bits_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ default:
+ break;
+ }
+ /*
+ * In case of sub-block error, break the loop for the rest of
+ * devices.
+ */
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void pcmdevice_select_cfg_blk(void *ctxt, int conf_no,
+ unsigned char block_type)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ struct pcmdevice_block_data **blk_data;
+ int j, k;
+
+ if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(pcm_dev->dev, "%s: conf_no should be less than %u\n",
+ __func__, regbin->ncfgs);
+ goto out;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, ret;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(pcm_dev->dev,
+ "%s: block_type should be out of range\n",
+ __func__);
+ goto out;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+ ret = pcmdevice_process_block(pcm_dev,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += ret;
+ if (blk_data[j]->block_size < length) {
+ dev_err(pcm_dev->dev,
+ "%s: %u %u out of boundary\n",
+ __func__, length,
+ blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != blk_data[j]->block_size)
+ dev_err(pcm_dev->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+
+out:
+ return;
+}
+
+static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+ unsigned char block_type;
+
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev, "%s: bin file not loaded\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mute)
+ block_type = PCMDEVICE_BIN_BLK_PRE_SHUTDOWN;
+ else
+ block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type);
+ mutex_unlock(&pcm_dev->codec_lock);
+ return 0;
+}
+
+static int pcmdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int ret = 0;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ break;
+ case 44100:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ break;
+ case 24:
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(pcm_dev->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ ret = bclk_rate;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcmdevice_dai_ops = {
+ .mute_stream = pcmdevice_mute,
+ .hw_params = pcmdevice_hw_params,
+};
+
+static struct snd_soc_dai_driver pcmdevice_dai_driver[] = {
+ {
+ .name = "pcmdevice-codec",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .ops = &pcmdevice_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcmdevice_of_match[] = {
+ { .compatible = "ti,adc3120" },
+ { .compatible = "ti,adc5120" },
+ { .compatible = "ti,adc6120" },
+ { .compatible = "ti,dix4192" },
+ { .compatible = "ti,pcm1690" },
+ { .compatible = "ti,pcm3120" },
+ { .compatible = "ti,pcm3140" },
+ { .compatible = "ti,pcm5120" },
+ { .compatible = "ti,pcm5140" },
+ { .compatible = "ti,pcm6120" },
+ { .compatible = "ti,pcm6140" },
+ { .compatible = "ti,pcm6240" },
+ { .compatible = "ti,pcm6260" },
+ { .compatible = "ti,pcm9211" },
+ { .compatible = "ti,pcmd3140" },
+ { .compatible = "ti,pcmd3180" },
+ { .compatible = "ti,pcmd512x" },
+ { .compatible = "ti,taa5212" },
+ { .compatible = "ti,taa5412" },
+ { .compatible = "ti,tad5212" },
+ { .compatible = "ti,tad5412" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pcmdevice_of_match);
+#endif
+
+static const struct regmap_range_cfg pcmdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = PCMDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config pcmdevice_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .ranges = pcmdevice_ranges,
+ .num_ranges = ARRAY_SIZE(pcmdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev)
+{
+ if (pcm_dev->irq)
+ free_irq(pcm_dev->irq, pcm_dev);
+ mutex_destroy(&pcm_dev->codec_lock);
+}
+
+static char *str_to_upper(char *str)
+{
+ char *orig = str;
+
+ if (!str)
+ return NULL;
+
+ while (*str) {
+ *str = toupper(*str);
+ str++;
+ }
+
+ return orig;
+}
+
+static int pcmdevice_i2c_probe(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev;
+ struct device_node *np;
+ unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES];
+ int ret = 0, i = 0, ndev = 0;
+
+ pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL);
+ if (!pcm_dev)
+ return -ENOMEM;
+
+ pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c);
+
+ pcm_dev->dev = &i2c->dev;
+ pcm_dev->client = i2c;
+
+ if (pcm_dev->chip_id >= MAX_DEVICE)
+ pcm_dev->chip_id = 0;
+
+ strscpy(pcm_dev->dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->dev_name));
+
+ strscpy(pcm_dev->upper_dev_name,
+ pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->upper_dev_name));
+
+ str_to_upper(pcm_dev->upper_dev_name);
+
+ pcm_dev->regmap = devm_regmap_init_i2c(i2c, &pcmdevice_i2c_regmap);
+ if (IS_ERR(pcm_dev->regmap)) {
+ ret = PTR_ERR(pcm_dev->regmap);
+ dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ i2c_set_clientdata(i2c, pcm_dev);
+ mutex_init(&pcm_dev->codec_lock);
+ np = pcm_dev->dev->of_node;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ u64 addr;
+
+ for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) {
+ if (of_property_read_reg(np, i, &addr, NULL))
+ break;
+ dev_addrs[ndev++] = addr;
+ }
+ } else {
+ ndev = 1;
+ dev_addrs[0] = i2c->addr;
+ }
+ pcm_dev->irq = of_irq_get(np, 0);
+
+ for (i = 0; i < ndev; i++)
+ pcm_dev->addr[i] = dev_addrs[i];
+
+ pcm_dev->ndev = ndev;
+
+ pcm_dev->hw_rst = devm_gpiod_get_optional(&i2c->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ /* No reset GPIO, no side-effect */
+ if (IS_ERR(pcm_dev->hw_rst)) {
+ if (pcm_dev->chip_id == PCM9211 || pcm_dev->chip_id == PCM1690)
+ pcm9211_sw_rst(pcm_dev);
+ else
+ pcmdevice_sw_rst(pcm_dev);
+ } else {
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 1);
+ }
+
+ if (pcm_dev->chip_id == PCM1690)
+ goto skip_interrupt;
+ if (pcm_dev->irq) {
+ dev_dbg(pcm_dev->dev, "irq = %d", pcm_dev->irq);
+ } else
+ dev_err(pcm_dev->dev, "No irq provided\n");
+
+skip_interrupt:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_pcmdevice, pcmdevice_dai_driver,
+ ARRAY_SIZE(pcmdevice_dai_driver));
+ if (ret < 0)
+ dev_err(&i2c->dev, "probe register comp failed %d\n", ret);
+
+out:
+ if (ret < 0)
+ pcmdevice_remove(pcm_dev);
+ return ret;
+}
+
+static void pcmdevice_i2c_remove(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev = i2c_get_clientdata(i2c);
+
+ pcmdevice_remove(pcm_dev);
+}
+
+static struct i2c_driver pcmdevice_i2c_driver = {
+ .driver = {
+ .name = "pcmdevice-codec",
+ .of_match_table = of_match_ptr(pcmdevice_of_match),
+ },
+ .probe = pcmdevice_i2c_probe,
+ .remove = pcmdevice_i2c_remove,
+ .id_table = pcmdevice_i2c_id,
+};
+module_i2c_driver(pcmdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>");
+MODULE_DESCRIPTION("ASoC PCM6240 Family Audio ADC/DAC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h
new file mode 100644
index 000000000000..2d8f9e798139
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC/Router
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family Audio chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#ifndef __PCM6240_H__
+#define __PCM6240_H__
+
+enum pcm_device {
+ ADC3120,
+ ADC5120,
+ ADC6120,
+ DIX4192,
+ PCM1690,
+ PCM3120,
+ PCM3140,
+ PCM5120,
+ PCM5140,
+ PCM6120,
+ PCM6140,
+ PCM6240,
+ PCM6260,
+ PCM9211,
+ PCMD3140,
+ PCMD3180,
+ PCMD512X,
+ TAA5212,
+ TAA5412,
+ TAD5212,
+ TAD5412,
+ MAX_DEVICE,
+};
+
+#define PCMDEV_GENERIC_VOL_CTRL 0x0
+#define PCMDEV_PCM1690_VOL_CTRL 0x1
+#define PCMDEV_PCM1690_FINE_VOL_CTRL 0x2
+
+/* Maximum number of I2C addresses */
+#define PCMDEVICE_MAX_I2C_DEVICES 4
+/* Maximum number defined in REGBIN protocol */
+#define PCMDEVICE_MAX_REGBIN_DEVICES 8
+#define PCMDEVICE_CONFIG_SUM 64
+#define PCMDEVICE_BIN_FILENAME_LEN 64
+
+#define PCMDEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define PCMDEVICE_MAX_CHANNELS 8
+#define PCMDEVICE_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)
+
+/* PAGE Control Register (available in page0 of each book) */
+#define PCMDEVICE_PAGE_SELECT 0x00
+#define PCMDEVICE_REG(page, reg) ((page * 128) + reg)
+#define PCMDEVICE_REG_SWRESET PCMDEVICE_REG(0X0, 0x01)
+#define PCMDEVICE_REG_SWRESET_RESET BIT(0)
+
+#define ADC5120_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define ADC5120_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define ADC5120_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define ADC5120_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+
+#define PCM1690_REG_MODE_CTRL PCMDEVICE_REG(0X0, 0x46)
+#define PCM1690_REG_MODE_CTRL_DAMS_MSK BIT(7)
+#define PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP 0x0
+#define PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE 0x80
+
+#define PCM1690_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM1690_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCM1690_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4a)
+#define PCM1690_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4b)
+#define PCM1690_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM1690_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM1690_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4e)
+#define PCM1690_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4f)
+
+#define PCM6240_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6240_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6240_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6240_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6240_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6240_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6240_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6240_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+
+#define PCM6260_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6260_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6260_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6260_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6260_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6260_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6260_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6260_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM6260_REG_CH5_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x51)
+#define PCM6260_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCM6260_REG_CH6_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x56)
+#define PCM6260_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+
+#define PCM9211_REG_SW_CTRL PCMDEVICE_REG(0X0, 0x40)
+#define PCM9211_REG_SW_CTRL_MRST_MSK BIT(7)
+#define PCM9211_REG_SW_CTRL_MRST 0x0
+
+#define PCM9211_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x46)
+#define PCM9211_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x47)
+
+#define PCMD3140_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3140_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3140_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3140_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+
+#define PCMD3140_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3140_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3140_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3140_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+
+#define PCMD3180_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3180_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3180_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3180_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+#define PCMD3180_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCMD3180_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+#define PCMD3180_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define PCMD3180_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x61)
+
+#define PCMD3180_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3180_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3180_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3180_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+#define PCMD3180_REG_CH5_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define PCMD3180_REG_CH6_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define PCMD3180_REG_CH7_FINE_GAIN PCMDEVICE_REG(0X0, 0x5D)
+#define PCMD3180_REG_CH8_FINE_GAIN PCMDEVICE_REG(0X0, 0x62)
+
+#define TAA5412_REG_CH1_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x52)
+#define TAA5412_REG_CH2_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x57)
+#define TAA5412_REG_CH3_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5B)
+#define TAA5412_REG_CH4_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5F)
+
+#define TAA5412_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define TAA5412_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define TAA5412_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define TAA5412_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x60)
+
+#define PCMDEVICE_CMD_SING_W 0x1
+#define PCMDEVICE_CMD_BURST 0x2
+#define PCMDEVICE_CMD_DELAY 0x3
+#define PCMDEVICE_CMD_FIELD_W 0x4
+
+enum pcmdevice_bin_blk_type {
+ PCMDEVICE_BIN_BLK_COEFF = 1,
+ PCMDEVICE_BIN_BLK_POST_POWER_UP,
+ PCMDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ PCMDEVICE_BIN_BLK_PRE_POWER_UP,
+ PCMDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+enum pcmdevice_fw_state {
+ PCMDEVICE_FW_LOAD_OK = 0,
+ PCMDEVICE_FW_LOAD_FAILED
+};
+
+struct pcmdevice_regbin_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned int timestamp;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[PCMDEVICE_MAX_REGBIN_DEVICES];
+ unsigned int nconfig;
+ unsigned int config_size[PCMDEVICE_CONFIG_SUM];
+};
+
+struct pcmdevice_block_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct pcmdevice_config_info {
+ char cfg_name[64];
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct pcmdevice_block_data **blk_data;
+};
+
+struct pcmdevice_regbin {
+ struct pcmdevice_regbin_hdr fw_hdr;
+ int ncfgs;
+ struct pcmdevice_config_info **cfg_info;
+};
+
+struct pcmdevice_priv {
+ struct snd_soc_component *component;
+ struct i2c_client *client;
+ struct device *dev;
+ struct mutex codec_lock;
+ struct gpio_desc *hw_rst;
+ struct regmap *regmap;
+ struct pcmdevice_regbin regbin;
+ int irq;
+ unsigned int addr[PCMDEVICE_MAX_I2C_DEVICES];
+ unsigned int chip_id;
+ int cur_conf;
+ int fw_state;
+ int ndev;
+ unsigned char bin_name[PCMDEVICE_BIN_FILENAME_LEN];
+ /* used for kcontrol name */
+ unsigned char upper_dev_name[I2C_NAME_SIZE];
+ unsigned char dev_name[I2C_NAME_SIZE];
+};
+
+/* mixer control */
+struct pcmdevice_mixer_control {
+ int max;
+ int reg;
+ unsigned int dev_no;
+ unsigned int shift;
+ unsigned int invert;
+};
+struct pcmdev_ctrl_info {
+ const unsigned int *gain;
+ const struct pcmdevice_mixer_control *pcmdev_ctrl;
+ unsigned int ctrl_array_size;
+ snd_kcontrol_get_t *get;
+ snd_kcontrol_put_t *put;
+ int pcmdev_ctrl_name_id;
+};
+#endif /* __PCM6240_H__ */
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
new file mode 100644
index 000000000000..c0c5b3c3e98b
--- /dev/null
+++ b/sound/soc/codecs/peb2466.c
@@ -0,0 +1,2064 @@
+// 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 <linux/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 ARRAY_SIZE(((unsigned int[]){TLV_DB_SCALE_ITEM(0, 0, 0)}))
+
+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);
+ if (!ret)
+ *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 const 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 int 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 -EINVAL;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+
+ return 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;
+ }
+
+ ret = peb2466_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ 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_enabled(&peb2466->spi->dev, "mclk");
+ if (IS_ERR(peb2466->mclk))
+ return PTR_ERR(peb2466->mclk);
+
+ 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:
+ return ret;
+}
+
+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,
+};
+
+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/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 000000000000..4ed09fbe3f54
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright, 2025 Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.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/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "pm4125.h"
+
+static struct pm4125_sdw_ch_info pm4125_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(PM4125_HPH_L, PM4125_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_HPH_R, PM4125_HPH_PORT, BIT(1)),
+};
+
+static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_ADC2, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(1)),
+};
+
+static struct sdw_dpn_prop pm4125_dpn_prop[PM4125_MAX_SWR_PORTS] = {
+ {
+ .num = 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 8,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 2,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+struct device *pm4125_sdw_device_get(struct device_node *np)
+{
+ return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_device_get);
+
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *priv, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ priv->sconfig.ch_count = 1;
+ priv->active_ports = 0;
+ for (i = 0; i < PM4125_MAX_SWR_PORTS; i++) {
+ ch_mask = priv->port_config[i].ch_mask;
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ priv->sconfig.ch_count++;
+
+ port_config[priv->active_ports] = priv->port_config[i];
+ priv->active_ports++;
+ }
+
+ priv->sconfig.bps = 1;
+ priv->sconfig.frame_rate = params_rate(params);
+ priv->sconfig.direction = priv->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+ priv->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(priv->sdev, &priv->sconfig, &port_config[0], priv->active_ports,
+ priv->sruntime);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_hw_params);
+
+static int pm4125_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+
+ if (priv->regmap && status == SDW_SLAVE_ATTACHED) {
+ /* Write out any cached changes that happened between probe and attach */
+ regcache_cache_only(priv->regmap, false);
+ return regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering the first irq of the slave_irq
+ * irq domain, which then will be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int pm4125_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+ struct irq_domain *slave_irq = priv->slave_irq;
+ u32 sts1, sts2, sts3;
+
+ do {
+ handle_nested_irq(irq_find_mapping(slave_irq, 0));
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_0, &sts1);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_1, &sts2);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_2, &sts3);
+
+ } while (sts1 || sts2 || sts3);
+
+ return IRQ_HANDLED;
+}
+
+static const struct reg_default pm4125_defaults[] = {
+ { PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01 },
+ { PM4125_ANA_MICBIAS_MICB_3_EN, 0x00 },
+ { PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x21 },
+ { PM4125_ANA_MICBIAS_LDO_1_CTRL, 0x01 },
+ { PM4125_ANA_TX_AMIC1, 0x00 },
+ { PM4125_ANA_TX_AMIC2, 0x00 },
+ { PM4125_ANA_MBHC_MECH, 0x39 },
+ { PM4125_ANA_MBHC_ELECT, 0x08 },
+ { PM4125_ANA_MBHC_ZDET, 0x10 },
+ { PM4125_ANA_MBHC_RESULT_1, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_2, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_3, 0x00 },
+ { PM4125_ANA_MBHC_BTN0_ZDET_VREF1, 0x00 },
+ { PM4125_ANA_MBHC_BTN1_ZDET_VREF2, 0x10 },
+ { PM4125_ANA_MBHC_BTN2_ZDET_VREF3, 0x20 },
+ { PM4125_ANA_MBHC_BTN3_ZDET_DBG_400, 0x30 },
+ { PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400, 0x40 },
+ { PM4125_ANA_MBHC_MICB2_RAMP, 0x00 },
+ { PM4125_ANA_MBHC_CTL_1, 0x02 },
+ { PM4125_ANA_MBHC_CTL_2, 0x05 },
+ { PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0xE9 },
+ { PM4125_ANA_MBHC_ZDET_ANA_CTL, 0x0F },
+ { PM4125_ANA_MBHC_ZDET_RAMP_CTL, 0x00 },
+ { PM4125_ANA_MBHC_FSM_STATUS, 0x00 },
+ { PM4125_ANA_MBHC_ADC_RESULT, 0x00 },
+ { PM4125_ANA_MBHC_CTL_CLK, 0x30 },
+ { PM4125_ANA_MBHC_ZDET_CALIB_RESULT, 0x00 },
+ { PM4125_ANA_NCP_EN, 0x00 },
+ { PM4125_ANA_NCP_VCTRL, 0xA7 },
+ { PM4125_ANA_HPHPA_CNP_CTL_1, 0x54 },
+ { PM4125_ANA_HPHPA_CNP_CTL_2, 0x2B },
+ { PM4125_ANA_HPHPA_PA_STATUS, 0x00 },
+ { PM4125_ANA_HPHPA_FSM_CLK, 0x12 },
+ { PM4125_ANA_HPHPA_L_GAIN, 0x00 },
+ { PM4125_ANA_HPHPA_R_GAIN, 0x00 },
+ { PM4125_SWR_HPHPA_HD2, 0x1B },
+ { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 },
+ { PM4125_ANA_SURGE_EN, 0x38 },
+ { PM4125_ANA_COMBOPA_CTL, 0x35 },
+ { PM4125_ANA_COMBOPA_CTL_4, 0x84 },
+ { PM4125_ANA_COMBOPA_CTL_5, 0x05 },
+ { PM4125_ANA_RXLDO_CTL, 0x86 },
+ { PM4125_ANA_MBIAS_EN, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID0, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID1, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID2, 0x0C },
+ { PM4125_DIG_SWR_CHIP_ID3, 0x01 },
+ { PM4125_DIG_SWR_SWR_TX_CLK_RATE, 0x00 },
+ { PM4125_DIG_SWR_CDC_RST_CTL, 0x03 },
+ { PM4125_DIG_SWR_TOP_CLK_CFG, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_CLK_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX_CLK_CTL, 0x33 },
+ { PM4125_DIG_SWR_SWR_RST_EN, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX0_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_RX1_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1, 0x00 },
+ { PM4125_DIG_SWR_CDC_COMP_CTL_0, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_DELAY_CTL, 0x66 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_0, 0x55 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_1, 0xA9 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX0_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX1_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_REQ0_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_REQ1_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_AMIC_CTL, 0x02 },
+ { PM4125_DIG_SWR_CDC_DMIC_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_RATE, 0x01 },
+ { PM4125_DIG_SWR_PDM_WD_CTL0, 0x00 },
+ { PM4125_DIG_SWR_PDM_WD_CTL1, 0x00 },
+ { PM4125_DIG_SWR_INTR_MODE, 0x00 },
+ { PM4125_DIG_SWR_INTR_MASK_0, 0xFF },
+ { PM4125_DIG_SWR_INTR_MASK_1, 0x7F },
+ { PM4125_DIG_SWR_INTR_MASK_2, 0x0C },
+ { PM4125_DIG_SWR_INTR_STATUS_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_1, 0x2A },
+ { PM4125_DIG_SWR_INTR_LEVEL_2, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX0_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX1_CTL, 0x00 },
+ { PM4125_DIG_SWR_LOOP_BACK_MODE, 0x00 },
+ { PM4125_DIG_SWR_DRIVE_STRENGTH_0, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_CTL, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_EN, 0x00 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA0, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA1, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA2, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA3, 0x01 },
+};
+
+static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MICBIAS_MICB_1_2_EN:
+ case PM4125_ANA_MICBIAS_MICB_3_EN:
+ case PM4125_ANA_MICBIAS_LDO_1_SETTING:
+ case PM4125_ANA_MICBIAS_LDO_1_CTRL:
+ case PM4125_ANA_TX_AMIC1:
+ case PM4125_ANA_TX_AMIC2:
+ case PM4125_ANA_MBHC_MECH:
+ case PM4125_ANA_MBHC_ELECT:
+ case PM4125_ANA_MBHC_ZDET:
+ case PM4125_ANA_MBHC_BTN0_ZDET_VREF1:
+ case PM4125_ANA_MBHC_BTN1_ZDET_VREF2:
+ case PM4125_ANA_MBHC_BTN2_ZDET_VREF3:
+ case PM4125_ANA_MBHC_BTN3_ZDET_DBG_400:
+ case PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400:
+ case PM4125_ANA_MBHC_MICB2_RAMP:
+ case PM4125_ANA_MBHC_CTL_1:
+ case PM4125_ANA_MBHC_CTL_2:
+ case PM4125_ANA_MBHC_PLUG_DETECT_CTL:
+ case PM4125_ANA_MBHC_ZDET_ANA_CTL:
+ case PM4125_ANA_MBHC_ZDET_RAMP_CTL:
+ case PM4125_ANA_MBHC_CTL_CLK:
+ case PM4125_ANA_NCP_EN:
+ case PM4125_ANA_NCP_VCTRL:
+ case PM4125_ANA_HPHPA_CNP_CTL_1:
+ case PM4125_ANA_HPHPA_CNP_CTL_2:
+ case PM4125_ANA_HPHPA_FSM_CLK:
+ case PM4125_ANA_HPHPA_L_GAIN:
+ case PM4125_ANA_HPHPA_R_GAIN:
+ case PM4125_ANA_HPHPA_SPARE_CTL:
+ case PM4125_SWR_HPHPA_HD2:
+ case PM4125_ANA_SURGE_EN:
+ case PM4125_ANA_COMBOPA_CTL:
+ case PM4125_ANA_COMBOPA_CTL_4:
+ case PM4125_ANA_COMBOPA_CTL_5:
+ case PM4125_ANA_RXLDO_CTL:
+ case PM4125_ANA_MBIAS_EN:
+ case PM4125_DIG_SWR_SWR_TX_CLK_RATE:
+ case PM4125_DIG_SWR_CDC_RST_CTL:
+ case PM4125_DIG_SWR_TOP_CLK_CFG:
+ case PM4125_DIG_SWR_CDC_RX_CLK_CTL:
+ case PM4125_DIG_SWR_CDC_TX_CLK_CTL:
+ case PM4125_DIG_SWR_SWR_RST_EN:
+ case PM4125_DIG_SWR_CDC_RX_RST:
+ case PM4125_DIG_SWR_CDC_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_RX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1:
+ case PM4125_DIG_SWR_CDC_COMP_CTL_0:
+ case PM4125_DIG_SWR_CDC_RX_DELAY_CTL:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_0:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_1:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_CTL:
+ case PM4125_DIG_SWR_CDC_TX0_CTL:
+ case PM4125_DIG_SWR_CDC_TX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_RST:
+ case PM4125_DIG_SWR_CDC_REQ0_CTL:
+ case PM4125_DIG_SWR_CDC_REQ1_CTL:
+ case PM4125_DIG_SWR_CDC_RST:
+ case PM4125_DIG_SWR_CDC_AMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_RATE:
+ case PM4125_DIG_SWR_PDM_WD_CTL0:
+ case PM4125_DIG_SWR_PDM_WD_CTL1:
+ case PM4125_DIG_SWR_INTR_MODE:
+ case PM4125_DIG_SWR_INTR_MASK_0:
+ case PM4125_DIG_SWR_INTR_MASK_1:
+ case PM4125_DIG_SWR_INTR_MASK_2:
+ case PM4125_DIG_SWR_INTR_CLEAR_0:
+ case PM4125_DIG_SWR_INTR_CLEAR_1:
+ case PM4125_DIG_SWR_INTR_CLEAR_2:
+ case PM4125_DIG_SWR_INTR_LEVEL_0:
+ case PM4125_DIG_SWR_INTR_LEVEL_1:
+ case PM4125_DIG_SWR_INTR_LEVEL_2:
+ case PM4125_DIG_SWR_CDC_CONN_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_CONN_RX1_CTL:
+ case PM4125_DIG_SWR_LOOP_BACK_MODE:
+ case PM4125_DIG_SWR_DRIVE_STRENGTH_0:
+ case PM4125_DIG_SWR_DIG_DEBUG_CTL:
+ case PM4125_DIG_SWR_DIG_DEBUG_EN:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA0:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA1:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA2:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA3:
+ return true;
+ }
+
+ return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+ return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config pm4125_regmap_config = {
+ .name = "pm4125_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = pm4125_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pm4125_defaults),
+ .max_register = PM4125_MAX_REGISTER,
+ .readable_reg = pm4125_readable_register,
+ .writeable_reg = pm4125_rdwr_register,
+ .volatile_reg = pm4125_volatile_register,
+};
+
+static const struct sdw_slave_ops pm4125_slave_ops = {
+ .update_status = pm4125_update_status,
+ .interrupt_callback = pm4125_interrupt_callback,
+};
+
+static int pm4125_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static void pm4125_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct component_ops pm4125_sdw_component_ops = {
+ .bind = pm4125_sdw_component_bind,
+ .unbind = pm4125_sdw_component_unbind,
+};
+
+static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct pm4125_sdw_priv *priv;
+ u8 master_ch_mask[PM4125_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Port map index starts at 0, however the data port for this codec starts at index 1 */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ priv->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+ priv->is_tx ? "TX" : "RX", ret);
+
+ priv->sdev = pdev;
+ dev_set_drvdata(dev, priv);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+
+ memset(master_ch_mask, 0, PM4125_MAX_SWR_CH_IDS);
+
+ if (priv->is_tx) {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (priv->is_tx) {
+ pdev->prop.source_ports = GENMASK(PM4125_MAX_TX_SWR_PORTS, 0);
+ pdev->prop.src_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+
+ pdev->prop.wake_capable = true;
+
+ priv->regmap = devm_regmap_init_sdw(pdev, &pm4125_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap), "regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(priv->regmap, true);
+ } else {
+ pdev->prop.sink_ports = GENMASK(PM4125_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+ }
+
+ ret = component_add(dev, &pm4125_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int pm4125_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &pm4125_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id pm4125_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10c, 0), /* Soundwire pm4125 RX/TX Device ID */
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, pm4125_slave_id);
+
+static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, true);
+ regcache_mark_dirty(priv->regmap);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, false);
+ regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops pm4125_sdw_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm4125_sdw_runtime_suspend, pm4125_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .ops = &pm4125_slave_ops,
+ .id_table = pm4125_slave_id,
+ .driver = {
+ .name = "pm4125-codec",
+ .pm = &pm4125_sdw_pm_ops,
+ }
+};
+module_sdw_driver(pm4125_codec_driver);
+
+MODULE_DESCRIPTION("PM4125 SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
new file mode 100644
index 000000000000..706fc668ffe2
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1780 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright (c) 2025, Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.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/regulator/consumer.h>
+#include <linux/slab.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/tlv.h>
+
+#include "pm4125.h"
+#include "wcd-mbhc-v2.h"
+
+#define WCD_MBHC_HS_V_MAX 1600
+#define PM4125_MBHC_MAX_BUTTONS 8
+
+#define PM4125_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 PM4125_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define PM4125_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Registers in SPMI addr space */
+#define PM4125_CODEC_RESET_REG 0xF3DB
+#define PM4125_CODEC_OFF 0x1
+#define PM4125_CODEC_ON 0x0
+#define PM4125_CODEC_FOUNDRY_ID_REG 0x7
+
+enum {
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct pm4125_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct pm4125_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode;
+ struct device_node *txnode;
+ struct regmap *regmap;
+ struct regmap *spmi_regmap;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct irq_domain *virq;
+ const struct regmap_irq_chip *pm4125_regmap_irq_chip;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[PM4125_MAX_MICBIAS];
+ s32 pullup_ref[PM4125_MAX_MICBIAS];
+ u32 micb1_mv;
+ u32 micb2_mv;
+ u32 micb3_mv;
+
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+
+ atomic_t gloal_mbias_cnt;
+};
+
+static const char * const pm4125_power_supplies[] = {
+ "vdd-io", "vdd-cp", "vdd-mic-bias", "vdd-pa-vpos",
+};
+
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field pm4125_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, PM4125_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, PM4125_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, PM4125_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, PM4125_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, PM4125_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, PM4125_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, PM4125_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, PM4125_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, PM4125_ANA_MBHC_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, PM4125_ANA_MBHC_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, PM4125_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, PM4125_ANA_MBHC_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, PM4125_ANA_MBHC_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, PM4125_ANA_MBHC_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, PM4125_ANA_MBHC_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, PM4125_ANA_MBHC_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, PM4125_ANA_MBHC_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, PM4125_ANA_MBHC_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, PM4125_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq pm4125_irqs[PM4125_NUM_IRQS] = {
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_SW_DET, 0, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_CNP_INT, 1, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_SCD_INT, 1, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_CNP_INT, 1, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_SCD_INT, 1, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int pm4125_handle_post_irq(void *data)
+{
+ struct pm4125_priv *pm4125 = (struct pm4125_priv *)data;
+
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_0, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_1, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_2, 0);
+
+ return IRQ_HANDLED;
+}
+
+static const u32 pm4125_config_regs[] = {
+ PM4125_DIG_SWR_INTR_LEVEL_0,
+};
+
+static struct regmap_irq_chip pm4125_regmap_irq_chip = {
+ .name = "pm4125",
+ .irqs = pm4125_irqs,
+ .num_irqs = ARRAY_SIZE(pm4125_irqs),
+ .num_regs = 3,
+ .status_base = PM4125_DIG_SWR_INTR_STATUS_0,
+ .mask_base = PM4125_DIG_SWR_INTR_MASK_0,
+ .ack_base = PM4125_DIG_SWR_INTR_CLEAR_0,
+ .use_ack = 1,
+ .clear_ack = 1,
+ .config_base = pm4125_config_regs,
+ .num_config_bases = ARRAY_SIZE(pm4125_config_regs),
+ .num_config_regs = 1,
+ .runtime_pm = true,
+ .handle_post_irq = pm4125_handle_post_irq,
+};
+
+static void pm4125_reset(struct pm4125_priv *pm4125)
+{
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_OFF);
+ usleep_range(20, 30);
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_ON);
+ usleep_range(5000, 5010);
+}
+
+static void pm4125_io_init(struct regmap *regmap)
+{
+ /* Disable HPH OCP */
+ regmap_update_bits(regmap, PM4125_ANA_HPHPA_CNP_CTL_2,
+ PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK | PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK,
+ PM4125_ANA_HPHPA_CNP_OCP_DISABLE);
+
+ /* Enable surge protection */
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+
+ /* Disable mic bias 2 pull down */
+ regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN,
+ PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ FIELD_PREP(PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ PM4125_ANA_MICBIAS_MICB_PULL_DISABLE));
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_dec_and_test(&pm4125->gloal_mbias_cnt)) {
+
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ }
+
+ return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_inc_return(&pm4125->gloal_mbias_cnt) == 1) {
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+ pm4125_global_mbias_enable(component);
+
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ usleep_range(5000, 5100);
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_68);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE);
+ snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL, 0x07, 0x06);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_ENABLE);
+ usleep_range(500, 510);
+
+ return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ 0x00);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+
+ pm4125_global_mbias_disable(component);
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_rxclk(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:
+ pm4125_rx_clk_enable(component);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_rx_clk_disable(component);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphl_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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_LEFT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp1_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphr_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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_RIGHT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp2_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled
+ * as per HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_ear_lo_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_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_hphl_wdt_irq(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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_wdt_irq(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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_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:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX1_M | PM4125_WDT_ENABLE_RX1_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphl_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:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_lo_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, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_LO);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(2000, 2010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_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, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+ if (micb_mv < 1600 || micb_mv > 2850) {
+ dev_err(dev, "%s: unsupported micbias voltage (%u mV)\n", __func__, micb_mv);
+ return -EINVAL;
+ }
+
+ return (micb_mv - 1600) / 50;
+}
+
+static int pm4125_codec_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);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable BCS for Headset mic */
+ if (w->shift == 1 &&
+ !(snd_soc_component_read(component, PM4125_ANA_TX_AMIC2) & 0x10)) {
+ set_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+ }
+ pm4125_global_mbias_enable(component);
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask))
+ clear_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ 0x00);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ 0x00);
+ pm4125_global_mbias_disable(component);
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_codec_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);
+ u16 dmic_clk_reg = w->reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_DMIC1);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_AMIC3);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component, int micb_num, int req,
+ bool is_dapm)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ u8 pullup_mask = 0, enable_mask = 0;
+
+ if ((micb_index < 0) || (micb_index > PM4125_MAX_MICBIAS - 1)) {
+ dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK;
+ enable_mask = 0x40;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK;
+ enable_mask = 0x04;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_3_EN;
+ pullup_mask = 0x02;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ };
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ pm4125->pullup_ref[micb_index]++;
+ if ((pm4125->pullup_ref[micb_index] == 1) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (pm4125->pullup_ref[micb_index] > 0)
+ pm4125->pullup_ref[micb_index]--;
+ if ((pm4125->pullup_ref[micb_index] == 0) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, 0x00);
+ break;
+ case MICB_ENABLE:
+ pm4125->micb_ref[micb_index]++;
+ if (pm4125->micb_ref[micb_index] == 1) {
+ pm4125_global_mbias_enable(component);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, enable_mask);
+ }
+ break;
+ case MICB_DISABLE:
+ if (pm4125->micb_ref[micb_index] > 0)
+ pm4125->micb_ref[micb_index]--;
+ if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] > 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ } else if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] == 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ }
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias(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 micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias_pullup(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 micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_connect_port(struct pm4125_sdw_priv *sdw_priv, u8 port_idx, u8 ch_id, bool enable)
+{
+ struct sdw_port_config *port_config = &sdw_priv->port_config[port_idx - 1];
+ const struct pm4125_sdw_ch_info *ch_info = &sdw_priv->ch_info[ch_id];
+ struct sdw_slave *sdev = sdw_priv->sdev;
+ u8 port_num = ch_info->port_num;
+ u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+
+ port_config->num = port_num;
+
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
+ port_config->ch_mask |= ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
+ port_config->ch_mask &= ~ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ ucontrol->value.integer.value[0] = hphr ? pm4125->comp2_enable : pm4125->comp1_enable;
+ return 0;
+}
+
+static int pm4125_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[AIF1_PB];
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ int portidx;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr) {
+ if (value == pm4125->comp2_enable)
+ return 0;
+
+ pm4125->comp2_enable = value;
+ } else {
+ if (value == pm4125->comp1_enable)
+ return 0;
+
+ pm4125->comp1_enable = value;
+ }
+
+ portidx = sdw_priv->ch_info[mc->reg].port_num;
+
+ pm4125_connect_port(sdw_priv, portidx, mc->reg, value ? true : false);
+
+ return 1;
+}
+
+static int pm4125_get_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = sdw_priv->port_enable[portidx];
+
+ return 0;
+}
+
+static int pm4125_set_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+ bool enable;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ enable = ucontrol->value.integer.value[0];
+
+ if (enable == sdw_priv->port_enable[portidx]) {
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+ return 0;
+ }
+
+ sdw_priv->port_enable[portidx] = enable;
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+
+ return 1;
+}
+
+static void pm4125_mbhc_bias_control(struct snd_soc_component *component, bool enable)
+{
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_ELECT,
+ PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK,
+ enable ? PM4125_ANA_MBHC_ELECT_BIAS_ENABLE :
+ PM4125_ANA_MBHC_ELECT_BIAS_DISABLE);
+}
+
+static void pm4125_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+ PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK, vth << 2);
+ }
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .mbhc_bias = pm4125_mbhc_bias_control,
+ .set_btn_thr = pm4125_mbhc_program_btn_thr,
+};
+
+static int pm4125_mbhc_init(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &pm4125->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_MBHC_SW_DET);
+
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET);
+
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET);
+
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET);
+
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_OCP_INT);
+
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_OCP_INT);
+
+ pm4125->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, pm4125_mbhc_fields, false);
+ if (IS_ERR(pm4125->wcd_mbhc))
+ return PTR_ERR(pm4125->wcd_mbhc);
+
+ return 0;
+}
+
+static void pm4125_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(pm4125->wcd_mbhc);
+}
+
+static const struct snd_kcontrol_new pm4125_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", PM4125_COMP_L, 0, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", PM4125_COMP_R, 1, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+
+ SOC_SINGLE_TLV("HPHL Volume", PM4125_ANA_HPHPA_L_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", PM4125_ANA_HPHPA_R_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", PM4125_ANA_TX_AMIC1, 0, 8, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", PM4125_ANA_TX_AMIC2, 0, 8, 0,
+ analog_gain),
+
+ SOC_SINGLE_EXT("HPHL Switch", PM4125_HPH_L, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", PM4125_HPH_R, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+
+ SOC_SINGLE_EXT("ADC1 Switch", PM4125_ADC1, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", PM4125_ADC2, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(PM4125_ANA_TX_AMIC2, 4,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_soc_dapm_widget pm4125_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ /* TX mixers */
+ SND_SOC_DAPM_MIXER("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch)),
+ SND_SOC_DAPM_MIXER("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch)),
+
+ /* MIC_BIAS widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* RX widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LO PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_lo_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, 0,
+ pm4125_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, 0,
+ pm4125_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphl_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_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphr_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_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_ear_lo_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_SUPPLY("HPHL_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphl_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("HPHR_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphr_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+ /* RX mixer widgets */
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, ear_rdac_switch,
+ ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("LO_RDAC", SND_SOC_NOPM, 0, 0, lo_rdac_switch,
+ ARRAY_SIZE(lo_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, hphl_rdac_switch,
+ ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch,
+ ARRAY_SIZE(hphr_rdac_switch)),
+
+ /* TX output widgets */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+ /* RX output widgets */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("LO"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+ /* MIC_BIAS pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 0, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 1, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX mixer widgets */
+ SND_SOC_DAPM_MIXER("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+ ARRAY_SIZE(dmic1_switch)),
+ SND_SOC_DAPM_MIXER("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch,
+ ARRAY_SIZE(dmic2_switch)),
+
+ /* Output widgets */
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route pm4125_audio_map[] = {
+ { "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+ { "ADC1_MIXER", "Switch", "ADC1" },
+ { "ADC1", NULL, "AMIC1" },
+
+ { "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+ { "ADC2_MIXER", "Switch", "ADC2" },
+ { "ADC2", NULL, "ADC2 MUX" },
+ { "ADC2 MUX", "INP3", "AMIC3" },
+ { "ADC2 MUX", "INP2", "AMIC2" },
+
+ { "IN1_HPHL", NULL, "PA_VPOS" },
+ { "RX1", NULL, "IN1_HPHL" },
+ { "RX1", NULL, "RXCLK" },
+ { "RX1", NULL, "HPHL_WDT_IRQ" },
+ { "RDAC1", NULL, "RX1" },
+ { "HPHL_RDAC", "Switch", "RDAC1" },
+ { "HPHL PGA", NULL, "HPHL_RDAC" },
+ { "HPHL", NULL, "HPHL PGA" },
+
+ { "IN2_HPHR", NULL, "PA_VPOS" },
+ { "RX2", NULL, "IN2_HPHR" },
+ { "RX2", NULL, "RXCLK" },
+ { "RX2", NULL, "HPHR_WDT_IRQ" },
+ { "RDAC2", NULL, "RX2" },
+ { "HPHR_RDAC", "Switch", "RDAC2" },
+ { "HPHR PGA", NULL, "HPHR_RDAC" },
+ { "HPHR", NULL, "HPHR PGA" },
+
+ { "RDAC3", NULL, "RX1" },
+ { "EAR_RDAC", "Switch", "RDAC3" },
+ { "EAR PGA", NULL, "EAR_RDAC" },
+ { "EAR", NULL, "EAR PGA" },
+
+ { "LO_RDAC", "Switch", "RDAC3" },
+ { "LO PGA", NULL, "LO_RDAC" },
+ { "LO", NULL, "LO PGA" },
+
+ { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+ { "DMIC1_MIXER", "Switch", "DMIC1" },
+
+ { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+ { "DMIC2_MIXER", "Switch", "DMIC2" },
+};
+
+static int pm4125_set_micbias_data(struct device *dev, struct pm4125_priv *pm4125)
+{
+ int vout_ctl;
+
+ /* Set micbias voltage */
+ vout_ctl = pm4125_get_micb_vout_ctl_val(dev, pm4125->micb1_mv);
+ if (vout_ctl < 0)
+ return -EINVAL;
+
+ regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+ PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK, vout_ctl << 3);
+ return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+ /*
+ * HPHR/HPHL Watchdog interrupt threaded handler
+ * Watchdog interrupts are expected to be enabled when switching on the HPHL/R
+ * in order to make sure the interrupts are acked by the regmap_irq handler
+ * io allow PDM sync. We could leave those interrupts masked but we would
+ * not haveany valid way to enable/disable them without violating irq layers.
+ *
+ * The HPHR/HPHL Watchdog interrupts are handled by regmap_irq, so requesting
+ * a threaded handler is the safest way to be able to ack those interrupts
+ * without colliding with the regmap_irq setup.
+ */
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip pm4125_codec_irq_chip = {
+ .name = "pm4125_codec",
+};
+
+static int pm4125_codec_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &pm4125_codec_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm4125_domain_ops = {
+ .map = pm4125_codec_irq_chip_map,
+};
+
+static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
+{
+ pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL);
+ if (!(pm4125->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ pm4125_regmap_irq_chip.irq_drv_data = pm4125;
+
+ return devm_regmap_add_irq_chip(dev, pm4125->regmap, irq_create_mapping(pm4125->virq, 0),
+ IRQF_ONESHOT, 0, &pm4125_regmap_irq_chip,
+ &pm4125->irq_chip);
+}
+
+static int pm4125_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int i, ret;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(5000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, pm4125->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ pm4125_io_init(pm4125->regmap);
+
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < pm4125_regmap_irq_chip.num_regs; i++)
+ regmap_write(pm4125->regmap, (PM4125_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ pm4125->hphr_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_PDM_WD_INT);
+ pm4125->hphl_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_PDM_WD_INT);
+
+ /* Request for watchdog interrupts */
+ ret = devm_request_threaded_irq(dev, pm4125->hphr_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHR wdt interrupt: %d\n", ret);
+
+ ret = devm_request_threaded_irq(dev, pm4125->hphl_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHL wdt interrupt: %d\n", ret);
+
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+
+ ret = pm4125_mbhc_init(component);
+ if (ret)
+ dev_err(component->dev, "mbhc initialization failed\n");
+
+ return ret;
+}
+
+static void pm4125_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ pm4125_mbhc_deinit(component);
+ free_irq(pm4125->hphl_pdm_wd_int, pm4125);
+ free_irq(pm4125->hphr_pdm_wd_int, pm4125);
+}
+
+static int pm4125_codec_set_jack(struct snd_soc_component *comp, struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (jack)
+ ret = wcd_mbhc_start(pm4125->wcd_mbhc, &pm4125->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(pm4125->wcd_mbhc);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pm4125 = {
+ .name = "pm4125_codec",
+ .probe = pm4125_soc_codec_probe,
+ .remove = pm4125_soc_codec_remove,
+ .controls = pm4125_snd_controls,
+ .num_controls = ARRAY_SIZE(pm4125_snd_controls),
+ .dapm_widgets = pm4125_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm4125_dapm_widgets),
+ .dapm_routes = pm4125_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm4125_audio_map),
+ .set_jack = pm4125_codec_set_jack,
+ .endianness = 1,
+};
+
+static void pm4125_dt_parse_micbias_info(struct device *dev, struct pm4125_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 prop_val = 0;
+ int ret;
+
+ ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
+ if (!ret)
+ priv->micb1_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias1 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
+ if (!ret)
+ priv->micb2_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias2 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
+ if (!ret)
+ priv->micb3_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias3 DT property not found\n");
+}
+
+static int pm4125_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return pm4125_sdw_hw_params(sdw_priv, substream, params, dai);
+}
+
+static int pm4125_codec_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return sdw_stream_remove_slave(sdw_priv->sdev, sdw_priv->sruntime);
+}
+
+static int pm4125_codec_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ sdw_priv->sruntime = stream;
+
+ return 0;
+}
+
+static int pm4125_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pm4125_sdw_dai_ops = {
+ .hw_params = pm4125_codec_hw_params,
+ .hw_free = pm4125_codec_free,
+ .set_stream = pm4125_codec_set_sdw_stream,
+ .get_channel_map = pm4125_get_channel_map,
+};
+
+static struct snd_soc_dai_driver pm4125_dais[] = {
+ [0] = {
+ .name = "pm4125-sdw-rx",
+ .playback = {
+ .stream_name = "PM4125 AIF Playback",
+ .rates = PM4125_RATES | PM4125_FRAC_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "pm4125-sdw-tx",
+ .capture = {
+ .stream_name = "PM4125 AIF Capture",
+ .rates = PM4125_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+};
+
+static int pm4125_bind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+ struct device_link *devlink;
+ int ret;
+
+ /* Give the soundwire subdevices some more time to settle */
+ usleep_range(15000, 15010);
+
+ ret = component_bind_all(dev, pm4125);
+ if (ret) {
+ dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ pm4125->rxdev = pm4125_sdw_device_get(pm4125->rxnode);
+ if (!pm4125->rxdev) {
+ dev_err(dev, "could not find rxslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_PB] = dev_get_drvdata(pm4125->rxdev);
+ pm4125->sdw_priv[AIF1_PB]->pm4125 = pm4125;
+
+ pm4125->txdev = pm4125_sdw_device_get(pm4125->txnode);
+ if (!pm4125->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_CAP] = dev_get_drvdata(pm4125->txdev);
+ pm4125->sdw_priv[AIF1_CAP]->pm4125 = pm4125;
+
+ pm4125->tx_sdw_dev = dev_to_sdw_dev(pm4125->txdev);
+ if (!pm4125->tx_sdw_dev) {
+ dev_err(dev, "could not get txslave with matching of dev\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ /*
+ * As TX is the main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link
+ */
+ devlink = device_link_add(pm4125->rxdev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink TX and RX\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ devlink = device_link_add(dev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and TX\n");
+ ret = -EINVAL;
+ goto link_remove_rx_tx;
+ }
+
+ devlink = device_link_add(dev, pm4125->rxdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and RX\n");
+ ret = -EINVAL;
+ goto link_remove_dev_tx;
+ }
+
+ pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
+ if (!pm4125->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto link_remove_dev_rx;
+ }
+
+ ret = pm4125_irq_init(pm4125, dev);
+ if (ret) {
+ dev_err(dev, "IRQ init failed: %d\n", ret);
+ goto link_remove_dev_rx;
+ }
+
+ pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+ pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+ ret = pm4125_set_micbias_data(dev, pm4125);
+ if (ret < 0) {
+ dev_err(dev, "Bad micbias pdata\n");
+ goto link_remove_dev_rx;
+ }
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+ pm4125_dais, ARRAY_SIZE(pm4125_dais));
+ if (!ret)
+ return ret;
+
+ dev_err(dev, "Codec registration failed\n");
+
+link_remove_dev_rx:
+ device_link_remove(dev, pm4125->rxdev);
+link_remove_dev_tx:
+ device_link_remove(dev, pm4125->txdev);
+link_remove_rx_tx:
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+error_unbind_all:
+ component_unbind_all(dev, pm4125);
+ return ret;
+}
+
+static void pm4125_unbind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, pm4125->txdev);
+ device_link_remove(dev, pm4125->rxdev);
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+ component_unbind_all(dev, pm4125);
+}
+
+static const struct component_master_ops pm4125_comp_ops = {
+ .bind = pm4125_bind,
+ .unbind = pm4125_unbind,
+};
+
+static int pm4125_add_slave_components(struct pm4125_priv *pm4125, struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ pm4125->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!pm4125->rxnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,rx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->rxnode);
+
+ pm4125->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!pm4125->txnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,tx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->txnode);
+
+ return 0;
+}
+
+static int pm4125_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device *dev = &pdev->dev;
+ struct pm4125_priv *pm4125;
+ struct wcd_mbhc_config *cfg;
+ int ret;
+
+ pm4125 = devm_kzalloc(dev, sizeof(*pm4125), GFP_KERNEL);
+ if (!pm4125)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pm4125);
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(pm4125_power_supplies),
+ pm4125_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pm4125->spmi_regmap)
+ return -ENXIO;
+
+ pm4125_reset(pm4125);
+
+ pm4125_dt_parse_micbias_info(dev, pm4125);
+ atomic_set(&pm4125->gloal_mbias_cnt, 0);
+
+ cfg = &pm4125->mbhc_cfg;
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = PM4125_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = pm4125->micb2_mv;
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+ ret = pm4125_add_slave_components(pm4125, dev, &match);
+ if (ret)
+ return ret;
+
+ ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct of_device_id pm4125_of_match[] = {
+ { .compatible = "qcom,pm4125-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+
+static struct platform_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .driver = {
+ .name = "pm4125_codec",
+ .of_match_table = pm4125_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(pm4125_codec_driver);
+MODULE_DESCRIPTION("PM4125 audio codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
new file mode 100644
index 000000000000..3520c711b744
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _PM4125_REGISTERS_H
+#define _PM4125_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define PM4125_ANA_BASE_ADDR 0x3000
+#define PM4125_DIG_BASE_ADDR 0x3400
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN (PM4125_ANA_BASE_ADDR+0x040)
+#define PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK BIT(5)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK BIT(1)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK BIT(0)
+#define PM4125_ANA_MICBIAS_MICB_PULL_ENABLE 1
+#define PM4125_ANA_MICBIAS_MICB_PULL_DISABLE 0
+#define PM4125_ANA_MICBIAS_MICB_3_EN (PM4125_ANA_BASE_ADDR+0x041)
+#define PM4125_ANA_MICBIAS_LDO_1_SETTING (PM4125_ANA_BASE_ADDR+0x042)
+#define PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define PM4125_ANA_MICBIAS_LDO_1_CTRL (PM4125_ANA_BASE_ADDR+0x043)
+#define PM4125_ANA_TX_AMIC1 (PM4125_ANA_BASE_ADDR+0x047)
+#define PM4125_ANA_TX_AMIC2 (PM4125_ANA_BASE_ADDR+0x048)
+#define PM4125_ANA_MBHC_MECH (PM4125_ANA_BASE_ADDR+0x05A)
+#define PM4125_ANA_MBHC_ELECT (PM4125_ANA_BASE_ADDR+0x05B)
+#define PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK BIT(0)
+#define PM4125_ANA_MBHC_ELECT_BIAS_ENABLE 1
+#define PM4125_ANA_MBHC_ELECT_BIAS_DISABLE 0
+#define PM4125_ANA_MBHC_ZDET (PM4125_ANA_BASE_ADDR+0x05C)
+#define PM4125_ANA_MBHC_RESULT_1 (PM4125_ANA_BASE_ADDR+0x05D)
+#define PM4125_ANA_MBHC_RESULT_2 (PM4125_ANA_BASE_ADDR+0x05E)
+#define PM4125_ANA_MBHC_RESULT_3 (PM4125_ANA_BASE_ADDR+0x05F)
+#define PM4125_ANA_MBHC_BTN0_ZDET_VREF1 (PM4125_ANA_BASE_ADDR+0x060)
+#define PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK GENMASK(7, 2)
+#define PM4125_ANA_MBHC_BTN1_ZDET_VREF2 (PM4125_ANA_BASE_ADDR+0x061)
+#define PM4125_ANA_MBHC_BTN2_ZDET_VREF3 (PM4125_ANA_BASE_ADDR+0x062)
+#define PM4125_ANA_MBHC_BTN3_ZDET_DBG_400 (PM4125_ANA_BASE_ADDR+0x063)
+#define PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400 (PM4125_ANA_BASE_ADDR+0x064)
+#define PM4125_ANA_MBHC_MICB2_RAMP (PM4125_ANA_BASE_ADDR+0x065)
+#define PM4125_ANA_MBHC_CTL_1 (PM4125_ANA_BASE_ADDR+0x066)
+#define PM4125_ANA_MBHC_CTL_2 (PM4125_ANA_BASE_ADDR+0x067)
+#define PM4125_ANA_MBHC_PLUG_DETECT_CTL (PM4125_ANA_BASE_ADDR+0x068)
+#define PM4125_ANA_MBHC_ZDET_ANA_CTL (PM4125_ANA_BASE_ADDR+0x069)
+#define PM4125_ANA_MBHC_ZDET_RAMP_CTL (PM4125_ANA_BASE_ADDR+0x06A)
+#define PM4125_ANA_MBHC_FSM_STATUS (PM4125_ANA_BASE_ADDR+0x06B)
+#define PM4125_ANA_MBHC_ADC_RESULT (PM4125_ANA_BASE_ADDR+0x06C)
+#define PM4125_ANA_MBHC_CTL_CLK (PM4125_ANA_BASE_ADDR+0x06D)
+#define PM4125_ANA_MBHC_ZDET_CALIB_RESULT (PM4125_ANA_BASE_ADDR+0x072)
+#define PM4125_ANA_NCP_EN (PM4125_ANA_BASE_ADDR+0x077)
+#define PM4125_ANA_NCP_ENABLE_MASK BIT(0)
+#define PM4125_ANA_NCP_ENABLE 1
+#define PM4125_ANA_NCP_DISABLE 0
+#define PM4125_ANA_NCP_VCTRL (PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1 (PM4125_ANA_BASE_ADDR+0x083)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN 1
+#define PM4125_ANA_HPHPA_CNP_CTL_2 (PM4125_ANA_BASE_ADDR+0x084)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK BIT(0)
+#define PM4125_ANA_HPHPA_CNP_OCP_ENABLE 1
+#define PM4125_ANA_HPHPA_CNP_OCP_DISABLE 0
+#define PM4125_ANA_HPHPA_PA_STATUS (PM4125_ANA_BASE_ADDR+0x087)
+#define PM4125_ANA_HPHPA_FSM_CLK (PM4125_ANA_BASE_ADDR+0x088)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK BIT(7)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE 1
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE 0
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK GENMASK(6, 0)
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_68 (0x11)
+#define PM4125_ANA_HPHPA_L_GAIN (PM4125_ANA_BASE_ADDR+0x08B)
+#define PM4125_ANA_HPHPA_R_GAIN (PM4125_ANA_BASE_ADDR+0x08C)
+#define PM4125_ANA_HPHPA_SPARE_CTL (PM4125_ANA_BASE_ADDR+0x08E)
+#define PM4125_SWR_HPHPA_HD2 (PM4125_ANA_BASE_ADDR+0x090)
+#define PM4125_SWR_HPHPA_HD2_LEFT_MASK GENMASK(5, 3)
+#define PM4125_SWR_HPHPA_HD2_RIGHT_MASK GENMASK(2, 0)
+#define PM4125_SWR_HPHPA_HD2_ENABLE (BIT(2) | BIT(1) | BIT(0))
+#define PM4125_ANA_SURGE_EN (PM4125_ANA_BASE_ADDR+0x097)
+#define PM4125_ANA_SURGE_PROTECTION_HPHL_MASK BIT(7)
+#define PM4125_ANA_SURGE_PROTECTION_HPHR_MASK BIT(6)
+#define PM4125_ANA_SURGE_PROTECTION_ENABLE 1
+#define PM4125_ANA_SURGE_PROTECTION_DISABLE 0
+#define PM4125_ANA_COMBOPA_CTL (PM4125_ANA_BASE_ADDR+0x09B)
+#define PM4125_ANA_COMBO_PA_SELECT_MASK BIT(6)
+#define PM4125_ANA_COMBO_PA_SELECT_EAR 0
+#define PM4125_ANA_COMBO_PA_SELECT_LO 1
+#define PM4125_ANA_COMBOPA_CTL_4 (PM4125_ANA_BASE_ADDR+0x09F)
+#define PM4125_ANA_COMBOPA_CTL_5 (PM4125_ANA_BASE_ADDR+0x0A0)
+#define PM4125_ANA_RXLDO_CTL (PM4125_ANA_BASE_ADDR+0x0B2)
+#define PM4125_ANA_MBIAS_EN (PM4125_ANA_BASE_ADDR+0x0B4)
+#define PM4125_ANA_MBIAS_EN_GLOBAL_MASK BIT(5)
+#define PM4125_ANA_MBIAS_EN_V2I_MASK BIT(4)
+#define PM4125_ANA_MBIAS_EN_ENABLE 1
+#define PM4125_ANA_MBIAS_EN_DISABLE 0
+
+#define PM4125_DIG_SWR_CHIP_ID0 (PM4125_DIG_BASE_ADDR+0x001)
+#define PM4125_DIG_SWR_CHIP_ID1 (PM4125_DIG_BASE_ADDR+0x002)
+#define PM4125_DIG_SWR_CHIP_ID2 (PM4125_DIG_BASE_ADDR+0x003)
+#define PM4125_DIG_SWR_CHIP_ID3 (PM4125_DIG_BASE_ADDR+0x004)
+#define PM4125_DIG_SWR_SWR_TX_CLK_RATE (PM4125_DIG_BASE_ADDR+0x040)
+#define PM4125_DIG_SWR_CDC_RST_CTL (PM4125_DIG_BASE_ADDR+0x041)
+#define PM4125_DIG_SWR_TOP_CLK_CFG (PM4125_DIG_BASE_ADDR+0x042)
+#define PM4125_DIG_SWR_CDC_RX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x043)
+#define PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK BIT(5)
+#define PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK BIT(4)
+#define PM4125_DIG_SWR_RX1_CLK_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_RX0_CLK_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_RX_CLK_ENABLE 1
+#define PM4125_DIG_SWR_RX_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_TX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x044)
+#define PM4125_DIG_SWR_SWR_RST_EN (PM4125_DIG_BASE_ADDR+0x045)
+#define PM4125_DIG_SWR_CDC_RX_RST (PM4125_DIG_BASE_ADDR+0x047)
+#define PM4125_DIG_SWR_CDC_RX0_CTL (PM4125_DIG_BASE_ADDR+0x048)
+#define PM4125_DIG_SWR_DSM_DITHER_EN_MASK BIT(7)
+#define PM4125_DIG_SWR_DSM_DITHER_DISABLE 0
+#define PM4125_DIG_SWR_DSM_DITHER_ENABLE 1
+#define PM4125_DIG_SWR_CDC_RX1_CTL (PM4125_DIG_BASE_ADDR+0x049)
+#define PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1 (PM4125_DIG_BASE_ADDR+0x04B)
+#define PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK GENMASK(7, 4)
+#define PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK GENMASK(3, 0)
+#define PM4125_DIG_SWR_TXD_MODE_ULPI (0x9)
+#define PM4125_DIG_SWR_TXD_MODE_NORMAL (0x3)
+#define PM4125_DIG_SWR_CDC_COMP_CTL_0 (PM4125_DIG_BASE_ADDR+0x04F)
+#define PM4125_DIG_SWR_COMP_HPHL_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_COMP_HPHR_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_COMP_ENABLE 1
+#define PM4125_DIG_SWR_COMP_DISABLE 0
+#define PM4125_DIG_SWR_CDC_RX_DELAY_CTL (PM4125_DIG_BASE_ADDR+0x052)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_0 (PM4125_DIG_BASE_ADDR+0x053)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_1 (PM4125_DIG_BASE_ADDR+0x054)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_CTL (PM4125_DIG_BASE_ADDR+0x057)
+#define PM4125_DIG_SWR_RX1_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_RX0_EN_MASK BIT(2)
+#define PM4125_DIG_SWR_RX_INPUT_DISABLE 0
+#define PM4125_DIG_SWR_RX_INPUT_ENABLE 1
+#define PM4125_DIG_SWR_CDC_TX0_CTL (PM4125_DIG_BASE_ADDR+0x060)
+#define PM4125_DIG_SWR_CDC_TX1_CTL (PM4125_DIG_BASE_ADDR+0x061)
+#define PM4125_DIG_SWR_CDC_TX_RST (PM4125_DIG_BASE_ADDR+0x063)
+#define PM4125_DIG_SWR_CDC_REQ0_CTL (PM4125_DIG_BASE_ADDR+0x064)
+#define PM4125_DIG_SWR_CDC_REQ1_CTL (PM4125_DIG_BASE_ADDR+0x065)
+#define PM4125_DIG_SWR_CDC_RST (PM4125_DIG_BASE_ADDR+0x067)
+#define PM4125_DIG_SWR_CDC_AMIC_CTL (PM4125_DIG_BASE_ADDR+0x06A)
+#define PM4125_DIG_SWR_AMIC_SELECT_MASK BIT(1)
+#define PM4125_DIG_SWR_AMIC_SELECT_DMIC1 0
+#define PM4125_DIG_SWR_AMIC_SELECT_AMIC3 1
+#define PM4125_DIG_SWR_CDC_DMIC_CTL (PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL (PM4125_DIG_BASE_ADDR+0x06C)
+#define PM4125_DIG_SWR_DMIC1_CLK_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_DMIC1_CLK_ENABLE 1
+#define PM4125_DIG_SWR_DMIC1_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_DMIC1_RATE (PM4125_DIG_BASE_ADDR+0x06D)
+#define PM4125_DIG_SWR_PDM_WD_CTL0 (PM4125_DIG_BASE_ADDR+0x070)
+#define PM4125_WDT_ENABLE_MASK GENMASK(1, 0)
+#define PM4125_WDT_ENABLE_RX0_L BIT(0)
+#define PM4125_WDT_ENABLE_RX0_M BIT(1)
+#define PM4125_DIG_SWR_PDM_WD_CTL1 (PM4125_DIG_BASE_ADDR+0x071)
+#define PM4125_WDT_ENABLE_RX1_L BIT(0)
+#define PM4125_WDT_ENABLE_RX1_M BIT(1)
+#define PM4125_DIG_SWR_INTR_MODE (PM4125_DIG_BASE_ADDR+0x080)
+#define PM4125_DIG_SWR_INTR_MASK_0 (PM4125_DIG_BASE_ADDR+0x081)
+#define PM4125_DIG_SWR_INTR_MASK_1 (PM4125_DIG_BASE_ADDR+0x082)
+#define PM4125_DIG_SWR_INTR_MASK_2 (PM4125_DIG_BASE_ADDR+0x083)
+#define PM4125_DIG_SWR_INTR_STATUS_0 (PM4125_DIG_BASE_ADDR+0x084)
+#define PM4125_DIG_SWR_INTR_STATUS_1 (PM4125_DIG_BASE_ADDR+0x085)
+#define PM4125_DIG_SWR_INTR_STATUS_2 (PM4125_DIG_BASE_ADDR+0x086)
+#define PM4125_DIG_SWR_INTR_CLEAR_0 (PM4125_DIG_BASE_ADDR+0x087)
+#define PM4125_DIG_SWR_INTR_CLEAR_1 (PM4125_DIG_BASE_ADDR+0x088)
+#define PM4125_DIG_SWR_INTR_CLEAR_2 (PM4125_DIG_BASE_ADDR+0x089)
+#define PM4125_DIG_SWR_INTR_LEVEL_0 (PM4125_DIG_BASE_ADDR+0x08A)
+#define PM4125_DIG_SWR_INTR_LEVEL_1 (PM4125_DIG_BASE_ADDR+0x08B)
+#define PM4125_DIG_SWR_INTR_LEVEL_2 (PM4125_DIG_BASE_ADDR+0x08C)
+#define PM4125_DIG_SWR_CDC_CONN_RX0_CTL (PM4125_DIG_BASE_ADDR+0x093)
+#define PM4125_DIG_SWR_CDC_CONN_RX1_CTL (PM4125_DIG_BASE_ADDR+0x094)
+#define PM4125_DIG_SWR_LOOP_BACK_MODE (PM4125_DIG_BASE_ADDR+0x097)
+#define PM4125_DIG_SWR_DRIVE_STRENGTH_0 (PM4125_DIG_BASE_ADDR+0x0A0)
+#define PM4125_DIG_SWR_DIG_DEBUG_CTL (PM4125_DIG_BASE_ADDR+0x0AB)
+#define PM4125_DIG_SWR_DIG_DEBUG_EN (PM4125_DIG_BASE_ADDR+0x0AC)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA0 (PM4125_DIG_BASE_ADDR+0x0B0)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA1 (PM4125_DIG_BASE_ADDR+0x0B1)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA2 (PM4125_DIG_BASE_ADDR+0x0B2)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA3 (PM4125_DIG_BASE_ADDR+0x0B3)
+
+#define PM4125_ANALOG_REGISTERS_MAX_SIZE (PM4125_ANA_BASE_ADDR+0x0B5)
+#define PM4125_DIGITAL_REGISTERS_MAX_SIZE (PM4125_DIG_BASE_ADDR+0x0B4)
+#define PM4125_ANALOG_MAX_REGISTER (PM4125_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define PM4125_DIGITAL_MAX_REGISTER (PM4125_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#define PM4125_MAX_REGISTER PM4125_DIGITAL_MAX_REGISTER
+
+#define PM4125_MAX_MICBIAS 3
+#define PM4125_MAX_SWR_CH_IDS 15
+#define PM4125_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
+
+enum pm4125_tx_sdw_ports {
+ PM4125_ADC_1_2_DMIC1L_BCS_PORT = 1,
+ PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+ PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+};
+
+enum pm4125_rx_sdw_ports {
+ PM4125_HPH_PORT = 1,
+ PM4125_COMP_PORT,
+ PM4125_MAX_SWR_PORTS = PM4125_COMP_PORT,
+};
+
+struct pm4125_sdw_ch_info {
+ int port_num;
+ unsigned int ch_mask;
+ unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask) \
+ [id] = { \
+ .port_num = pn, \
+ .ch_mask = cmask, \
+ .master_ch_mask = cmask, \
+ }
+
+struct pm4125_priv;
+struct pm4125_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ struct pm4125_sdw_ch_info *ch_info;
+ bool port_enable[PM4125_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
+ int active_ports;
+ int num_ports;
+ bool is_tx;
+ struct pm4125_priv *pm4125;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_PM4125_SDW)
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125, struct snd_soc_dai *dai, void *stream,
+ int direction);
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
+
+struct device *pm4125_sdw_device_get(struct device_node *np);
+
+#else
+static inline int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+ struct snd_soc_dai *dai, void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ PM4125_IRQ_MBHC_SW_DET,
+ PM4125_IRQ_HPHR_OCP_INT,
+ PM4125_IRQ_HPHR_CNP_INT,
+ PM4125_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ PM4125_IRQ_HPHL_CNP_INT,
+ PM4125_IRQ_EAR_CNP_INT,
+ PM4125_IRQ_EAR_SCD_INT,
+ PM4125_IRQ_AUX_CNP_INT,
+ PM4125_IRQ_AUX_SCD_INT,
+ PM4125_IRQ_HPHL_PDM_WD_INT,
+ PM4125_IRQ_HPHR_PDM_WD_INT,
+ PM4125_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ PM4125_IRQ_LDORT_SCD_INT,
+ PM4125_IRQ_MBHC_MOISTURE_INT,
+ PM4125_IRQ_HPHL_SURGE_DET_INT,
+ PM4125_IRQ_HPHR_SURGE_DET_INT,
+ PM4125_NUM_IRQS,
+};
+
+enum pm4125_tx_sdw_channels {
+ PM4125_ADC1,
+ PM4125_ADC2,
+};
+
+enum pm4125_rx_sdw_channels {
+ PM4125_HPH_L,
+ PM4125_HPH_R,
+ PM4125_COMP_L,
+ PM4125_COMP_R,
+};
+
+#endif /* _PM4125_REGISTERS_H */
diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c
new file mode 100644
index 000000000000..8b51e87a1711
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.c
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip RK3308 internal audio codec driver
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2024, Vivax-Metrotech Ltd
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/util_macros.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rk3308_codec.h"
+
+#define ADC_LR_GROUP_MAX 4
+
+#define GRF_CHIP_ID 0x800
+
+enum {
+ ACODEC_VERSION_A = 'A',
+ ACODEC_VERSION_B,
+ ACODEC_VERSION_C,
+};
+
+struct rk3308_codec_priv {
+ const struct device *dev;
+ struct regmap *regmap;
+ struct regmap *grf;
+ struct reset_control *reset;
+ struct clk *hclk;
+ struct clk *mclk_rx;
+ struct clk *mclk_tx;
+ struct snd_soc_component *component;
+ unsigned char codec_ver;
+};
+
+static struct clk_bulk_data rk3308_codec_clocks[] = {
+ { .id = "hclk" },
+ { .id = "mclk_rx" },
+ { .id = "mclk_tx" },
+};
+
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, -3900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, -600, 600, 0);
+
+static const DECLARE_TLV_DB_RANGE(rk3308_codec_dac_lineout_gain_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-300, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-150, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
+);
+
+static const char * const rk3308_codec_hpf_cutoff_text[] = {
+ "20 Hz", "245 Hz", "612 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum12, RK3308_ADC_DIG_CON04(0), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum34, RK3308_ADC_DIG_CON04(1), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum56, RK3308_ADC_DIG_CON04(2), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum78, RK3308_ADC_DIG_CON04(3), 0,
+ rk3308_codec_hpf_cutoff_text);
+
+static const struct snd_kcontrol_new rk3308_codec_controls[] = {
+ /* Despite the register names, these set the gain when AGC is OFF */
+ SOC_SINGLE_RANGE_TLV("MIC1 Capture Volume",
+ RK3308_ADC_ANA_CON03(0),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC2 Capture Volume",
+ RK3308_ADC_ANA_CON04(0),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC3 Capture Volume",
+ RK3308_ADC_ANA_CON03(1),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC4 Capture Volume",
+ RK3308_ADC_ANA_CON04(1),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC5 Capture Volume",
+ RK3308_ADC_ANA_CON03(2),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC6 Capture Volume",
+ RK3308_ADC_ANA_CON04(2),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC7 Capture Volume",
+ RK3308_ADC_ANA_CON03(3),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC8 Capture Volume",
+ RK3308_ADC_ANA_CON04(3),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+
+ SOC_SINGLE("MIC1 Capture Switch", RK3308_ADC_ANA_CON00(0), 3, 1, 0),
+ SOC_SINGLE("MIC2 Capture Switch", RK3308_ADC_ANA_CON00(0), 7, 1, 0),
+ SOC_SINGLE("MIC3 Capture Switch", RK3308_ADC_ANA_CON00(1), 3, 1, 0),
+ SOC_SINGLE("MIC4 Capture Switch", RK3308_ADC_ANA_CON00(1), 7, 1, 0),
+ SOC_SINGLE("MIC5 Capture Switch", RK3308_ADC_ANA_CON00(2), 3, 1, 0),
+ SOC_SINGLE("MIC6 Capture Switch", RK3308_ADC_ANA_CON00(2), 7, 1, 0),
+ SOC_SINGLE("MIC7 Capture Switch", RK3308_ADC_ANA_CON00(3), 3, 1, 0),
+ SOC_SINGLE("MIC8 Capture Switch", RK3308_ADC_ANA_CON00(3), 7, 1, 0),
+
+ SOC_SINGLE("MIC12 HPF Capture Switch", RK3308_ADC_DIG_CON04(0), 2, 1, 1),
+ SOC_SINGLE("MIC34 HPF Capture Switch", RK3308_ADC_DIG_CON04(1), 2, 1, 1),
+ SOC_SINGLE("MIC56 HPF Capture Switch", RK3308_ADC_DIG_CON04(2), 2, 1, 1),
+ SOC_SINGLE("MIC78 HPF Capture Switch", RK3308_ADC_DIG_CON04(3), 2, 1, 1),
+
+ SOC_ENUM("MIC12 HPF Cutoff", rk3308_codec_hpf_cutoff_enum12),
+ SOC_ENUM("MIC34 HPF Cutoff", rk3308_codec_hpf_cutoff_enum34),
+ SOC_ENUM("MIC56 HPF Cutoff", rk3308_codec_hpf_cutoff_enum56),
+ SOC_ENUM("MIC78 HPF Cutoff", rk3308_codec_hpf_cutoff_enum78),
+
+ SOC_DOUBLE_TLV("Line Out Playback Volume",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_GAIN_SFT,
+ RK3308_DAC_R_LINEOUT_GAIN_SFT,
+ RK3308_DAC_x_LINEOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_lineout_gain_tlv),
+ SOC_DOUBLE("Line Out Playback Switch",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_MUTE_SFT,
+ RK3308_DAC_R_LINEOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+ RK3308_DAC_ANA_CON05,
+ RK3308_DAC_ANA_CON06,
+ RK3308_DAC_x_HPOUT_GAIN_SFT,
+ RK3308_DAC_x_HPOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_hpout_gain_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ RK3308_DAC_ANA_CON03,
+ RK3308_DAC_L_HPOUT_MUTE_SFT,
+ RK3308_DAC_R_HPOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_RANGE_TLV("DAC HPMIX Playback Volume",
+ RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_SFT,
+ RK3308_DAC_R_HPMIX_GAIN_SFT,
+ 1, 2, 0, rk3308_codec_dac_hpmix_gain_tlv),
+};
+
+static int rk3308_codec_pop_sound_set(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 rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ unsigned int val = (event == SND_SOC_DAPM_POST_PMU) ?
+ RK3308_DAC_HPOUT_POP_SOUND_x_WORK :
+ RK3308_DAC_HPOUT_POP_SOUND_x_INIT;
+ unsigned int mask = RK3308_DAC_HPOUT_POP_SOUND_x_MSK;
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01,
+ mask << w->shift, val << w->shift);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rk3308_codec_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_INPUT("MIC5"),
+ SND_SOC_DAPM_INPUT("MIC6"),
+ SND_SOC_DAPM_INPUT("MIC7"),
+ SND_SOC_DAPM_INPUT("MIC8"),
+
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN12", RK3308_ADC_ANA_CON06(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN34", RK3308_ADC_ANA_CON06(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN56", RK3308_ADC_ANA_CON06(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN78", RK3308_ADC_ANA_CON06(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_EN", RK3308_ADC_ANA_CON00(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_EN", RK3308_ADC_ANA_CON00(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_EN", RK3308_ADC_ANA_CON00(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_EN", RK3308_ADC_ANA_CON00(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_EN", RK3308_ADC_ANA_CON00(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_EN", RK3308_ADC_ANA_CON00(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_EN", RK3308_ADC_ANA_CON00(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_EN", RK3308_ADC_ANA_CON00(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_WORK", RK3308_ADC_ANA_CON00(0), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_WORK", RK3308_ADC_ANA_CON00(0), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_WORK", RK3308_ADC_ANA_CON00(1), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_WORK", RK3308_ADC_ANA_CON00(1), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_WORK", RK3308_ADC_ANA_CON00(2), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_WORK", RK3308_ADC_ANA_CON00(2), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_WORK", RK3308_ADC_ANA_CON00(3), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_WORK", RK3308_ADC_ANA_CON00(3), 6, 1, 1, 0),
+
+ /*
+ * In theory MIC1 and MIC2 can switch to LINE IN, but this is not
+ * supported so all we can do is enabling the MIC input.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH1_IN_SEL", RK3308_ADC_ANA_CON07(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH2_IN_SEL", RK3308_ADC_ANA_CON07(0), 6, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC_MCLK_GATE", RK3308_GLB_CON, 5, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_CLK_EN", RK3308_ADC_ANA_CON05(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_CLK_EN", RK3308_ADC_ANA_CON05(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_CLK_EN", RK3308_ADC_ANA_CON05(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_CLK_EN", RK3308_ADC_ANA_CON05(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_CLK_EN", RK3308_ADC_ANA_CON05(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_CLK_EN", RK3308_ADC_ANA_CON05(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_CLK_EN", RK3308_ADC_ANA_CON05(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_CLK_EN", RK3308_ADC_ANA_CON05(3), 4, 0, NULL, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_EN", RK3308_ADC_ANA_CON02(0), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_EN", RK3308_ADC_ANA_CON02(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_EN", RK3308_ADC_ANA_CON02(1), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_EN", RK3308_ADC_ANA_CON02(1), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_EN", RK3308_ADC_ANA_CON02(2), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_EN", RK3308_ADC_ANA_CON02(2), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_EN", RK3308_ADC_ANA_CON02(3), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_EN", RK3308_ADC_ANA_CON02(3), 4, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1_EN", RK3308_ADC_ANA_CON05(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2_EN", RK3308_ADC_ANA_CON05(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC3_EN", RK3308_ADC_ANA_CON05(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC4_EN", RK3308_ADC_ANA_CON05(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC5_EN", RK3308_ADC_ANA_CON05(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC6_EN", RK3308_ADC_ANA_CON05(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC7_EN", RK3308_ADC_ANA_CON05(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC8_EN", RK3308_ADC_ANA_CON05(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC1_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 2, 0),
+ SND_SOC_DAPM_ADC("ADC2_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 6, 0),
+ SND_SOC_DAPM_ADC("ADC3_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 2, 0),
+ SND_SOC_DAPM_ADC("ADC4_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 6, 0),
+ SND_SOC_DAPM_ADC("ADC5_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 2, 0),
+ SND_SOC_DAPM_ADC("ADC6_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 6, 0),
+ SND_SOC_DAPM_ADC("ADC7_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 2, 0),
+ SND_SOC_DAPM_ADC("ADC8_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 6, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_WORK", RK3308_ADC_ANA_CON02(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_WORK", RK3308_ADC_ANA_CON02(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_WORK", RK3308_ADC_ANA_CON02(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_WORK", RK3308_ADC_ANA_CON02(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_WORK", RK3308_ADC_ANA_CON02(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_WORK", RK3308_ADC_ANA_CON02(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_WORK", RK3308_ADC_ANA_CON02(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_WORK", RK3308_ADC_ANA_CON02(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS Current", RK3308_ADC_ANA_CON08(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RK3308_ADC_ANA_CON07(1), 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RK3308_ADC_ANA_CON07(2), 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_EN", RK3308_DAC_ANA_CON13, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_EN", RK3308_DAC_ANA_CON13, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_WORK", RK3308_DAC_ANA_CON13, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_WORK", RK3308_DAC_ANA_CON13, 5, 0, NULL, 0),
+ /* HPMIX is not actually acting as a mixer as the only supported input is I2S */
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_SEL", RK3308_DAC_ANA_CON12, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_SEL", RK3308_DAC_ANA_CON12, 6, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Left", RK3308_DAC_ANA_CON13, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Right", RK3308_DAC_ANA_CON13, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_MCLK_GATE", RK3308_GLB_CON, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_CURRENT_EN", RK3308_DAC_ANA_CON00, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_REF_EN", RK3308_DAC_ANA_CON02, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_REF_EN", RK3308_DAC_ANA_CON02, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_CLK_EN", RK3308_DAC_ANA_CON02, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_CLK_EN", RK3308_DAC_ANA_CON02, 5, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC_L_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 3, 0),
+ SND_SOC_DAPM_DAC("DAC_R_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 7, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_L", RK3308_DAC_ANA_CON01, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_R", RK3308_DAC_ANA_CON01, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_L", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_R", SND_SOC_NOPM, 4, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_EN", RK3308_DAC_ANA_CON03, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_EN", RK3308_DAC_ANA_CON03, 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_WORK", RK3308_DAC_ANA_CON03, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_WORK", RK3308_DAC_ANA_CON03, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUT_L"),
+ SND_SOC_DAPM_OUTPUT("HPOUT_R"),
+
+ SND_SOC_DAPM_OUT_DRV("L_LINEOUT_EN", RK3308_DAC_ANA_CON04, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_LINEOUT_EN", RK3308_DAC_ANA_CON04, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_L"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_R"),
+};
+
+static const struct snd_soc_dapm_route rk3308_codec_dapm_routes[] = {
+ { "MICBIAS1", NULL, "MICBIAS Current" },
+ { "MICBIAS2", NULL, "MICBIAS Current" },
+
+ { "MIC1_EN", NULL, "MIC1" },
+ { "MIC2_EN", NULL, "MIC2" },
+ { "MIC3_EN", NULL, "MIC3" },
+ { "MIC4_EN", NULL, "MIC4" },
+ { "MIC5_EN", NULL, "MIC5" },
+ { "MIC6_EN", NULL, "MIC6" },
+ { "MIC7_EN", NULL, "MIC7" },
+ { "MIC8_EN", NULL, "MIC8" },
+
+ { "MIC1_WORK", NULL, "MIC1_EN" },
+ { "MIC2_WORK", NULL, "MIC2_EN" },
+ { "MIC3_WORK", NULL, "MIC3_EN" },
+ { "MIC4_WORK", NULL, "MIC4_EN" },
+ { "MIC5_WORK", NULL, "MIC5_EN" },
+ { "MIC6_WORK", NULL, "MIC6_EN" },
+ { "MIC7_WORK", NULL, "MIC7_EN" },
+ { "MIC8_WORK", NULL, "MIC8_EN" },
+
+ { "CH1_IN_SEL", NULL, "MIC1_WORK" },
+ { "CH2_IN_SEL", NULL, "MIC2_WORK" },
+
+ { "ALC1_EN", NULL, "CH1_IN_SEL" },
+ { "ALC2_EN", NULL, "CH2_IN_SEL" },
+ { "ALC3_EN", NULL, "MIC3_WORK" },
+ { "ALC4_EN", NULL, "MIC4_WORK" },
+ { "ALC5_EN", NULL, "MIC5_WORK" },
+ { "ALC6_EN", NULL, "MIC6_WORK" },
+ { "ALC7_EN", NULL, "MIC7_WORK" },
+ { "ALC8_EN", NULL, "MIC8_WORK" },
+
+ { "ADC1_EN", NULL, "ALC1_EN" },
+ { "ADC2_EN", NULL, "ALC2_EN" },
+ { "ADC3_EN", NULL, "ALC3_EN" },
+ { "ADC4_EN", NULL, "ALC4_EN" },
+ { "ADC5_EN", NULL, "ALC5_EN" },
+ { "ADC6_EN", NULL, "ALC6_EN" },
+ { "ADC7_EN", NULL, "ALC7_EN" },
+ { "ADC8_EN", NULL, "ALC8_EN" },
+
+ { "ADC1_WORK", NULL, "ADC1_EN" },
+ { "ADC2_WORK", NULL, "ADC2_EN" },
+ { "ADC3_WORK", NULL, "ADC3_EN" },
+ { "ADC4_WORK", NULL, "ADC4_EN" },
+ { "ADC5_WORK", NULL, "ADC5_EN" },
+ { "ADC6_WORK", NULL, "ADC6_EN" },
+ { "ADC7_WORK", NULL, "ADC7_EN" },
+ { "ADC8_WORK", NULL, "ADC8_EN" },
+
+ { "ADC1_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC2_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC3_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC4_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC5_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC6_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC7_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+ { "ADC8_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+
+ { "ADC1_WORK", NULL, "ADC1_BUF_REF_EN" },
+ { "ADC2_WORK", NULL, "ADC2_BUF_REF_EN" },
+ { "ADC3_WORK", NULL, "ADC3_BUF_REF_EN" },
+ { "ADC4_WORK", NULL, "ADC4_BUF_REF_EN" },
+ { "ADC5_WORK", NULL, "ADC5_BUF_REF_EN" },
+ { "ADC6_WORK", NULL, "ADC6_BUF_REF_EN" },
+ { "ADC7_WORK", NULL, "ADC7_BUF_REF_EN" },
+ { "ADC8_WORK", NULL, "ADC8_BUF_REF_EN" },
+
+ { "ADC1_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC2_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC3_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC4_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC5_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC6_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC7_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC8_CLK_EN", NULL, "ADC_MCLK_GATE" },
+
+ { "ADC1_WORK", NULL, "ADC1_CLK_EN" },
+ { "ADC2_WORK", NULL, "ADC2_CLK_EN" },
+ { "ADC3_WORK", NULL, "ADC3_CLK_EN" },
+ { "ADC4_WORK", NULL, "ADC4_CLK_EN" },
+ { "ADC5_WORK", NULL, "ADC5_CLK_EN" },
+ { "ADC6_WORK", NULL, "ADC6_CLK_EN" },
+ { "ADC7_WORK", NULL, "ADC7_CLK_EN" },
+ { "ADC8_WORK", NULL, "ADC8_CLK_EN" },
+
+ { "ALC1_WORK", NULL, "ADC1_WORK" },
+ { "ALC2_WORK", NULL, "ADC2_WORK" },
+ { "ALC3_WORK", NULL, "ADC3_WORK" },
+ { "ALC4_WORK", NULL, "ADC4_WORK" },
+ { "ALC5_WORK", NULL, "ADC5_WORK" },
+ { "ALC6_WORK", NULL, "ADC6_WORK" },
+ { "ALC7_WORK", NULL, "ADC7_WORK" },
+ { "ALC8_WORK", NULL, "ADC8_WORK" },
+
+ { "HiFi Capture", NULL, "ALC1_WORK" },
+ { "HiFi Capture", NULL, "ALC2_WORK" },
+ { "HiFi Capture", NULL, "ALC3_WORK" },
+ { "HiFi Capture", NULL, "ALC4_WORK" },
+ { "HiFi Capture", NULL, "ALC5_WORK" },
+ { "HiFi Capture", NULL, "ALC6_WORK" },
+ { "HiFi Capture", NULL, "ALC7_WORK" },
+ { "HiFi Capture", NULL, "ALC8_WORK" },
+
+ { "DAC_L_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_R_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_L_HPMIX_WORK", NULL, "DAC_L_HPMIX_EN" },
+ { "DAC_R_HPMIX_WORK", NULL, "DAC_R_HPMIX_EN" },
+ { "DAC HPMIX Left", NULL, "DAC_L_HPMIX_WORK" },
+ { "DAC HPMIX Right", NULL, "DAC_R_HPMIX_WORK" },
+
+ { "DAC_L_DAC_WORK", NULL, "DAC HPMIX Left" },
+ { "DAC_R_DAC_WORK", NULL, "DAC HPMIX Right" },
+
+ { "DAC_L_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_R_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_L_REF_EN" },
+ { "DAC_R_CLK_EN", NULL, "DAC_R_REF_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_R_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_L_DAC_WORK", NULL, "DAC_L_CLK_EN" },
+ { "DAC_R_DAC_WORK", NULL, "DAC_R_CLK_EN" },
+ { "DAC_L_HPMIX_SEL", NULL, "DAC_L_DAC_WORK" },
+ { "DAC_R_HPMIX_SEL", NULL, "DAC_R_DAC_WORK" },
+
+ { "HPOUT_L", NULL, "DAC_BUF_REF_L" },
+ { "HPOUT_R", NULL, "DAC_BUF_REF_R" },
+ { "L_HPOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_HPOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "L_HPOUT_WORK", NULL, "L_HPOUT_EN" },
+ { "R_HPOUT_WORK", NULL, "R_HPOUT_EN" },
+ { "HPOUT_POP_SOUND_L", NULL, "L_HPOUT_WORK" },
+ { "HPOUT_POP_SOUND_R", NULL, "R_HPOUT_WORK" },
+ { "HPOUT_L", NULL, "HPOUT_POP_SOUND_L" },
+ { "HPOUT_R", NULL, "HPOUT_POP_SOUND_R" },
+
+ { "L_LINEOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_LINEOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "LINEOUT_L", NULL, "L_LINEOUT_EN" },
+ { "LINEOUT_R", NULL, "R_LINEOUT_EN" },
+};
+
+static int rk3308_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ const unsigned int inv_bits = fmt & SND_SOC_DAIFMT_INV_MASK;
+ const bool inv_bitclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_IB_NF);
+ const bool inv_frmclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_NB_IF);
+ const unsigned int dac_master_bits = rk3308->codec_ver < ACODEC_VERSION_C ?
+ RK3308_DAC_IO_MODE_MASTER | RK3308_DAC_MODE_MASTER :
+ RK3308BS_DAC_IO_MODE_MASTER | RK3308BS_DAC_MODE_MASTER;
+ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0;
+ bool is_master = false;
+ int grp;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adc_aif2 |= RK3308_ADC_IO_MODE_MASTER;
+ adc_aif2 |= RK3308_ADC_MODE_MASTER;
+ dac_aif2 |= dac_master_bits;
+ is_master = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_PCM;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_I2S;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_RJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_LJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inv_bitclk) {
+ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL;
+ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL;
+ }
+
+ if (inv_frmclk) {
+ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL;
+ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL;
+ }
+
+ /*
+ * Hold ADC Digital registers start at master mode
+ *
+ * There are 8 ADCs which use the same internal SCLK and LRCK for
+ * master mode. We need to make sure that they are in effect at the
+ * same time, otherwise they will cause abnormal clocks.
+ */
+ if (is_master)
+ regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_LRC_POL_REVERSAL |
+ RK3308_ADC_I2S_MODE_MSK,
+ adc_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp),
+ RK3308_ADC_IO_MODE_MASTER |
+ RK3308_ADC_MODE_MASTER |
+ RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL,
+ adc_aif2);
+ }
+
+ /* Hold ADC Digital registers end at master mode */
+ if (is_master)
+ regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_LRC_POL_REVERSAL |
+ RK3308_DAC_I2S_MODE_MSK,
+ dac_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02,
+ dac_master_bits | RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL,
+ dac_aif2);
+
+ return 0;
+}
+
+static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int dac_aif1 = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_VALID_LEN_MSK, dac_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, RK3308_DAC_I2S_WORK);
+
+ return 0;
+}
+
+static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int adc_aif1 = 0;
+ /*
+ * grp 0 = ADC1 and ADC2
+ * grp 1 = ADC3 and ADC4
+ * grp 2 = ADC5 and ADC6
+ * grp 3 = ADC7 and ADC8
+ */
+ u32 used_adc_grps;
+ int grp;
+
+ switch (params_channels(params)) {
+ case 1:
+ adc_aif1 |= RK3308_ADC_I2S_MONO;
+ used_adc_grps = 1;
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ used_adc_grps = params_channels(params) / 2;
+ break;
+ default:
+ dev_err(rk3308->dev, "Invalid channel number %d\n", params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (grp = 0; grp < used_adc_grps; grp++) {
+ regmap_update_bits(rk3308->regmap,
+ RK3308_ADC_DIG_CON03(grp),
+ RK3308_ADC_L_CH_BIST_MSK | RK3308_ADC_R_CH_BIST_MSK,
+ RK3308_ADC_L_CH_NORMAL_LEFT | RK3308_ADC_R_CH_NORMAL_RIGHT);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_VALID_LEN_MSK | RK3308_ADC_I2S_MONO, adc_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), RK3308_ADC_I2S_WORK);
+ }
+
+ return 0;
+}
+
+static int rk3308_codec_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 rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ return (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ rk3308_codec_dac_dig_config(rk3308, params) :
+ rk3308_codec_adc_dig_config(rk3308, params);
+}
+
+static const struct snd_soc_dai_ops rk3308_codec_dai_ops = {
+ .hw_params = rk3308_codec_hw_params,
+ .set_fmt = rk3308_codec_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rk3308_codec_dai_driver = {
+ .name = "rk3308-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .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 = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &rk3308_codec_dai_ops,
+};
+
+static void rk3308_codec_reset(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ reset_control_assert(rk3308->reset);
+ usleep_range(10000, 11000); /* estimated value */
+ reset_control_deassert(rk3308->reset);
+
+ regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00);
+ usleep_range(10000, 11000); /* estimated value */
+ regmap_write(rk3308->regmap, RK3308_GLB_CON,
+ RK3308_SYS_WORK |
+ RK3308_DAC_DIG_WORK |
+ RK3308_ADC_DIG_WORK);
+}
+
+/*
+ * Initialize register whose default after HW reset is problematic or which
+ * are never modified.
+ */
+static int rk3308_codec_initialize(struct rk3308_codec_priv *rk3308)
+{
+ int grp;
+
+ /*
+ * Init ADC digital vol to 0 dB (reset value is 0xff, undocumented).
+ * Range: -97dB ~ +32dB.
+ */
+ if (rk3308->codec_ver == ACODEC_VERSION_C) {
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON05(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON06(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ }
+ }
+
+ /* set HPMIX default gains (reset value is 0, which is illegal) */
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_MSK |
+ RK3308_DAC_R_HPMIX_GAIN_MSK,
+ RK3308_DAC_L_HPMIX_GAIN_NDB_6 |
+ RK3308_DAC_R_HPMIX_GAIN_NDB_6);
+
+ /* recover DAC digital gain to 0 dB (reset value is 0xff, undocumented) */
+ if (rk3308->codec_ver == ACODEC_VERSION_C)
+ regmap_write(rk3308->regmap, RK3308_DAC_DIG_CON04,
+ RK3308BS_DAC_DIG_GAIN_0DB);
+
+ /*
+ * Unconditionally enable zero-cross detection (needed for AGC,
+ * harmless without AGC)
+ */
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++)
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp),
+ RK3308_ADC_CH1_ZEROCROSS_DET_EN |
+ RK3308_ADC_CH2_ZEROCROSS_DET_EN);
+
+ return 0;
+}
+
+static int rk3308_codec_probe(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ rk3308->component = component;
+
+ rk3308_codec_reset(component);
+ rk3308_codec_initialize(rk3308);
+
+ return 0;
+}
+
+static int rk3308_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rk3308_codec_priv *rk3308 = 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)
+ break;
+
+ /* Sequence from TRM Section 8.6.3 "Power Up" */
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 0x7f);
+ msleep(20); /* estimated value */
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Sequence from TRM Section 8.6.4 "Power Down" */
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ msleep(20); /* estimated value */
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_component_driver rk3308_codec_component_driver = {
+ .probe = rk3308_codec_probe,
+ .set_bias_level = rk3308_codec_set_bias_level,
+ .controls = rk3308_codec_controls,
+ .num_controls = ARRAY_SIZE(rk3308_codec_controls),
+ .dapm_widgets = rk3308_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk3308_codec_dapm_widgets),
+ .dapm_routes = rk3308_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk3308_codec_dapm_routes),
+};
+
+static const struct regmap_config rk3308_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = RK3308_DAC_ANA_CON15,
+};
+
+static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308)
+{
+ unsigned int chip_id;
+ int err;
+
+ err = regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id);
+ if (err)
+ return err;
+
+ switch (chip_id) {
+ case 3306:
+ rk3308->codec_ver = ACODEC_VERSION_A;
+ break;
+ case 0x3308:
+ rk3308->codec_ver = ACODEC_VERSION_B;
+ return dev_err_probe(rk3308->dev, -EINVAL, "Chip version B not supported\n");
+ case 0x3308c:
+ rk3308->codec_ver = ACODEC_VERSION_C;
+ break;
+ default:
+ return dev_err_probe(rk3308->dev, -EINVAL, "Unknown chip_id: 0x%x\n", chip_id);
+ }
+
+ dev_info(rk3308->dev, "Found codec version %c\n", rk3308->codec_ver);
+ return 0;
+}
+
+static int rk3308_codec_set_micbias_level(struct rk3308_codec_priv *rk3308)
+{
+ struct device_node *np = rk3308->dev->of_node;
+ u32 percent;
+ u32 mult;
+ int err;
+
+ err = of_property_read_u32(np, "rockchip,micbias-avdd-percent", &percent);
+ if (err == -EINVAL)
+ return 0;
+ if (err)
+ return dev_err_probe(rk3308->dev, err,
+ "Error reading 'rockchip,micbias-avdd-percent'\n");
+
+ /* Convert percent to register value, linerarly (50% -> 0, 5% step = +1) */
+ mult = (percent - 50) / 5;
+
+ /* Check range and that the percent was an exact value allowed */
+ if (mult > RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX || mult * 5 + 50 != percent)
+ return dev_err_probe(rk3308->dev, -EINVAL,
+ "Invalid value %u for 'rockchip,micbias-avdd-percent'\n",
+ percent);
+
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0),
+ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK,
+ mult << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT);
+
+ return 0;
+}
+
+static int rk3308_codec_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct rk3308_codec_priv *rk3308;
+ void __iomem *base;
+ int err;
+
+ rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL);
+ if (!rk3308)
+ return -ENOMEM;
+
+ rk3308->dev = dev;
+
+ rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(rk3308->grf))
+ return dev_err_probe(dev, PTR_ERR(rk3308->grf), "Error getting GRF\n");
+
+ rk3308->reset = devm_reset_control_get_optional_exclusive(dev, "codec");
+ if (IS_ERR(rk3308->reset))
+ return dev_err_probe(dev, PTR_ERR(rk3308->reset), "Failed to get reset control\n");
+
+ err = devm_clk_bulk_get(dev, ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to get clocks\n");
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to enable clocks\n");
+
+ err = rk3308_codec_get_version(rk3308);
+ if (err)
+ return err;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rk3308->regmap = devm_regmap_init_mmio(dev, base, &rk3308_codec_regmap_config);
+ if (IS_ERR(rk3308->regmap))
+ return dev_err_probe(dev, PTR_ERR(rk3308->regmap),
+ "Failed to init regmap\n");
+
+ platform_set_drvdata(pdev, rk3308);
+
+ err = rk3308_codec_set_micbias_level(rk3308);
+ if (err)
+ return err;
+
+ err = devm_snd_soc_register_component(dev, &rk3308_codec_component_driver,
+ &rk3308_codec_dai_driver, 1);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register codec\n");
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused rk3308_codec_of_match[] = {
+ { .compatible = "rockchip,rk3308-codec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk3308_codec_of_match);
+
+static struct platform_driver rk3308_codec_driver = {
+ .driver = {
+ .name = "rk3308-acodec",
+ .of_match_table = rk3308_codec_of_match,
+ },
+ .probe = rk3308_codec_platform_probe,
+};
+module_platform_driver(rk3308_codec_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("ASoC RK3308 Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h
new file mode 100644
index 000000000000..a4226b235ab7
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.h
@@ -0,0 +1,579 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Rockchip RK3308 internal audio codec driver -- register definitions
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2022, Vivax-Metrotech Ltd
+ */
+
+#ifndef __RK3308_CODEC_H__
+#define __RK3308_CODEC_H__
+
+#define RK3308_GLB_CON 0x00
+
+/* ADC DIGITAL REGISTERS */
+
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_DIG_OFFSET(ch) (((ch) & 0x3) * 0xc0 + 0x0)
+
+#define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x14) // ver.C only
+#define RK3308_ADC_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x18) // ver.C only
+#define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x1c)
+
+#define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x40)
+#define RK3308_ALC_L_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x44)
+#define RK3308_ALC_L_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x48)
+#define RK3308_ALC_L_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x4c)
+#define RK3308_ALC_L_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x50)
+#define RK3308_ALC_L_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x54)
+#define RK3308_ALC_L_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x58)
+#define RK3308_ALC_L_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x5c)
+#define RK3308_ALC_L_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x60)
+#define RK3308_ALC_L_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x64)
+#define RK3308_ALC_L_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x70)
+
+#define RK3308_ALC_R_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x80)
+#define RK3308_ALC_R_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x84)
+#define RK3308_ALC_R_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x88)
+#define RK3308_ALC_R_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x8c)
+#define RK3308_ALC_R_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x90)
+#define RK3308_ALC_R_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x94)
+#define RK3308_ALC_R_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x98)
+#define RK3308_ALC_R_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x9c)
+#define RK3308_ALC_R_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa0)
+#define RK3308_ALC_R_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa4)
+#define RK3308_ALC_R_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xb0)
+
+/* DAC DIGITAL REGISTERS */
+#define RK3308_DAC_DIG_OFFSET 0x300
+#define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + 0x04)
+#define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + 0x08)
+#define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + 0x0c)
+#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + 0x10)
+#define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + 0x14)
+#define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + 0x28)
+#define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + 0x2c)
+#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + 0x34)
+#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + 0x38)
+
+/* ADC ANALOG REGISTERS */
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_ANA_OFFSET(ch) (((ch) & 0x3) * 0x40 + 0x340)
+#define RK3308_ADC_ANA_CON00(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x00)
+#define RK3308_ADC_ANA_CON01(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_ANA_CON02(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_ANA_CON03(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_ANA_CON04(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_ANA_CON05(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x14)
+#define RK3308_ADC_ANA_CON06(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x18)
+#define RK3308_ADC_ANA_CON07(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x1c)
+#define RK3308_ADC_ANA_CON08(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x20)
+#define RK3308_ADC_ANA_CON10(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x28)
+#define RK3308_ADC_ANA_CON11(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x2c)
+
+/* DAC ANALOG REGISTERS */
+#define RK3308_DAC_ANA_OFFSET 0x440
+#define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + 0x00)
+#define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + 0x04)
+#define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + 0x08)
+#define RK3308_DAC_ANA_CON03 (RK3308_DAC_ANA_OFFSET + 0x0c)
+#define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + 0x10)
+#define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + 0x14)
+#define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + 0x18)
+#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + 0x1c)
+#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + 0x20)
+#define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + 0x30)
+#define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + 0x34)
+#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + 0x38)
+#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + 0x3c)
+
+/*
+ * These are the bits for registers
+ */
+
+/* RK3308_GLB_CON - REG: 0x0000 */
+#define RK3308_ADC_BIST_WORK BIT(7)
+#define RK3308_DAC_BIST_WORK BIT(6)
+#define RK3308_ADC_MCLK_GATING BIT(5)
+#define RK3308_DAC_MCLK_GATING BIT(4)
+#define RK3308_ADC_DIG_WORK BIT(2)
+#define RK3308_DAC_DIG_WORK BIT(1)
+#define RK3308_SYS_WORK BIT(0)
+
+/* RK3308_ADC_DIG_CON01 - REG: 0x0004 */
+#define RK3308_ADC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_ADC_I2S_VALID_LEN_SFT 5
+#define RK3308_ADC_I2S_VALID_LEN_MSK (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_32BITS (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_24BITS (0x2 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_20BITS (0x1 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_16BITS (0x0 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_MODE_SFT 3
+#define RK3308_ADC_I2S_MODE_MSK (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_PCM (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_I2S (0x2 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_LJ (0x1 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_RJ (0x0 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_LR_SWAP BIT(1)
+#define RK3308_ADC_I2S_MONO BIT(0)
+
+/* RK3308_ADC_DIG_CON02 - REG: 0x0008 */
+#define RK3308_ADC_IO_MODE_MASTER BIT(5)
+#define RK3308_ADC_MODE_MASTER BIT(4)
+#define RK3308_ADC_I2S_FRAME_LEN_SFT 2
+#define RK3308_ADC_I2S_FRAME_LEN_MSK (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_32BITS (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_24BITS (0x2 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_20BITS (0x1 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_16BITS (0x0 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_WORK BIT(1)
+#define RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_ADC_DIG_CON03 - REG: 0x000c */
+#define RK3308_ADC_L_CH_BIST_SFT 2
+#define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_SFT 0
+#define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */
+#define RK3308_ADC_HPF_PATH_DIS BIT(2)
+#define RK3308_ADC_HPF_CUTOFF_SFT 0
+#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT)
+
+/* RK3308_ADC_DIG_CON07 - REG: 0x001c */
+#define RK3308_ADCL_DATA_SFT 4
+#define RK3308_ADCR_DATA_SFT 2
+#define RK3308_ADCL_DATA_SEL_ADCL BIT(1)
+#define RK3308_ADCR_DATA_SEL_ADCR BIT(0)
+
+/*
+ * RK3308_ALC_L_DIG_CON00 - REG: 0x0040 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON00 - REG: 0x0080 + ch * 0xc0
+ */
+#define RK3308_GAIN_ATTACK_JACK BIT(6)
+#define RK3308_CTRL_GEN_SFT 4
+#define RK3308_CTRL_GEN_MSK (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK3 (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK2 (0x2 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK1 (0x1 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_NORMAL (0x0 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_AGC_HOLD_TIME_SFT 0
+#define RK3308_AGC_HOLD_TIME_MSK (0xf << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_1S (0xa << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_512MS (0x9 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_256MS (0x8 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_128MS (0x7 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_64MS (0x6 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_32MS (0x5 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_16MS (0x4 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_8MS (0x3 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_4MS (0x2 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_2MS (0x1 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_0MS (0x0 << RK3308_AGC_HOLD_TIME_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON01 - REG: 0x0044 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON01 - REG: 0x0084 + ch * 0xc0
+ */
+#define RK3308_AGC_DECAY_TIME_SFT 4
+#define RK3308_AGC_ATTACK_TIME_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON02 - REG: 0x0048 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON02 - REG: 0x0088 + ch * 0xc0
+ */
+#define RK3308_AGC_MODE_LIMITER BIT(7)
+#define RK3308_AGC_ZERO_CRO_EN BIT(6)
+#define RK3308_AGC_AMP_RECOVER_GAIN BIT(5)
+#define RK3308_AGC_FAST_DEC_EN BIT(4)
+#define RK3308_AGC_NOISE_GATE_EN BIT(3)
+#define RK3308_AGC_NOISE_GATE_THRESH_SFT 0
+#define RK3308_AGC_NOISE_GATE_THRESH_MSK (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON03 - REG: 0x004c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON03 - REG: 0x008c + ch * 0xc0
+ */
+#define RK3308_AGC_PGA_ZERO_CRO_EN BIT(5)
+#define RK3308_AGC_PGA_GAIN_MAX 0x1f
+#define RK3308_AGC_PGA_GAIN_MIN 0
+#define RK3308_AGC_PGA_GAIN_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON04 - REG: 0x0050 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON04 - REG: 0x0090 + ch * 0xc0
+ */
+#define RK3308_AGC_SLOW_CLK_EN BIT(3)
+#define RK3308_AGC_APPROX_RATE_SFT 0
+#define RK3308_AGC_APPROX_RATE_MSK (0x7 << RK3308_AGC_APPROX_RATE_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON05 - REG: 0x0054 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON05 - REG: 0x0094 + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON06 - REG: 0x0058 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON06 - REG: 0x0098 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON07 - REG: 0x005c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON07 - REG: 0x009c + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON08 - REG: 0x0060 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON08 - REG: 0x00a0 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON09 - REG: 0x0064 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON09 - REG: 0x00a4 + ch * 0xc0
+ */
+#define RK3308_AGC_FUNC_SEL BIT(6)
+#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MAX_GAIN_PGA_MIN 0
+#define RK3308_AGC_MAX_GAIN_PGA_SFT 3
+#define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT)
+#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MIN_GAIN_PGA_MIN 0
+#define RK3308_AGC_MIN_GAIN_PGA_SFT 0
+#define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON12 - REG: 0x0068 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON12 - REG: 0x00a8 + ch * 0xc0
+ */
+#define RK3308_AGC_GAIN_MSK 0x1f
+
+/* RK3308_DAC_DIG_CON01 - REG: 0x0304 */
+#define RK3308_DAC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_DAC_I2S_VALID_LEN_SFT 5
+#define RK3308_DAC_I2S_VALID_LEN_MSK (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_32BITS (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_24BITS (0x2 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_20BITS (0x1 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_16BITS (0x0 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_MODE_SFT 3
+#define RK3308_DAC_I2S_MODE_MSK (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_PCM (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_I2S (0x2 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_LJ (0x1 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_RJ (0x0 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_LR_SWAP BIT(2)
+
+/* RK3308_DAC_DIG_CON02 - REG: 0x0308 */
+#define RK3308BS_DAC_IO_MODE_MASTER BIT(7)
+#define RK3308BS_DAC_MODE_MASTER BIT(6)
+#define RK3308_DAC_IO_MODE_MASTER BIT(5)
+#define RK3308_DAC_MODE_MASTER BIT(4)
+#define RK3308_DAC_I2S_FRAME_LEN_SFT 2
+#define RK3308_DAC_I2S_FRAME_LEN_MSK (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_32BITS (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_24BITS (0x2 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_20BITS (0x1 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_16BITS (0x0 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_WORK BIT(1)
+#define RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_DAC_DIG_CON03 - REG: 0x030C */
+#define RK3308_DAC_L_CH_BIST_SFT 2
+#define RK3308_DAC_L_CH_BIST_MSK (0x3 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_LEFT (0x3 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_L_CH_BIST_CUBE (0x2 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_SINE (0x1 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_RIGHT (0x0 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_SFT 0
+#define RK3308_DAC_R_CH_BIST_MSK (0x3 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_LEFT (0x3 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_CUBE (0x2 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */
+/* Versions up to B: */
+#define RK3308_DAC_MODULATOR_GAIN_SFT 4
+#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT)
+#define RK3308_DAC_CIC_IF_GAIN_SFT 0
+#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT)
+/* Version C: */
+#define RK3308BS_DAC_DIG_GAIN_SFT 0
+#define RK3308BS_DAC_DIG_GAIN_MSK (0xff << RK3308BS_DAC_DIG_GAIN_SFT)
+#define RK3308BS_DAC_DIG_GAIN_0DB (0xed << RK3308BS_DAC_DIG_GAIN_SFT)
+
+/* RK3308BS_ADC_DIG_CON05..06 (Version C only) */
+#define RK3308_ADC_DIG_VOL_CON_x_SFT 0
+#define RK3308_ADC_DIG_VOL_CON_x_MSK (0xff << RK3308_ADC_DIG_VOL_CON_x_SFT)
+#define RK3308_ADC_DIG_VOL_CON_x_0DB (0xc2 << RK3308_ADC_DIG_VOL_CON_x_SFT)
+
+/* RK3308_DAC_DIG_CON05 - REG: 0x0314 */
+#define RK3308_DAC_L_REG_CTL_INDATA BIT(2)
+#define RK3308_DAC_R_REG_CTL_INDATA BIT(1)
+
+/* RK3308_DAC_DIG_CON10 - REG: 0x0328 */
+#define RK3308_DAC_DATA_HI4(x) ((x) & 0xf)
+
+/* RK3308_DAC_DIG_CON11 - REG: 0x032c */
+#define RK3308_DAC_DATA_LO8(x) ((x) & 0xff)
+
+/* RK3308_ADC_ANA_CON00 - REG: 0x0340 */
+#define RK3308_ADC_CH1_CH2_MIC_ALL_MSK (0xff << 0)
+#define RK3308_ADC_CH1_CH2_MIC_ALL 0xff
+#define RK3308_ADC_CH2_MIC_UNMUTE BIT(7)
+#define RK3308_ADC_CH2_MIC_WORK BIT(6)
+#define RK3308_ADC_CH2_MIC_EN BIT(5)
+#define RK3308_ADC_CH2_BUF_REF_EN BIT(4)
+#define RK3308_ADC_CH1_MIC_UNMUTE BIT(3)
+#define RK3308_ADC_CH1_MIC_WORK BIT(2)
+#define RK3308_ADC_CH1_MIC_EN BIT(1)
+#define RK3308_ADC_CH1_BUF_REF_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON01 - REG: 0x0344
+ *
+ * The PGA of MIC-INs:
+ * - HW version A:
+ * 0x0 - MIC1~MIC8 0 dB (recommended when ADC used as loopback)
+ * 0x3 - MIC1~MIC8 20 dB (recommended when ADC used as MIC input)
+ * - HW version B:
+ * 0x0 - MIC1~MIC8 0 dB
+ * 0x1 - MIC1~MIC8 6.6 dB
+ * 0x2 - MIC1~MIC8 13 dB
+ * 0x3 - MIC1~MIC8 20 dB
+ */
+#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH2_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH2_MIC_GAIN_SFT 4
+#define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+
+#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH1_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH1_MIC_GAIN_SFT 0
+#define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON02 - REG: 0x0348 */
+#define RK3308_ADC_CH2_ZEROCROSS_DET_EN BIT(6)
+#define RK3308_ADC_CH2_ALC_WORK BIT(5)
+#define RK3308_ADC_CH2_ALC_EN BIT(4)
+#define RK3308_ADC_CH1_ZEROCROSS_DET_EN BIT(2)
+#define RK3308_ADC_CH1_ALC_WORK BIT(1)
+#define RK3308_ADC_CH1_ALC_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON03 - REG: 0x034c */
+#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH1_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH1_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT)
+#define RK3308_ADC_CH1_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH1_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON04 - REG: 0x0350 */
+#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH2_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH2_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT)
+#define RK3308_ADC_CH2_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH2_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON05 - REG: 0x0354 */
+#define RK3308_ADC_CH2_ADC_WORK BIT(6)
+#define RK3308_ADC_CH2_ADC_EN BIT(5)
+#define RK3308_ADC_CH2_CLK_EN BIT(4)
+#define RK3308_ADC_CH1_ADC_WORK BIT(2)
+#define RK3308_ADC_CH1_ADC_EN BIT(1)
+#define RK3308_ADC_CH1_CLK_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON06 - REG: 0x0358 */
+#define RK3308_ADC_CURRENT_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON07 - REG: 0x035c */
+/* Note: The register configuration is only valid for ADC2 */
+#define RK3308_ADC_CH2_IN_SEL_SFT 6
+#define RK3308_ADC_CH2_IN_SEL_MSK (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN (0x2 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_MIC (0x1 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_NONE (0x0 << RK3308_ADC_CH2_IN_SEL_SFT)
+/* Note: The register configuration is only valid for ADC1 */
+#define RK3308_ADC_CH1_IN_SEL_SFT 4
+#define RK3308_ADC_CH1_IN_SEL_MSK (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN (0x2 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_MIC_BIAS_BUF_EN BIT(3)
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX 7
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT)
+
+/* RK3308_ADC_ANA_CON08 - REG: 0x0360 */
+#define RK3308_ADC_MICBIAS_CURRENT_EN BIT(4)
+
+/* RK3308_ADC_ANA_CON10 - REG: 0x0368 */
+#define RK3308_ADC_REF_EN BIT(7)
+#define RK3308_ADC_CURRENT_CHARGE_SFT 0
+#define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT)
+
+/* RK3308_ADC_ANA_CON11 - REG: 0x036c */
+#define RK3308_ADC_ALCR_CON_GAIN_PGAR_EN BIT(1)
+#define RK3308_ADC_ALCL_CON_GAIN_PGAL_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON00 - REG: 0x0440 */
+#define RK3308_DAC_HEADPHONE_DET_EN BIT(1)
+#define RK3308_DAC_CURRENT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON01 - REG: 0x0444 */
+#define RK3308_DAC_BUF_REF_R_EN BIT(6)
+#define RK3308_DAC_BUF_REF_L_EN BIT(2)
+#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0
+// unshifted values for both L and R:
+#define RK3308_DAC_HPOUT_POP_SOUND_x_MSK 0x3
+#define RK3308_DAC_HPOUT_POP_SOUND_x_WORK 0x2
+#define RK3308_DAC_HPOUT_POP_SOUND_x_INIT 0x1
+
+/* RK3308_DAC_ANA_CON02 - REG: 0x0448 */
+#define RK3308_DAC_R_DAC_WORK BIT(7)
+#define RK3308_DAC_R_DAC_EN BIT(6)
+#define RK3308_DAC_R_CLK_EN BIT(5)
+#define RK3308_DAC_R_REF_EN BIT(4)
+#define RK3308_DAC_L_DAC_WORK BIT(3)
+#define RK3308_DAC_L_DAC_EN BIT(2)
+#define RK3308_DAC_L_CLK_EN BIT(1)
+#define RK3308_DAC_L_REF_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON03 - REG: 0x044c */
+#define RK3308_DAC_R_HPOUT_WORK BIT(6)
+#define RK3308_DAC_R_HPOUT_EN BIT(5)
+#define RK3308_DAC_R_HPOUT_MUTE_SFT 4
+#define RK3308_DAC_L_HPOUT_WORK BIT(2)
+#define RK3308_DAC_L_HPOUT_EN BIT(1)
+#define RK3308_DAC_L_HPOUT_MUTE_SFT 0
+
+/* RK3308_DAC_ANA_CON04 - REG: 0x0450 */
+#define RK3308_DAC_x_LINEOUT_GAIN_MAX 0x3
+#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6
+#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_MUTE_SFT 5
+#define RK3308_DAC_R_LINEOUT_EN BIT(4)
+#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2
+#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_MUTE_SFT 1
+#define RK3308_DAC_L_LINEOUT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */
+/* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */
+#define RK3308_DAC_x_HPOUT_GAIN_MAX 0x1e
+#define RK3308_DAC_x_HPOUT_GAIN_SFT 0
+#define RK3308_DAC_x_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_x_HPOUT_GAIN_SFT)
+#define RK3308_DAC_x_HPOUT_GAIN_MIN (0x00 << RK3308_DAC_x_HPOUT_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON07 - REG: 0x045c */
+#define RK3308_DAC_R_HPOUT_DRV_SFT 4
+#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT)
+#define RK3308_DAC_L_HPOUT_DRV_SFT 0
+#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */
+#define RK3308_DAC_R_LINEOUT_DRV_SFT 4
+#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT)
+#define RK3308_DAC_L_LINEOUT_DRV_SFT 0
+#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON12 - REG: 0x0470 */
+#define RK3308_DAC_R_HPMIX_SEL_SFT 6
+#define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_SEL_SFT 2
+#define RK3308_DAC_L_HPMIX_SEL_MSK (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_x_HPMIX_GAIN_MIN 0x1 /* 0x0 and 0x3 are reserved */
+#define RK3308_DAC_x_HPMIX_GAIN_MAX 0x2
+#define RK3308_DAC_R_HPMIX_GAIN_SFT 4
+#define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_SFT 0
+#define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON13 - REG: 0x0474 */
+#define RK3308_DAC_R_HPMIX_UNMUTE BIT(6)
+#define RK3308_DAC_R_HPMIX_WORK BIT(5)
+#define RK3308_DAC_R_HPMIX_EN BIT(4)
+#define RK3308_DAC_L_HPMIX_UNMUTE BIT(2)
+#define RK3308_DAC_L_HPMIX_WORK BIT(1)
+#define RK3308_DAC_L_HPMIX_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */
+#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4)
+#define RK3308_DAC_CURRENT_CHARGE_SFT 0
+#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT)
+
+/* RK3308_DAC_ANA_CON15 - REG: 0x047C */
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+
+#endif /* __RK3308_CODEC_H__ */
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644
index 000000000000..9697aefc6e03
--- /dev/null
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -0,0 +1,534 @@
+// 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/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..3c9957b00881
--- /dev/null
+++ b/sound/soc/codecs/rk817_codec.c
@@ -0,0 +1,540 @@
+// 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/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_CBC_CFC:
+ i2s_mst |= RK817_I2S_MODE_SLV;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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 = 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
index 7ef3b5476bcc..d1fc1706422f 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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>
@@ -83,8 +80,8 @@ int rl6231_calc_dmic_clk(int rate)
for (i = 0; i < ARRAY_SIZE(div); i++) {
if ((div[i] % 3) == 0)
continue;
- /* find divider that gives DMIC frequency below 3.072MHz */
- if (3072000 * div[i] >= rate)
+ /* find divider that gives DMIC frequency below 1.536MHz */
+ if (1536000 * div[i] >= rate)
return i;
}
@@ -100,11 +97,15 @@ struct pll_calc_map {
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},
- {19200000, 24576000, 3, 30, 3, false},
+ {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,
@@ -130,7 +131,7 @@ static unsigned int find_best_div(unsigned int in,
* 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 and bypass flag.
+ * @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.
*
@@ -145,7 +146,7 @@ int rl6231_pll_calc(const unsigned int freq_in,
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 bypass = false;
+ bool m_bypass = false, k_bypass = false;
if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
return -EINVAL;
@@ -156,7 +157,8 @@ int rl6231_pll_calc(const unsigned int freq_in,
k = pll_preset_table[i].k;
m = pll_preset_table[i].m;
n = pll_preset_table[i].n;
- bypass = pll_preset_table[i].m_bp;
+ 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;
}
@@ -174,12 +176,14 @@ int rl6231_pll_calc(const unsigned int freq_in,
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) {
- bypass = true;
+ m_bypass = true;
n = n_t;
k = k_t;
goto code_find;
@@ -187,7 +191,7 @@ int rl6231_pll_calc(const unsigned int freq_in,
out_t = in_t / (k_t + 2);
red = abs(f_out - out_t);
if (red < red_t) {
- bypass = true;
+ m_bypass = true;
n = n_t;
m = 0;
k = k_t;
@@ -199,7 +203,7 @@ int rl6231_pll_calc(const unsigned int freq_in,
out_t = in_t / ((m_t + 2) * (k_t + 2));
red = abs(f_out - out_t);
if (red < red_t) {
- bypass = false;
+ m_bypass = false;
n = n_t;
m = m_t;
k = k_t;
@@ -213,8 +217,13 @@ int rl6231_pll_calc(const unsigned int freq_in,
pr_debug("Only get approximation about PLL\n");
code_find:
+ if (k == -1) {
+ k_bypass = true;
+ k = 0;
+ }
- pll_code->m_bp = bypass;
+ 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;
diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h
index 4c77b441fba2..928082750860 100644
--- a/sound/soc/codecs/rl6231.h
+++ b/sound/soc/codecs/rl6231.h
@@ -1,19 +1,16 @@
+/* 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>
- *
- * 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 __RL6231_H__
#define __RL6231_H__
-#define RL6231_PLL_INP_MAX 40000000
+#define RL6231_PLL_INP_MAX 50000000
#define RL6231_PLL_INP_MIN 256000
#define RL6231_PLL_N_MAX 0x1ff
#define RL6231_PLL_K_MAX 0x1f
@@ -21,6 +18,7 @@
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;
diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c
index 8f571cf8edd4..fa8ac34549eb 100644
--- a/sound/soc/codecs/rl6347a.c
+++ b/sound/soc/codecs/rl6347a.c
@@ -1,13 +1,10 @@
+// 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>
- *
- * 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>
@@ -64,8 +61,8 @@ 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;
- unsigned int index, vid, buf = 0x0;
+ __be32 be_reg, buf = 0x0;
+ unsigned int index, vid;
/* handle index registers */
if (reg <= 0xff) {
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
index e127919cb36b..761455a2fa38 100644
--- a/sound/soc/codecs/rl6347a.h
+++ b/sound/soc/codecs/rl6347a.h
@@ -1,13 +1,10 @@
+/* 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>
- *
- * 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 __RL6347A_H__
#define __RL6347A_H__
diff --git a/sound/soc/codecs/rt-sdw-common.c b/sound/soc/codecs/rt-sdw-common.c
new file mode 100644
index 000000000000..ad61943ce75f
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt-sdw-common.c
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/jack.h>
+
+#include "rt-sdw-common.h"
+
+/**
+ * rt_sdca_index_write - Write a value to Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_write(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to set value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_write);
+
+/**
+ * rt_sdca_index_read - Read value from Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_read(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to get value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_read);
+
+/**
+ * rt_sdca_index_update_bits - Update value on Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt_sdca_index_read(map, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt_sdca_index_write(map, nid, reg, tmp);
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_update_bits);
+
+/**
+ * rt_sdca_btn_type - Decision of button type.
+ *
+ * @buffer: UMP message buffer.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_btn_type(unsigned char *buffer)
+{
+ u8 btn_type = 0;
+ int ret = 0;
+
+ btn_type |= buffer[0] & 0xf;
+ btn_type |= (buffer[0] >> 4) & 0xf;
+ btn_type |= buffer[1] & 0xf;
+ btn_type |= (buffer[1] >> 4) & 0xf;
+
+ if (btn_type & BIT(0))
+ ret |= SND_JACK_BTN_2;
+ if (btn_type & BIT(1))
+ ret |= SND_JACK_BTN_3;
+ if (btn_type & BIT(2))
+ ret |= SND_JACK_BTN_0;
+ if (btn_type & BIT(3))
+ ret |= SND_JACK_BTN_1;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_btn_type);
+
+/**
+ * rt_sdca_headset_detect - Headset jack type detection.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ *
+ * A headset jack type will be returned, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id)
+{
+ unsigned int det_mode, jack_type;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x03:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ jack_type = SND_JACK_HEADSET;
+ break;
+ default:
+ jack_type = 0;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_SELECTED_MODE, 0), det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ return jack_type;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_headset_detect);
+
+/**
+ * rt_sdca_button_detect - Read UMP message and decide button type.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ * @hid_buf_addr: HID buffer address.
+ * @hid_id: Report ID for HID.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ unsigned char buf[3];
+ int ret;
+
+ /* get current UMP message owner */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(map, hid_buf_addr + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID */
+ if (buf[0] == hid_id)
+ btn_type = rt_sdca_btn_type(&buf[1]);
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(map,
+ SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_button_detect);
+
+MODULE_DESCRIPTION("Realtek soundwire common functions");
+MODULE_AUTHOR("jack yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt-sdw-common.h b/sound/soc/codecs/rt-sdw-common.h
new file mode 100644
index 000000000000..4759516feb38
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+//
+// rt-sdw-common.h
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#ifndef __RT_SDW_COMMON_H__
+#define __RT_SDW_COMMON_H__
+
+#define SDCA_NUM_JACK_CODEC 0x01
+#define SDCA_NUM_MIC_ARRAY 0x02
+#define SDCA_NUM_HID 0x03
+#define SDCA_NUM_AMP 0x04
+#define RT_SDCA_CTL_SELECTED_MODE 0x01
+#define RT_SDCA_CTL_DETECTED_MODE 0x02
+#define RT_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+
+struct rt_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+#define RT_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount, \
+ xinfo, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = xinfo, \
+ .get = xget, \
+ .put = xput, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array, xinfo) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = xinfo, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value);
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value);
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val);
+int rt_sdca_btn_type(unsigned char *buffer);
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id);
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id);
+
+#endif /* __RT_SDW_COMMON_H__ */
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
new file mode 100644
index 000000000000..b84dd18ddde9
--- /dev/null
+++ b/sound/soc/codecs/rt1011.c
@@ -0,0 +1,2492 @@
+// 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/i2c.h>
+#include <linux/acpi.h>
+#include <linux/regmap.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_CBC_CFC:
+ 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_MAPLE,
+ .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" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1011_i2c_id[] = {
+ { "rt1011" },
+ { }
+};
+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 = 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..818b45226b72
--- /dev/null
+++ b/sound/soc/codecs/rt1015.c
@@ -0,0 +1,1193 @@
+// 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/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 char * const rt1015_dac_output_vol_select[] = {
+ "immediate",
+ "zero detection + immediate change",
+ "zero detection + inc/dec change",
+ "zero detection + soft inc/dec change",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_dac_vol_ctl_enum,
+ RT1015_DAC3, 2, rt1015_dac_output_vol_select);
+
+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),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Control", rt1015_dac_vol_ctl_enum),
+};
+
+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_CBP_CFP:
+ reg_val |= RT1015_TCON_TDM_MS_M;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { }
+};
+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" },
+ { }
+};
+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 = 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..44e7fe3c32da
--- /dev/null
+++ b/sound/soc/codecs/rt1015p.c
@@ -0,0 +1,154 @@
+// 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/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..9f86f071fca8
--- /dev/null
+++ b/sound/soc/codecs/rt1016.c
@@ -0,0 +1,693 @@
+// 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 <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_CBP_CFP:
+ reg_val |= RT1016_I2S_MS_M;
+ rt1016->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { }
+};
+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" },
+ { }
+};
+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 = 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/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
new file mode 100644
index 000000000000..a9c000876be8
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2023 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/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt1017-sdca-sdw.h"
+
+static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3206:
+ case 0xc000:
+ case 0xc001:
+ case 0xc022:
+ case 0xc030:
+ case 0xc104:
+ case 0xc10b:
+ case 0xc10c:
+ case 0xc110:
+ case 0xc112:
+ case 0xc300:
+ case 0xc301:
+ case 0xc318:
+ case 0xc325 ... 0xc328:
+ case 0xc331:
+ case 0xc340:
+ case 0xc350 ... 0xc351:
+ case 0xc500:
+ case 0xc502:
+ case 0xc504:
+ case 0xc507:
+ case 0xc509:
+ case 0xc510:
+ case 0xc512:
+ case 0xc518:
+ case 0xc51b:
+ case 0xc51d:
+ case 0xc520:
+ case 0xc540 ... 0xc542:
+ case 0xc550 ... 0xc552:
+ case 0xc600:
+ case 0xc602:
+ case 0xc612:
+ case 0xc622:
+ case 0xc632:
+ case 0xc642:
+ case 0xc651:
+ case 0xca00:
+ case 0xca09 ... 0xca0c:
+ case 0xca0e ... 0xca0f:
+ case 0xca10 ... 0xca11:
+ case 0xca16 ... 0xca17:
+ case 0xcb00:
+ case 0xcc00:
+ case 0xcc02:
+ case 0xd017:
+ case 0xd01a ... 0xd01c:
+ case 0xd101:
+ case 0xd20c:
+ case 0xd300:
+ case 0xd370:
+ case 0xd500:
+ case 0xd545 ... 0xd548:
+ case 0xd5a5 ... 0xd5a8:
+ case 0xd5aa ... 0xd5ad:
+ case 0xda04 ... 0xda07:
+ case 0xda09 ... 0xda0a:
+ case 0xda0c ... 0xda0f:
+ case 0xda11 ... 0xda14:
+ case 0xda16 ... 0xda19:
+ case 0xdab6 ... 0xdabb:
+ case 0xdb09 ... 0xdb0a:
+ case 0xdb14:
+
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0xc000:
+ case 0xc022:
+ case 0xc351:
+ case 0xc518:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_sequence rt1017_blind_write[] = {
+ { 0xc001, 0x43 },
+ { 0x2f55, 0x02 },
+ { 0x3206, 0x80 },
+ { 0x005f, 0x7f },
+ { 0xd101, 0xa0 },
+ { 0xc112, 0xc0 },
+ { 0xc104, 0xaa },
+ { 0xc110, 0x59 },
+ { 0xc112, 0xc0 },
+ { 0xc340, 0x80 },
+ { 0xd017, 0x2c },
+ { 0xd01a, 0xc8 },
+ { 0xd01b, 0xcf },
+ { 0xd01c, 0x0c },
+ { 0xd20c, 0x14 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0x7f },
+ { 0xdb14, 0x03 },
+ { 0xcb00, 0x31 },
+ { 0xc318, 0x44 },
+ { 0xc325, 0xce },
+ { 0xc326, 0x13 },
+ { 0xc327, 0x5f },
+ { 0xc328, 0xf3 },
+ { 0xc350, 0xe1 },
+ { 0xc351, 0x88 },
+ { 0xc030, 0x14 },
+ { 0xc331, 0xf2 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc651, 0xc0 },
+ { 0xc550, 0xd0 },
+ { 0xc612, 0x00 },
+ { 0xc622, 0x00 },
+ { 0xc632, 0x00 },
+ { 0xc642, 0x00 },
+ { 0xc602, 0xf0 },
+ { 0xc600, 0xd0 },
+ { 0xcc02, 0x78 },
+ { 0xcc00, 0x90 },
+ { 0xc300, 0x3f },
+ { 0xc301, 0x1d },
+ { 0xc10b, 0x2e },
+ { 0xc10c, 0x36 },
+
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x6a },
+ { 0xd5a7, 0xaa },
+ { 0xd5a8, 0xaa },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x16 },
+ { 0xd5ac, 0xdb },
+ { 0xd5ad, 0x6d },
+ { 0xd545, 0x09 },
+ { 0xd546, 0x30 },
+ { 0xd547, 0xf0 },
+ { 0xd548, 0xf0 },
+ { 0xd500, 0x20 },
+ { 0xc504, 0x3f },
+ { 0xc540, 0x00 },
+ { 0xc541, 0x0a },
+ { 0xc542, 0x1a },
+ { 0xc512, 0x00 },
+ { 0xc520, 0x40 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc500, 0x40 },
+ { 0xc502, 0xde },
+ { 0xc507, 0x05 },
+ { 0xc509, 0x05 },
+ { 0xc510, 0x40 },
+ { 0xc518, 0xc0 },
+ { 0xc500, 0xc0 },
+
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x0b },
+ { 0xda0e, 0x55 },
+ { 0xda0f, 0x55 },
+ { 0xda04, 0x00 },
+ { 0xda05, 0x51 },
+ { 0xda06, 0xeb },
+ { 0xda07, 0x85 },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda11, 0x26 },
+ { 0xda12, 0x66 },
+ { 0xda13, 0x66 },
+ { 0xda14, 0x66 },
+ { 0xda16, 0x79 },
+ { 0xda17, 0x99 },
+ { 0xda18, 0x99 },
+ { 0xda19, 0x99 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x07 },
+ { 0xca0b, 0x89 },
+ { 0xca0c, 0x61 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x03 },
+ { 0xca10, 0xc4 },
+ { 0xca11, 0xb0 },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x00 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x00 },
+ { 0xd017, 0x0e },
+ { 0xca00, 0xcd },
+ { 0xc022, 0x84 },
+};
+
+#define RT1017_MAX_REG_NUM 0x4108ffff
+
+static const struct regmap_config rt1017_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1017_sdca_readable_register,
+ .volatile_reg = rt1017_sdca_volatile_register,
+ .max_register = RT1017_MAX_REG_NUM,
+ .reg_defaults = rt1017_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1017_sdca_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
+ * port = 1 for AMP playback
+ * port = 2 for IV capture
+ */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = BIT(1); /* 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 = 64;
+
+ return 0;
+}
+
+static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (rt1017->hw_init)
+ return 0;
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_only(rt1017->regmap, false);
+ regcache_cache_bypass(rt1017->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(rt1017->regmap, 0xc000, 0x02);
+
+ /* initial settings - blind write */
+ regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write,
+ ARRAY_SIZE(rt1017_blind_write));
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_bypass(rt1017->regmap, false);
+ regcache_mark_dirty(rt1017->regmap);
+ } else
+ rt1017->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1017->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "hw_init complete\n");
+ return 0;
+}
+
+static int rt1017_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1017->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1017_sdca_io_init(&slave->dev, slave);
+}
+
+static const char * const rt1017_rx_data_ch_select[] = {
+ "Bypass",
+ "CN1",
+ "CN2",
+ "CN3",
+ "CN4",
+ "(1+2)/2",
+ "(1+3)/2",
+ "(1+4)/2",
+ "(2+3)/2",
+ "(2+4)/2",
+ "(3+4)/2",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0),
+ 0, rt1017_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1017_sdca_controls[] = {
+ /* UDMPU Cluster Selection */
+ SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1017_sto_dac =
+ SOC_DAPM_SINGLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1),
+ 0, 1, 1);
+
+static int rt1017_sdca_pde23_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 rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1017_sdca_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 rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1,
+ RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG);
+ regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1017_sdca_feedback_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 rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+
+ SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_pde23_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 rt1017_sdca_dapm_routes[] = {
+
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "CLASS D", NULL, "PDE23" },
+ { "SPO", NULL, "CLASS D" },
+
+ { "I Sense", NULL, "I Gen" },
+ { "V Sense", NULL, "V Gen" },
+ { "I Sense", NULL, "PDE23" },
+ { "V Sense", NULL, "PDE23" },
+ { "DP2TX", NULL, "I Sense" },
+ { "DP2TX", NULL, "V Sense" },
+};
+
+static const struct sdw_slave_ops rt1017_sdca_slave_ops = {
+ .read_prop = rt1017_sdca_read_prop,
+ .update_status = rt1017_sdca_update_status,
+};
+
+static int rt1017_sdca_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static void rt1017_sdca_component_remove(struct snd_soc_component *component)
+{
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1017->regmap, true);
+}
+
+static const struct snd_soc_component_driver soc_sdca_component_rt1017 = {
+ .probe = rt1017_sdca_component_probe,
+ .remove = rt1017_sdca_component_remove,
+ .controls = rt1017_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt1017_sdca_controls),
+ .dapm_widgets = rt1017_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets),
+ .dapm_routes = rt1017_sdca_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes),
+ .endianness = 1,
+};
+
+static int rt1017_sdca_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 rt1017_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1017_sdca_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 rt1017_sdca_priv *rt1017 = 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 (!rt1017->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;
+
+ dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n",
+ params_rate(params), num_channels, snd_pcm_format_width(params_format(params)),
+ direction, ch_mask, port);
+
+ retval = sdw_stream_add_slave(rt1017->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 44100:
+ sampling_rate = RT1017_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1017_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1017_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1017_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(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1017->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt1017_sdca_ops = {
+ .hw_params = rt1017_sdca_pcm_hw_params,
+ .hw_free = rt1017_sdca_pcm_hw_free,
+ .set_stream = rt1017_sdca_set_sdw_stream,
+ .shutdown = rt1017_sdca_shutdown,
+};
+
+#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1017_sdca_dai[] = {
+ {
+ .name = "rt1017-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .ops = &rt1017_sdca_ops,
+ },
+};
+
+static int rt1017_sdca_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017;
+ int ret;
+
+ rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL);
+ if (!rt1017)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1017);
+ rt1017->sdw_slave = slave;
+ rt1017->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1017->hw_init = false;
+ rt1017->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_component_rt1017,
+ rt1017_sdca_dai,
+ ARRAY_SIZE(rt1017_sdca_dai));
+
+ return ret;
+}
+
+static int rt1017_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1017_sdca_init(&slave->dev, regmap, slave);
+}
+
+static int rt1017_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (rt1017->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1017_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id);
+
+static int rt1017_sdca_dev_suspend(struct device *dev)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (!rt1017->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1017->regmap, true);
+
+ return 0;
+}
+
+#define RT1017_PROBE_TIMEOUT 5000
+
+static int rt1017_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1017->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1017_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(rt1017->regmap, false);
+ regcache_sync(rt1017->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1017_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1017_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt1017-sdca",
+ .pm = pm_ptr(&rt1017_sdca_pm),
+ },
+ .probe = rt1017_sdca_sdw_probe,
+ .remove = rt1017_sdca_sdw_remove,
+ .ops = &rt1017_sdca_slave_ops,
+ .id_table = rt1017_sdca_id,
+};
+module_sdw_driver(rt1017_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h
new file mode 100644
index 000000000000..4932b5dbe3c0
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1017_SDW_H__
+#define __RT1017_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>
+
+/* RT1017 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1017 SDCA entity */
+#define RT1017_SDCA_ENT_PDE23 0x31
+#define RT1017_SDCA_ENT_PDE22 0x33
+#define RT1017_SDCA_ENT_CS21 0x21
+#define RT1017_SDCA_ENT_SAPU29 0x29
+#define RT1017_SDCA_ENT_XU22 0x22
+#define RT1017_SDCA_ENT_FU 0x03
+#define RT1017_SDCA_ENT_UDMPU21 0x02
+
+/* RT1017 SDCA control */
+#define RT1017_SDCA_CTL_FS_INDEX 0x10
+#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1017_SDCA_CTL_PROT_STAT 0x11
+#define RT1017_SDCA_CTL_BYPASS 0x01
+#define RT1017_SDCA_CTL_FU_MUTE 0x01
+#define RT1017_SDCA_CTL_FU_VOLUME 0x02
+#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+
+#define RT1017_CLASSD_INT_1 0xd300
+#define RT1017_PWM_TRIM_1 0xd370
+
+
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2)
+
+enum {
+ RT1017_SDCA_RATE_44100HZ = 0x8,
+ RT1017_SDCA_RATE_48000HZ = 0x9,
+ RT1017_SDCA_RATE_96000HZ = 0xb,
+ RT1017_SDCA_RATE_192000HZ = 0xd,
+};
+
+struct rt1017_sdca_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+static const struct reg_default rt1017_sdca_reg_defaults[] = {
+ { 0x3206, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc030, 0x54 },
+ { 0xc104, 0x8a },
+ { 0xc10b, 0x2f },
+ { 0xc10c, 0x2f },
+ { 0xc110, 0x49 },
+ { 0xc112, 0x10 },
+ { 0xc300, 0xff },
+ { 0xc301, 0xdd },
+ { 0xc318, 0x40 },
+ { 0xc325, 0x00 },
+ { 0xc326, 0x00 },
+ { 0xc327, 0x00 },
+ { 0xc328, 0x02 },
+ { 0xc331, 0xb2 },
+ { 0xc340, 0x02 },
+ { 0xc350, 0x21 },
+ { 0xc500, 0x00 },
+ { 0xc502, 0x00 },
+ { 0xc504, 0x3f },
+ { 0xc507, 0x1f },
+ { 0xc509, 0x1f },
+ { 0xc510, 0x40 },
+ { 0xc512, 0x00 },
+ { 0xc518, 0x02 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc520, 0x00 },
+ { 0xc540, 0x80 },
+ { 0xc541, 0x00 },
+ { 0xc542, 0x0a },
+ { 0xc550, 0x80 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc600, 0x10 },
+ { 0xc602, 0x83 },
+ { 0xc612, 0x40 },
+ { 0xc622, 0x40 },
+ { 0xc632, 0x40 },
+ { 0xc642, 0x40 },
+ { 0xc651, 0x00 },
+ { 0xca00, 0xc1 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x51 },
+ { 0xca0b, 0xeb },
+ { 0xca0c, 0x85 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x10 },
+ { 0xca10, 0x62 },
+ { 0xca11, 0x4d },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xcb00, 0x10 },
+ { 0xcc00, 0x10 },
+ { 0xcc02, 0x0b },
+ { 0xd017, 0x09 },
+ { 0xd01a, 0x00 },
+ { 0xd01b, 0x00 },
+ { 0xd01c, 0x00 },
+ { 0xd101, 0xa0 },
+ { 0xd20c, 0x14 },
+ { 0xd300, 0x0f },
+ { 0xd370, 0x18 },
+ { 0xd500, 0x00 },
+ { 0xd545, 0x0b },
+ { 0xd546, 0xf9 },
+ { 0xd547, 0xb2 },
+ { 0xd548, 0xa9 },
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x00 },
+ { 0xd5a7, 0x00 },
+ { 0xd5a8, 0x00 },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x00 },
+ { 0xd5ac, 0x00 },
+ { 0xd5ad, 0x00 },
+ { 0xda04, 0x03 },
+ { 0xda05, 0x33 },
+ { 0xda06, 0x33 },
+ { 0xda07, 0x33 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x01 },
+ { 0xda0e, 0x5d },
+ { 0xda0f, 0x86 },
+ { 0xda11, 0x20 },
+ { 0xda12, 0x00 },
+ { 0xda13, 0x00 },
+ { 0xda14, 0x00 },
+ { 0xda16, 0x7f },
+ { 0xda17, 0xff },
+ { 0xda18, 0xff },
+ { 0xda19, 0xff },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x01 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x01 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0xff },
+ { 0xdb14, 0x00 },
+
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+#endif /* __RT1017_SDW_H__ */
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
new file mode 100644
index 000000000000..86539c6f6cc1
--- /dev/null
+++ b/sound/soc/codecs/rt1019.c
@@ -0,0 +1,609 @@
+// 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 <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_MAPLE,
+ .reg_defaults = rt1019_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1019_reg),
+};
+
+static const struct i2c_device_id rt1019_i2c_id[] = {
+ { "rt1019" },
+ { }
+};
+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" },
+ { }
+};
+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 = 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..26b7382f97ef
--- /dev/null
+++ b/sound/soc/codecs/rt1305.c
@@ -0,0 +1,1179 @@
+// 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/i2c.h>
+#include <linux/regmap.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_CBP_CFP:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_M;
+ rt1305->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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_MAPLE,
+ .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" },
+ { "10EC1306" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1305_i2c_id[] = {
+ { "rt1305" },
+ { "rt1306" },
+ { }
+};
+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 = 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..b6c224832a43
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -0,0 +1,817 @@
+// 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 0xcf01:
+ 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_MAPLE,
+ .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, hibernation_flag;
+
+ if (rt1308->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1308->regmap, false);
+ if (rt1308->first_hw_init)
+ regcache_cache_bypass(rt1308->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt1308->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag);
+ if ((hibernation_flag != 0x00) && rt1308->first_hw_init)
+ goto _preset_ready_;
+
+ /* 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);
+
+ /* apply BQ params */
+ rt1308_apply_bq_params(rt1308);
+
+ regmap_write(rt1308->regmap, 0xcf01, 0x01);
+
+_preset_ready_:
+ 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_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);
+
+ 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 || 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);
+
+ if (!rt1308->first_hw_init)
+ return 0;
+
+ 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;
+
+ regcache_cache_only(rt1308->regmap, true);
+
+ /*
+ * 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));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+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);
+
+ return rt1308_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1308_sdw_remove(struct sdw_slave *slave)
+{
+ 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume)
+ RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1308_sdw_driver = {
+ .driver = {
+ .name = "rt1308",
+ .pm = pm_ptr(&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..f816c73e247e
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -0,0 +1,172 @@
+/* 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;
+ 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..df50b38c24b9
--- /dev/null
+++ b/sound/soc/codecs/rt1308.c
@@ -0,0 +1,871 @@
+// 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/i2c.h>
+#include <linux/regmap.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_CBC_CFC:
+ 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_MAPLE,
+ .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" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1308_i2c_id[] = {
+ { "rt1308" },
+ { }
+};
+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 = 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..01a977398864
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -0,0 +1,794 @@
+// 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_MAPLE,
+ .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;
+
+ regcache_cache_only(rt1316->regmap, false);
+ if (rt1316->first_hw_init) {
+ regcache_cache_bypass(rt1316->regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&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_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);
+
+ 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 || 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 char * const rt1316_dac_output_vol_select[] = {
+ "immediately",
+ "zero crossing",
+ "zero crossing with soft ramp",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum,
+ 0xc010, 6, rt1316_dac_output_vol_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),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum),
+};
+
+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, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Could not allocate bq_params memory\n", __func__);
+ 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, "%s: Could not read list of realtek,bq-params\n", __func__);
+ }
+ }
+
+ 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);
+
+ if (!rt1316->first_hw_init)
+ return 0;
+
+ 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;
+
+ regcache_cache_only(rt1316->regmap, true);
+
+ /*
+ * 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));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+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)
+{
+ 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 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 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume)
+ RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1316_sdw_driver = {
+ .driver = {
+ .name = "rt1316-sdca",
+ .pm = pm_ptr(&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..dc1bfe40edd3
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -0,0 +1,52 @@
+/* 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;
+ 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..70db5450d6d2
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -0,0 +1,868 @@
+// 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_MAPLE,
+ .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;
+
+ regcache_cache_only(rt1318->regmap, false);
+ if (rt1318->first_hw_init) {
+ regcache_cache_bypass(rt1318->regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&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_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);
+
+ 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 || 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, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Rate %d is not supported\n",
+ __func__, 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;
+
+ if (!rt1318->first_hw_init)
+ return 0;
+
+ 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;
+
+ regcache_cache_only(rt1318->regmap, true);
+
+ /*
+ * 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));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(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)
+{
+ 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 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 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume)
+ RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1318_sdw_driver = {
+ .driver = {
+ .name = "rt1318-sdca",
+ .pm = pm_ptr(&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..86e83d63a017
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.h
@@ -0,0 +1,96 @@
+/* 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;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+#endif /* __RT1318_SDW_H__ */
diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c
new file mode 100644
index 000000000000..ae01b2ce630b
--- /dev/null
+++ b/sound/soc/codecs/rt1318.c
@@ -0,0 +1,1353 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318.c -- RT1318 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// Copyright(c) 2024 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 <sound/rt1318.h>
+
+#include "rt1318.h"
+
+static const struct reg_sequence init_list[] = {
+ { 0x0000C000, 0x01},
+ { 0x0000F20D, 0x00},
+ { 0x0000F212, 0x3E},
+ { 0x0000C001, 0x02},
+ { 0x0000C003, 0x22},
+ { 0x0000C004, 0x44},
+ { 0x0000C005, 0x44},
+ { 0x0000C007, 0x64},
+ { 0x0000C00E, 0xE7},
+ { 0x0000F223, 0x7F},
+ { 0x0000F224, 0xDB},
+ { 0x0000F225, 0xEE},
+ { 0x0000F226, 0x3F},
+ { 0x0000F227, 0x0F},
+ { 0x0000F21A, 0x78},
+ { 0x0000F242, 0x3C},
+ { 0x0000C120, 0x40},
+ { 0x0000C125, 0x03},
+ { 0x0000C321, 0x0A},
+ { 0x0000C200, 0xD8},
+ { 0x0000C201, 0x27},
+ { 0x0000C202, 0x0F},
+ { 0x0000C400, 0x0E},
+ { 0x0000C401, 0x43},
+ { 0x0000C402, 0xE0},
+ { 0x0000C403, 0x00},
+ { 0x0000C404, 0x4C},
+ { 0x0000C406, 0x40},
+ { 0x0000C407, 0x02},
+ { 0x0000C408, 0x3F},
+ { 0x0000C300, 0x01},
+ { 0x0000C125, 0x03},
+ { 0x0000DF00, 0x10},
+ { 0x0000F20B, 0x2A},
+ { 0x0000DF5F, 0x01},
+ { 0x0000DF60, 0xA7},
+ { 0x0000C203, 0x84},
+ { 0x0000C206, 0x78},
+ { 0x0000F10A, 0x09},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x03},
+ { 0x0000F109, 0xE0},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x04},
+ { 0x0000F109, 0x65},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x02},
+ { 0x0000F109, 0x30},
+ { 0x0000F10B, 0x5C},
+ { 0x0000E706, 0x0F},
+ { 0x0000E707, 0x30},
+ { 0x0000E806, 0x0F},
+ { 0x0000E807, 0x30},
+ { 0x0000CE04, 0x03},
+ { 0x0000CE05, 0x5F},
+ { 0x0000CE06, 0xA2},
+ { 0x0000CE07, 0x6B},
+ { 0x0000CF04, 0x03},
+ { 0x0000CF05, 0x5F},
+ { 0x0000CF06, 0xA2},
+ { 0x0000CF07, 0x6B},
+ { 0x0000CE60, 0xE3},
+ { 0x0000C130, 0x51},
+ { 0x0000E000, 0xA8},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x04},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x41001888, 0x00},
+ { 0x0000C121, 0x0B},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x00},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F800, 0x20},
+ { 0x0000CA00, 0x80},
+ { 0x0000CA10, 0x00},
+ { 0x0000CA02, 0x78},
+ { 0x0000CA12, 0x78},
+ { 0x0000ED00, 0x90},
+ { 0x0000E604, 0x00},
+ { 0x0000DB00, 0x0C},
+ { 0x0000DD00, 0x0C},
+ { 0x0000DC19, 0x00},
+ { 0x0000DC1A, 0x6A},
+ { 0x0000DC1B, 0xAA},
+ { 0x0000DC1C, 0xAB},
+ { 0x0000DC1D, 0x00},
+ { 0x0000DC1E, 0x16},
+ { 0x0000DC1F, 0xDB},
+ { 0x0000DC20, 0x6D},
+ { 0x0000DE19, 0x00},
+ { 0x0000DE1A, 0x6A},
+ { 0x0000DE1B, 0xAA},
+ { 0x0000DE1C, 0xAB},
+ { 0x0000DE1D, 0x00},
+ { 0x0000DE1E, 0x16},
+ { 0x0000DE1F, 0xDB},
+ { 0x0000DE20, 0x6D},
+ { 0x0000DB32, 0x00},
+ { 0x0000DD32, 0x00},
+ { 0x0000DB33, 0x0A},
+ { 0x0000DD33, 0x0A},
+ { 0x0000DB34, 0x1A},
+ { 0x0000DD34, 0x1A},
+ { 0x0000DB15, 0xEF},
+ { 0x0000DD15, 0xEF},
+ { 0x0000DB17, 0xEF},
+ { 0x0000DD17, 0xEF},
+ { 0x0000DB94, 0x70},
+ { 0x0000DD94, 0x70},
+ { 0x0000DB19, 0x40},
+ { 0x0000DD19, 0x40},
+ { 0x0000DB12, 0xC0},
+ { 0x0000DD12, 0xC0},
+ { 0x0000DB00, 0x4C},
+ { 0x0000DB04, 0x05},
+ { 0x0000DB05, 0x03},
+ { 0x0000DD04, 0x05},
+ { 0x0000DD05, 0x03},
+ { 0x0000DBBB, 0x09},
+ { 0x0000DBBC, 0x30},
+ { 0x0000DBBD, 0xF0},
+ { 0x0000DBBE, 0xF1},
+ { 0x0000DDBB, 0x09},
+ { 0x0000DDBC, 0x30},
+ { 0x0000DDBD, 0xF0},
+ { 0x0000DDBE, 0xF1},
+ { 0x0000DB01, 0x79},
+ { 0x0000DD01, 0x79},
+ { 0x0000DB08, 0x40},
+ { 0x0000DD08, 0x40},
+ { 0x0000DC52, 0xEF},
+ { 0x0000DE52, 0xEF},
+ { 0x0000DB00, 0xCC},
+ { 0x0000CC2C, 0x00},
+ { 0x0000CC2D, 0x2A},
+ { 0x0000CC2E, 0x83},
+ { 0x0000CC2F, 0xA8},
+ { 0x0000CD2C, 0x00},
+ { 0x0000CD2D, 0x2A},
+ { 0x0000CD2E, 0x83},
+ { 0x0000CD2F, 0xA8},
+ { 0x0000CC24, 0x00},
+ { 0x0000CC25, 0x51},
+ { 0x0000CC26, 0xEB},
+ { 0x0000CC27, 0x85},
+ { 0x0000CD24, 0x00},
+ { 0x0000CD25, 0x51},
+ { 0x0000CD26, 0xEB},
+ { 0x0000CD27, 0x85},
+ { 0x0000CC20, 0x00},
+ { 0x0000CC21, 0x00},
+ { 0x0000CC22, 0x43},
+ { 0x0000CD20, 0x00},
+ { 0x0000CD21, 0x00},
+ { 0x0000CD22, 0x43},
+ { 0x0000CC16, 0x0F},
+ { 0x0000CC17, 0x00},
+ { 0x0000CD16, 0x0F},
+ { 0x0000CD17, 0x00},
+ { 0x0000CC29, 0x5D},
+ { 0x0000CC2A, 0xC0},
+ { 0x0000CD29, 0x5D},
+ { 0x0000CD2A, 0xC0},
+ { 0x0000CC31, 0x20},
+ { 0x0000CC32, 0x00},
+ { 0x0000CC33, 0x00},
+ { 0x0000CC34, 0x00},
+ { 0x0000CD31, 0x20},
+ { 0x0000CD32, 0x00},
+ { 0x0000CD33, 0x00},
+ { 0x0000CD34, 0x00},
+ { 0x0000CC36, 0x79},
+ { 0x0000CC37, 0x99},
+ { 0x0000CC38, 0x99},
+ { 0x0000CC39, 0x99},
+ { 0x0000CD36, 0x79},
+ { 0x0000CD37, 0x99},
+ { 0x0000CD38, 0x99},
+ { 0x0000CD39, 0x99},
+ { 0x0000CC09, 0x00},
+ { 0x0000CC0A, 0x07},
+ { 0x0000CC0B, 0x5F},
+ { 0x0000CC0C, 0x6F},
+ { 0x0000CD09, 0x00},
+ { 0x0000CD0A, 0x07},
+ { 0x0000CD0B, 0x5F},
+ { 0x0000CD0C, 0x6F},
+ { 0x0000CC0E, 0x00},
+ { 0x0000CC0F, 0x03},
+ { 0x0000CC10, 0xAF},
+ { 0x0000CC11, 0xB7},
+ { 0x0000CD0E, 0x00},
+ { 0x0000CD0F, 0x03},
+ { 0x0000CD10, 0xAF},
+ { 0x0000CD11, 0xB7},
+ { 0x0000CCD6, 0x00},
+ { 0x0000CCD7, 0x03},
+ { 0x0000CDD6, 0x00},
+ { 0x0000CDD7, 0x03},
+ { 0x0000CCD8, 0x00},
+ { 0x0000CCD9, 0x03},
+ { 0x0000CDD8, 0x00},
+ { 0x0000CDD9, 0x03},
+ { 0x0000CCDA, 0x00},
+ { 0x0000CCDB, 0x03},
+ { 0x0000CDDA, 0x00},
+ { 0x0000CDDB, 0x03},
+ { 0x0000C320, 0x20},
+ { 0x0000C203, 0x9C},
+};
+
+static const struct reg_default rt1318_reg[] = {
+ { 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 },
+ { 0xc130, 0x59 },
+ { 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 },
+ { 0xc400, 0x00 },
+ { 0xc401, 0x00 },
+ { 0xc402, 0x00 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x00 },
+ { 0xc405, 0x00 },
+ { 0xc406, 0x00 },
+ { 0xc407, 0x00 },
+ { 0xc408, 0x00 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xce04, 0x08 },
+ { 0xce05, 0x00 },
+ { 0xce06, 0x00 },
+ { 0xce07, 0x00 },
+ { 0xce60, 0x63 },
+ { 0xcf04, 0x08 },
+ { 0xcf05, 0x00 },
+ { 0xcf06, 0x00 },
+ { 0xcf07, 0x00 },
+ { 0xdb00, 0x00 },
+ { 0xdb08, 0x40 },
+ { 0xdb12, 0x00 },
+ { 0xdb35, 0x00 },
+ { 0xdbb5, 0x00 },
+ { 0xdbb6, 0x40 },
+ { 0xdbb7, 0x00 },
+ { 0xdbb8, 0x00 },
+ { 0xdbc5, 0x00 },
+ { 0xdbc6, 0x00 },
+ { 0xdbc7, 0x00 },
+ { 0xdbc8, 0x00 },
+ { 0xdd08, 0x40 },
+ { 0xdd12, 0x00 },
+ { 0xdd35, 0x00 },
+ { 0xddb5, 0x00 },
+ { 0xddb6, 0x40 },
+ { 0xddb7, 0x00 },
+ { 0xddb8, 0x00 },
+ { 0xddc5, 0x00 },
+ { 0xddc6, 0x00 },
+ { 0xddc7, 0x00 },
+ { 0xddc8, 0x00 },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xdf00, 0x00 },
+ { 0xdf5f, 0x00 },
+ { 0xdf60, 0x00 },
+ { 0xe000, 0x08 },
+ { 0xe300, 0xa0 },
+ { 0xe400, 0x22 },
+ { 0xe706, 0x2f },
+ { 0xe707, 0x2f },
+ { 0xe806, 0x2f },
+ { 0xe807, 0x2f },
+ { 0xea00, 0x43 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0x00 },
+ { 0xf105, 0x00 },
+ { 0xf106, 0x00 },
+ { 0xf107, 0x00 },
+ { 0xf108, 0x00 },
+ { 0xf109, 0x00 },
+ { 0xf10a, 0x03 },
+ { 0xf10b, 0x40 },
+ { 0xf20b, 0x28 },
+ { 0xf20d, 0x00 },
+ { 0xf212, 0x00 },
+ { 0xf21a, 0x00 },
+ { 0xf223, 0x40 },
+ { 0xf224, 0x00 },
+ { 0xf225, 0x00 },
+ { 0xf226, 0x00 },
+ { 0xf227, 0x00 },
+ { 0xf242, 0x0c },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+};
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc130:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc400 ... 0xc408:
+ 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 0xe000:
+ case 0xe300:
+ case 0xe400:
+ case 0xe706 ... 0xe707:
+ case 0xe806 ... 0xe807:
+ case 0xea00:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf102 ... 0xf10b:
+ case 0xf20b:
+ case 0xf20d ... 0xf242:
+ case 0xf800 ... 0xf807:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1318_dvol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->rt1318_dvol = ucontrol->value.integer.value[0];
+
+ if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) {
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rt1318_dvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1318->rt1318_dvol;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+ SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0,
+ rt1318_dvol_get, rt1318_dvol_put),
+};
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ rt1318_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("Amp"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ {"DAC", NULL, "AIF1RX"},
+ {"Amp", NULL, "DAC"},
+};
+
+static int rt1318_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 4, 8, 16, 24};
+
+ 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 rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (lrclk) {
+ case RT1318_LRCLK_48000:
+ case RT1318_LRCLK_44100:
+ case RT1318_LRCLK_16000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4);
+ break;
+ case RT1318_LRCLK_96000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2);
+ break;
+ case RT1318_LRCLK_192000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock rate.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt1318_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_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int data_len = 0, ch_len = 0;
+ int pre_div, ret;
+
+ rt1318->lrck = params_rate(params);
+ pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ ret = rt1318_clk_ip_info(component, rt1318->lrck);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data_len = RT1318_I2S_DL_20;
+ ch_len = RT1318_I2S_DL_20;
+ break;
+ case 24:
+ data_len = RT1318_I2S_DL_24;
+ ch_len = RT1318_I2S_DL_24;
+ break;
+ case 32:
+ data_len = RT1318_I2S_DL_32;
+ ch_len = RT1318_I2S_DL_32;
+ break;
+ case 8:
+ data_len = RT1318_I2S_DL_8;
+ ch_len = RT1318_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_CLK2,
+ RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK,
+ pre_div << RT1318_DIV_AP_SFT |
+ pre_div << RT1318_DIV_DAMOD_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK3,
+ RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK,
+ pre_div << RT1318_AD_STO1_SFT |
+ pre_div << RT1318_AD_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK4,
+ RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK,
+ pre_div << RT1318_AD_ANA_STO1_SFT |
+ pre_div << RT1318_AD_ANA_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK5,
+ RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK,
+ pre_div << RT1318_DIV_FIFO_IN_SFT |
+ pre_div << RT1318_DIV_FIFO_OUT_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK6,
+ RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK |
+ RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT |
+ pre_div << RT1318_DIV_AD_MONO_SFT |
+ pre_div << RT1318_DIV_POST_G_SFT);
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK,
+ ch_len << RT1318_I2S_TX_CHL_SFT |
+ ch_len << RT1318_I2S_RX_CHL_SFT);
+
+ return 0;
+}
+
+static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(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 |= RT1318_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 |= RT1318_FMT_LEFT_J;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1318_FMT_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1318_FMT_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_I2S_FMT_MASK, reg_val);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int reg_val = 0;
+
+ if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1318_SCLK_S_BCLK:
+ reg_val |= RT1318_SYSCLK_BCLK;
+ break;
+ case RT1318_SCLK_S_SDW:
+ reg_val |= RT1318_SYSCLK_SDW;
+ break;
+ case RT1318_SCLK_S_PLL2F:
+ reg_val |= RT1318_SYSCLK_PLL2F;
+ break;
+ case RT1318_SCLK_S_PLL2B:
+ reg_val |= RT1318_SYSCLK_PLL2B;
+ break;
+ case RT1318_SCLK_S_MCLK:
+ reg_val |= RT1318_SYSCLK_MCLK;
+ break;
+ case RT1318_SCLK_S_RC0:
+ reg_val |= RT1318_SYSCLK_RC1;
+ break;
+ case RT1318_SCLK_S_RC1:
+ reg_val |= RT1318_SYSCLK_RC2;
+ break;
+ case RT1318_SCLK_S_RC2:
+ reg_val |= RT1318_SYSCLK_RC3;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1318->sysclk = freq;
+ rt1318->sysclk_src = clk_id;
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_SYSCLK_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {512000, 4096000, 22, 190, 0, true, false},
+ {1024000, 4096000, 22, 94, 0, true, false},
+ {1024000, 16384000, 4, 190, 0, true, false},
+ {1411200, 11289600, 6, 62, 0, true, false},
+ {1536000, 12288000, 6, 62, 0, true, false},
+ {2822400, 11289600, 6, 62, 0, true, false},
+ {2822400, 45158400, 0, 62, 0, true, false},
+ {2822400, 49152000, 0, 62, 0, true, false},
+ {3072000, 12288000, 6, 62, 0, true, false},
+ {3072000, 24576000, 2, 62, 0, true, false},
+ {3072000, 49152000, 0, 62, 0, true, false},
+ {6144000, 24576000, 2, 94, 4, false, false},
+ {6144000, 49152000, 0, 30, 0, true, false},
+ {6144000, 98304000, 0, 94, 4, false, true},
+ {12288000, 49152000, 0, 62, 6, false, false},
+};
+
+static int rt1318_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt1318_pll_code *pll_code)
+{
+ int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX;
+ int i, k, red, n_t, pll_out, in_t, out_t;
+ int n = 0, m = 0, m_t = 0;
+ int red_t = abs(freq_out - freq_in);
+ bool m_bypass = false, k_bypass = false;
+
+ if (RT1318_PLL_INP_MAX < freq_in || RT1318_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;
+ goto code_find;
+ }
+ }
+
+ k = 100000000 / freq_out - 2;
+ if (k > RT1318_PLL_K_MAX)
+ k = RT1318_PLL_K_MAX;
+ if (k < 0) {
+ k = 0;
+ k_bypass = true;
+ }
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = freq_in / (k_bypass ? 1 : (k + 2));
+ pll_out = freq_out / (n_t + 2);
+ if (in_t < 0)
+ continue;
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ red = abs(in_t - pll_out);
+ if (red < red_t) {
+ m_bypass = true;
+ n = n_t;
+ m = m_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);
+ red = abs(out_t - pll_out);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+ 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;
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ struct rt1318_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1318->pll_in = 0;
+ rt1318->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1318->pll_src && freq_in == rt1318->pll_in &&
+ freq_out == rt1318->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1318_PLL_S_BCLK0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0);
+ break;
+ case RT1318_PLL_S_BCLK1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1);
+ break;
+ case RT1318_PLL_S_RC:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_RC);
+ break;
+ case RT1318_PLL_S_MCLK:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK);
+ break;
+ case RT1318_PLL_S_SDW_IN_PLL:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1);
+ break;
+ case RT1318_PLL_S_SDW_0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2);
+ break;
+ case RT1318_PLL_S_SDW_1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3);
+ break;
+ case RT1318_PLL_S_SDW_2:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt1318_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupport 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_update_bits(rt1318->regmap, RT1318_PLL1_K,
+ RT1318_K_PLL1_MASK, pll_code.k_code);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_M,
+ RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code));
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8,
+ RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0,
+ RT1318_N_7_0_PLL1_MASK, pll_code.n_code);
+
+ rt1318->pll_in = freq_in;
+ rt1318->pll_out = freq_out;
+ rt1318->pll_src = source;
+
+ return 0;
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn |= RT1318_I2S_CH_TX_4CH;
+ cn |= RT1318_I2S_CH_RX_4CH;
+ break;
+ case 6:
+ cn |= RT1318_I2S_CH_TX_6CH;
+ cn |= RT1318_I2S_CH_RX_6CH;
+ break;
+ case 8:
+ cn |= RT1318_I2S_CH_TX_8CH;
+ cn |= RT1318_I2S_CH_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl |= RT1318_I2S_TX_CHL_20;
+ cl |= RT1318_I2S_RX_CHL_20;
+ break;
+ case 24:
+ cl |= RT1318_I2S_TX_CHL_24;
+ cl |= RT1318_I2S_RX_CHL_24;
+ break;
+ case 32:
+ cl |= RT1318_I2S_TX_CHL_32;
+ cl |= RT1318_I2S_RX_CHL_32;
+ break;
+ case 8:
+ cl |= RT1318_I2S_TX_CHL_8;
+ cl |= RT1318_I2S_RX_CHL_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_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1318_probe(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ schedule_work(&rt1318->cali_work);
+ rt1318->rt1318_dvol = RT1318_DVOL_STEP;
+
+ return 0;
+}
+
+static void rt1318_remove(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1318->cali_work);
+}
+
+#ifdef CONFIG_PM
+static int rt1318_suspend(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, true);
+ regcache_mark_dirty(rt1318->regmap);
+ return 0;
+}
+
+static int rt1318_resume(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+ return 0;
+}
+#else
+#define rt1318_suspend NULL
+#define rt1318_resume NULL
+#endif
+
+#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1318_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 rt1318_aif_dai_ops = {
+ .hw_params = rt1318_hw_params,
+ .set_fmt = rt1318_set_dai_fmt,
+ .set_sysclk = rt1318_set_dai_sysclk,
+ .set_pll = rt1318_set_dai_pll,
+ .set_tdm_slot = rt1318_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1318_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1318 = {
+ .probe = rt1318_probe,
+ .remove = rt1318_remove,
+ .suspend = rt1318_suspend,
+ .resume = rt1318_resume,
+ .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),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1318_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41001888,
+ .reg_defaults = rt1318_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt1318_i2c_id[] = {
+ { "rt1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id);
+
+static const struct of_device_id rt1318_of_match[] = {
+ { .compatible = "realtek,rt1318", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1318_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1318_acpi_match[] = {
+ { "10EC1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match);
+#endif
+
+static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,r0_l",
+ &rt1318->pdata.init_r0_l);
+ device_property_read_u32(dev, "realtek,r0_r",
+ &rt1318->pdata.init_r0_r);
+
+ return 0;
+}
+
+static void rt1318_calibration_sequence(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_CLK1, 0x22);
+ regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40);
+ regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20);
+ regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00);
+ regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x1C);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2);
+}
+
+static void rt1318_r0_calculate(struct rt1318_priv *rt1318)
+{
+ unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3;
+ unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3;
+ unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor;
+ unsigned int format = 16777216; /* 2^24 */
+
+ regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3);
+ r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3;
+ r0_l_integer = format / r0_l;
+ r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10;
+
+ regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3);
+ r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3;
+ r0_r_integer = format / r0_r;
+ r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10;
+
+ dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor);
+ dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor);
+}
+
+static void rt1318_r0_restore(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24,
+ (rt1318->pdata.init_r0_l >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16,
+ (rt1318->pdata.init_r0_l >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8,
+ (rt1318->pdata.init_r0_l >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0,
+ (rt1318->pdata.init_r0_l >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24,
+ (rt1318->pdata.init_r0_r >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16,
+ (rt1318->pdata.init_r0_r >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8,
+ (rt1318->pdata.init_r0_r >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0,
+ (rt1318->pdata.init_r0_r >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x9c);
+}
+
+static int rt1318_calibrate(struct rt1318_priv *rt1318)
+{
+ int chk_cnt = 30, count = 0;
+ int val, val2;
+
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1);
+ usleep_range(0, 10000);
+ rt1318_calibration_sequence(rt1318);
+
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ val = (val >> 1) & 0x1;
+ val2 = (val2 >> 1) & 0x1;
+ if (val & val2) {
+ dev_dbg(rt1318->component->dev, "Calibration done.\n");
+ break;
+ }
+ count++;
+ if (count == chk_cnt) {
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ return RT1318_R0_CALIB_NOT_DONE;
+ }
+ }
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ if ((val & 0x1) & (val2 & 0x1))
+ return RT1318_R0_IN_RANGE;
+ else
+ return RT1318_R0_OUT_OF_RANGE;
+}
+
+static void rt1318_calibration_work(struct work_struct *work)
+{
+ struct rt1318_priv *rt1318 =
+ container_of(work, struct rt1318_priv, cali_work);
+ int ret;
+
+ if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r)
+ rt1318_r0_restore(rt1318);
+ else {
+ ret = rt1318_calibrate(rt1318);
+ if (ret == RT1318_R0_IN_RANGE)
+ rt1318_r0_calculate(rt1318);
+ dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret);
+ }
+}
+
+static int rt1318_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1318_priv *rt1318;
+ int ret, val, val2, dev_id;
+
+ rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv),
+ GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1318);
+
+ if (pdata)
+ rt1318->pdata = *pdata;
+ else
+ rt1318_parse_dt(rt1318, &i2c->dev);
+
+ rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap);
+ if (IS_ERR(rt1318->regmap)) {
+ ret = PTR_ERR(rt1318->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val);
+ regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2);
+ dev_id = (val << 8) | val2;
+ if (dev_id != 0x6821) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt1318\n",
+ dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_register_patch(rt1318->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ INIT_WORK(&rt1318->cali_work, rt1318_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai));
+}
+
+static struct i2c_driver rt1318_i2c_driver = {
+ .driver = {
+ .name = "rt1318",
+ .of_match_table = of_match_ptr(rt1318_of_match),
+ .acpi_match_table = ACPI_PTR(rt1318_acpi_match),
+ },
+ .probe = rt1318_i2c_probe,
+ .id_table = rt1318_i2c_id,
+};
+module_i2c_driver(rt1318_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h
new file mode 100644
index 000000000000..cec40b484216
--- /dev/null
+++ b/sound/soc/codecs/rt1318.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318.h -- Platform data for RT1318
+ *
+ * Copyright 2024 Realtek Semiconductor Corp.
+ */
+#include <sound/rt1318.h>
+
+#ifndef __RT1318_H__
+#define __RT1318_H__
+
+struct rt1318_priv {
+ struct snd_soc_component *component;
+ struct rt1318_platform_data pdata;
+ struct work_struct cali_work;
+ struct regmap *regmap;
+
+ unsigned int r0_l_integer;
+ unsigned int r0_l_factor;
+ unsigned int r0_r_integer;
+ unsigned int r0_r_factor;
+ int rt1318_init;
+ int rt1318_dvol;
+ int sysclk_src;
+ int sysclk;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#define RT1318_PLL_INP_MAX 40000000
+#define RT1318_PLL_INP_MIN 256000
+#define RT1318_PLL_N_MAX 0x1ff
+#define RT1318_PLL_K_MAX 0x1f
+#define RT1318_PLL_M_MAX 0x1f
+
+#define RT1318_LRCLK_192000 192000
+#define RT1318_LRCLK_96000 96000
+#define RT1318_LRCLK_48000 48000
+#define RT1318_LRCLK_44100 44100
+#define RT1318_LRCLK_16000 16000
+#define RT1318_DVOL_STEP 383
+
+#define RT1318_CLK1 0xc001
+#define RT1318_CLK2 0xc003
+#define RT1318_CLK3 0xc004
+#define RT1318_CLK4 0xc005
+#define RT1318_CLK5 0xc006
+#define RT1318_CLK6 0xc007
+#define RT1318_CLK7 0xc008
+#define RT1318_PWR_STA1 0xc121
+#define RT1318_SPK_VOL_TH 0xc130
+#define RT1318_TCON 0xc203
+#define RT1318_SRC_TCON 0xc204
+#define RT1318_TCON_RELATE 0xc206
+#define RT1318_DA_VOL_L_8 0xc20b
+#define RT1318_DA_VOL_L_1_7 0xc20c
+#define RT1318_DA_VOL_R_8 0xc20d
+#define RT1318_DA_VOL_R_1_7 0xc20e
+#define RT1318_FEEDBACK_PATH 0xc321
+#define RT1318_STP_TEMP_L 0xdb00
+#define RT1318_STP_SEL_L 0xdb08
+#define RT1318_STP_R0_EN_L 0xdb12
+#define RT1318_R0_CMP_L_FLAG 0xdb35
+#define RT1318_PRE_R0_L_24 0xdbb5
+#define RT1318_PRE_R0_L_23_16 0xdbb6
+#define RT1318_PRE_R0_L_15_8 0xdbb7
+#define RT1318_PRE_R0_L_7_0 0xdbb8
+#define RT1318_R0_L_24 0xdbc5
+#define RT1318_R0_L_23_16 0xdbc6
+#define RT1318_R0_L_15_8 0xdbc7
+#define RT1318_R0_L_7_0 0xdbc8
+#define RT1318_STP_SEL_R 0xdd08
+#define RT1318_STP_R0_EN_R 0xdd12
+#define RT1318_R0_CMP_R_FLAG 0xdd35
+#define RT1318_PRE_R0_R_24 0xddb5
+#define RT1318_PRE_R0_R_23_16 0xddb6
+#define RT1318_PRE_R0_R_15_8 0xddb7
+#define RT1318_PRE_R0_R_7_0 0xddb8
+#define RT1318_R0_R_24 0xddc5
+#define RT1318_R0_R_23_16 0xddc6
+#define RT1318_R0_R_15_8 0xddc7
+#define RT1318_R0_R_7_0 0xddc8
+#define RT1318_DEV_ID1 0xf012
+#define RT1318_DEV_ID2 0xf013
+#define RT1318_PLL1_K 0xf20d
+#define RT1318_PLL1_M 0xf20f
+#define RT1318_PLL1_N_8 0xf211
+#define RT1318_PLL1_N_7_0 0xf212
+#define RT1318_SINE_GEN0 0xf800
+#define RT1318_TDM_CTRL1 0xf900
+#define RT1318_TDM_CTRL2 0xf901
+#define RT1318_TDM_CTRL3 0xf902
+#define RT1318_TDM_CTRL9 0xf908
+
+
+/* Clock-1 (0xC001) */
+#define RT1318_PLLIN_MASK (0x7 << 4)
+#define RT1318_PLLIN_BCLK0 (0x0 << 4)
+#define RT1318_PLLIN_BCLK1 (0x1 << 4)
+#define RT1318_PLLIN_RC (0x2 << 4)
+#define RT1318_PLLIN_MCLK (0x3 << 4)
+#define RT1318_PLLIN_SDW1 (0x4 << 4)
+#define RT1318_PLLIN_SDW2 (0x5 << 4)
+#define RT1318_PLLIN_SDW3 (0x6 << 4)
+#define RT1318_PLLIN_SDW4 (0x7 << 4)
+#define RT1318_SYSCLK_SEL_MASK (0x7 << 0)
+#define RT1318_SYSCLK_BCLK (0x0 << 0)
+#define RT1318_SYSCLK_SDW (0x1 << 0)
+#define RT1318_SYSCLK_PLL2F (0x2 << 0)
+#define RT1318_SYSCLK_PLL2B (0x3 << 0)
+#define RT1318_SYSCLK_MCLK (0x4 << 0)
+#define RT1318_SYSCLK_RC1 (0x5 << 0)
+#define RT1318_SYSCLK_RC2 (0x6 << 0)
+#define RT1318_SYSCLK_RC3 (0x7 << 0)
+/* Clock-2 (0xC003) */
+#define RT1318_DIV_AP_MASK (0x3 << 4)
+#define RT1318_DIV_AP_SFT 4
+#define RT1318_DIV_AP_DIV1 (0x0 << 4)
+#define RT1318_DIV_AP_DIV2 (0x1 << 4)
+#define RT1318_DIV_AP_DIV4 (0x2 << 4)
+#define RT1318_DIV_AP_DIV8 (0x3 << 4)
+#define RT1318_DIV_DAMOD_MASK (0x3 << 0)
+#define RT1318_DIV_DAMOD_SFT 0
+#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0)
+#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0)
+#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0)
+#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0)
+/* Clock-3 (0xC004) */
+#define RT1318_AD_STO1_MASK (0x7 << 4)
+#define RT1318_AD_STO1_SFT 4
+#define RT1318_AD_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_STO2_MASK (0x7 << 0)
+#define RT1318_AD_STO2_SFT 0
+#define RT1318_AD_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_STO2_SFT 0
+/* Clock-4 (0xC005) */
+#define RT1318_AD_ANA_STO1_MASK (0x7 << 4)
+#define RT1318_AD_ANA_STO1_SFT 4
+#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_ANA_STO2_MASK (0x7 << 0)
+#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_ANA_STO2_SFT 0
+/* Clock-5 (0xC006) */
+#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4)
+#define RT1318_DIV_FIFO_IN_SFT 4
+#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4)
+#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4)
+#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4)
+#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4)
+#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_SFT 0
+/* Clock-6 (0xC007) */
+#define RT1318_DIV_NLMS_MASK (0x3 << 6)
+#define RT1318_DIV_NLMS_SFT 6
+#define RT1318_DIV_NLMS_DIV1 (0x0 << 6)
+#define RT1318_DIV_NLMS_DIV2 (0x1 << 6)
+#define RT1318_DIV_NLMS_DIV4 (0x2 << 6)
+#define RT1318_DIV_NLMS_DIV8 (0x3 << 6)
+#define RT1318_DIV_AD_MONO_MASK (0x7 << 3)
+#define RT1318_DIV_AD_MONO_SFT 3
+#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3)
+#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3)
+#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3)
+#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3)
+#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3)
+#define RT1318_DIV_POST_G_MASK (0x7 << 0)
+#define RT1318_DIV_POST_G_SFT 0
+#define RT1318_DIV_POST_G_DIV1 (0x0 << 0)
+#define RT1318_DIV_POST_G_DIV2 (0x1 << 0)
+#define RT1318_DIV_POST_G_DIV4 (0x2 << 0)
+#define RT1318_DIV_POST_G_DIV8 (0x3 << 0)
+#define RT1318_DIV_POST_G_DIV16 (0x4 << 0)
+/* Power Status 1 (0xC121) */
+#define RT1318_PDB_CTRL_MASK (0x1)
+#define RT1318_PDB_CTRL_LOW (0x0)
+#define RT1318_PDB_CTRL_HIGH (0x1)
+#define RT1318_PDB_CTRL_SFT 0
+/* SRC Tcon(0xc204) */
+#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6)
+#define RT1318_SRCIN_IN_48K (0x0 << 6)
+#define RT1318_SRCIN_IN_44P1 (0x1 << 6)
+#define RT1318_SRCIN_IN_32K (0x2 << 6)
+#define RT1318_SRCIN_IN_16K (0x3 << 6)
+#define RT1318_SRCIN_F12288_MASK (0x3 << 4)
+#define RT1318_SRCIN_TCON1 (0x0 << 4)
+#define RT1318_SRCIN_TCON2 (0x1 << 4)
+#define RT1318_SRCIN_TCON4 (0x2 << 4)
+#define RT1318_SRCIN_TCON8 (0x3 << 4)
+#define RT1318_SRCIN_DACLK_MASK (0x3 << 2)
+#define RT1318_DACLK_TCON1 (0x0 << 2)
+#define RT1318_DACLK_TCON2 (0x1 << 2)
+#define RT1318_DACLK_TCON4 (0x2 << 2)
+#define RT1318_DACLK_TCON8 (0x3 << 2)
+/* R0 Compare Flag (0xDB35) */
+#define RT1318_R0_RANGE_MASK (0x1)
+#define RT1318_R0_OUTOFRANGE (0x0)
+#define RT1318_R0_INRANGE (0x1)
+/* PLL internal setting (0xF20D), K value */
+#define RT1318_K_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF20F), M value */
+#define RT1318_M_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF211), N_8 value */
+#define RT1318_N_8_PLL1_MASK (0x1 << 0)
+/* PLL internal setting (0xF212), N_7_0 value */
+#define RT1318_N_7_0_PLL1_MASK (0xff << 0)
+/* TDM CTRL 1 (0xf900) */
+#define RT1318_TDM_BCLK_MASK (0x1 << 7)
+#define RT1318_TDM_BCLK_NORM (0x0 << 7)
+#define RT1318_TDM_BCLK_INV (0x1 << 7)
+#define RT1318_I2S_FMT_MASK (0x7 << 0)
+#define RT1318_FMT_I2S (0x0 << 0)
+#define RT1318_FMT_LEFT_J (0x1 << 0)
+#define RT1318_FMT_PCM_A_R (0x2 << 0)
+#define RT1318_FMT_PCM_B_R (0x3 << 0)
+#define RT1318_FMT_PCM_A_F (0x6 << 0)
+#define RT1318_FMT_PCM_B_F (0x7 << 0)
+#define RT1318_I2S_FMT_SFT 0
+/* TDM CTRL 2 (0xf901) */
+#define RT1318_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1318_I2S_CH_TX_2CH (0x0 << 6)
+#define RT1318_I2S_CH_TX_4CH (0x1 << 6)
+#define RT1318_I2S_CH_TX_6CH (0x2 << 6)
+#define RT1318_I2S_CH_TX_8CH (0x3 << 6)
+#define RT1318_I2S_CH_RX_MASK (0x3 << 4)
+#define RT1318_I2S_CH_RX_2CH (0x0 << 4)
+#define RT1318_I2S_CH_RX_4CH (0x1 << 4)
+#define RT1318_I2S_CH_RX_6CH (0x2 << 4)
+#define RT1318_I2S_CH_RX_8CH (0x3 << 4)
+#define RT1318_I2S_DL_MASK 0x7
+#define RT1318_I2S_DL_SFT 0
+#define RT1318_I2S_DL_16 0x0
+#define RT1318_I2S_DL_20 0x1
+#define RT1318_I2S_DL_24 0x2
+#define RT1318_I2S_DL_32 0x3
+#define RT1318_I2S_DL_8 0x4
+/* TDM CTRL 3 (0xf902) */
+#define RT1318_I2S_TX_CHL_MASK (0x7 << 4)
+#define RT1318_I2S_TX_CHL_SFT 4
+#define RT1318_I2S_TX_CHL_16 (0x0 << 4)
+#define RT1318_I2S_TX_CHL_20 (0x1 << 4)
+#define RT1318_I2S_TX_CHL_24 (0x2 << 4)
+#define RT1318_I2S_TX_CHL_32 (0x3 << 4)
+#define RT1318_I2S_TX_CHL_8 (0x4 << 4)
+#define RT1318_I2S_RX_CHL_MASK (0x7 << 0)
+#define RT1318_I2S_RX_CHL_SFT 0
+#define RT1318_I2S_RX_CHL_16 (0x0 << 0)
+#define RT1318_I2S_RX_CHL_20 (0x1 << 0)
+#define RT1318_I2S_RX_CHL_24 (0x2 << 0)
+#define RT1318_I2S_RX_CHL_32 (0x3 << 0)
+#define RT1318_I2S_RX_CHL_8 (0x4 << 0)
+/* TDM CTRL 9 (0xf908) */
+#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+#define RT1318_REG_DISP_LEN 23
+
+/* System Clock Source */
+enum {
+ RT1318_SCLK_S_BCLK,
+ RT1318_SCLK_S_SDW,
+ RT1318_SCLK_S_PLL2F,
+ RT1318_SCLK_S_PLL2B,
+ RT1318_SCLK_S_MCLK,
+ RT1318_SCLK_S_RC0,
+ RT1318_SCLK_S_RC1,
+ RT1318_SCLK_S_RC2,
+};
+
+/* PLL Source */
+enum {
+ RT1318_PLL_S_BCLK0,
+ RT1318_PLL_S_BCLK1,
+ RT1318_PLL_S_RC,
+ RT1318_PLL_S_MCLK,
+ RT1318_PLL_S_SDW_IN_PLL,
+ RT1318_PLL_S_SDW_0,
+ RT1318_PLL_S_SDW_1,
+ RT1318_PLL_S_SDW_2,
+};
+
+/* TDM channel */
+enum {
+ RT1318_2CH,
+ RT1318_4CH,
+ RT1318_6CH,
+ RT1318_8CH,
+};
+
+/* R0 calibration result */
+enum {
+ RT1318_R0_OUT_OF_RANGE,
+ RT1318_R0_IN_RANGE,
+ RT1318_R0_CALIB_NOT_DONE,
+};
+
+/* PLL pre-defined M/N/K */
+
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+ bool k_bp;
+};
+
+struct rt1318_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;
+};
+
+#endif /* __RT1318_H__ */
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
new file mode 100644
index 000000000000..e3f9b03df3aa
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -0,0 +1,1823 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1320-sdw.c -- rt1320 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2024 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 <sound/tlv.h>
+#include <sound/sdw.h>
+#include "rt1320-sdw.h"
+#include "rt-sdw-common.h"
+
+/*
+ * The 'blind writes' is an SDCA term to deal with platform-specific initialization.
+ * It might include vendor-specific or SDCA control registers.
+ */
+static const struct reg_sequence rt1320_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xc01b, 0xfc },
+ { 0xc5c3, 0xf2 },
+ { 0xc5c2, 0x00 },
+ { 0xc5c6, 0x10 },
+ { 0xc5c4, 0x12 },
+ { 0xc5c8, 0x03 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc5d0, 0x0f },
+ { 0xc5d1, 0x89 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xc053, 0x55 },
+ { 0xc052, 0x55 },
+ { 0xc051, 0x13 },
+ { 0xc050, 0x15 },
+ { 0xc060, 0x77 },
+ { 0xc061, 0x55 },
+ { 0xc063, 0x55 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xca05, 0xd6 },
+ { 0xca25, 0xd6 },
+ { 0xcd00, 0x05 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0xff },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x04 },
+ { 0xc900, 0x73 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xc081, 0xfe },
+ { 0xce31, 0x0d },
+ { 0xce30, 0xae },
+ { 0xce37, 0x0b },
+ { 0xce36, 0xd2 },
+ { 0xce39, 0x04 },
+ { 0xce38, 0x80 },
+ { 0xce3f, 0x00 },
+ { 0xce3e, 0x00 },
+ { 0xd470, 0x8b },
+ { 0xd471, 0x18 },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x3f },
+ { 0xd486, 0xc3 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x01 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x02 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x0000d540, 0x01 },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { 0xd478, 0xff },
+};
+
+static const struct reg_sequence rt1320_vc_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xe80a, 0x01 },
+ { 0xc5c3, 0xf3 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xca05, 0xd6 },
+ { 0xca07, 0x07 },
+ { 0xca25, 0xd6 },
+ { 0xca27, 0x07 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xda81, 0x14 },
+ { 0xda8d, 0x14 },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0x7f },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x09 },
+ { 0xc900, 0xd0 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xc086, 0x02 },
+ { 0xc085, 0x7f },
+ { 0xc084, 0x00 },
+ { 0xc081, 0xfe },
+ { 0xf084, 0x0f },
+ { 0xf083, 0xff },
+ { 0xf082, 0xff },
+ { 0xf081, 0xff },
+ { 0xf080, 0xff },
+ { 0xe802, 0xf8 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xd470, 0xec },
+ { 0xd471, 0x3a },
+ { 0xd474, 0x11 },
+ { 0xd475, 0x32 },
+ { 0xd478, 0xff },
+ { 0xd479, 0x20 },
+ { 0xd47a, 0x10 },
+ { 0xd47c, 0xff },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x0b },
+ { 0xd487, 0x3b },
+ { 0xd486, 0xc3 },
+ { 0xc598, 0x04 },
+ { 0xdb03, 0xf0 },
+ { 0xdb09, 0x00 },
+ { 0xdb08, 0x7a },
+ { 0xdb19, 0x02 },
+ { 0xdb07, 0x5a },
+ { 0xdb05, 0x45 },
+ { 0xd500, 0x00 },
+ { 0xd500, 0x17 },
+ { 0xd600, 0x01 },
+ { 0xd601, 0x02 },
+ { 0xd602, 0x03 },
+ { 0xd603, 0x04 },
+ { 0xd64c, 0x03 },
+ { 0xd64d, 0x03 },
+ { 0xd64e, 0x03 },
+ { 0xd64f, 0x03 },
+ { 0xd650, 0x03 },
+ { 0xd651, 0x03 },
+ { 0xd652, 0x03 },
+ { 0xd610, 0x01 },
+ { 0xd608, 0x03 },
+ { 0xd609, 0x00 },
+ { 0x3fc2bf83, 0x00 },
+ { 0x3fc2bf82, 0x00 },
+ { 0x3fc2bf81, 0x00 },
+ { 0x3fc2bf80, 0x00 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x00 },
+ { 0x3fc2bfc3, 0x00 },
+ { 0x3fc2bfc2, 0x00 },
+ { 0x3fc2bfc1, 0x00 },
+ { 0x3fc2bfc0, 0x03 },
+ { 0x0000d486, 0x43 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00 },
+ { 0x1000db00, 0x07 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x1000db0b, 0x02 },
+ { 0x1000db0c, 0xf2 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0xe0 },
+ { 0x1000db10, 0x00 },
+ { 0x1000db11, 0x10 },
+ { 0x1000db12, 0x00 },
+ { 0x1000db13, 0x00 },
+ { 0x1000db14, 0x45 },
+ { 0x1000db15, 0x0d },
+ { 0x1000db16, 0x01 },
+ { 0x1000db17, 0x00 },
+ { 0x1000db18, 0x00 },
+ { 0x1000db19, 0xbf },
+ { 0x1000db1a, 0x13 },
+ { 0x1000db1b, 0x09 },
+ { 0x1000db1c, 0x00 },
+ { 0x1000db1d, 0x00 },
+ { 0x1000db1e, 0x00 },
+ { 0x1000db1f, 0x12 },
+ { 0x1000db20, 0x09 },
+ { 0x1000db21, 0x00 },
+ { 0x1000db22, 0x00 },
+ { 0x1000db23, 0x00 },
+ { 0x0000d540, 0x01 },
+ { 0x0000c081, 0xfc },
+ { 0x0000f01e, 0x80 },
+ { 0xc01b, 0xfc },
+ { 0xc5d1, 0x89 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_sequence rt1321_blind_write[] = {
+ { 0x0000c003, 0xf0 },
+ { 0x0000c01b, 0xfc },
+ { 0x0000c5c3, 0xf2 },
+ { 0x0000c5c2, 0x00 },
+ { 0x0000c5c1, 0x10 },
+ { 0x0000c5c0, 0x04 },
+ { 0x0000c5c7, 0x03 },
+ { 0x0000c5c6, 0x10 },
+ { 0x0000c526, 0x47 },
+ { 0x0000c5c4, 0x12 },
+ { 0x0000c5c5, 0x60 },
+ { 0x0000c520, 0x10 },
+ { 0x0000c521, 0x32 },
+ { 0x0000c5c7, 0x00 },
+ { 0x0000c5c8, 0x03 },
+ { 0x0000c5d3, 0x08 },
+ { 0x0000c5d2, 0x0a },
+ { 0x0000c5d1, 0x49 },
+ { 0x0000c5d0, 0x0f },
+ { 0x0000c580, 0x10 },
+ { 0x0000c581, 0x32 },
+ { 0x0000c582, 0x01 },
+ { 0x0000cb00, 0x03 },
+ { 0x0000cb02, 0x52 },
+ { 0x0000cb04, 0x80 },
+ { 0x0000cb0b, 0x01 },
+ { 0x0000c682, 0x60 },
+ { 0x0000c019, 0x10 },
+ { 0x0000c5f0, 0x01 },
+ { 0x0000c5f7, 0x22 },
+ { 0x0000c5f6, 0x22 },
+ { 0x0000c057, 0x51 },
+ { 0x0000c054, 0x55 },
+ { 0x0000c053, 0x55 },
+ { 0x0000c052, 0x55 },
+ { 0x0000c051, 0x01 },
+ { 0x0000c050, 0x15 },
+ { 0x0000c060, 0x99 },
+ { 0x0000c030, 0x55 },
+ { 0x0000c061, 0x55 },
+ { 0x0000c063, 0x55 },
+ { 0x0000c065, 0xa5 },
+ { 0x0000c06b, 0x0a },
+ { 0x0000ca05, 0xd6 },
+ { 0x0000ca07, 0x07 },
+ { 0x0000ca25, 0xd6 },
+ { 0x0000ca27, 0x07 },
+ { 0x0000cd00, 0x05 },
+ { 0x0000c604, 0x40 },
+ { 0x0000c609, 0x40 },
+ { 0x0000c046, 0xf7 },
+ { 0x0000c045, 0xff },
+ { 0x0000c044, 0xff },
+ { 0x0000c043, 0xff },
+ { 0x0000c042, 0xff },
+ { 0x0000c041, 0xff },
+ { 0x0000c040, 0xff },
+ { 0x0000c049, 0xff },
+ { 0x0000c028, 0x3f },
+ { 0x0000c020, 0x3f },
+ { 0x0000c032, 0x13 },
+ { 0x0000c033, 0x01 },
+ { 0x0000cc10, 0x01 },
+ { 0x0000dc20, 0x03 },
+ { 0x0000de03, 0x05 },
+ { 0x0000dc00, 0x00 },
+ { 0x0000c700, 0xf0 },
+ { 0x0000c701, 0x13 },
+ { 0x0000c900, 0xc3 },
+ { 0x0000c570, 0x08 },
+ { 0x0000c086, 0x02 },
+ { 0x0000c085, 0x7f },
+ { 0x0000c084, 0x00 },
+ { 0x0000c081, 0xff },
+ { 0x0000f084, 0x0f },
+ { 0x0000f083, 0xff },
+ { 0x0000f082, 0xff },
+ { 0x0000f081, 0xff },
+ { 0x0000f080, 0xff },
+ { 0x20003003, 0x3f },
+ { 0x20005818, 0x81 },
+ { 0x20009018, 0x81 },
+ { 0x2000301c, 0x81 },
+ { 0x0000c003, 0xc0 },
+ { 0x0000c047, 0x80 },
+ { 0x0000d541, 0x80 },
+ { 0x0000d487, 0x0b },
+ { 0x0000d487, 0x3b },
+ { 0x0000d486, 0xc3 },
+ { 0x0000d470, 0x89 },
+ { 0x0000d471, 0x3a },
+ { 0x0000d472, 0x1d },
+ { 0x0000d478, 0xff },
+ { 0x0000d479, 0x20 },
+ { 0x0000d47a, 0x10 },
+ { 0x0000d73c, 0xb7 },
+ { 0x0000d73d, 0xd7 },
+ { 0x0000d73e, 0x00 },
+ { 0x0000d73f, 0x10 },
+ { 0x3fc2dfc3, 0x00 },
+ { 0x3fc2dfc2, 0x00 },
+ { 0x3fc2dfc1, 0x00 },
+ { 0x3fc2dfc0, 0x07 },
+ { 0x3fc2dfc7, 0x00 },
+ { 0x3fc2dfc6, 0x00 },
+ { 0x3fc2dfc5, 0x00 },
+ { 0x3fc2dfc4, 0x01 },
+ { 0x3fc2df83, 0x00 },
+ { 0x3fc2df82, 0x00 },
+ { 0x3fc2df81, 0x00 },
+ { 0x3fc2df80, 0x00 },
+ { 0x0000d541, 0x40 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x03 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x10 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x45 },
+ { 0x1000db06, 0x12 },
+ { 0x1000db07, 0x09 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x00 },
+ { 0x1000db0b, 0x13 },
+ { 0x1000db0c, 0x09 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0x00 },
+ { 0x0000d540, 0x21 },
+ { 0x41000189, 0x00 },
+ { 0x4100018a, 0x00 },
+ { 0x41001988, 0x00 },
+ { 0x41081400, 0x09 },
+ { 0x40801508, 0x03 },
+ { 0x40801588, 0x03 },
+ { 0x40801809, 0x00 },
+ { 0x4080180a, 0x00 },
+ { 0x4080180b, 0x00 },
+ { 0x4080180c, 0x00 },
+ { 0x40801b09, 0x00 },
+ { 0x40801b0a, 0x00 },
+ { 0x40801b0b, 0x00 },
+ { 0x40801b0c, 0x00 },
+ { 0x0000d714, 0x17 },
+ { 0x20009012, 0x00 },
+ { 0x0000dd0b, 0x0d },
+ { 0x0000dd0a, 0xff },
+ { 0x0000dd09, 0x0d },
+ { 0x0000dd08, 0xff },
+ { 0x0000d172, 0x2a },
+ { 0x41001988, 0x03 },
+};
+
+static const struct reg_default rt1320_reg_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x0b },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_default rt1320_mbq_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+};
+
+static bool rt1320_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc086:
+ case 0xc400 ... 0xc409:
+ case 0xc480 ... 0xc48f:
+ case 0xc4c0 ... 0xc4c4:
+ case 0xc4e0 ... 0xc4e7:
+ case 0xc500:
+ case 0xc560 ... 0xc56b:
+ case 0xc570:
+ case 0xc580 ... 0xc59a:
+ case 0xc5b0 ... 0xc60f:
+ case 0xc640 ... 0xc64f:
+ case 0xc670:
+ case 0xc680 ... 0xc683:
+ case 0xc700 ... 0xc76f:
+ case 0xc800 ... 0xc801:
+ case 0xc820:
+ case 0xc900 ... 0xc901:
+ case 0xc920 ... 0xc921:
+ case 0xca00 ... 0xca07:
+ case 0xca20 ... 0xca27:
+ case 0xca40 ... 0xca4b:
+ case 0xca60 ... 0xca68:
+ case 0xca80 ... 0xca88:
+ case 0xcb00 ... 0xcb0c:
+ case 0xcc00 ... 0xcc12:
+ case 0xcc80 ... 0xcc81:
+ case 0xcd00:
+ case 0xcd80 ... 0xcd82:
+ case 0xce00 ... 0xce4d:
+ case 0xcf00 ... 0xcf25:
+ case 0xd000 ... 0xd0ff:
+ case 0xd100 ... 0xd1ff:
+ case 0xd200 ... 0xd2ff:
+ case 0xd300 ... 0xd3ff:
+ case 0xd400 ... 0xd403:
+ case 0xd410 ... 0xd417:
+ case 0xd470 ... 0xd497:
+ case 0xd4dc ... 0xd50f:
+ case 0xd520 ... 0xd543:
+ case 0xd560 ... 0xd5ef:
+ case 0xd600 ... 0xd663:
+ case 0xda00 ... 0xda6e:
+ case 0xda80 ... 0xda9e:
+ case 0xdb00 ... 0xdb7f:
+ case 0xdc00:
+ case 0xdc20 ... 0xdc21:
+ case 0xdd00 ... 0xdd17:
+ case 0xde00 ... 0xde09:
+ case 0xdf00 ... 0xdf1b:
+ case 0xe000 ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x1000cd91 ... 0x1000cd96:
+ case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc29d80 ... 0x3fc29d83:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x40880900/0x40880980 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x40881500 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ /* 0x41000189/0x4100018a */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x41001388 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41001988 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41080000 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ /* 0x41080200 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0):
+ /* 0x41080900 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41080980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081080 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081480/0x41081488 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ /* 0x41081980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc003:
+ case 0xc081:
+ case 0xc402 ... 0xc406:
+ case 0xc48c ... 0xc48f:
+ case 0xc560:
+ case 0xc5b5 ... 0xc5b7:
+ case 0xc5fc ... 0xc5ff:
+ case 0xc680 ... 0xc683:
+ case 0xc820:
+ case 0xc900:
+ case 0xc920:
+ case 0xca42:
+ case 0xca62:
+ case 0xca82:
+ case 0xcd00:
+ case 0xce03:
+ case 0xce10:
+ case 0xce14 ... 0xce17:
+ case 0xce44 ... 0xce49:
+ case 0xce4c ... 0xce4d:
+ case 0xcf0c:
+ case 0xcf10 ... 0xcf25:
+ case 0xd486 ... 0xd487:
+ case 0xd4e5 ... 0xd4e6:
+ case 0xd4e8 ... 0xd4ff:
+ case 0xd530:
+ case 0xd540 ... 0xd541:
+ case 0xd543:
+ case 0xdb58 ... 0xdb5f:
+ case 0xdb60 ... 0xdb63:
+ case 0xdb68 ... 0xdb69:
+ case 0xdb6d:
+ case 0xdb70 ... 0xdb71:
+ case 0xdb76:
+ case 0xdb7a:
+ case 0xdb7c ... 0xdb7f:
+ case 0xdd0c ... 0xdd13:
+ case 0xde02:
+ case 0xdf14 ... 0xdf1b:
+ case 0xe83c ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x10000000 ... 0x10008fff:
+ case 0x1000c000 ... 0x1000dfff:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2b780:
+ case 0x3fc2bf80 ... 0x3fc2bf83:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1320_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1320_readable_register,
+ .volatile_reg = rt1320_volatile_register,
+ .max_register = 0x41081980,
+ .reg_defaults = rt1320_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt1320_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt1320_mbq_readable_register,
+ .max_register = 0x41000192,
+ .reg_defaults = rt1320_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1320_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;
+
+ /*
+ * Due to support the multi-lane, we call 'sdw_slave_read_prop' to get the lane mapping
+ */
+ sdw_slave_read_prop(slave);
+
+ 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;
+ prop->lane_control_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(4) | BIT(8) | BIT(10);
+ 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 = 64;
+
+ /* BIOS may set wake_capable. Make sure it is 0 as wake events are disabled. */
+ prop->wake_capable = 0;
+
+ return 0;
+}
+
+static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 2000, val;
+
+ pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(func, entity, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt1320->sdw_slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * The 'patch code' is written to the patch code area.
+ * The patch code area is used for SDCA register expansion flexibility.
+ */
+static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ const struct firmware *patch;
+ const char *filename;
+ unsigned int addr, val, min_addr, max_addr;
+ const unsigned char *ptr;
+ int ret, i;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id <= RT1320_VB)
+ filename = RT1320_VAB_MCU_PATCH;
+ else
+ filename = RT1320_VC_MCU_PATCH;
+ min_addr = 0x10007000;
+ max_addr = 0x10007fff;
+ break;
+ case RT1321_DEV_ID:
+ filename = RT1321_VA_MCU_PATCH;
+ min_addr = 0x10008000;
+ max_addr = 0x10008fff;
+ break;
+ default:
+ dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ /* load the patch code here */
+ ret = request_firmware(&patch, filename, &slave->dev);
+ if (ret) {
+ dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename);
+ regmap_write(rt1320->regmap, 0xc598, 0x00);
+ regmap_write(rt1320->regmap, min_addr, 0x67);
+ regmap_write(rt1320->regmap, min_addr + 0x1, 0x80);
+ regmap_write(rt1320->regmap, min_addr + 0x2, 0x00);
+ regmap_write(rt1320->regmap, min_addr + 0x3, 0x00);
+ if (rt1320->dev_id == RT1321_DEV_ID) {
+ regmap_write(rt1320->regmap, 0xd73c, 0x67);
+ regmap_write(rt1320->regmap, 0xd73d, 0x80);
+ regmap_write(rt1320->regmap, 0xd73e, 0x00);
+ regmap_write(rt1320->regmap, 0xd73f, 0x00);
+ }
+ } else {
+ ptr = (const unsigned char *)patch->data;
+ if ((patch->size % 8) == 0) {
+ for (i = 0; i < patch->size; i += 8) {
+ addr = (ptr[i] & 0xff) | (ptr[i + 1] & 0xff) << 8 |
+ (ptr[i + 2] & 0xff) << 16 | (ptr[i + 3] & 0xff) << 24;
+ val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 |
+ (ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24;
+
+ if (addr > max_addr || addr < min_addr) {
+ dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr);
+ goto _exit_;
+ }
+ if (val > 0xff) {
+ dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val);
+ goto _exit_;
+ }
+ regmap_write(rt1320->regmap, addr, val);
+ }
+ }
+_exit_:
+ release_firmware(patch);
+ }
+}
+
+static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_blind_write); i++) {
+ reg = rt1320_blind_write[i].reg;
+ val = rt1320_blind_write[i].def;
+ delay = rt1320_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bfc7)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+ }
+}
+
+static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ unsigned int i, reg, val, delay, retry, tmp;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_vc_blind_write); i++) {
+ reg = rt1320_vc_blind_write[i].reg;
+ val = rt1320_vc_blind_write[i].def;
+ delay = rt1320_vc_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bf83)
+ rt1320_load_mcu_patch(rt1320);
+
+ if ((reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0)) &&
+ (val == 0x00)) {
+ retry = 200;
+ while (retry) {
+ regmap_read(rt1320->regmap, RT1320_KR0_INT_READY, &tmp);
+ dev_dbg(&slave->dev, "%s, RT1320_KR0_INT_READY=0x%x\n", __func__, tmp);
+ if (tmp == 0x1f)
+ break;
+ usleep_range(1000, 1500);
+ retry--;
+ }
+ if (!retry)
+ dev_warn(&slave->dev, "%s MCU is NOT ready!", __func__);
+ }
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static void rt1321_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) {
+ reg = rt1321_blind_write[i].reg;
+ val = rt1321_blind_write[i].def;
+ delay = rt1321_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2dfc3)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned int amp_func_status, val, tmp;
+
+ if (rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, true);
+ regcache_cache_bypass(rt1320->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ if (rt1320->version_id < 0) {
+ regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val);
+ rt1320->version_id = val;
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val);
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp);
+ rt1320->dev_id = (val << 8) | tmp;
+ }
+
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ /* initialization write */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id < RT1320_VC)
+ rt1320_vab_preset(rt1320);
+ else
+ rt1320_vc_preset(rt1320);
+ break;
+ case RT1321_DEV_ID:
+ rt1321_preset(rt1320);
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+ if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_1, &tmp);
+ val = (tmp << 8) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_2, &tmp);
+ val = (tmp << 16) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_3, &tmp);
+ val = (tmp << 24) | val;
+ dev_dbg(dev, "%s ROM version=0x%x\n", __func__, val);
+ /*
+ * We call the version b which has the new DSP ROM code against version a.
+ * Therefore, we read the DSP address to check the ID.
+ */
+ if (val == RT1320_VER_B_ID)
+ rt1320->version_id = RT1320_VB;
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3);
+ }
+ dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id);
+
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, false);
+ regcache_cache_bypass(rt1320->mbq_regmap, false);
+ regcache_mark_dirty(rt1320->regmap);
+ regcache_mark_dirty(rt1320->mbq_regmap);
+ }
+
+ /* Mark Slave initialization complete */
+ rt1320->first_hw_init = true;
+ rt1320->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1320_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1320->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1320->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1320_io_init(&slave->dev, slave);
+}
+
+static int rt1320_pde11_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_pde23_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_set_gain_put(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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_l_val, gain_r_val;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int changed = 0, reg_base;
+ struct rt_sdca_dmic_kctrl_priv *p;
+ unsigned int regvalue[4], gain_val[4], i;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt1320->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+
+ if (lvalue == gain_l_val && rvalue == gain_r_val)
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt1320->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt1320->mbq_regmap, mc->rreg, gain_r_val);
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ break;
+ }
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ break;
+ }
+
+ if (err < 0)
+ dev_err(&rt1320->sdw_slave->dev, "0x%08x can't be set\n", reg_base + i);
+ }
+
+_done_:
+ return 1;
+}
+
+static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int reg_base, regvalue, ctl, i;
+ struct rt_sdca_dmic_kctrl_priv *p;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r);
+
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+
+ if (read_l != read_r)
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ break;
+ }
+
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+ ucontrol->value.integer.value[i] = ctl;
+ }
+_done_:
+ return 0;
+}
+
+static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) {
+ ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2)
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ else
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+ break;
+ case RT1321_DEV_ID:
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ break;
+ default:
+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt1320->fu_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt1320->fu_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt1320->fu_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt1320_set_fu_capture_ctl(rt1320);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt1320_dmic_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt1320_dmic_fu_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt1320->fu_dapm_mute = false;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt1320->fu_dapm_mute = true;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ }
+ return 0;
+}
+
+static const char * const rt1320_rx_data_ch_select[] = {
+ "L,R",
+ "R,L",
+ "L,L",
+ "R,R",
+ "L,L+R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0,
+ rt1320_rx_data_ch_select);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+
+static const struct snd_kcontrol_new rt1320_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0, rt1320_set_gain_get, rt1320_set_gain_put, out_vol_tlv),
+ SOC_ENUM("RX Channel Select", rt1320_rx_data_ch_enum),
+
+ RT_SDCA_FU_CTRL("FU Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4, rt1320_dmic_fu_info, rt1320_dmic_fu_capture_get, rt1320_dmic_fu_capture_put),
+ RT_SDCA_EXT_TLV("FU Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info),
+};
+
+static const struct snd_kcontrol_new rt1320_spk_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt1320_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP8-10TX", "DP8-10 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("FU21", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt1320_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt1320_pde11_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC("FU 113", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("FU 14", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA_E("FU", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1320_dmic_fu_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt1320_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt1320_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+
+ /* Input */
+ SND_SOC_DAPM_PGA("AEC Data", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("AEC Gen"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+};
+
+static const struct snd_soc_dapm_route rt1320_dapm_routes[] = {
+ { "FU21", NULL, "DP1RX" },
+ { "FU21", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU21" },
+ { "OT23 R", "Switch", "FU21" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+
+ { "AEC Data", NULL, "AEC Gen" },
+ { "DP4TX", NULL, "AEC Data" },
+
+ {"DP8-10TX", NULL, "FU"},
+ {"FU", NULL, "PDE 11"},
+ {"FU", NULL, "FU 113"},
+ {"FU", NULL, "FU 14"},
+ {"FU 113", NULL, "DMIC1"},
+ {"FU 14", NULL, "DMIC2"},
+};
+
+static int rt1320_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 rt1320_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1320_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 rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_port_config dmic_port_config[2];
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ 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 (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 1;
+ else
+ return -EINVAL;
+ } else {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 4;
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[1].num = 10;
+ break;
+ case RT1321_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ }
+
+ if (dai->id == RT1320_AIF1)
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 2, sdw_stream);
+ break;
+ case RT1321_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 1, sdw_stream);
+ break;
+ default:
+ dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT1320_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT1320_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT1320_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1320_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1320_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1320_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT1320_AIF1)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ else {
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ }
+
+ return 0;
+}
+
+static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1320->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 rt1320_slave_ops = {
+ .read_prop = rt1320_read_prop,
+ .update_status = rt1320_update_status,
+};
+
+static int rt1320_sdw_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ rt1320->component = component;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ dev_dbg(&rt1320->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_rt1320 = {
+ .probe = rt1320_sdw_component_probe,
+ .controls = rt1320_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1320_snd_controls),
+ .dapm_widgets = rt1320_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1320_dapm_widgets),
+ .dapm_routes = rt1320_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1320_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1320_aif_dai_ops = {
+ .hw_params = rt1320_sdw_hw_params,
+ .hw_free = rt1320_sdw_pcm_hw_free,
+ .set_stream = rt1320_set_sdw_stream,
+ .shutdown = rt1320_sdw_shutdown,
+};
+
+#define RT1320_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 RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt1320_sdw_dai[] = {
+ {
+ .name = "rt1320-aif1",
+ .id = RT1320_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+ /* DMIC: DP8 2ch + DP10 2ch */
+ {
+ .name = "rt1320-aif2",
+ .id = RT1320_AIF2,
+ .capture = {
+ .stream_name = "DP8-10 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+};
+
+static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320;
+ int ret;
+
+ rt1320 = devm_kzalloc(dev, sizeof(*rt1320), GFP_KERNEL);
+ if (!rt1320)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1320);
+ rt1320->sdw_slave = slave;
+ rt1320->mbq_regmap = mbq_regmap;
+ rt1320->regmap = regmap;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1320->hw_init = false;
+ rt1320->first_hw_init = false;
+ rt1320->version_id = -1;
+ rt1320->fu_dapm_mute = true;
+ rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] =
+ rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1320,
+ rt1320_sdw_dai,
+ ARRAY_SIZE(rt1320_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1320_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt1320_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt1320_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt1320_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+/*
+ * Version A/B will use the class id 0
+ * The newer version than A/B will use the class id 1, so add it in advance
+ */
+static const struct sdw_device_id rt1320_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1320_id);
+
+static int rt1320_dev_suspend(struct device *dev)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+ return 0;
+}
+
+#define RT1320_PROBE_TIMEOUT 5000
+
+static int rt1320_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1320_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_sync(rt1320->regmap);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ regcache_sync(rt1320->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt1320_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume)
+ RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1320_sdw_driver = {
+ .driver = {
+ .name = "rt1320-sdca",
+ .pm = pm_ptr(&rt1320_pm),
+ },
+ .probe = rt1320_sdw_probe,
+ .remove = rt1320_sdw_remove,
+ .ops = &rt1320_slave_ops,
+ .id_table = rt1320_id,
+};
+module_sdw_driver(rt1320_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
new file mode 100644
index 000000000000..a6d90e259dc9
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1320-sdw.h -- RT1320 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1320_SDW_H__
+#define __RT1320_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>
+
+#define RT1320_DEV_ID 0x6981
+#define RT1321_DEV_ID 0x7045
+
+/* imp-defined registers */
+#define RT1320_DEV_VERSION_ID_1 0xc404
+#define RT1320_DEV_ID_1 0xc405
+#define RT1320_DEV_ID_0 0xc406
+
+#define RT1321_PATCH_MAIN_VER 0x1000cffe
+#define RT1321_PATCH_BETA_VER 0x1000cfff
+
+#define RT1320_KR0_STATUS_CNT 0x1000f008
+#define RT1320_KR0_INT_READY 0x1000f021
+#define RT1320_HIFI_VER_0 0x3fe2e000
+#define RT1320_HIFI_VER_1 0x3fe2e001
+#define RT1320_HIFI_VER_2 0x3fe2e002
+#define RT1320_HIFI_VER_3 0x3fe2e003
+
+/* RT1320 SDCA Control - function number */
+#define FUNC_NUM_AMP 0x04
+#define FUNC_NUM_MIC 0x02
+
+/* RT1320 SDCA entity */
+#define RT1320_SDCA_ENT0 0x00
+#define RT1320_SDCA_ENT_PDE11 0x2a
+#define RT1320_SDCA_ENT_PDE23 0x33
+#define RT1320_SDCA_ENT_PDE27 0x27
+#define RT1320_SDCA_ENT_FU14 0x32
+#define RT1320_SDCA_ENT_FU21 0x03
+#define RT1320_SDCA_ENT_FU113 0x30
+#define RT1320_SDCA_ENT_CS14 0x13
+#define RT1320_SDCA_ENT_CS21 0x21
+#define RT1320_SDCA_ENT_CS113 0x12
+#define RT1320_SDCA_ENT_SAPU 0x29
+#define RT1320_SDCA_ENT_PPU21 0x04
+
+/* RT1320 SDCA control */
+#define RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1320_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1320_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+#define RT1320_SDCA_CTL_FU_MUTE 0x01
+#define RT1320_SDCA_CTL_FU_VOLUME 0x02
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_MODE 0x10
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11
+#define RT1320_SDCA_CTL_POSTURE_NUMBER 0x10
+#define RT1320_SDCA_CTL_FUNC_STATUS 0x10
+
+/* RT1320 SDCA channel */
+#define CH_01 0x01
+#define CH_02 0x02
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+/* Sample Frequency Index */
+#define RT1320_SDCA_RATE_16000HZ 0x04
+#define RT1320_SDCA_RATE_32000HZ 0x07
+#define RT1320_SDCA_RATE_44100HZ 0x08
+#define RT1320_SDCA_RATE_48000HZ 0x09
+#define RT1320_SDCA_RATE_96000HZ 0x0b
+#define RT1320_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT1320_AIF1,
+ RT1320_AIF2,
+};
+
+/*
+ * The version id will be useful to distinguish the capability between the different IC versions.
+ * Currently, VA and VB have different DSP FW versions.
+ */
+enum rt1320_version_id {
+ RT1320_VA,
+ RT1320_VB,
+ RT1320_VC,
+};
+
+#define RT1320_VER_B_ID 0x07392238
+#define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin"
+#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
+#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
+
+struct rt1320_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int version_id;
+ unsigned int dev_id;
+ bool fu_dapm_mute;
+ bool fu_mixer_mute[4];
+};
+
+#endif /* __RT1320_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 8f92e5c4dd9d..2c055c45111f 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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>
@@ -38,7 +35,7 @@ struct rt274_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
struct delayed_work jack_detect_work;
@@ -338,13 +335,13 @@ static bool rt274_readable_register(struct device *dev, unsigned int reg)
}
#ifdef CONFIG_PM
-static void rt274_index_sync(struct snd_soc_codec *codec)
+static void rt274_index_sync(struct snd_soc_component *component)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt274->index_cache[i].reg,
+ snd_soc_component_write(component, rt274->index_cache[i].reg,
rt274->index_cache[i].def);
}
}
@@ -353,16 +350,23 @@ static void rt274_index_sync(struct snd_soc_codec *codec)
static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic)
{
unsigned int buf;
+ int ret;
*hp = false;
*mic = false;
- if (!rt274->codec)
+ if (!rt274->component)
return -EINVAL;
- regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf);
+ ret = regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf);
+ if (ret)
+ return ret;
+
*hp = buf & 0x80000000;
- regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf);
+ 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);
@@ -381,10 +385,10 @@ static void rt274_jack_detect_work(struct work_struct *work)
if (rt274_jack_detect(rt274, &hp, &mic) < 0)
return;
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt274->jack, status,
@@ -393,10 +397,12 @@ static void rt274_jack_detect_work(struct work_struct *work)
static irqreturn_t rt274_irq(int irq, void *data);
-static int rt274_mic_detect(struct snd_soc_codec *codec,
+static int rt274_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ rt274->jack = jack;
if (jack == NULL) {
/* Disable jack detection */
@@ -405,7 +411,6 @@ static int rt274_mic_detect(struct snd_soc_codec *codec,
return 0;
}
- rt274->jack = jack;
regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
RT274_IRQ_EN, RT274_IRQ_EN);
@@ -609,8 +614,8 @@ static int rt274_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 rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -620,7 +625,7 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -628,7 +633,7 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt274->sys_clk);
return -EINVAL;
}
@@ -636,7 +641,7 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt274->sys_clk);
return -EINVAL;
}
@@ -647,7 +652,7 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ dev_err(component->dev, "Unsupported channels %d\n",
params_channels(params));
return -EINVAL;
}
@@ -685,29 +690,29 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
if (rt274->master)
c_len_code = 0x3;
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, 0xc018, d_len_code << 3 | c_len_code << 14);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT274_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT274_ADC_FORMAT, 0x407f, 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_codec *codec = dai->codec;
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_S);
rt274->master = false;
break;
@@ -717,27 +722,27 @@ static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec, RT274_I2S_CTRL1,
+ 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_update_bits(codec, RT274_I2S_CTRL1,
+ 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_update_bits(codec, RT274_I2S_CTRL1,
+ 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_update_bits(codec, RT274_I2S_CTRL1,
+ 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_update_bits(codec, RT274_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT274_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -745,47 +750,49 @@ static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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_codec *codec = dai->codec;
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, RT274_PLL2_CTRL,
+ snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_MCLK);
break;
default:
- dev_warn(codec->dev, "invalid pll source, use BCLK\n");
+ dev_warn(component->dev, "invalid pll source, use BCLK\n");
+ fallthrough;
case RT274_PLL2_S_BCLK:
- snd_soc_update_bits(codec, RT274_PLL2_CTRL,
+ 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_update_bits(codec, RT274_MCLK_CTRL,
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
(0x3 << 12), (0x3 << 12));
switch (rt274->fs) {
case 50:
- snd_soc_write(codec, 0x7a, 0xaab6);
- snd_soc_write(codec, 0x7b, 0x0301);
- snd_soc_write(codec, 0x7c, 0x04fe);
+ 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_write(codec, 0x7a, 0xaa96);
- snd_soc_write(codec, 0x7b, 0x8003);
- snd_soc_write(codec, 0x7c, 0x081e);
+ 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_write(codec, 0x7a, 0xaa96);
- snd_soc_write(codec, 0x7b, 0x8003);
- snd_soc_write(codec, 0x7c, 0x080e);
+ 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(codec->dev, "invalid freq_in, assume 4.8M\n");
+ dev_warn(component->dev, "invalid freq_in, assume 4.8M\n");
+ fallthrough;
case 100:
- snd_soc_write(codec, 0x7a, 0xaab6);
- snd_soc_write(codec, 0x7b, 0x0301);
- snd_soc_write(codec, 0x7c, 0x047e);
+ snd_soc_component_write(component, 0x7a, 0xaab6);
+ snd_soc_component_write(component, 0x7b, 0x0301);
+ snd_soc_component_write(component, 0x7c, 0x047e);
break;
}
}
@@ -796,11 +803,11 @@ static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt274_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
switch (clk_id) {
case RT274_SCLK_S_MCLK:
@@ -818,43 +825,43 @@ static int rt274_set_dai_sysclk(struct snd_soc_dai *dai,
default:
mclk_en = RT274_MCLK_MODE_DIS;
clk_src = RT274_CLK_SRC_MCLK;
- dev_warn(codec->dev, "invalid sysclk source, use PLL1\n");
+ dev_warn(component->dev, "invalid sysclk source, use PLL1\n");
break;
}
- snd_soc_update_bits(codec, RT274_MCLK_CTRL,
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
RT274_MCLK_MODE_MASK, mclk_en);
- snd_soc_update_bits(codec, RT274_CLK_CTRL,
+ 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(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (clk_id == RT274_SCLK_S_MCLK) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_MCLK_CTRL, 0x1fcf, 0x0008);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_MCLK_CTRL, 0x1fcf, 0x1543);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -866,16 +873,16 @@ static int rt274_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt274_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
rt274->fs = ratio;
if ((ratio / 50) == 0)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, 0x1000, 0x0);
@@ -886,28 +893,28 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (rx_mask || tx_mask) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_EN);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_DIS);
return 0;
}
switch (slots) {
case 4:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_4CH);
break;
case 2:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_2CH);
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Support 2 or 4 slots TDM only\n");
return -EINVAL;
}
@@ -915,20 +922,20 @@ static int rt274_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int rt274_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_bias_level(codec)) {
- snd_soc_write(codec,
+ 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_write(codec,
+ snd_soc_component_write(component,
RT274_SET_AUDIO_POWER, AC_PWRST_D3);
break;
@@ -953,10 +960,10 @@ static irqreturn_t rt274_irq(int irq, void *data)
ret = rt274_jack_detect(rt274, &hp, &mic);
if (ret == 0) {
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt274->jack, status,
@@ -968,35 +975,31 @@ static irqreturn_t rt274_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt274_probe(struct snd_soc_codec *codec)
+static int rt274_probe(struct snd_soc_component *component)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
- rt274->codec = codec;
+ rt274->component = component;
+ INIT_DELAYED_WORK(&rt274->jack_detect_work, rt274_jack_detect_work);
- if (rt274->i2c->irq) {
- 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));
- }
-
+ msecs_to_jiffies(1250));
return 0;
}
-static int rt274_remove(struct snd_soc_codec *codec)
+static void rt274_remove(struct snd_soc_component *component)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt274->jack_detect_work);
-
- return 0;
+ rt274->component = NULL;
}
#ifdef CONFIG_PM
-static int rt274_suspend(struct snd_soc_codec *codec)
+static int rt274_suspend(struct snd_soc_component *component)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt274->regmap, true);
regcache_mark_dirty(rt274->regmap);
@@ -1004,12 +1007,12 @@ static int rt274_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt274_resume(struct snd_soc_codec *codec)
+static int rt274_resume(struct snd_soc_component *component)
{
- struct rt274_priv *rt274 = snd_soc_codec_get_drvdata(codec);
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt274->regmap, false);
- rt274_index_sync(codec);
+ rt274_index_sync(component);
regcache_sync(rt274->regmap);
return 0;
@@ -1051,26 +1054,25 @@ static struct snd_soc_dai_driver rt274_dai[] = {
.formats = RT274_FORMATS,
},
.ops = &rt274_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt274 = {
- .probe = rt274_probe,
- .remove = rt274_remove,
- .suspend = rt274_suspend,
- .resume = rt274_resume,
- .set_bias_level = rt274_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .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),
- },
- .set_jack = rt274_mic_detect,
+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 = {
@@ -1089,26 +1091,27 @@ static const struct regmap_config rt274_regmap = {
#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},
+ {"rt274"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt274_acpi_match[] = {
- { "10EC0274", 0 },
- { "INT34C2", 0 },
- {},
+ { "10EC0274" },
+ { "INT34C2" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
+#endif
-static int rt274_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt274_i2c_probe(struct i2c_client *i2c)
{
struct rt274_priv *rt274;
@@ -1128,8 +1131,11 @@ static int rt274_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt274->regmap,
+ 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);
@@ -1186,26 +1192,24 @@ static int rt274_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt274,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt274,
rt274_dai, ARRAY_SIZE(rt274_dai));
return ret;
}
-static int rt274_i2c_remove(struct i2c_client *i2c)
+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);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
diff --git a/sound/soc/codecs/rt274.h b/sound/soc/codecs/rt274.h
index 4fd1bcb73dba..0fcf942fa183 100644
--- a/sound/soc/codecs/rt274.h
+++ b/sound/soc/codecs/rt274.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt274.h -- RT274 ALSA SoC audio driver
*
* Copyright 2016 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT274_H__
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index af6325c78292..fd8de8b49793 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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>
@@ -40,7 +37,7 @@ struct rt286_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt286_platform_data pdata;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
@@ -174,6 +171,9 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
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):
@@ -187,13 +187,13 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
}
#ifdef CONFIG_PM
-static void rt286_index_sync(struct snd_soc_codec *codec)
+static void rt286_index_sync(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt286->index_cache[i].reg,
+ snd_soc_component_write(component, rt286->index_cache[i].reg,
rt286->index_cache[i].def);
}
}
@@ -220,10 +220,10 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*hp = false;
*mic = false;
- if (!rt286->codec)
+ if (!rt286->component)
return -EINVAL;
- dapm = snd_soc_codec_get_dapm(rt286->codec);
+ dapm = snd_soc_component_get_dapm(rt286->component);
if (rt286->pdata.cbj_en) {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -255,11 +255,16 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
msleep(300);
regmap_read(rt286->regmap,
RT286_CBJ_CTRL2, &val);
- if (0x0070 == (val & 0x0070))
+ if (0x0070 == (val & 0x0070)) {
*mic = true;
- else
+ } else {
*mic = false;
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
}
+
regmap_update_bits(rt286->regmap,
RT286_DC_GAIN, 0x200, 0x0);
@@ -276,11 +281,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*mic = buf & 0x80000000;
}
- snd_soc_dapm_disable_pin(dapm, "HV");
- snd_soc_dapm_disable_pin(dapm, "VREF");
- if (!*hp)
+ 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);
+ snd_soc_dapm_sync(dapm);
+ }
return 0;
}
@@ -295,20 +301,21 @@ static void rt286_jack_detect_work(struct work_struct *work)
rt286_jack_detect(rt286, &hp, &mic);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+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_codec_get_dapm(codec);
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -329,13 +336,12 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
return 0;
}
-EXPORT_SYMBOL_GPL(rt286_mic_detect);
static int is_mclk_mode(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -434,15 +440,15 @@ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
break;
@@ -456,14 +462,14 @@ static int rt286_spk_event(struct snd_soc_dapm_widget *w,
static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0);
break;
default:
return 0;
@@ -475,14 +481,14 @@ static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x08);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x30);
break;
default:
return 0;
@@ -494,19 +500,19 @@ static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
break;
default:
@@ -674,8 +680,8 @@ static int rt286_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 rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -687,7 +693,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -695,7 +701,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -703,7 +709,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -714,12 +720,11 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ 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:
@@ -745,27 +750,27 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x800);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x0);
break;
default:
@@ -774,27 +779,27 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
- snd_soc_update_bits(codec,
+ 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_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -802,58 +807,58 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
if (RT286_SCLK_S_MCLK == clk_id) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x20);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0100);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL, 0x4, 0x4);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x8);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -865,42 +870,42 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
-static int rt286_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_bias_level(codec)) {
- snd_soc_write(codec,
+ 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_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x200);
}
break;
case SND_SOC_BIAS_ON:
mdelay(10);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
break;
@@ -923,10 +928,10 @@ static irqreturn_t rt286_irq(int irq, void *data)
/* Clear IRQ */
regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
@@ -937,38 +942,31 @@ static irqreturn_t rt286_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt286_probe(struct snd_soc_codec *codec)
+static int rt286_probe(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- rt286->codec = codec;
+ rt286->component = component;
+ INIT_DELAYED_WORK(&rt286->jack_detect_work, rt286_jack_detect_work);
- if (rt286->i2c->irq) {
- regmap_update_bits(rt286->regmap,
- RT286_IRQ_CTRL, 0x2, 0x2);
-
- 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(1250));
- }
-
+ msecs_to_jiffies(50));
return 0;
}
-static int rt286_remove(struct snd_soc_codec *codec)
+static void rt286_remove(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt286->jack_detect_work);
-
- return 0;
+ rt286->component = NULL;
}
#ifdef CONFIG_PM
-static int rt286_suspend(struct snd_soc_codec *codec)
+static int rt286_suspend(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, true);
regcache_mark_dirty(rt286->regmap);
@@ -976,12 +974,12 @@ static int rt286_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt286_resume(struct snd_soc_codec *codec)
+static int rt286_resume(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, false);
- rt286_index_sync(codec);
+ rt286_index_sync(component);
regcache_sync(rt286->regmap);
return 0;
@@ -1021,7 +1019,7 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt286-aif2",
@@ -1041,26 +1039,26 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt286 = {
- .probe = rt286_probe,
- .remove = rt286_remove,
- .suspend = rt286_suspend,
- .resume = rt286_resume,
- .set_bias_level = rt286_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .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),
- },
+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 = {
@@ -1077,17 +1075,20 @@ static const struct regmap_config rt286_regmap = {
};
static const struct i2c_device_id rt286_i2c_id[] = {
- {"rt286", 0},
- {"rt288", 0},
+ {"rt286"},
+ {"rt288"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt286_acpi_match[] = {
- { "INT343A", 0 },
- {},
+ { "10EC0286" },
+ { "INT343A" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+#endif
static const struct dmi_system_id force_combo_jack_table[] = {
{
@@ -1119,23 +1120,21 @@ static const struct dmi_system_id force_combo_jack_table[] = {
{ }
};
-static const struct dmi_system_id dmi_dell_dino[] = {
+static const struct dmi_system_id dmi_dell[] = {
{
- .ident = "Dell Dino",
+ .ident = "Dell",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343")
}
},
{ }
};
-static int rt286_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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, val;
+ int i, ret, vendor_id;
rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
GFP_KERNEL);
@@ -1151,14 +1150,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
}
ret = regmap_read(rt286->regmap,
- RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+ 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 (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
+ if (vendor_id != RT286_VENDOR_ID && vendor_id != RT288_VENDOR_ID) {
dev_err(&i2c->dev,
- "Device with ID register %#x is not rt286\n", val);
+ "Device with ID register %#x is not rt286\n",
+ vendor_id);
return -ENODEV;
}
@@ -1182,8 +1182,8 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt286->pdata = *pdata;
- if (dmi_check_system(force_combo_jack_table) ||
- dmi_check_system(dmi_dell_dino))
+ 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);
@@ -1206,7 +1206,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
mdelay(10);
if (!rt286->pdata.gpio2_en)
- regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x40);
else
regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
@@ -1222,7 +1222,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
- if (dmi_check_system(dmi_dell_dino)) {
+ 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,
@@ -1238,26 +1238,24 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt286,
rt286_dai, ARRAY_SIZE(rt286_dai));
return ret;
}
-static int rt286_i2c_remove(struct i2c_client *i2c)
+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);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
index 7130edb152ef..4b7a3bd6043d 100644
--- a/sound/soc/codecs/rt286.h
+++ b/sound/soc/codecs/rt286.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt286.h -- RT286 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 __RT286_H__
@@ -199,7 +196,5 @@ enum {
RT286_AIFS,
};
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
-
#endif /* __RT286_H__ */
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index ce963768449f..ee3d9291eea6 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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>
@@ -39,7 +36,7 @@ struct rt298_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt298_platform_data pdata;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
@@ -194,13 +191,13 @@ static bool rt298_readable_register(struct device *dev, unsigned int reg)
}
#ifdef CONFIG_PM
-static void rt298_index_sync(struct snd_soc_codec *codec)
+static void rt298_index_sync(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt298->index_cache[i].reg,
+ snd_soc_component_write(component, rt298->index_cache[i].reg,
rt298->index_cache[i].def);
}
}
@@ -227,10 +224,10 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
*hp = false;
*mic = false;
- if (!rt298->codec)
+ if (!rt298->component)
return -EINVAL;
- dapm = snd_soc_codec_get_dapm(rt298->codec);
+ dapm = snd_soc_component_get_dapm(rt298->component);
if (rt298->pdata.cbj_en) {
regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
@@ -270,11 +267,16 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
msleep(300);
regmap_read(rt298->regmap,
RT298_CBJ_CTRL2, &val);
- if (0x0070 == (val & 0x0070))
+ if (0x0070 == (val & 0x0070)) {
*mic = true;
- else
+ } else {
*mic = false;
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
}
+
regmap_update_bits(rt298->regmap,
RT298_DC_GAIN, 0x200, 0x0);
@@ -290,9 +292,10 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf);
*mic = buf & 0x80000000;
}
-
- snd_soc_dapm_disable_pin(dapm, "HV");
- snd_soc_dapm_disable_pin(dapm, "VREF");
+ 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);
@@ -313,55 +316,53 @@ static void rt298_jack_detect_work(struct work_struct *work)
if (rt298_jack_detect(rt298, &hp, &mic) < 0)
return;
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt298->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
-int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+static int rt298_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm;
- bool hp = false;
- bool mic = false;
- int status = 0;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- /* If jack in NULL, disable HS jack */
- if (!jack) {
+ 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);
- dapm = snd_soc_codec_get_dapm(codec);
+ 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;
}
-
- rt298->jack = jack;
- regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
-
- rt298_jack_detect(rt298, &hp, &mic);
- if (hp == true)
- status |= SND_JACK_HEADPHONE;
-
- if (mic == true)
- status |= SND_JACK_MICROPHONE;
-
- snd_soc_jack_report(rt298->jack, status,
- SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ snd_soc_dapm_sync(dapm);
return 0;
}
-EXPORT_SYMBOL_GPL(rt298_mic_detect);
static int is_mclk_mode(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -458,15 +459,15 @@ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT298_SPK_EAPD, RT298_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT298_SPK_EAPD, RT298_SET_EAPD_LOW);
break;
@@ -480,14 +481,14 @@ static int rt298_spk_event(struct snd_soc_dapm_widget *w,
static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0x20);
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0);
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0);
break;
default:
return 0;
@@ -499,39 +500,39 @@ static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
static int rt298_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_update_bits(codec,
+ 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_read(codec, RT298_VAD_CTRL) & 0x200)) {
+ if (!(snd_soc_component_read(component, RT298_VAD_CTRL) & 0x200)) {
pr_info("NO MCLK\n");
switch (nid) {
case RT298_ADC_IN1:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_D_FILTER_CTRL, 0x2, 0x2);
mdelay(10);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_D_FILTER_CTRL, 0x2, 0x0);
break;
case RT298_ADC_IN2:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_D_FILTER_CTRL, 0x4, 0x4);
mdelay(10);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_D_FILTER_CTRL, 0x4, 0x0);
break;
}
}
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7080);
break;
@@ -545,19 +546,19 @@ static int rt298_adc_event(struct snd_soc_dapm_widget *w,
static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL3, 0xc000, 0x8000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL2, 0xc000, 0x8000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL3, 0xc000, 0x0000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL2, 0xc000, 0x0000);
break;
default:
@@ -745,8 +746,8 @@ static int rt298_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 rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -756,7 +757,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -764,7 +765,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt298->sys_clk);
return -EINVAL;
}
@@ -772,7 +773,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt298->sys_clk);
return -EINVAL;
}
@@ -783,12 +784,11 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ 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:
@@ -814,27 +814,27 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x0018, d_len_code << 3);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x407f, 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x800, 0x800);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x800, 0x0);
break;
default:
@@ -843,27 +843,27 @@ static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
- snd_soc_update_bits(codec,
+ 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_update_bits(codec, RT298_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -871,56 +871,56 @@ static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
if (RT298_SCLK_S_MCLK == clk_id) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x0100, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_PLL_CTRL1, 0x20, 0x20);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x0100, 0x0100);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT298_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT298_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x8, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x8, 0x8);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -932,39 +932,39 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
-static int rt298_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_bias_level(codec)) {
- snd_soc_write(codec,
+ snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_write(component,
RT298_SET_AUDIO_POWER, AC_PWRST_D0);
- snd_soc_update_bits(codec, 0x0d, 0x200, 0x200);
- snd_soc_update_bits(codec, 0x52, 0x80, 0x0);
+ snd_soc_component_update_bits(component, 0x0d, 0x200, 0x200);
+ snd_soc_component_update_bits(component, 0x52, 0x80, 0x0);
mdelay(20);
- snd_soc_update_bits(codec, 0x0d, 0x200, 0x0);
- snd_soc_update_bits(codec, 0x52, 0x80, 0x80);
+ 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_write(codec,
+ snd_soc_component_write(component,
RT298_SET_AUDIO_POWER, AC_PWRST_D3);
break;
@@ -988,10 +988,10 @@ static irqreturn_t rt298_irq(int irq, void *data)
regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1);
if (ret == 0) {
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt298->jack, status,
@@ -1003,38 +1003,31 @@ static irqreturn_t rt298_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt298_probe(struct snd_soc_codec *codec)
+static int rt298_probe(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- rt298->codec = codec;
+ rt298->component = component;
+ INIT_DELAYED_WORK(&rt298->jack_detect_work, rt298_jack_detect_work);
- if (rt298->i2c->irq) {
- regmap_update_bits(rt298->regmap,
- RT298_IRQ_CTRL, 0x2, 0x2);
-
- 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));
- }
-
+ msecs_to_jiffies(1250));
return 0;
}
-static int rt298_remove(struct snd_soc_codec *codec)
+static void rt298_remove(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt298->jack_detect_work);
-
- return 0;
+ rt298->component = NULL;
}
#ifdef CONFIG_PM
-static int rt298_suspend(struct snd_soc_codec *codec)
+static int rt298_suspend(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
rt298->is_hp_in = -1;
regcache_cache_only(rt298->regmap, true);
@@ -1043,12 +1036,12 @@ static int rt298_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt298_resume(struct snd_soc_codec *codec)
+static int rt298_resume(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt298->regmap, false);
- rt298_index_sync(codec);
+ rt298_index_sync(component);
regcache_sync(rt298->regmap);
return 0;
@@ -1088,7 +1081,7 @@ static struct snd_soc_dai_driver rt298_dai[] = {
.formats = RT298_FORMATS,
},
.ops = &rt298_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt298-aif2",
@@ -1108,26 +1101,26 @@ static struct snd_soc_dai_driver rt298_dai[] = {
.formats = RT298_FORMATS,
},
.ops = &rt298_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt298 = {
- .probe = rt298_probe,
- .remove = rt298_remove,
- .suspend = rt298_suspend,
- .resume = rt298_resume,
- .set_bias_level = rt298_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .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),
- },
+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 = {
@@ -1144,16 +1137,19 @@ static const struct regmap_config rt298_regmap = {
};
static const struct i2c_device_id rt298_i2c_id[] = {
- {"rt298", 0},
+ {"rt298"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt298_acpi_match[] = {
- { "INT343A", 0 },
- {},
+ { "10EC0298" },
+ { "INT343A" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
+#endif
static const struct dmi_system_id force_combo_jack_table[] = {
{
@@ -1170,11 +1166,17 @@ static const struct dmi_system_id force_combo_jack_table[] = {
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,
- const struct i2c_device_id *id)
+static int rt298_i2c_probe(struct i2c_client *i2c)
{
struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt298_priv *rt298;
@@ -1283,26 +1285,24 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt298,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt298,
rt298_dai, ARRAY_SIZE(rt298_dai));
return ret;
}
-static int rt298_i2c_remove(struct i2c_client *i2c)
+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);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
index 3638f3d61209..f1be9c135401 100644
--- a/sound/soc/codecs/rt298.h
+++ b/sound/soc/codecs/rt298.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt298.h -- RT298 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 __RT298_H__
@@ -210,7 +207,5 @@ enum {
RT298_AIFS,
};
-int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
-
#endif /* __RT298_H__ */
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
index 2144edca97b0..54d84581ec47 100644
--- a/sound/soc/codecs/rt5514-spi.c
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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,7 +15,6 @@
#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>
@@ -35,6 +31,8 @@
#include "rt5514-spi.h"
+#define DRV_NAME "rt5514-spi"
+
static struct spi_device *rt5514_spi;
struct rt5514_dsp {
@@ -89,6 +87,14 @@ static void rt5514_spi_copy_work(struct work_struct *work)
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,
@@ -147,13 +153,11 @@ done:
static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp)
{
- size_t period_bytes;
u8 buf[8];
if (!rt5514_dsp->substream)
return;
- period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
rt5514_dsp->get_size = 0;
/**
@@ -181,10 +185,6 @@ static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp)
rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base;
- if (rt5514_dsp->buf_size % period_bytes)
- rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) *
- period_bytes;
-
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);
@@ -200,25 +200,23 @@ static irqreturn_t rt5514_spi_irq(int irq, void *data)
}
/* PCM for streaming audio from the DSP buffer */
-static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+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_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int rt5514_spi_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(rtd->platform);
- int ret;
+ snd_soc_component_get_drvdata(component);
u8 buf[8];
mutex_lock(&rt5514_dsp->dma_lock);
- ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
rt5514_dsp->substream = substream;
rt5514_dsp->dma_offset = 0;
@@ -229,14 +227,14 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&rt5514_dsp->dma_lock);
- return ret;
+ return 0;
}
-static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+static int rt5514_spi_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(rtd->platform);
+ snd_soc_component_get_drvdata(component);
mutex_lock(&rt5514_dsp->dma_lock);
rt5514_dsp->substream = NULL;
@@ -244,41 +242,35 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
cancel_delayed_work_sync(&rt5514_dsp->copy_work);
- return snd_pcm_lib_free_vmalloc_buffer(substream);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(rtd->platform);
+ snd_soc_component_get_drvdata(component);
return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
}
-static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
- .open = rt5514_spi_pcm_open,
- .hw_params = rt5514_spi_hw_params,
- .hw_free = rt5514_spi_hw_free,
- .pointer = rt5514_spi_pcm_pointer,
- .mmap = snd_pcm_lib_mmap_vmalloc,
- .page = snd_pcm_lib_get_vmalloc_page,
-};
-static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
{
struct rt5514_dsp *rt5514_dsp;
int ret;
- rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+ 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_platform_set_drvdata(platform, rt5514_dsp);
+ snd_soc_component_set_drvdata(component, rt5514_dsp);
if (rt5514_spi->irq) {
ret = devm_request_threaded_irq(&rt5514_spi->dev,
@@ -287,7 +279,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
rt5514_dsp);
if (ret)
dev_err(&rt5514_spi->dev,
- "%s Failed to reguest IRQ: %d\n", __func__,
+ "%s Failed to request IRQ: %d\n", __func__,
ret);
else
device_init_wakeup(rt5514_dsp->dev, true);
@@ -296,13 +288,23 @@ static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
return 0;
}
-static const struct snd_soc_platform_driver rt5514_spi_platform = {
- .probe = rt5514_spi_pcm_probe,
- .ops = &rt5514_spi_pcm_ops,
-};
+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_dai_component = {
- .name = "rt5514-spi-dai",
+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,
};
/**
@@ -445,14 +447,8 @@ static int rt5514_spi_probe(struct spi_device *spi)
rt5514_spi = spi;
- ret = devm_snd_soc_register_platform(&spi->dev, &rt5514_spi_platform);
- if (ret < 0) {
- dev_err(&spi->dev, "Failed to register platform.\n");
- return ret;
- }
-
ret = devm_snd_soc_register_component(&spi->dev,
- &rt5514_spi_dai_component,
+ &rt5514_spi_component,
&rt5514_spi_dai, 1);
if (ret < 0) {
dev_err(&spi->dev, "Failed to register component.\n");
@@ -462,7 +458,7 @@ static int rt5514_spi_probe(struct spi_device *spi)
return 0;
}
-static int __maybe_unused rt5514_suspend(struct device *dev)
+static int rt5514_suspend(struct device *dev)
{
int irq = to_spi_device(dev)->irq;
@@ -472,11 +468,9 @@ static int __maybe_unused rt5514_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused rt5514_resume(struct device *dev)
+static int rt5514_resume(struct device *dev)
{
- struct snd_soc_platform *platform = snd_soc_lookup_platform(dev);
- struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(platform);
+ struct rt5514_dsp *rt5514_dsp = dev_get_drvdata(dev);
int irq = to_spi_device(dev)->irq;
u8 buf[8];
@@ -496,7 +490,7 @@ static int __maybe_unused rt5514_resume(struct device *dev)
}
static const struct dev_pm_ops rt5514_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume)
+ SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume)
};
static const struct of_device_id rt5514_of_match[] = {
@@ -508,7 +502,7 @@ MODULE_DEVICE_TABLE(of, rt5514_of_match);
static struct spi_driver rt5514_spi_driver = {
.driver = {
.name = "rt5514",
- .pm = &rt5514_pm_ops,
+ .pm = pm_ptr(&rt5514_pm_ops),
.of_match_table = of_match_ptr(rt5514_of_match),
},
.probe = rt5514_spi_probe,
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
index c1a36647c119..cedb19709c9a 100644
--- a/sound/soc/codecs/rt5514-spi.h
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5514-spi.h -- RT5514 driver
*
* Copyright 2015 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 __RT5514_SPI_H__
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 198df016802f..ab9d81c32be8 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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/acpi.h>
@@ -20,7 +17,6 @@
#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>
@@ -64,8 +60,8 @@ static const struct reg_sequence rt5514_patch[] = {
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_ADCFED, 0x00000800},
{RT5514_ASRC_IN_CTRL1, 0x00000003},
- {RT5514_DOWNFILTER0_CTRL3, 0x10000362},
- {RT5514_DOWNFILTER1_CTRL3, 0x10000362},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
};
static const struct reg_default rt5514_reg[] = {
@@ -89,12 +85,13 @@ static const struct reg_default rt5514_reg[] = {
{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, 0x10000362},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
{RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
{RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
- {RT5514_DOWNFILTER1_CTRL3, 0x10000362},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_LDO18_16, 0x02000345},
{RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
@@ -181,6 +178,7 @@ static bool rt5514_readable_register(struct device *dev, unsigned int reg)
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:
@@ -238,6 +236,7 @@ static bool rt5514_i2c_readable_register(struct device *dev,
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:
@@ -327,14 +326,13 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
- struct snd_soc_codec *codec = rt5514->codec;
const struct firmware *fw = NULL;
u8 buf[8];
if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
return 0;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
rt5514->dsp_enabled = ucontrol->value.integer.value[0];
if (rt5514->dsp_enabled) {
@@ -342,11 +340,11 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
!IS_ERR(rt5514->dsp_calib_clk)) {
if (clk_set_rate(rt5514->dsp_calib_clk,
rt5514->pdata.dsp_calib_clk_rate))
- dev_err(codec->dev,
+ dev_err(component->dev,
"Can't set rate for mclk");
if (clk_prepare_enable(rt5514->dsp_calib_clk))
- dev_err(codec->dev,
+ dev_err(component->dev,
"Can't enable dsp_calib_clk");
rt5514_calibration(rt5514, true);
@@ -354,11 +352,11 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
msleep(20);
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 |
- RT5514_DSP_MAPPING,
- (u8 *)&buf, sizeof(buf));
+ RT5514_DSP_MAPPING, buf, sizeof(buf));
#else
- dev_err(codec->dev, "There is no SPI driver for"
+ 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;
@@ -369,26 +367,26 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
rt5514_enable_dsp_prepare(rt5514);
- request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
+ 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(codec->dev, "There is no SPI driver for"
+ 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, codec->dev);
+ 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(codec->dev, "There is no SPI driver for"
+ dev_err(component->dev, "There is no SPI driver for"
" loading the firmware\n");
#endif
release_firmware(fw);
@@ -420,7 +418,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
}
}
- return 0;
+ return 1;
}
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
@@ -487,14 +485,15 @@ static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
/**
* 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_codec *codec, int rate)
+static int rt5514_calc_dmic_clk(struct snd_soc_component *component, int rate)
{
- int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
+ static const int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
int i;
if (rate < 1000000 * div[0]) {
@@ -508,20 +507,20 @@ static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate)
return i;
}
- dev_warn(codec->dev, "Base clock rate %d is too high\n", rate);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, rt5514->sysclk);
+ idx = rt5514_calc_dmic_clk(component, rt5514->sysclk);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ 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,
@@ -536,8 +535,8 @@ static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w,
static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -548,8 +547,8 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int rt5514_i2s_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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);
}
@@ -751,21 +750,21 @@ static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
static int rt5514_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -808,8 +807,8 @@ static int rt5514_hw_params(struct snd_pcm_substream *substream,
static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -862,8 +861,8 @@ static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -879,7 +878,7 @@ static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
@@ -897,13 +896,13 @@ static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5514->pll_in = 0;
rt5514->pll_out = 0;
@@ -930,17 +929,17 @@ static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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);
@@ -961,8 +960,8 @@ static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
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_codec *codec = dai->codec;
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1047,18 +1046,15 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int rt5514_set_bias_level(struct snd_soc_codec *codec,
+static int rt5514_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(rt5514->mclk);
} else {
ret = clk_prepare_enable(rt5514->mclk);
@@ -1068,7 +1064,7 @@ static int rt5514_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ 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
@@ -1092,15 +1088,14 @@ static int rt5514_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5514_probe(struct snd_soc_codec *codec)
+static int rt5514_probe(struct snd_soc_component *component)
{
- struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
- struct platform_device *pdev = container_of(codec->dev,
- struct platform_device, dev);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct platform_device *pdev = to_platform_device(component->dev);
- rt5514->mclk = devm_clk_get(codec->dev, "mclk");
- if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ rt5514->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5514->mclk))
+ return PTR_ERR(rt5514->mclk);
if (rt5514->pdata.dsp_calib_clk_name) {
rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
@@ -1109,7 +1104,7 @@ static int rt5514_probe(struct snd_soc_codec *codec)
return -EPROBE_DEFER;
}
- rt5514->codec = codec;
+ rt5514->component = component;
rt5514->pll3_cal_value = 0x0078b000;
return 0;
@@ -1162,18 +1157,17 @@ static struct snd_soc_dai_driver rt5514_dai[] = {
}
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
- .probe = rt5514_probe,
- .idle_bias_off = true,
- .set_bias_level = rt5514_set_bias_level,
- .component_driver = {
- .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),
- },
+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 = {
@@ -1196,14 +1190,15 @@ static const struct regmap_config rt5514_regmap = {
.reg_read = rt5514_i2c_read,
.reg_write = rt5514_i2c_write,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5514_reg,
.num_reg_defaults = ARRAY_SIZE(rt5514_reg),
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
};
static const struct i2c_device_id rt5514_i2c_id[] = {
- { "rt5514", 0 },
+ { "rt5514" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
@@ -1211,15 +1206,15 @@ 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},
- {},
+ { "10EC5514" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match);
#endif
@@ -1236,7 +1231,7 @@ static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev)
return 0;
}
-static __maybe_unused int rt5514_i2c_resume(struct device *dev)
+static int rt5514_i2c_resume(struct device *dev)
{
struct rt5514_priv *rt5514 = dev_get_drvdata(dev);
unsigned int val;
@@ -1251,8 +1246,7 @@ static __maybe_unused int rt5514_i2c_resume(struct device *dev)
return 0;
}
-static int rt5514_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5514_i2c_probe(struct i2c_client *i2c)
{
struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5514_priv *rt5514;
@@ -1313,19 +1307,13 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5514,
rt5514_dai, ARRAY_SIZE(rt5514_dai));
}
-static int rt5514_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static const struct dev_pm_ops rt5514_i2_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
+ SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
};
static struct i2c_driver rt5514_i2c_driver = {
@@ -1333,10 +1321,9 @@ static struct i2c_driver rt5514_i2c_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,
+ .pm = pm_ptr(&rt5514_i2_pm_ops),
},
.probe = rt5514_i2c_probe,
- .remove = rt5514_i2c_remove,
.id_table = rt5514_i2c_id,
};
module_i2c_driver(rt5514_i2c_driver);
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
index f0f3400ce6b1..75755599f940 100644
--- a/sound/soc/codecs/rt5514.h
+++ b/sound/soc/codecs/rt5514.h
@@ -1,12 +1,9 @@
+/* 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>
- *
- * 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 __RT5514_H__
@@ -272,7 +269,7 @@ enum {
struct rt5514_priv {
struct rt5514_platform_data pdata;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regmap *i2c_regmap, *regmap;
struct clk *mclk, *dsp_calib_clk;
int sysclk;
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 0e5f54a9bc7e..1ec486707ff9 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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>
@@ -142,7 +139,7 @@ static const struct reg_default rt5616_reg[] = {
};
struct rt5616_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct delayed_work patch_work;
struct regmap *regmap;
struct clk *mclk;
@@ -351,7 +348,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
{
unsigned int val;
- val = snd_soc_read(snd_soc_dapm_to_codec(source->dapm), RT5616_GLB_CLK);
+ 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;
@@ -466,16 +463,16 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
+ 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_update_bits(codec, RT5616_ADC_DIG_VOL,
+ snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
break;
@@ -490,51 +487,51 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* depop parameters */
- snd_soc_update_bits(codec, RT5616_DEPOP_M2,
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M2,
RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_write(codec, RT5616_PR_BASE +
+ snd_soc_component_write(component, RT5616_PR_BASE +
RT5616_HP_DCC_INT1, 0x9f00);
/* headphone amp power on */
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5616_PWR_VOL,
+ 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_update_bits(codec, RT5616_PWR_ANLG1,
+ 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_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
- snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
+ snd_soc_component_update_bits(component, RT5616_CHARGE_PUMP,
RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
- snd_soc_update_bits(codec, RT5616_PR_BASE +
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_update_bits(codec, RT5616_PR_BASE +
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_update_bits(codec, RT5616_DEPOP_M1,
+ 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 |
@@ -542,7 +539,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
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_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_HP_L | RT5616_PWR_HP_R |
RT5616_PWR_HA, 0);
break;
@@ -556,61 +553,61 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_update_bits(codec, RT5616_DEPOP_M3,
+ 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_write(codec, RT5616_PR_BASE +
+ snd_soc_component_write(component, RT5616_PR_BASE +
RT5616_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
RT5616_RSTN_MASK, RT5616_RSTN_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_update_bits(codec, RT5616_HP_VOL,
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
RT5616_L_MUTE | RT5616_R_MUTE, 0);
msleep(100);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
+ 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_update_bits(codec, RT5616_DEPOP_M3,
+ 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_write(codec, RT5616_PR_BASE +
+ snd_soc_component_write(component, RT5616_PR_BASE +
RT5616_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
RT5616_RSTP_MASK, RT5616_RSTP_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
+ 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_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
+ snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET,
RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
msleep(90);
- snd_soc_update_bits(codec, RT5616_HP_VOL,
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
msleep(30);
@@ -626,21 +623,21 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_LM, RT5616_PWR_LM);
- snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
+ 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_update_bits(codec, RT5616_LOUT_CTRL1,
+ snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_LM, 0);
break;
@@ -654,16 +651,16 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
+ 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_update_bits(codec, RT5616_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
RT5616_PWR_BST1_OP2, 0);
break;
@@ -677,16 +674,16 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
+ 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_update_bits(codec, RT5616_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
RT5616_PWR_BST2_OP2, 0);
break;
@@ -960,8 +957,8 @@ static int rt5616_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 rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -970,12 +967,12 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5616->sysclk, rt5616->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -1004,24 +1001,24 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5616_I2S_PD1_MASK;
val_clk = pre_div << RT5616_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5616_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
RT5616_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
+ 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_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5616->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5616_I2S_MS_S;
rt5616->master[dai->id] = 0;
break;
@@ -1055,7 +1052,7 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5616_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
RT5616_I2S_DF_MASK, reg_val);
@@ -1065,8 +1062,8 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1080,11 +1077,11 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5616_SCLK_SRC_PLL1;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
RT5616_SCLK_SRC_MASK, reg_val);
rt5616->sysclk = freq;
rt5616->sysclk_src = clk_id;
@@ -1097,8 +1094,8 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1107,11 +1104,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5616->pll_in = 0;
rt5616->pll_out = 0;
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
RT5616_SCLK_SRC_MASK,
RT5616_SCLK_SRC_MCLK);
return 0;
@@ -1119,34 +1116,34 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
switch (source) {
case RT5616_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
+ 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_update_bits(codec, RT5616_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
RT5616_PLL1_SRC_MASK,
RT5616_PLL1_SRC_BCLK1);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5616_PLL_CTRL1,
+ snd_soc_component_write(component, RT5616_PLL_CTRL1,
pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5616_PLL_CTRL2,
+ 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);
@@ -1158,10 +1155,10 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5616_set_bias_level(struct snd_soc_codec *codec,
+static int rt5616_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1177,10 +1174,7 @@ static int rt5616_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (IS_ERR(rt5616->mclk))
- break;
-
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(rt5616->mclk);
} else {
ret = clk_prepare_enable(rt5616->mclk);
@@ -1190,30 +1184,30 @@ static int rt5616_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
+ 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_update_bits(codec, RT5616_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
- snd_soc_update_bits(codec, RT5616_D_MISC,
+ 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_update_bits(codec, RT5616_D_MISC, RT5616_D_GATE_EN, 0);
- snd_soc_write(codec, RT5616_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5616_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5616_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5616_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5616_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5616_PWR_ANLG2, 0x0000);
+ 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:
@@ -1223,24 +1217,24 @@ static int rt5616_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5616_probe(struct snd_soc_codec *codec)
+static int rt5616_probe(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
/* Check if MCLK provided */
- rt5616->mclk = devm_clk_get(codec->dev, "mclk");
- if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ rt5616->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5616->mclk))
+ return PTR_ERR(rt5616->mclk);
- rt5616->codec = codec;
+ rt5616->component = component;
return 0;
}
#ifdef CONFIG_PM
-static int rt5616_suspend(struct snd_soc_codec *codec)
+static int rt5616_suspend(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5616->regmap, true);
regcache_mark_dirty(rt5616->regmap);
@@ -1248,9 +1242,9 @@ static int rt5616_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5616_resume(struct snd_soc_codec *codec)
+static int rt5616_resume(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5616->regmap, false);
regcache_sync(rt5616->regmap);
@@ -1294,31 +1288,31 @@ static struct snd_soc_dai_driver rt5616_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5616 = {
- .probe = rt5616_probe,
- .suspend = rt5616_suspend,
- .resume = rt5616_resume,
- .set_bias_level = rt5616_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .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),
- },
+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_rw = true,
+ .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,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5616_reg,
.num_reg_defaults = ARRAY_SIZE(rt5616_reg),
.ranges = rt5616_ranges,
@@ -1326,7 +1320,7 @@ static const struct regmap_config rt5616_regmap = {
};
static const struct i2c_device_id rt5616_i2c_id[] = {
- { "rt5616", 0 },
+ { "rt5616" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id);
@@ -1339,8 +1333,7 @@ static const struct of_device_id rt5616_of_match[] = {
MODULE_DEVICE_TABLE(of, rt5616_of_match);
#endif
-static int rt5616_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5616_i2c_probe(struct i2c_client *i2c)
{
struct rt5616_priv *rt5616;
unsigned int val;
@@ -1387,16 +1380,13 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5616,
rt5616_dai, ARRAY_SIZE(rt5616_dai));
}
-static int rt5616_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
+static void rt5616_i2c_remove(struct i2c_client *i2c)
+{}
static void rt5616_i2c_shutdown(struct i2c_client *client)
{
diff --git a/sound/soc/codecs/rt5616.h b/sound/soc/codecs/rt5616.h
index f88cdddbc34a..ad9c5de9052d 100644
--- a/sound/soc/codecs/rt5616.h
+++ b/sound/soc/codecs/rt5616.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5616.h -- RT5616 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 __RT5616_H__
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 55b04c55fb4b..d523477c5102 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5631.c -- RT5631 ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
* Author: flove <flove@realtek.com>
*
* Based on WM8753.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -68,33 +64,33 @@ static const struct reg_default rt5631_reg[] = {
{ RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
};
-/**
+/*
* rt5631_write_index - write index register of 2nd layer
*/
-static void rt5631_write_index(struct snd_soc_codec *codec,
+static void rt5631_write_index(struct snd_soc_component *component,
unsigned int reg, unsigned int value)
{
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- snd_soc_write(codec, RT5631_INDEX_DATA, 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_codec *codec,
+static unsigned int rt5631_read_index(struct snd_soc_component *component,
unsigned int reg)
{
unsigned int value;
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- value = snd_soc_read(codec, RT5631_INDEX_DATA);
+ 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_codec *codec)
+static int rt5631_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5631_RESET, 0);
+ return snd_soc_component_write(component, RT5631_RESET, 0);
}
static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
@@ -105,9 +101,9 @@ static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
case RT5631_INDEX_ADD:
case RT5631_INDEX_DATA:
case RT5631_EQ_CTRL:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -164,9 +160,9 @@ static bool rt5631_readable_register(struct device *dev, unsigned int reg)
case RT5631_VENDOR_ID:
case RT5631_VENDOR_ID1:
case RT5631_VENDOR_ID2:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -187,8 +183,8 @@ static const DECLARE_TLV_DB_RANGE(mic_bst_tlv,
static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -198,8 +194,8 @@ static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -229,10 +225,10 @@ static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
static const struct snd_kcontrol_new rt5631_snd_controls[] = {
/* MIC */
SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum),
- SOC_SINGLE_TLV("MIC1 Boost", RT5631_MIC_CTRL_2,
+ 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", RT5631_MIC_CTRL_2,
+ 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),
@@ -286,304 +282,308 @@ static const struct snd_kcontrol_new rt5631_snd_controls[] = {
static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_GLOBAL_CLK_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_L_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_R_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_TEST_MODE_CTRL, 0x84c0);
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x309f);
- rt5631_write_index(codec, RT5631_CP_INTL_REG2, 0x6530);
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_CAP_FREE_DEPOP);
} else {
/* power off capless block */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_2, 0);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, 0);
msleep(100);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x307f);
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_power_stage - step by step depop sequence in power stage.
+ * 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x303e);
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303e);
/* power on headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x303F);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_HP_DEPOP_DIS, 0);
/* config depop sequence parameter */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN);
/* power down headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, 0);
- onebit_depop_power_stage(codec, 0);
+ onebit_depop_mute_stage(component, 0);
+ onebit_depop_power_stage(component, 0);
} else {
- depop_seq_mute_stage(codec, 0);
- depop_seq_power_stage(codec, 0);
+ 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(codec, 1);
- onebit_depop_mute_stage(codec, 1);
+ onebit_depop_power_stage(component, 1);
+ onebit_depop_mute_stage(component, 1);
} else {
- depop_seq_power_stage(codec, 1);
- depop_seq_mute_stage(codec, 1);
+ depop_seq_power_stage(component, 1);
+ depop_seq_mute_stage(component, 1);
}
break;
@@ -597,20 +597,20 @@ static int hp_event(struct snd_soc_dapm_widget *w,
static int set_dmic_params(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_64FS);
break;
@@ -618,7 +618,7 @@ static int set_dmic_params(struct snd_soc_dapm_widget *w,
case 16000:
case 11025:
case 8000:
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_128FS);
break;
@@ -1283,7 +1283,7 @@ static const struct pll_div codec_slave_pll_div[] = {
{3072000, 12288000, 0x0a90},
};
-static struct coeff_clk_div coeff_div[] = {
+static const struct coeff_clk_div coeff_div[] = {
/* sysclk is 256fs */
{2048000, 8000 * 32, 8000, 0x1000},
{2048000, 8000 * 64, 8000, 0x0000},
@@ -1352,16 +1352,16 @@ static int get_coeff(int mclk, int rate, int timesofbclk)
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_codec *codec = dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
rt5631->bclk_rate = snd_soc_params_to_bclk(params);
if (rt5631->bclk_rate < 0) {
- dev_err(codec->dev, "Fail to get BCLK rate\n");
+ dev_err(component->dev, "Fail to get BCLK rate\n");
return rt5631->bclk_rate;
}
rt5631->rx_rate = params_rate(params);
@@ -1373,7 +1373,7 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
timesofbclk);
if (coeff < 0) {
- dev_err(codec->dev, "Fail to get coeff\n");
+ dev_err(component->dev, "Fail to get coeff\n");
return coeff;
}
@@ -1393,9 +1393,9 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5631_SDP_CTRL,
+ snd_soc_component_update_bits(component, RT5631_SDP_CTRL,
RT5631_SDP_I2S_DL_MASK, iface);
- snd_soc_write(codec, RT5631_STEREO_AD_DA_CLK_CTRL,
+ snd_soc_component_write(component, RT5631_STEREO_AD_DA_CLK_CTRL,
coeff_div[coeff].reg_val);
return 0;
@@ -1404,17 +1404,17 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
unsigned int iface = 0;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5631->master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface |= RT5631_SDP_MODE_SEL_SLAVE;
rt5631->master = 0;
break;
@@ -1448,7 +1448,7 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, RT5631_SDP_CTRL, iface);
+ snd_soc_component_write(component, RT5631_SDP_CTRL, iface);
return 0;
}
@@ -1456,10 +1456,10 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "enter %s, syclk=%d\n", __func__, freq);
+ dev_dbg(component->dev, "enter %s, syclk=%d\n", __func__, freq);
if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) {
rt5631->sysclk = freq;
@@ -1472,16 +1472,16 @@ static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
int i, ret = -EINVAL;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
- snd_soc_update_bits(codec, RT5631_GLOBAL_CLK_CTRL,
+ snd_soc_component_update_bits(component, RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK,
RT5631_SYSCLK_SOUR_SEL_MCLK);
@@ -1492,13 +1492,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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(codec->dev,
+ dev_info(component->dev,
"change PLL in master mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_master_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1511,13 +1511,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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(codec->dev,
+ dev_info(component->dev,
"change PLL in slave mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_slave_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1531,26 +1531,26 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return ret;
}
-static int rt5631_set_bias_level(struct snd_soc_codec *codec,
+static int rt5631_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD2,
+ 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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);
@@ -1559,10 +1559,10 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD1, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD2, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD3, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD4, 0x0000);
+ 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:
@@ -1572,48 +1572,48 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5631_probe(struct snd_soc_codec *codec)
+static int rt5631_probe(struct snd_soc_component *component)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
unsigned int val;
- val = rt5631_read_index(codec, RT5631_ADDA_MIXER_INTL_REG3);
+ val = rt5631_read_index(component, RT5631_ADDA_MIXER_INTL_REG3);
if (val & 0x0002)
rt5631->codec_version = 1;
else
rt5631->codec_version = 0;
- rt5631_reset(codec);
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
/* power off ClassD auto Recovery */
if (rt5631->codec_version)
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0x2000);
else
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0);
/* DMIC */
if (rt5631->dmic_used_flag) {
- snd_soc_update_bits(codec, RT5631_GPIO_CTRL,
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ 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_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_component_init_bias_level(component, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -1653,23 +1653,24 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
- .probe = rt5631_probe,
- .set_bias_level = rt5631_set_bias_level,
- .suspend_bias_off = true,
- .component_driver = {
- .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),
- },
+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 },
+ { "rt5631" },
+ { "alc5631" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
@@ -1692,11 +1693,12 @@ static const struct regmap_config rt5631_regmap_config = {
.max_register = RT5631_VENDOR_ID2,
.reg_defaults = rt5631_reg,
.num_reg_defaults = ARRAY_SIZE(rt5631_reg),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int rt5631_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5631_i2c_probe(struct i2c_client *i2c)
{
struct rt5631_priv *rt5631;
int ret;
@@ -1712,23 +1714,21 @@ static int rt5631_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(rt5631->regmap))
return PTR_ERR(rt5631->regmap);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5631,
rt5631_dai, ARRAY_SIZE(rt5631_dai));
return ret;
}
-static int rt5631_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+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 = rt5631_i2c_probe,
+ .probe = rt5631_i2c_probe,
.remove = rt5631_i2c_remove,
.id_table = rt5631_i2c_id,
};
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 438fe52a12df..f50e771db24b 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1,13 +1,10 @@
+// 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.
- *
- * 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,15 +12,15 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.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>
@@ -55,7 +52,6 @@ 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},
};
@@ -166,9 +162,9 @@ static const struct reg_default rt5640_reg[] = {
{ 0xff, 0x6231 },
};
-static int rt5640_reset(struct snd_soc_codec *codec)
+static int rt5640_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5640_RESET, 0);
+ return snd_soc_component_write(component, RT5640_RESET, 0);
}
static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
@@ -197,6 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
+ case RT5640_GCTL2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -328,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
case RT5640_HP_CALIB2:
case RT5640_SV_ZCD1:
case RT5640_SV_ZCD2:
- case RT5640_DUMMY1:
- case RT5640_DUMMY2:
+ case RT5640_GCTL1:
+ case RT5640_GCTL2:
case RT5640_DUMMY3:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
@@ -341,9 +338,9 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
}
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_MINMAX(dac_vol_tlv, -6562, 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_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 */
@@ -402,6 +399,9 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
/* 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),
@@ -423,7 +423,7 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
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,
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1,
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,
@@ -445,9 +445,6 @@ 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),
-
- SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
- RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv),
};
/**
@@ -461,40 +458,26 @@ static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
idx << RT5640_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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
-
- val = snd_soc_read(codec, RT5640_GLB_CLK);
- val &= RT5640_SCLK_SRC_MASK;
- if (val == RT5640_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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -930,9 +913,9 @@ static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
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_codec *codec)
+static void hp_amp_power_on(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
/* depop parameters */
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
@@ -956,9 +939,9 @@ static void hp_amp_power_on(struct snd_soc_codec *codec)
RT5640_PWR_FV1 | RT5640_PWR_FV2);
}
-static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+static void rt5640_pmu_depop(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -984,17 +967,17 @@ static void rt5640_pmu_depop(struct snd_soc_codec *codec)
static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
- rt5640->hp_mute = 0;
+ rt5640_pmu_depop(component);
+ rt5640->hp_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- rt5640->hp_mute = 1;
+ rt5640->hp_mute = true;
msleep(70);
break;
@@ -1008,22 +991,22 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
static int rt5640_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power_on(codec);
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ hp_amp_power_on(component);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_LM, RT5640_PWR_LM);
- snd_soc_update_bits(codec, RT5640_OUTPUT,
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
RT5640_L_MUTE | RT5640_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5640_OUTPUT,
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
RT5640_L_MUTE | RT5640_R_MUTE,
RT5640_L_MUTE | RT5640_R_MUTE);
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_LM, 0);
break;
@@ -1037,11 +1020,11 @@ static int rt5640_lout_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power_on(codec);
+ hp_amp_power_on(component);
break;
default:
return 0;
@@ -1053,8 +1036,8 @@ static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1071,9 +1054,6 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
}
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
- SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
- RT5640_PWR_PLL_BIT, 0, NULL, 0),
-
/* ASRC */
SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
15, 0, NULL, 0),
@@ -1427,22 +1407,18 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"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 Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
{"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
{"Stereo ADC MIXR", NULL, "Stereo Filter"},
- {"Stereo 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, "Mono Left Filter"},
- {"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, "Mono Right Filter"},
- {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"IF2 ADC L", NULL, "Mono ADC MIXL"},
{"IF2 ADC R", NULL, "Mono ADC MIXR"},
@@ -1512,10 +1488,8 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
{"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
{"DAC R1", NULL, "DAC R1 Power"},
{"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
@@ -1622,10 +1596,8 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
{"DAC L2", NULL, "Mono DAC MIXL"},
- {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
{"DAC L2", NULL, "DAC L2 Power"},
{"DAC R2", NULL, "Mono DAC MIXR"},
- {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
{"DAC R2", NULL, "DAC R2 Power"},
{"SPK MIXL", "DAC L2 Switch", "DAC L2"},
@@ -1671,14 +1643,14 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
{"IF2 DAC R", NULL, "DAC R2 Power"},
};
-static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+static int get_sdp_info(struct snd_soc_component *component, int dai_id)
{
int ret = 0, val;
- if (codec == NULL)
+ if (component == NULL)
return -EINVAL;
- val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ 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:
@@ -1689,6 +1661,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_113:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_312:
case RT5640_IF_213:
ret |= RT5640_U_IF2;
@@ -1704,6 +1677,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_223:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_123:
case RT5640_IF_321:
ret |= RT5640_U_IF2;
@@ -1722,21 +1696,21 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
static int rt5640_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 rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return frame_size;
}
if (frame_size > 32)
@@ -1766,26 +1740,26 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ 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_update_bits(codec, RT5640_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ 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_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
return 0;
@@ -1793,16 +1767,16 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5640->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5640_I2S_MS_S;
rt5640->master[dai->id] = 0;
break;
@@ -1836,18 +1810,18 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ 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_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
RT5640_I2S_DF_MASK, reg_val);
}
@@ -1858,28 +1832,34 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
-
- if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
- return 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(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ 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;
@@ -1891,8 +1871,8 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1901,48 +1881,48 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5640->pll_in = 0;
rt5640->pll_out = 0;
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ 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_update_bits(codec, RT5640_GLB_CLK,
+ 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_update_bits(codec, RT5640_GLB_CLK,
+ 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_update_bits(codec, RT5640_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5640_PLL_CTRL1,
- pll_code.n_code << RT5640_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, 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);
+ 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;
@@ -1951,10 +1931,10 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+static int rt5640_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1969,10 +1949,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (IS_ERR(rt5640->mclk))
- break;
-
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(rt5640->mclk);
} else {
ret = clk_prepare_enable(rt5640->mclk);
@@ -1982,33 +1959,38 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ 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_update_bits(codec, RT5640_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
- snd_soc_update_bits(codec, RT5640_DUMMY1,
- 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_MICBIAS,
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
+ 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004);
- snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100);
- snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0);
- snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5640_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000);
+ 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_GCTL1, 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:
@@ -2018,10 +2000,10 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+int rt5640_dmic_enable(struct snd_soc_component *component,
bool dmic1_data_pin, bool dmic2_data_pin)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -2044,10 +2026,10 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
-int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int asrc2_mask = 0;
unsigned int asrc2_value = 0;
@@ -2099,43 +2081,601 @@ int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
| (clk_src << RT5640_MAD_R_M_SFT);
}
- snd_soc_update_bits(codec, RT5640_ASRC_2,
+ snd_soc_component_update_bits(component, RT5640_ASRC_2,
asrc2_mask, asrc2_value);
- if (snd_soc_read(codec, RT5640_ASRC_2)) {
+ if (snd_soc_component_read(component, RT5640_ASRC_2)) {
rt5640->asrc_en = true;
- snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x3);
} else {
rt5640->asrc_en = false;
- snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x0);
}
return 0;
}
EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
-static int rt5640_probe(struct snd_soc_codec *codec)
+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 snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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_GCTL2);
+ 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_GCTL2,
+ (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)
+ delay = 100;
+
+ if (rt5640->jack)
+ mod_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_disable_irq_and_cancel_work(void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ if (rt5640->jd_gpio_irq_requested) {
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+ rt5640->jd_gpio_irq_requested = false;
+ }
+
+ if (rt5640->irq_requested) {
+ free_irq(rt5640->irq, rt5640);
+ rt5640->irq_requested = false;
+ }
+
+ 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;
+
+ rt5640_disable_irq_and_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 = 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_GCTL1, 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_GCTL2,
+ 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_GCTL2,
+ 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 request 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_GCTL2, 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_GCTL1, 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 request IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640->jack = NULL;
+ return;
+ }
+ rt5640->irq_requested = true;
+
+ /* 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(codec->dev, "mclk");
- if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ rt5640->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5640->mclk))
+ return PTR_ERR(rt5640->mclk);
- rt5640->codec = codec;
+ rt5640->component = component;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
- snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 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_read(codec, RT5640_RESET) & RT5640_ID_MASK) {
+ switch (snd_soc_component_read(component, RT5640_RESET) & RT5640_ID_MASK) {
case RT5640_ID_5640:
case RT5640_ID_5642:
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
rt5640_specific_snd_controls,
ARRAY_SIZE(rt5640_specific_snd_controls));
snd_soc_dapm_new_controls(dapm,
@@ -2154,52 +2694,169 @@ static int rt5640_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5639_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5639 RT5640 or RT5642 only\n");
return -ENODEV;
}
- if (rt5640->pdata.dmic_en)
- rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
- rt5640->pdata.dmic2_data_pin);
+ /*
+ * 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_GCTL1,
+ 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 int rt5640_remove(struct snd_soc_codec *codec)
+static void rt5640_remove(struct snd_soc_component *component)
{
- rt5640_reset(codec);
-
- return 0;
+ rt5640_reset(component);
}
#ifdef CONFIG_PM
-static int rt5640_suspend(struct snd_soc_codec *codec)
+static int rt5640_suspend(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (rt5640->jack) {
+ /* disable jack interrupts during system suspend */
+ disable_irq(rt5640->irq);
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->bp_work);
+ }
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
- rt5640_reset(codec);
+ 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->pdata.ldo1_en))
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0);
+ if (rt5640->ldo1_en)
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 0);
return 0;
}
-static int rt5640_resume(struct snd_soc_codec *codec)
+static int rt5640_resume(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1);
+ if (rt5640->ldo1_en) {
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 1);
msleep(400);
}
regcache_cache_only(rt5640->regmap, false);
regcache_sync(rt5640->regmap);
+ if (rt5640->jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ snd_soc_component_update_bits(component,
+ RT5640_GCTL2, 0x1100, 0x1100);
+ } else {
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_GCTL2,
+ 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_GCTL2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+ }
+
+ enable_irq(rt5640->irq);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+
return 0;
}
#else
@@ -2259,34 +2916,35 @@ static struct snd_soc_dai_driver rt5640_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
- .probe = rt5640_probe,
- .remove = rt5640_remove,
- .suspend = rt5640_suspend,
- .resume = rt5640_resume,
- .set_bias_level = rt5640_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .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),
- },
+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_rw = true,
+ .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,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5640_reg,
.num_reg_defaults = ARRAY_SIZE(rt5640_reg),
.ranges = rt5640_ranges,
@@ -2294,9 +2952,9 @@ static const struct regmap_config rt5640_regmap = {
};
static const struct i2c_device_id rt5640_i2c_id[] = {
- { "rt5640", 0 },
- { "rt5639", 0 },
- { "rt5642", 0 },
+ { "rt5640" },
+ { "rt5639" },
+ { "rt5642" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
@@ -2305,49 +2963,25 @@ MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
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 },
- { },
+ { "10EC3276" },
+ { "10EC5640" },
+ { "10EC5642" },
+ { "INT33CA" },
+ { "INTCCFFD" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
#endif
-static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
-{
- rt5640->pdata.in1_diff = of_property_read_bool(np,
- "realtek,in1-differential");
- rt5640->pdata.in2_diff = of_property_read_bool(np,
- "realtek,in2-differential");
-
- rt5640->pdata.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->pdata.ldo1_en) &&
- (rt5640->pdata.ldo1_en != -ENOENT))
- return rt5640->pdata.ldo1_en;
-
- return 0;
-}
-
-static int rt5640_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
{
- struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5640_priv *rt5640;
int ret;
unsigned int val;
@@ -2359,22 +2993,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
- if (pdata) {
- rt5640->pdata = *pdata;
- /*
- * Translate zero'd out (default) pdata value to an invalid
- * GPIO ID. This makes the pdata and DT paths consistent in
- * terms of the value left in this field when no GPIO is
- * specified, but means we can't actually use GPIO 0.
- */
- if (!rt5640->pdata.ldo1_en)
- rt5640->pdata.ldo1_en = -EINVAL;
- } else if (i2c->dev.of_node) {
- ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
- if (ret)
- return ret;
- } else
- rt5640->pdata.ldo1_en = -EINVAL;
+ rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5640->ldo1_en))
+ return PTR_ERR(rt5640->ldo1_en);
+
+ if (rt5640->ldo1_en) {
+ gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN");
+ msleep(400);
+ }
rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
if (IS_ERR(rt5640->regmap)) {
@@ -2384,19 +3012,12 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return ret;
}
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.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->pdata.ldo1_en, ret);
- return ret;
- }
- msleep(400);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if (val != RT5640_DEVICE_ID) {
+ usleep_range(60000, 100000);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
}
- 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);
@@ -2410,34 +3031,24 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- regmap_update_bits(rt5640->regmap, RT5640_DUMMY1,
+ regmap_update_bits(rt5640->regmap, RT5640_GCTL1,
RT5640_MCLK_DET, RT5640_MCLK_DET);
- if (rt5640->pdata.in1_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
- RT5640_IN_DF1, RT5640_IN_DF1);
+ 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);
- if (rt5640->pdata.in2_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
- RT5640_IN_DF2, RT5640_IN_DF2);
-
- if (rt5640->pdata.in3_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
- RT5640_IN_DF2, RT5640_IN_DF2);
-
- rt5640->hp_mute = 1;
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5640_disable_irq_and_cancel_work, rt5640);
+ if (ret)
+ return ret;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai));
}
-static int rt5640_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
@@ -2445,7 +3056,6 @@ static struct i2c_driver rt5640_i2c_driver = {
.of_match_table = of_match_ptr(rt5640_of_match),
},
.probe = rt5640_i2c_probe,
- .remove = rt5640_i2c_remove,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index b8a811732a52..8a12cee76bdc 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -1,19 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5640.h -- RT5640 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 _RT5640_H
#define _RT5640_H
#include <linux/clk.h>
-#include <sound/rt5640.h>
+#include <linux/gpio/consumer.h>
+#include <linux/workqueue.h>
+#include <dt-bindings/sound/rt5640.h>
/* Info */
#define RT5640_RESET 0x00
@@ -140,12 +139,13 @@
#define RT5640_SV_ZCD1 0xd9
#define RT5640_SV_ZCD2 0xda
/* Dummy Register */
-#define RT5640_DUMMY1 0xfa
-#define RT5640_DUMMY2 0xfb
+#define RT5640_GCTL1 0xfa
+#define RT5640_GCTL2 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
@@ -1607,10 +1607,17 @@
#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_CLR (0x1 << 3)
-#define RT5640_MB1_OC_CLR_SFT 3
-#define RT5640_MB2_OC_CLR (0x1 << 2)
-#define RT5640_MB2_OC_CLR_SFT 2
+#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)
@@ -1971,13 +1978,38 @@
#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 2 (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
@@ -2102,11 +2134,13 @@ enum {
};
struct rt5640_priv {
- struct snd_soc_codec *codec;
- struct rt5640_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
struct clk *mclk;
+ struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */
+ int irq;
+ int jd_gpio_irq;
int sysclk;
int sysclk_src;
int lrck[RT5640_AIFS];
@@ -2119,11 +2153,41 @@ struct rt5640_priv {
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;
};
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+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_codec *codec,
+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
index 8f140c8b93ac..29a403526cd9 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -1,12 +1,9 @@
+// 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>
- *
- * 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>
@@ -17,7 +14,6 @@
#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>
@@ -37,6 +33,7 @@
#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)
@@ -45,6 +42,8 @@ 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
@@ -81,6 +80,9 @@ static const struct reg_sequence init_list[] = {
static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
+ {RT5645_PWR_ANLG1, 0x02},
+ {RT5645_IL_CMD3, 0x6728},
+ {RT5645_PR_BASE + 0x3a, 0x0000},
};
static const struct reg_default rt5645_reg[] = {
@@ -401,17 +403,50 @@ struct rt5645_eq_param_s {
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;
+
+ /* Only 1 speaker connected */
+ bool mono_speaker;
+
+ /* 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_codec *codec;
+ struct snd_soc_component *component;
struct rt5645_platform_data pdata;
struct regmap *regmap;
struct i2c_client *i2c;
struct gpio_desc *gpiod_hp_det;
+ struct gpio_desc *gpiod_cbj_sleeve;
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;
@@ -419,6 +454,7 @@ struct rt5645_priv {
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
struct rt5645_eq_param_s *eq_param;
struct timer_list btn_check_timer;
+ struct mutex jd_mutex;
int codec_type;
int sysclk;
@@ -433,13 +469,12 @@ struct rt5645_priv {
int jack_type;
bool en_button_func;
- bool hp_on;
int v_id;
};
-static int rt5645_reset(struct snd_soc_codec *codec)
+static int rt5645_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5645_RESET, 0);
+ return snd_soc_component_write(component, RT5645_RESET, 0);
}
static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
@@ -672,8 +707,8 @@ static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- struct rt5645_eq_param_s *eq_param =
- (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ 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++) {
@@ -686,7 +721,7 @@ static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
static bool rt5645_validate_hweq(unsigned short reg)
{
- if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+ if ((reg >= 0x1a4 && reg <= 0x1cd) || (reg >= 0x1e5 && reg <= 0x1f8) ||
(reg == RT5645_EQ_CTRL2))
return true;
@@ -698,36 +733,33 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- struct rt5645_eq_param_s *eq_param =
- (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ 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 = be16_to_cpu(eq_param[i].reg);
- eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ 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 (eq_param[i].reg == 0)
+ if (rt5645->eq_param[i].reg == 0)
continue;
- else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+ 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(eq_param[i].reg) &&
- eq_param[i].reg != 0)
+ if (!rt5645_validate_hweq(rt5645->eq_param[i].reg) &&
+ rt5645->eq_param[i].reg != 0)
return 0;
- else if (eq_param[i].reg == 0)
+ else if (rt5645->eq_param[i].reg == 0)
break;
}
- memcpy(rt5645->eq_param, eq_param,
- RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
-
return 0;
}
@@ -846,17 +878,17 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5645_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_DMIC_CTRL1,
RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
return idx;
}
@@ -864,10 +896,10 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int val;
- val = snd_soc_read(codec, RT5645_GLB_CLK);
+ val = snd_soc_component_read(component, RT5645_GLB_CLK);
val &= RT5645_SCLK_SRC_MASK;
if (val == RT5645_SCLK_SRC_PLL1)
return 1;
@@ -878,7 +910,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -910,7 +942,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -923,9 +955,9 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
-static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+static int rt5645_enable_hweq(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < RT5645_HWEQ_NUM; i++) {
@@ -941,7 +973,7 @@ static int rt5645_enable_hweq(struct snd_soc_codec *codec)
/**
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -953,7 +985,7 @@ static int rt5645_enable_hweq(struct snd_soc_codec *codec)
* 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_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
unsigned int asrc2_mask = 0;
@@ -1009,11 +1041,11 @@ int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_2,
+ snd_soc_component_update_bits(component, RT5645_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_3,
+ snd_soc_component_update_bits(component, RT5645_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
@@ -1288,30 +1320,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_dac_r2_mux =
SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
-
-/* INL/R source */
-static const char * const rt5645_inl_src[] = {
- "IN2P", "MonoP"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inl_enum, RT5645_INL1_INR1_VOL,
- RT5645_INL_SEL_SFT, rt5645_inl_src);
-
-static const struct snd_kcontrol_new rt5645_inl_mux =
- SOC_DAPM_ENUM("INL source", rt5645_inl_enum);
-
-static const char * const rt5645_inr_src[] = {
- "IN2N", "MonoN"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inr_enum, RT5645_INL1_INR1_VOL,
- RT5645_INR_SEL_SFT, rt5645_inr_src);
-
-static const struct snd_kcontrol_new rt5645_inr_mux =
- SOC_DAPM_ENUM("INR source", rt5645_inr_enum);
-
/* Stereo1 ADC source */
/* MX-27 [12] */
static const char * const rt5645_stereo_adc1_src[] = {
@@ -1611,18 +1619,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
-/* MX-2F [1:0] */
-static const char * const rt5645_if3_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "VAD_ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA,
- RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src);
-
-static const struct snd_kcontrol_new rt5645_if3_adc_in_mux =
- SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5645_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1678,56 +1674,65 @@ 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_codec *codec, int on)
+static void hp_amp_power(struct snd_soc_component *component, int on)
{
static int hp_amp_power_count;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, RT5645_DEPOP_M2, 0x3100);
- snd_soc_write(codec, RT5645_CHARGE_PUMP,
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x3100);
+ snd_soc_component_write(component, RT5645_CHARGE_PUMP,
0x0e06);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_HP_DCC_INT1, 0x9f01);
- msleep(20);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ 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_write(codec, RT5645_DEPOP_M3, 0x0737);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R);
msleep(90);
- rt5645->hp_on = true;
} else {
/* depop parameters */
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ 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_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5645_PWR_VOL,
+ 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_update_bits(codec, RT5645_PWR_ANLG1,
+ 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_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ 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 +
@@ -1743,15 +1748,16 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
regmap_write(rt5645->regmap, RT5645_PR_BASE +
0x3e, 0x7400);
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
msleep(100);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001);
-
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0);
} else {
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK |
RT5645_HP_L_SMT_MASK |
RT5645_HP_R_SMT_MASK,
@@ -1759,11 +1765,11 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
RT5645_HP_L_SMT_DIS |
RT5645_HP_R_SMT_DIS);
/* headphone amp power down */
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ 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_update_bits(codec, RT5645_DEPOP_M2,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
RT5645_DEPOP_MASK, 0);
}
}
@@ -1773,15 +1779,15 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, 1);
+ hp_amp_power(component, 1);
/* headphone unmute sequence */
if (rt5645->codec_type == CODEC_TYPE_RT5645) {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ 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) |
@@ -1789,16 +1795,16 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTN_MASK, RT5645_RSTN_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ 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_update_bits(codec, RT5645_DEPOP_M1,
+ 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);
@@ -1808,7 +1814,7 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
if (rt5645->codec_type == CODEC_TYPE_RT5645) {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ 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) |
@@ -1816,17 +1822,17 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTP_MASK, RT5645_RSTP_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ 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(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1839,25 +1845,25 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- rt5645_enable_hweq(codec);
- snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ 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_update_bits(codec, RT5645_GEN_CTRL3,
+ 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_update_bits(codec, RT5645_GEN_CTRL3,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
- snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
- snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ 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;
@@ -1872,24 +1878,24 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power(codec, 1);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ hp_amp_power(component, 1);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, RT5645_PWR_LM);
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE,
RT5645_L_MUTE | RT5645_R_MUTE);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, 0);
- hp_amp_power(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1902,16 +1908,16 @@ static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+ 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_update_bits(codec, RT5645_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
RT5645_PWR_BST2_P, 0);
break;
@@ -1922,41 +1928,20 @@ static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt5650_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- if (rt5645->hp_on) {
- msleep(100);
- rt5645->hp_on = false;
- }
- 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5645_GEN_CTRL2,
+ 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_update_bits(codec, RT5645_GEN_CTRL2,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
RT5645_MICBIAS1_POW_CTRL_SEL_MASK,
RT5645_MICBIAS1_POW_CTRL_SEL_A);
break;
@@ -1971,17 +1956,17 @@ static int rt5645_set_micbias1_event(struct snd_soc_dapm_widget *w,
static int rt5645_set_micbias2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5645_GEN_CTRL2,
+ 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_update_bits(codec, RT5645_GEN_CTRL2,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
RT5645_MICBIAS2_POW_CTRL_SEL_MASK,
RT5645_MICBIAS2_POW_CTRL_SEL_A);
break;
@@ -2279,7 +2264,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("PDM1R"),
SND_SOC_DAPM_OUTPUT("SPOL"),
SND_SOC_DAPM_OUTPUT("SPOR"),
- SND_SOC_DAPM_POST("DAPM_POST", rt5650_hp_event),
};
static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = {
@@ -2768,20 +2752,20 @@ static const struct snd_soc_dapm_route rt5645_old_dapm_routes[] = {
static int rt5645_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 rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk, dl_sft;
int pre_div, bclk_ms, frame_size;
rt5645->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -2822,20 +2806,20 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
case RT5645_AIF1:
mask_clk = RT5645_I2S_PD1_MASK;
val_clk = pre_div << RT5645_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S1_SDP,
(0x3 << dl_sft), (val_len << dl_sft));
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5645_AIF2:
mask_clk = RT5645_I2S_BCLK_MS2_MASK | RT5645_I2S_PD2_MASK;
val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT |
pre_div << RT5645_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S2_SDP,
(0x3 << dl_sft), (val_len << dl_sft));
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2844,8 +2828,8 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0, pol_sft;
switch (rt5645->codec_type) {
@@ -2858,10 +2842,10 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5645->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5645_I2S_MS_S;
rt5645->master[dai->id] = 0;
break;
@@ -2896,17 +2880,17 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (dai->id) {
case RT5645_AIF1:
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S1_SDP,
RT5645_I2S_MS_MASK | (1 << pol_sft) |
RT5645_I2S_DF_MASK, reg_val);
break;
case RT5645_AIF2:
- snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S2_SDP,
RT5645_I2S_MS_MASK | (1 << pol_sft) |
RT5645_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -2915,8 +2899,8 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src)
@@ -2933,10 +2917,10 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5645_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_SCLK_SRC_MASK, reg_val);
rt5645->sysclk = freq;
rt5645->sysclk_src = clk_id;
@@ -2949,8 +2933,8 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -2959,56 +2943,56 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5645->pll_in = 0;
rt5645->pll_out = 0;
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_SCLK_SRC_MASK, RT5645_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5645_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_MCLK);
break;
case RT5645_PLL1_S_BCLK1:
case RT5645_PLL1_S_BCLK2:
switch (dai->id) {
case RT5645_AIF1:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1);
break;
case RT5645_AIF2:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5645_PLL_CTRL1,
+ snd_soc_component_write(component, RT5645_PLL_CTRL1,
pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5645_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT |
- pll_code.m_bp << RT5645_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5645_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT) |
+ (pll_code.m_bp << RT5645_PLL_M_BP_SFT));
rt5645->pll_in = freq_in;
rt5645->pll_out = freq_out;
@@ -3020,8 +3004,8 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft;
unsigned int mask, val = 0;
@@ -3044,7 +3028,7 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (rx_mask || tx_mask) {
val |= (1 << en_sft);
if (rt5645->codec_type == CODEC_TYPE_RT5645)
- snd_soc_update_bits(codec, RT5645_BASS_BACK,
+ snd_soc_component_update_bits(component, RT5645_BASS_BACK,
RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
}
@@ -3078,45 +3062,45 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
}
- snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, mask, val);
+ snd_soc_component_update_bits(component, RT5645_TDM_CTRL_1, mask, val);
return 0;
}
-static int rt5645_set_bias_level(struct snd_soc_codec *codec,
+static int rt5645_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
}
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
msleep(40);
if (rt5645->en_button_func)
queue_delayed_work(system_power_efficient_wq,
@@ -3126,11 +3110,11 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1100);
if (!rt5645->en_button_func)
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, 0);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2 |
RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
@@ -3143,27 +3127,39 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5645_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
if (enable) {
snd_soc_dapm_force_enable_pin(dapm, "ADC L power");
snd_soc_dapm_force_enable_pin(dapm, "ADC R power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
- snd_soc_update_bits(codec,
- RT5645_INT_IRQ_ST, 0x8, 0x8);
- snd_soc_update_bits(codec,
- RT5650_4BTN_IL_CMD2, 0x8000, 0x8000);
- snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_RST);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_NORM);
+ msleep(50);
+ ret = snd_soc_component_read(component, RT5645_INT_IRQ_ST);
+ pr_debug("%s read %x = %x\n", __func__, RT5645_INT_IRQ_ST,
+ snd_soc_component_read(component, RT5645_INT_IRQ_ST));
+ snd_soc_component_write(component, RT5645_INT_IRQ_ST, ret);
+ ret = snd_soc_component_read(component, RT5650_4BTN_IL_CMD1);
pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1,
- snd_soc_read(codec, RT5650_4BTN_IL_CMD1));
+ snd_soc_component_read(component, RT5650_4BTN_IL_CMD1));
+ snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, ret);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
+ snd_soc_component_update_bits(component,
+ RT5645_INT_IRQ_ST, 0x8, 0x8);
} else {
- snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
- snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
+ snd_soc_component_update_bits(component, RT5645_INT_IRQ_ST, 0x8, 0x0);
snd_soc_dapm_disable_pin(dapm, "ADC L power");
snd_soc_dapm_disable_pin(dapm, "ADC R power");
@@ -3171,20 +3167,20 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
}
}
-static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5645_jack_detect(struct snd_soc_component *component, int jack_insert)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (jack_insert) {
- regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206);
/* for jack type detect */
snd_soc_dapm_force_enable_pin(dapm, "LDO2");
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- if (!dapm->card->instantiated) {
+ if (!snd_soc_card_is_instantiated(dapm->card)) {
/* Power up necessary bits for JD if dapm is
not ready yet */
regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
@@ -3205,24 +3201,33 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
RT5645_CBJ_MN_JD, 0);
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 1);
+
msleep(600);
regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val);
val &= 0x7;
- dev_dbg(codec->dev, "val = %d\n", val);
+ dev_dbg(component->dev, "val = %d\n", val);
- if (val == 1 || val == 2) {
+ if ((val == 1 || val == 2) && !rt5645->pdata.no_headset_mic) {
rt5645->jack_type = SND_JACK_HEADSET;
if (rt5645->en_button_func) {
- rt5645_enable_push_button_irq(codec, true);
+ rt5645_enable_push_button_irq(component, true);
}
} else {
+ if (rt5645->en_button_func)
+ rt5645_enable_push_button_irq(component, false);
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
rt5645->jack_type = SND_JACK_HEADPHONE;
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
}
if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
+
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
} else { /* jack out */
rt5645->jack_type = 0;
@@ -3235,7 +3240,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
RT5645_CBJ_BST1_EN, 0);
if (rt5645->en_button_func)
- rt5645_enable_push_button_irq(codec, false);
+ rt5645_enable_push_button_irq(component, false);
if (rt5645->pdata.jd_mode == 0)
snd_soc_dapm_disable_pin(dapm, "LDO2");
@@ -3244,30 +3249,33 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
}
return rt5645->jack_type;
}
-static int rt5645_button_detect(struct snd_soc_codec *codec)
+static int rt5645_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ val = snd_soc_component_read(component, RT5650_4BTN_IL_CMD1);
pr_debug("val=0x%x\n", val);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val);
+ snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, val);
return btn_type;
}
static irqreturn_t rt5645_irq(int irq, void *data);
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+int rt5645_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
rt5645->hp_jack = hp_jack;
rt5645->mic_jack = mic_jack;
@@ -3278,6 +3286,8 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec,
RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+ regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
+ RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
}
rt5645_irq(0, rt5645);
@@ -3285,43 +3295,71 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
+static int rt5645_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct snd_soc_jack *mic_jack = NULL;
+ struct snd_soc_jack *btn_jack = NULL;
+ int type;
+
+ if (hs_jack) {
+ type = *(int *)data;
+
+ if (type & SND_JACK_MICROPHONE)
+ mic_jack = hs_jack;
+ if (type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ btn_jack = hs_jack;
+ }
+
+ return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack);
+}
+
static void rt5645_jack_detect_work(struct work_struct *work)
{
struct rt5645_priv *rt5645 =
container_of(work, struct rt5645_priv, jack_detect_work.work);
int val, btn_type, gpio_state = 0, report = 0;
- if (!rt5645->codec)
+ if (!rt5645->component)
return;
+ mutex_lock(&rt5645->jd_mutex);
+
switch (rt5645->pdata.jd_mode) {
case 0: /* Not using rt5645 JD */
if (rt5645->gpiod_hp_det) {
gpio_state = gpiod_get_value(rt5645->gpiod_hp_det);
- dev_dbg(rt5645->codec->dev, "gpio_state = %d\n",
+ if (rt5645->pdata.inv_hp_pol)
+ gpio_state ^= 1;
+ dev_dbg(rt5645->component->dev, "gpio_state = %d\n",
gpio_state);
- report = rt5645_jack_detect(rt5645->codec, gpio_state);
+ report = rt5645_jack_detect(rt5645->component, gpio_state);
}
snd_soc_jack_report(rt5645->hp_jack,
report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
+ mutex_unlock(&rt5645->jd_mutex);
return;
+ case 4:
+ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
+ break;
default: /* read rt5645 jd1_1 status */
- val = snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x1000;
+ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000;
break;
}
if (!val && (rt5645->jack_type == 0)) { /* jack in */
- report = rt5645_jack_detect(rt5645->codec, 1);
- } else if (!val && rt5645->jack_type != 0) {
+ report = rt5645_jack_detect(rt5645->component, 1);
+ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) {
/* for push button and jack out */
btn_type = 0;
- if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
+ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
- btn_type = rt5645_button_detect(rt5645->codec);
+ btn_type = rt5645_button_detect(rt5645->component);
/* rt5650 can report three kinds of button behavior,
one click, double click and hold. However,
currently we will report button pressed/released
@@ -3351,7 +3389,7 @@ static void rt5645_jack_detect_work(struct work_struct *work)
case 0x0000: /* unpressed */
break;
default:
- dev_err(rt5645->codec->dev,
+ dev_err(rt5645->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -3366,11 +3404,13 @@ static void rt5645_jack_detect_work(struct work_struct *work)
} else {
/* jack out */
report = 0;
- snd_soc_update_bits(rt5645->codec,
+ snd_soc_component_update_bits(rt5645->component,
RT5645_INT_IRQ_ST, 0x1, 0x0);
- rt5645_jack_detect(rt5645->codec, 0);
+ rt5645_jack_detect(rt5645->component, 0);
}
+ mutex_unlock(&rt5645->jd_mutex);
+
snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE);
if (rt5645->en_button_func)
@@ -3400,18 +3440,19 @@ static irqreturn_t rt5645_irq(int irq, void *data)
static void rt5645_btn_check_callback(struct timer_list *t)
{
- struct rt5645_priv *rt5645 = from_timer(rt5645, t, btn_check_timer);
+ struct rt5645_priv *rt5645 = timer_container_of(rt5645, t,
+ btn_check_timer);
queue_delayed_work(system_power_efficient_wq,
&rt5645->jack_detect_work, msecs_to_jiffies(5));
}
-static int rt5645_probe(struct snd_soc_codec *codec)
+static int rt5645_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- rt5645->codec = codec;
+ rt5645->component = component;
switch (rt5645->codec_type) {
case CODEC_TYPE_RT5645:
@@ -3437,7 +3478,7 @@ static int rt5645_probe(struct snd_soc_codec *codec)
break;
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
/* for JD function */
if (rt5645->pdata.jd_mode) {
@@ -3447,24 +3488,27 @@ static int rt5645_probe(struct snd_soc_codec *codec)
}
if (rt5645->pdata.long_name)
- codec->component.card->long_name = rt5645->pdata.long_name;
+ component->card->long_name = rt5645->pdata.long_name;
+
+ rt5645->eq_param = devm_kcalloc(component->dev,
+ RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s),
+ GFP_KERNEL);
- rt5645->eq_param = devm_kzalloc(codec->dev,
- RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+ if (!rt5645->eq_param)
+ return -ENOMEM;
return 0;
}
-static int rt5645_remove(struct snd_soc_codec *codec)
+static void rt5645_remove(struct snd_soc_component *component)
{
- rt5645_reset(codec);
- return 0;
+ rt5645_reset(component);
}
#ifdef CONFIG_PM
-static int rt5645_suspend(struct snd_soc_codec *codec)
+static int rt5645_suspend(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, true);
regcache_mark_dirty(rt5645->regmap);
@@ -3472,9 +3516,9 @@ static int rt5645_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5645_resume(struct snd_soc_codec *codec)
+static int rt5645_resume(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, false);
regcache_sync(rt5645->regmap);
@@ -3539,33 +3583,34 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
- .probe = rt5645_probe,
- .remove = rt5645_remove,
- .suspend = rt5645_suspend,
- .resume = rt5645_resume,
- .set_bias_level = rt5645_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5645_snd_controls,
- .num_controls = ARRAY_SIZE(rt5645_snd_controls),
- .dapm_widgets = rt5645_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
- .dapm_routes = rt5645_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_rt5645 = {
+ .probe = rt5645_probe,
+ .remove = rt5645_remove,
+ .suspend = rt5645_suspend,
+ .resume = rt5645_resume,
+ .set_bias_level = rt5645_set_bias_level,
+ .controls = rt5645_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5645_snd_controls),
+ .dapm_widgets = rt5645_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
+ .dapm_routes = rt5645_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+ .set_jack = rt5645_component_set_jack,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5645_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
RT5645_PR_SPACING),
.volatile_reg = rt5645_volatile_register,
.readable_reg = rt5645_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5645_reg,
.num_reg_defaults = ARRAY_SIZE(rt5645_reg),
.ranges = rt5645_ranges,
@@ -3575,13 +3620,14 @@ static const struct regmap_config rt5645_regmap = {
static const struct regmap_config rt5650_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
RT5645_PR_SPACING),
.volatile_reg = rt5645_volatile_register,
.readable_reg = rt5645_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5650_reg,
.num_reg_defaults = ARRAY_SIZE(rt5650_reg),
.ranges = rt5645_ranges,
@@ -3592,14 +3638,15 @@ static const struct regmap_config temp_regmap = {
.name="nocache",
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1,
.cache_type = REGCACHE_NONE,
};
static const struct i2c_device_id rt5645_i2c_id[] = {
- { "rt5645", 0 },
- { "rt5650", 0 },
+ { "rt5645" },
+ { "rt5650" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
@@ -3615,12 +3662,12 @@ MODULE_DEVICE_TABLE(of, rt5645_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5645_acpi_match[] = {
- { "10EC5645", 0 },
- { "10EC5648", 0 },
- { "10EC5650", 0 },
- { "10EC5640", 0 },
- { "10EC3270", 0 },
- {},
+ { "10EC3270" },
+ { "10EC5640" },
+ { "10EC5645" },
+ { "10EC5648" },
+ { "10EC5650" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
#endif
@@ -3634,13 +3681,14 @@ static const struct rt5645_platform_data intel_braswell_platform_data = {
static const struct rt5645_platform_data buddy_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
- .jd_mode = 3,
+ .jd_mode = 4,
.level_trigger_irq = true,
};
static const struct rt5645_platform_data gpd_win_platform_data = {
.jd_mode = 3,
.inv_jd1_1 = true,
+ .mono_speaker = true,
.long_name = "gpd-win-pocket-rt5645",
/* The GPD pocket has a diff. mic, for the win this does not matter. */
.in2_diff = true,
@@ -3653,10 +3701,61 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = {
.inv_jd1_1 = true,
};
+static const struct rt5645_platform_data asus_t101ha_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_IN2N,
+ .dmic2_data_pin = RT5645_DMIC2_DISABLE,
+ .jd_mode = 3,
+};
+
+static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
+ .jd_mode = 3,
+ .in2_diff = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_monospk_platform_data = {
+ .jd_mode = 3,
+ .mono_speaker = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_inv_data = {
+ .jd_mode = 3,
+ .inv_jd1_1 = true,
+};
+
static const struct rt5645_platform_data jd_mode3_platform_data = {
.jd_mode = 3,
};
+static const struct rt5645_platform_data lattepanda_board_platform_data = {
+ .jd_mode = 2,
+ .inv_jd1_1 = true
+};
+
+static const struct rt5645_platform_data kahlee_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+};
+
+static const struct rt5645_platform_data ecs_ef20_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC1_DISABLE,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .inv_hp_pol = 1,
+};
+
+static const struct acpi_gpio_params ef20_hp_detect = { 1, 0, false };
+
+static const struct acpi_gpio_mapping cht_rt5645_ef20_gpios[] = {
+ { "hp-detect-gpios", &ef20_hp_detect, 1 },
+ { },
+};
+
+static int cht_rt5645_ef20_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_rt5645_gpios = cht_rt5645_ef20_gpios;
+ return 1;
+}
+
static const struct dmi_system_id dmi_platform_data[] = {
{
.ident = "Chrome Buddy",
@@ -3721,6 +3820,14 @@ static const struct dmi_system_id dmi_platform_data[] = {
.driver_data = (void *)&asus_t100ha_platform_data,
},
{
+ .ident = "ASUS T101HA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"),
+ },
+ .driver_data = (void *)&asus_t101ha_platform_data,
+ },
+ {
.ident = "MINIX Z83-4",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
@@ -3734,7 +3841,85 @@ static const struct dmi_system_id dmi_platform_data[] = {
DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"),
},
- .driver_data = (void *)&jd_mode3_platform_data,
+ .driver_data = (void *)&jd_mode3_monospk_platform_data,
+ },
+ {
+ .ident = "Lenovo Ideapad Miix 310",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
+ },
+ .driver_data = (void *)&lenovo_ideapad_miix_310_pdata,
+ },
+ {
+ .ident = "Lenovo Ideapad Miix 320",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "LattePanda board",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
+ /*
+ * Above strings are too generic, LattePanda BIOS versions for
+ * all 4 hw revisions are:
+ * DF-BI-7-S70CR100-*
+ * DF-BI-7-S70CR110-*
+ * DF-BI-7-S70CR200-*
+ * LP-BS-7-S70CR700-*
+ * Do a partial match for S70CR to avoid false positive matches.
+ */
+ DMI_MATCH(DMI_BIOS_VERSION, "S70CR"),
+ },
+ .driver_data = (void *)&lattepanda_board_platform_data,
+ },
+ {
+ .ident = "Chrome Kahlee",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"),
+ },
+ .driver_data = (void *)&kahlee_platform_data,
+ },
+ {
+ .ident = "Medion E1239T",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1239T MD60568"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "EF20",
+ .callback = cht_rt5645_ef20_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "EF20"),
+ },
+ .driver_data = (void *)&ecs_ef20_platform_data,
+ },
+ {
+ .ident = "Acer Switch V 10 (SW5-017)",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "Meegopad T08",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+ DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+ },
+ .driver_data = (void *)&jd_mode3_inv_data,
},
{ }
};
@@ -3750,25 +3935,68 @@ static bool rt5645_check_dp(struct device *dev)
return false;
}
-static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
+static void rt5645_parse_dt(struct device *dev, struct rt5645_platform_data *pdata)
{
- rt5645->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- device_property_read_u32(dev,
- "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
- device_property_read_u32(dev,
- "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
- device_property_read_u32(dev,
- "realtek,jd-mode", &rt5645->pdata.jd_mode);
-
- return 0;
+ pdata->in2_diff = device_property_read_bool(dev, "realtek,in2-differential");
+ device_property_read_u32(dev, "realtek,dmic1-data-pin", &pdata->dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic2-data-pin", &pdata->dmic2_data_pin);
+ device_property_read_u32(dev, "realtek,jd-mode", &pdata->jd_mode);
}
-static int rt5645_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void rt5645_get_pdata(struct device *codec_dev, struct rt5645_platform_data *pdata)
{
- struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
const struct dmi_system_id *dmi_data;
+
+ dmi_data = dmi_first_match(dmi_platform_data);
+ if (dmi_data) {
+ dev_info(codec_dev, "Detected %s platform\n", dmi_data->ident);
+ *pdata = *((struct rt5645_platform_data *)dmi_data->driver_data);
+ } else if (rt5645_check_dp(codec_dev)) {
+ rt5645_parse_dt(codec_dev, pdata);
+ } else {
+ *pdata = jd_mode3_platform_data;
+ }
+
+ if (quirk != -1) {
+ pdata->in2_diff = QUIRK_IN2_DIFF(quirk);
+ pdata->level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
+ pdata->inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
+ pdata->inv_hp_pol = QUIRK_INV_HP_POL(quirk);
+ pdata->jd_mode = QUIRK_JD_MODE(quirk);
+ pdata->dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
+ pdata->dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
+ }
+}
+
+const char *rt5645_components(struct device *codec_dev)
+{
+ struct rt5645_platform_data pdata = { };
+ static char buf[32];
+ const char *mic;
+ int spk = 2;
+
+ rt5645_get_pdata(codec_dev, &pdata);
+
+ if (pdata.mono_speaker)
+ spk = 1;
+
+ if (pdata.dmic1_data_pin && pdata.dmic2_data_pin)
+ mic = "dmics12";
+ else if (pdata.dmic1_data_pin)
+ mic = "dmic1";
+ else if (pdata.dmic2_data_pin)
+ mic = "dmic2";
+ else
+ mic = "in2";
+
+ snprintf(buf, sizeof(buf), "cfg-spk:%d cfg-mic:%s", spk, mic);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(rt5645_components);
+
+static int rt5645_i2c_probe(struct i2c_client *i2c)
+{
struct rt5645_priv *rt5645;
int ret, i;
unsigned int val;
@@ -3781,27 +4009,17 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
rt5645->i2c = i2c;
i2c_set_clientdata(i2c, rt5645);
+ rt5645_get_pdata(&i2c->dev, &rt5645->pdata);
- dmi_data = dmi_first_match(dmi_platform_data);
- if (dmi_data) {
- dev_info(&i2c->dev, "Detected %s platform\n", dmi_data->ident);
- pdata = dmi_data->driver_data;
- }
-
- if (pdata)
- rt5645->pdata = *pdata;
- else if (rt5645_check_dp(&i2c->dev))
- rt5645_parse_dt(rt5645, &i2c->dev);
- else
- rt5645->pdata = jd_mode3_platform_data;
+ if (has_acpi_companion(&i2c->dev)) {
+ if (cht_rt5645_gpios) {
+ if (devm_acpi_dev_add_driver_gpios(&i2c->dev, cht_rt5645_gpios))
+ dev_dbg(&i2c->dev, "Failed to add driver gpios\n");
+ }
- if (quirk != -1) {
- rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk);
- rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
- rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
- rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk);
- rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
- rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
+ /* The ALC3270 package has the headset-mic pin not-connected */
+ if (acpi_dev_hid_uid_match(ACPI_COMPANION(&i2c->dev), "10EC3270", NULL))
+ rt5645->pdata.no_headset_mic = true;
}
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
@@ -3818,6 +4036,16 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ rt5645->gpiod_cbj_sleeve = devm_gpiod_get_optional(&i2c->dev, "cbj-sleeve",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(rt5645->gpiod_cbj_sleeve)) {
+ ret = PTR_ERR(rt5645->gpiod_cbj_sleeve);
+ dev_info(&i2c->dev, "failed to initialize gpiod, ret=%d\n", ret);
+ if (ret != -ENOENT)
+ return ret;
+ }
+
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
rt5645->supplies[i].supply = rt5645_supply_names[i];
@@ -3841,7 +4069,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
/*
@@ -3849,7 +4077,11 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
* read and power On.
*/
msleep(TIME_TO_POWER_MS);
- regmap_read(regmap, RT5645_VENDOR_ID2, &val);
+ ret = regmap_read(regmap, RT5645_VENDOR_ID2, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: 0x%02X\n, ret = %d", RT5645_VENDOR_ID2, ret);
+ goto err_enable;
+ }
switch (val) {
case RT5645_DEVICE_ID:
@@ -3872,7 +4104,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(rt5645->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
regmap_write(rt5645->regmap, RT5645_RESET, 0);
@@ -3882,13 +4114,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080);
- ret = regmap_register_patch(rt5645->regmap, init_list,
+ ret = regmap_multi_reg_write(rt5645->regmap, init_list,
ARRAY_SIZE(init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- ret = regmap_register_patch(rt5645->regmap, rt5650_init_list,
+ ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list,
ARRAY_SIZE(rt5650_init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n",
@@ -3993,6 +4225,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
RT5645_JD1_MODE_1);
break;
case 3:
+ case 4:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
@@ -4015,6 +4248,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
timer_setup(&rt5645->btn_check_timer, rt5645_btn_check_callback, 0);
+ mutex_init(&rt5645->jd_mutex);
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
@@ -4023,12 +4257,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5645", rt5645);
if (ret) {
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
goto err_enable;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5645,
rt5645_dai, ARRAY_SIZE(rt5645_dai));
if (ret)
goto err_irq;
@@ -4043,21 +4277,26 @@ err_enable:
return ret;
}
-static int rt5645_i2c_remove(struct i2c_client *i2c)
+static void rt5645_i2c_remove(struct i2c_client *i2c)
{
struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt5645);
+ /*
+ * Since the rt5645_btn_check_callback() can queue jack_detect_work,
+ * the timer need to be delted first
+ */
+ timer_delete_sync(&rt5645->btn_check_timer);
+
cancel_delayed_work_sync(&rt5645->jack_detect_work);
cancel_delayed_work_sync(&rt5645->rcclock_work);
- del_timer_sync(&rt5645->btn_check_timer);
- snd_soc_unregister_codec(&i2c->dev);
- regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
- return 0;
+ regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
}
static void rt5645_i2c_shutdown(struct i2c_client *i2c)
@@ -4072,13 +4311,48 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
0);
msleep(20);
regmap_write(rt5645->regmap, RT5645_RESET, 0);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
+}
+
+static int rt5645_sys_suspend(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ timer_delete_sync(&rt5645->btn_check_timer);
+ cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ regcache_cache_only(rt5645->regmap, true);
+ regcache_mark_dirty(rt5645->regmap);
+ return 0;
+}
+
+static int rt5645_sys_resume(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ regcache_cache_only(rt5645->regmap, false);
+ regcache_sync(rt5645->regmap);
+
+ if (rt5645->hp_jack) {
+ rt5645->jack_type = 0;
+ rt5645_jack_detect_work(&rt5645->jack_detect_work.work);
+ }
+ return 0;
}
+static const struct dev_pm_ops rt5645_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume)
+};
+
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
.of_match_table = of_match_ptr(rt5645_of_match),
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
+ .pm = pm_ptr(&rt5645_pm),
},
.probe = rt5645_i2c_probe,
.remove = rt5645_i2c_remove,
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 940325b28c29..bef74b29fd54 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5645.h -- RT5645 ALSA SoC audio driver
*
* Copyright 2013 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT5645_H__
#define __RT5645_H__
-#include <sound/rt5645.h>
-
/* Info */
#define RT5645_RESET 0x00
#define RT5645_VENDOR_ID 0xfd
@@ -2016,6 +2011,12 @@
#define RT5645_ZCD_HP_DIS (0x0 << 15)
#define RT5645_ZCD_HP_EN (0x1 << 15)
+/* Buttons Inline Command Function 2 (0xe0) */
+#define RT5645_EN_4BTN_IL_MASK (0x1 << 15)
+#define RT5645_EN_4BTN_IL_EN (0x1 << 15)
+#define RT5645_RST_4BTN_IL_MASK (0x1 << 14)
+#define RT5645_RST_4BTN_IL_RST (0x0 << 14)
+#define RT5645_RST_4BTN_IL_NORM (0x1 << 14)
/* Codec Private Register definition */
/* DAC ADC Digital Volume (0x00) */
@@ -2200,10 +2201,13 @@ enum {
RT5645_AD_MONO_R_FILTER = (0x1 << 5),
};
-int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+int rt5645_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack);
+
+const char *rt5645_components(struct device *codec_dev);
+
#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 831b297978a4..9eeeba8cd6ff 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1,25 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5651.c -- RT5651 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@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.
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/dmi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -32,10 +28,6 @@
#include "rl6231.h"
#include "rt5651.h"
-#define RT5651_JD_MAP(quirk) ((quirk) & GENMASK(7, 0))
-#define RT5651_IN2_DIFF BIT(16)
-#define RT5651_DMIC_EN BIT(17)
-
#define RT5651_DEVICE_ID_VALUE 0x6281
#define RT5651_PR_RANGE_BASE (0xff + 1)
@@ -43,8 +35,6 @@
#define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING))
-static unsigned long rt5651_quirk;
-
static const struct regmap_range_cfg rt5651_ranges[] = {
{ .name = "PR", .range_min = RT5651_PR_BASE,
.range_max = RT5651_PR_BASE + 0xb4,
@@ -295,9 +285,9 @@ static bool rt5651_readable_register(struct device *dev, unsigned int reg)
}
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_MINMAX(dac_vol_tlv, -6562, 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_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 */
@@ -338,11 +328,13 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
175, 0, dac_vol_tlv),
- /* IN1/IN2 Control */
+ /* IN1/IN2/IN3 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2,
RT5651_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2,
RT5651_BST_SFT2, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3,
+ RT5651_BST_SFT1, 8, 0, bst_tlv),
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
@@ -384,36 +376,22 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5651->sysclk / rl6231_get_pre_div(rt5651->regmap,
RT5651_ADDA_CLK1, RT5651_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
idx << RT5651_DMIC_CLK_SFT);
return idx;
}
-static int is_sysclk_from_pll(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
-
- val = snd_soc_read(codec, RT5651_GLB_CLK);
- val &= RT5651_SCLK_SRC_MASK;
- if (val == RT5651_SCLK_SRC_PLL1)
- return 1;
- else
- return 0;
-}
-
/* Digital Mixer */
static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
@@ -703,8 +681,8 @@ static const struct snd_kcontrol_new rt5651_pdm_r_mux =
static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -741,8 +719,8 @@ static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -767,11 +745,11 @@ static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
RT5651_HP_CP_PD | RT5651_HP_SG_EN);
regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400);
- rt5651->hp_mute = 0;
+ rt5651->hp_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- rt5651->hp_mute = 1;
+ rt5651->hp_mute = true;
usleep_range(70000, 75000);
break;
@@ -786,8 +764,8 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -806,16 +784,16 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST1_OP2, RT5651_PWR_BST1_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST1_OP2, 0);
break;
@@ -829,16 +807,16 @@ static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST2_OP2, RT5651_PWR_BST2_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST2_OP2, 0);
break;
@@ -852,16 +830,16 @@ static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst3_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST3_OP2, RT5651_PWR_BST3_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST3_OP2, 0);
break;
@@ -885,12 +863,6 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
11, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2,
- RT5651_PWR_PLL_BIT, 0, NULL, 0),
- /* Input Side */
- SND_SOC_DAPM_SUPPLY("JD Power", RT5651_PWR_ANLG2,
- RT5651_PWM_JD_M_BIT, 0, NULL, 0),
-
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1,
RT5651_PWR_LDO_BIT, 0, NULL, 0),
@@ -1169,7 +1141,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
{"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
{"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"},
- {"Stereo1 Filter", NULL, "PLL1", is_sysclk_from_pll},
{"Stereo1 Filter", NULL, "ADC ASRC"},
{"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
@@ -1179,7 +1150,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"},
{"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"},
{"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"},
- {"Stereo2 Filter", NULL, "PLL1", is_sysclk_from_pll},
{"Stereo2 Filter", NULL, "ADC ASRC"},
{"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
@@ -1246,10 +1216,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"PDM R Mux", "DD MIX", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC R1", NULL, "DAC R1 Power"},
{"DD MIXL", "DAC L1 Switch", "DAC MIXL"},
@@ -1313,8 +1281,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
static int rt5651_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 rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
@@ -1322,12 +1290,12 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -1358,19 +1326,19 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
case RT5651_AIF1:
mask_clk = RT5651_I2S_PD1_MASK;
val_clk = pre_div << RT5651_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S1_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5651_AIF2:
mask_clk = RT5651_I2S_BCLK_MS2_MASK | RT5651_I2S_PD2_MASK;
val_clk = pre_div << RT5651_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S2_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -1379,15 +1347,15 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5651->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5651_I2S_MS_S;
rt5651->master[dai->id] = 0;
break;
@@ -1423,17 +1391,17 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5651_AIF1:
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S1_SDP,
RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
RT5651_I2S_DF_MASK, reg_val);
break;
case RT5651_AIF2:
- snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S2_SDP,
RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
RT5651_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -1442,9 +1410,10 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
+ unsigned int pll_bit = 0;
if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src)
return 0;
@@ -1455,15 +1424,18 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
break;
case RT5651_SCLK_S_PLL1:
reg_val |= RT5651_SCLK_SRC_PLL1;
+ pll_bit |= RT5651_PWR_PLL;
break;
case RT5651_SCLK_S_RCCLK:
reg_val |= RT5651_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ RT5651_PWR_PLL, pll_bit);
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_SCLK_SRC_MASK, reg_val);
rt5651->sysclk = freq;
rt5651->sysclk_src = clk_id;
@@ -1476,8 +1448,8 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -1486,48 +1458,48 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5651->pll_in = 0;
rt5651->pll_out = 0;
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5651_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK);
break;
case RT5651_PLL1_S_BCLK1:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK1);
break;
case RT5651_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5651_PLL_CTRL1,
+ snd_soc_component_write(component, RT5651_PLL_CTRL1,
pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5651_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT |
- pll_code.m_bp << RT5651_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5651_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT) |
+ (pll_code.m_bp << RT5651_PLL_M_BP_SFT));
rt5651->pll_in = freq_in;
rt5651->pll_out = freq_out;
@@ -1536,46 +1508,44 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5651_set_bias_level(struct snd_soc_codec *codec,
+static int rt5651_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
-
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) {
+ if (snd_soc_component_read(component, RT5651_PLL_MODE_1) & 0x9200)
+ snd_soc_component_update_bits(component, RT5651_D_MISC,
+ 0xc00, 0xc00);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
RT5651_PWR_VREF1 | RT5651_PWR_MB |
RT5651_PWR_BG | RT5651_PWR_VREF2,
RT5651_PWR_VREF1 | RT5651_PWR_MB |
RT5651_PWR_BG | RT5651_PWR_VREF2);
usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
RT5651_PWR_FV1 | RT5651_PWR_FV2,
RT5651_PWR_FV1 | RT5651_PWR_FV2);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_LDO_DVO_MASK,
- RT5651_PWR_LDO_DVO_1_2V);
- snd_soc_update_bits(codec, RT5651_D_MISC, 0x1, 0x1);
- if (snd_soc_read(codec, RT5651_PLL_MODE_1) & 0x9200)
- snd_soc_update_bits(codec, RT5651_D_MISC,
- 0xc00, 0xc00);
+ snd_soc_component_update_bits(component, RT5651_D_MISC, 0x1, 0x1);
}
break;
- case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec, RT5651_D_MISC, 0x0010);
- snd_soc_write(codec, RT5651_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5651_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000);
- if (rt5651->pdata.jd_src) {
- snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0204);
- snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0002);
- } else {
- snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000);
- }
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, RT5651_D_MISC, 0x0010);
+ snd_soc_component_write(component, RT5651_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_MIXER, 0x0000);
+ /* Do not touch the LDO voltage select bits on bias-off */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
+ ~RT5651_PWR_LDO_DVO_MASK, 0);
+ /* Leave PLL1 and jack-detect power as is, all others off */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ ~(RT5651_PWR_PLL | RT5651_PWR_JD_M), 0);
break;
default:
@@ -1585,53 +1555,538 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5651_probe(struct snd_soc_codec *codec)
+static void rt5651_enable_micbias1_for_ovcd(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, "LDO");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "micbias1");
+ /* OVCD is unreliable when used with RCCLK as sysclk-source */
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt5651_disable_micbias1_for_ovcd(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, "Platform Clock");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "micbias1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LDO");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt5651_enable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_NOR);
+ rt5651->ovcd_irq_enabled = true;
+}
+
+static void rt5651_disable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_BP);
+ rt5651->ovcd_irq_enabled = false;
+}
+
+static void rt5651_clear_micbias1_ovcd(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_MB1_OC_CLR, 0);
+}
+
+static bool rt5651_micbias1_ovcd(struct snd_soc_component *component)
+{
+ int val;
+
+ val = snd_soc_component_read(component, RT5651_IRQ_CTRL2);
+ dev_dbg(component->dev, "irq ctrl2 %#04x\n", val);
+
+ return (val & RT5651_MB1_OC_CLR);
+}
+
+static bool rt5651_jack_inserted(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ int val;
+
+ if (rt5651->gpiod_hp_det) {
+ val = gpiod_get_value_cansleep(rt5651->gpiod_hp_det);
+ dev_dbg(component->dev, "jack-detect gpio %d\n", val);
+ return val;
+ }
+
+ val = snd_soc_component_read(component, RT5651_INT_IRQ_ST);
+ dev_dbg(component->dev, "irq status %#04x\n", val);
+
+ switch (rt5651->jd_src) {
+ case RT5651_JD1_1:
+ val &= 0x1000;
+ break;
+ case RT5651_JD1_2:
+ val &= 0x2000;
+ break;
+ case RT5651_JD2:
+ val &= 0x4000;
+ break;
+ default:
+ break;
+ }
+
+ if (rt5651->jd_active_high)
+ return val != 0;
+ else
+ return val == 0;
+}
+
+/* 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 rt5651_start_button_press_work(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ rt5651->poll_count = 0;
+ rt5651->press_count = 0;
+ rt5651->release_count = 0;
+ rt5651->pressed = false;
+ rt5651->press_reported = false;
+ rt5651_clear_micbias1_ovcd(component);
+ schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static void rt5651_button_press_work(struct work_struct *work)
+{
+ struct rt5651_priv *rt5651 =
+ container_of(work, struct rt5651_priv, bp_work.work);
+ struct snd_soc_component *component = rt5651->component;
+
+ /* Check the jack was not removed underneath us */
+ if (!rt5651_jack_inserted(component))
+ return;
+
+ if (rt5651_micbias1_ovcd(component)) {
+ rt5651->release_count = 0;
+ rt5651->press_count++;
+ /* Remember till after JACK_UNPLUG_TIME wait */
+ if (rt5651->press_count >= BP_THRESHOLD)
+ rt5651->pressed = true;
+ rt5651_clear_micbias1_ovcd(component);
+ } else {
+ rt5651->press_count = 0;
+ rt5651->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.
+ */
+ rt5651->poll_count++;
+ if (rt5651->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) {
+ schedule_delayed_work(&rt5651->bp_work,
+ msecs_to_jiffies(BP_POLL_TIME));
+ return;
+ }
+
+ if (rt5651->pressed && !rt5651->press_reported) {
+ dev_dbg(component->dev, "headset button press\n");
+ snd_soc_jack_report(rt5651->hp_jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ rt5651->press_reported = true;
+ }
+
+ if (rt5651->release_count >= BP_THRESHOLD) {
+ if (rt5651->press_reported) {
+ dev_dbg(component->dev, "headset button release\n");
+ snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0);
+ }
+ /* Re-enable OVCD IRQ to detect next press */
+ rt5651_enable_micbias1_ovcd_irq(component);
+ return; /* Stop polling */
+ }
+
+ schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static int rt5651_detect_headset(struct snd_soc_component *component)
+{
+ 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 */
+ rt5651_clear_micbias1_ovcd(component);
+
+ msleep(JACK_SETTLE_TIME);
+
+ /* Check the jack is still connected before checking ovcd */
+ if (!rt5651_jack_inserted(component))
+ return 0;
+
+ if (rt5651_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, "mic-gnd shorted\n");
+ headset_count = 0;
+ headphone_count++;
+ if (headphone_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADPHONE;
+ } else {
+ dev_dbg(component->dev, "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;
+}
+
+static bool rt5651_support_button_press(struct rt5651_priv *rt5651)
+{
+ if (!rt5651->hp_jack)
+ return false;
+
+ /* Button press support only works with internal jack-detection */
+ return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) &&
+ rt5651->gpiod_hp_det == NULL;
+}
+
+static void rt5651_jack_detect_work(struct work_struct *work)
+{
+ struct rt5651_priv *rt5651 =
+ container_of(work, struct rt5651_priv, jack_detect_work);
+ struct snd_soc_component *component = rt5651->component;
+ int report;
+
+ if (!rt5651_jack_inserted(component)) {
+ /* Jack removed, or spurious IRQ? */
+ if (rt5651->hp_jack->status & SND_JACK_HEADPHONE) {
+ if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) {
+ cancel_delayed_work_sync(&rt5651->bp_work);
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_disable_micbias1_for_ovcd(component);
+ }
+ snd_soc_jack_report(rt5651->hp_jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(component->dev, "jack unplugged\n");
+ }
+ } else if (!(rt5651->hp_jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted */
+ WARN_ON(rt5651->ovcd_irq_enabled);
+ rt5651_enable_micbias1_for_ovcd(component);
+ report = rt5651_detect_headset(component);
+ dev_dbg(component->dev, "detect report %#02x\n", report);
+ snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET);
+ if (rt5651_support_button_press(rt5651)) {
+ /* Enable ovcd IRQ for button press detect. */
+ rt5651_enable_micbias1_ovcd_irq(component);
+ } else {
+ /* No more need for overcurrent detect. */
+ rt5651_disable_micbias1_for_ovcd(component);
+ }
+ } else if (rt5651->ovcd_irq_enabled && rt5651_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.
+ */
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_start_button_press_work(component);
+
+ /*
+ * If the jack-detect IRQ flag goes high (unplug) after our
+ * above rt5651_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_work(system_long_wq, &rt5651->jack_detect_work);
+ }
+}
+
+static irqreturn_t rt5651_irq(int irq, void *data)
+{
+ struct rt5651_priv *rt5651 = data;
+
+ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work);
+
+ return IRQ_HANDLED;
+}
+
+static void rt5651_cancel_work(void *data)
+{
+ struct rt5651_priv *rt5651 = data;
+
+ cancel_work_sync(&rt5651->jack_detect_work);
+ cancel_delayed_work_sync(&rt5651->bp_work);
+}
+
+static void rt5651_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hp_jack,
+ struct gpio_desc *gpiod_hp_det)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ bool using_internal_jack_detect = true;
+
+ /* Select jack detect source */
+ switch (rt5651->jd_src) {
+ case RT5651_JD_NULL:
+ rt5651->gpiod_hp_det = gpiod_hp_det;
+ if (!rt5651->gpiod_hp_det)
+ return; /* No jack detect */
+ using_internal_jack_detect = false;
+ break;
+ case RT5651_JD1_1:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN);
+ break;
+ case RT5651_JD1_2:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN);
+ break;
+ case RT5651_JD2:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN);
+ break;
+ default:
+ dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
+ return;
+ }
+
+ if (using_internal_jack_detect) {
+ /* IRQ output on GPIO1 */
+ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
+ RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ);
+
+ /* Enable jack detect power */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ RT5651_PWR_JD_M, RT5651_PWR_JD_M);
+ }
+
+ /* Set OVCD threshold current and scale-factor */
+ snd_soc_component_write(component, RT5651_PR_BASE + RT5651_BIAS_CUR4,
+ 0xa800 | rt5651->ovcd_sf);
+
+ snd_soc_component_update_bits(component, RT5651_MICBIAS,
+ RT5651_MIC1_OVCD_MASK |
+ RT5651_MIC1_OVTH_MASK |
+ RT5651_PWR_CLK12M_MASK |
+ RT5651_PWR_MB_MASK,
+ RT5651_MIC1_OVCD_EN |
+ rt5651->ovcd_th |
+ RT5651_PWR_MB_PU |
+ RT5651_PWR_CLK12M_PU);
+
+ /*
+ * 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, RT5651_IRQ_CTRL2,
+ RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN);
+
+ rt5651->hp_jack = hp_jack;
+ if (rt5651_support_button_press(rt5651)) {
+ rt5651_enable_micbias1_for_ovcd(component);
+ rt5651_enable_micbias1_ovcd_irq(component);
+ }
+
+ enable_irq(rt5651->irq);
+ /* sync initial jack state */
+ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work);
+}
+
+static void rt5651_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ disable_irq(rt5651->irq);
+ rt5651_cancel_work(rt5651);
+
+ if (rt5651_support_button_press(rt5651)) {
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_disable_micbias1_for_ovcd(component);
+ snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0);
+ }
+
+ rt5651->hp_jack = NULL;
+}
+
+static int rt5651_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ rt5651_enable_jack_detect(component, jack, data);
+ else
+ rt5651_disable_jack_detect(component);
+
+ return 0;
+}
+
+/*
+ * Note on some platforms the platform code may need to add device-properties,
+ * rather then relying only on properties set by the firmware. Therefor the
+ * property parsing MUST be done from the component driver's probe function,
+ * rather then from the i2c driver's probe function, so that the platform-code
+ * can attach extra properties before calling snd_soc_register_card().
+ */
+static void rt5651_apply_properties(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
-
- rt5651->codec = codec;
-
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_VREF1 | RT5651_PWR_MB |
- RT5651_PWR_BG | RT5651_PWR_VREF2,
- RT5651_PWR_VREF1 | RT5651_PWR_MB |
- RT5651_PWR_BG | RT5651_PWR_VREF2);
- usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_FV1 | RT5651_PWR_FV2,
- RT5651_PWR_FV1 | RT5651_PWR_FV2);
-
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
-
- if (rt5651->pdata.jd_src) {
- snd_soc_dapm_force_enable_pin(dapm, "JD Power");
- snd_soc_dapm_force_enable_pin(dapm, "LDO");
- snd_soc_dapm_sync(dapm);
-
- regmap_update_bits(rt5651->regmap, RT5651_MICBIAS,
- 0x38, 0x38);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ u32 val;
+
+ if (device_property_read_bool(component->dev, "realtek,in2-differential"))
+ snd_soc_component_update_bits(component, RT5651_IN1_IN2,
+ RT5651_IN_DF2, RT5651_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,dmic-en"))
+ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
+ RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+
+ if (device_property_read_u32(component->dev,
+ "realtek,jack-detect-source", &val) == 0)
+ rt5651->jd_src = val;
+
+ if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
+ rt5651->jd_active_high = 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.
+ */
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_2000UA;
+ rt5651->ovcd_sf = RT5651_MIC_OVCD_SF_0P75;
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-threshold-microamp", &val) == 0) {
+ switch (val) {
+ case 600:
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_600UA;
+ break;
+ case 1500:
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_1500UA;
+ break;
+ case 2000:
+ rt5651->ovcd_th = RT5651_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 <= RT5651_OVCD_SF_1P5)
+ rt5651->ovcd_sf = val << RT5651_MIC_OVCD_SF_SFT;
+ else
+ dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n",
+ val);
}
+}
+
+static int rt5651_probe(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ rt5651->component = component;
+
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
+ RT5651_PWR_LDO_DVO_MASK, RT5651_PWR_LDO_DVO_1_2V);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ rt5651_apply_properties(component);
return 0;
}
#ifdef CONFIG_PM
-static int rt5651_suspend(struct snd_soc_codec *codec)
+static int rt5651_suspend(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5651->regmap, true);
regcache_mark_dirty(rt5651->regmap);
return 0;
}
-static int rt5651_resume(struct snd_soc_codec *codec)
+static int rt5651_resume(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5651->regmap, false);
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -1692,20 +2147,20 @@ static struct snd_soc_dai_driver rt5651_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
- .probe = rt5651_probe,
- .suspend = rt5651_suspend,
- .resume = rt5651_resume,
- .set_bias_level = rt5651_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5651_snd_controls,
- .num_controls = ARRAY_SIZE(rt5651_snd_controls),
- .dapm_widgets = rt5651_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
- .dapm_routes = rt5651_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_rt5651 = {
+ .probe = rt5651_probe,
+ .suspend = rt5651_suspend,
+ .resume = rt5651_resume,
+ .set_bias_level = rt5651_set_bias_level,
+ .set_jack = rt5651_set_jack,
+ .controls = rt5651_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5651_snd_controls),
+ .dapm_widgets = rt5651_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
+ .dapm_routes = rt5651_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5651_regmap = {
@@ -1717,166 +2172,47 @@ static const struct regmap_config rt5651_regmap = {
.volatile_reg = rt5651_volatile_register,
.readable_reg = rt5651_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5651_reg,
.num_reg_defaults = ARRAY_SIZE(rt5651_reg),
.ranges = rt5651_ranges,
.num_ranges = ARRAY_SIZE(rt5651_ranges),
+ .use_single_read = true,
+ .use_single_write = true,
};
#if defined(CONFIG_OF)
static const struct of_device_id rt5651_of_match[] = {
{ .compatible = "realtek,rt5651", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5651_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5651_acpi_match[] = {
- { "10EC5651", 0 },
- { },
+ { "10EC5640" },
+ { "10EC5651" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
#endif
static const struct i2c_device_id rt5651_i2c_id[] = {
- { "rt5651", 0 },
+ { "rt5651" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
-static int rt5651_quirk_cb(const struct dmi_system_id *id)
-{
- rt5651_quirk = (unsigned long) id->driver_data;
- return 1;
-}
-
-static const struct dmi_system_id rt5651_quirk_table[] = {
- {
- .callback = rt5651_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "KIANO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"),
- },
- .driver_data = (unsigned long *) RT5651_JD1_1,
- },
- {}
-};
-
-static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np)
-{
- if (of_property_read_bool(np, "realtek,in2-differential"))
- rt5651_quirk |= RT5651_IN2_DIFF;
- if (of_property_read_bool(np, "realtek,dmic-en"))
- rt5651_quirk |= RT5651_DMIC_EN;
-
- return 0;
-}
-
-static void rt5651_set_pdata(struct rt5651_priv *rt5651)
-{
- if (rt5651_quirk & RT5651_IN2_DIFF)
- rt5651->pdata.in2_diff = true;
- if (rt5651_quirk & RT5651_DMIC_EN)
- rt5651->pdata.dmic_en = true;
- if (RT5651_JD_MAP(rt5651_quirk))
- rt5651->pdata.jd_src = RT5651_JD_MAP(rt5651_quirk);
-}
-
-static irqreturn_t rt5651_irq(int irq, void *data)
-{
- struct rt5651_priv *rt5651 = data;
-
- queue_delayed_work(system_power_efficient_wq,
- &rt5651->jack_detect_work, msecs_to_jiffies(250));
-
- return IRQ_HANDLED;
-}
-
-static int rt5651_jack_detect(struct snd_soc_codec *codec, int jack_insert)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- int jack_type;
-
- if (jack_insert) {
- snd_soc_dapm_force_enable_pin(dapm, "LDO");
- snd_soc_dapm_sync(dapm);
-
- snd_soc_update_bits(codec, RT5651_MICBIAS,
- RT5651_MIC1_OVCD_MASK |
- RT5651_MIC1_OVTH_MASK |
- RT5651_PWR_CLK12M_MASK |
- RT5651_PWR_MB_MASK,
- RT5651_MIC1_OVCD_EN |
- RT5651_MIC1_OVTH_600UA |
- RT5651_PWR_MB_PU |
- RT5651_PWR_CLK12M_PU);
- msleep(100);
- if (snd_soc_read(codec, RT5651_IRQ_CTRL2) & RT5651_MB1_OC_CLR)
- jack_type = SND_JACK_HEADPHONE;
- else
- jack_type = SND_JACK_HEADSET;
- snd_soc_update_bits(codec, RT5651_IRQ_CTRL2,
- RT5651_MB1_OC_CLR, 0);
- } else { /* jack out */
- jack_type = 0;
-
- snd_soc_update_bits(codec, RT5651_MICBIAS,
- RT5651_MIC1_OVCD_MASK,
- RT5651_MIC1_OVCD_DIS);
- }
-
- return jack_type;
-}
-
-static void rt5651_jack_detect_work(struct work_struct *work)
-{
- struct rt5651_priv *rt5651 =
- container_of(work, struct rt5651_priv, jack_detect_work.work);
-
- int report, val = 0;
-
- if (!rt5651->codec)
- return;
-
- switch (rt5651->pdata.jd_src) {
- case RT5651_JD1_1:
- val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x1000;
- break;
- case RT5651_JD1_2:
- val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x2000;
- break;
- case RT5651_JD2:
- val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x4000;
- break;
- default:
- break;
- }
-
- report = rt5651_jack_detect(rt5651->codec, !val);
-
- snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET);
-}
-
-int rt5651_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hp_jack)
-{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
-
- rt5651->hp_jack = hp_jack;
- rt5651_irq(0, rt5651);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rt5651_set_jack_detect);
-
-static int rt5651_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/*
+ * Note this function MUST not look at device-properties, see the comment
+ * above rt5651_apply_properties().
+ */
+static int rt5651_i2c_probe(struct i2c_client *i2c)
{
- struct rt5651_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5651_priv *rt5651;
int ret;
+ int err;
rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
GFP_KERNEL);
@@ -1885,15 +2221,6 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5651);
- if (pdata)
- rt5651->pdata = *pdata;
- else if (i2c->dev.of_node)
- rt5651_parse_dt(rt5651, i2c->dev.of_node);
- else
- dmi_check_system(rt5651_quirk_table);
-
- rt5651_set_pdata(rt5651);
-
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
if (IS_ERR(rt5651->regmap)) {
ret = PTR_ERR(rt5651->regmap);
@@ -1902,7 +2229,10 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+ err = regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+ if (err)
+ return err;
+
if (ret != RT5651_DEVICE_ID_VALUE) {
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5651\n", ret);
@@ -1916,85 +2246,33 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5651->pdata.in2_diff)
- regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2,
- RT5651_IN_DF2, RT5651_IN_DF2);
+ rt5651->irq = i2c->irq;
+ rt5651->hp_mute = true;
- if (rt5651->pdata.dmic_en)
- regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
- RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
-
- rt5651->hp_mute = 1;
+ INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work);
+ INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work);
- if (rt5651->pdata.jd_src) {
-
- /* IRQ output on GPIO1 */
- regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
- RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ);
-
- switch (rt5651->pdata.jd_src) {
- case RT5651_JD1_1:
- regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2,
- RT5651_JD_TRG_SEL_MASK,
- RT5651_JD_TRG_SEL_JD1_1);
- regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1,
- RT5651_JD1_1_IRQ_EN,
- RT5651_JD1_1_IRQ_EN);
- break;
- case RT5651_JD1_2:
- regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2,
- RT5651_JD_TRG_SEL_MASK,
- RT5651_JD_TRG_SEL_JD1_2);
- regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1,
- RT5651_JD1_2_IRQ_EN,
- RT5651_JD1_2_IRQ_EN);
- break;
- case RT5651_JD2:
- regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2,
- RT5651_JD_TRG_SEL_MASK,
- RT5651_JD_TRG_SEL_JD2);
- regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1,
- RT5651_JD2_IRQ_EN,
- RT5651_JD2_IRQ_EN);
- break;
- case RT5651_JD_NULL:
- break;
- default:
- dev_warn(&i2c->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
- break;
- }
- }
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5651_cancel_work, rt5651);
+ if (ret)
+ return ret;
- INIT_DELAYED_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work);
-
- if (i2c->irq) {
- ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
- rt5651_irq,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT, "rt5651", rt5651);
- if (ret) {
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
- return ret;
- }
+ ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
+ if (ret) {
+ dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ rt5651->irq, ret);
+ rt5651->irq = -ENXIO;
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5651,
rt5651_dai, ARRAY_SIZE(rt5651_dai));
return ret;
}
-static int rt5651_i2c_remove(struct i2c_client *i2c)
-{
- struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c);
-
- cancel_delayed_work_sync(&rt5651->jack_detect_work);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5651_i2c_driver = {
.driver = {
.name = "rt5651",
@@ -2002,7 +2280,6 @@ static struct i2c_driver rt5651_i2c_driver = {
.of_match_table = of_match_ptr(rt5651_of_match),
},
.probe = rt5651_i2c_probe,
- .remove = rt5651_i2c_remove,
.id_table = rt5651_i2c_id,
};
module_i2c_driver(rt5651_i2c_driver);
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
index 4f8b202121d7..20c33a3ece37 100644
--- a/sound/soc/codecs/rt5651.h
+++ b/sound/soc/codecs/rt5651.h
@@ -1,18 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5651.h -- RT5651 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 __RT5651_H__
#define __RT5651_H__
-#include <sound/rt5651.h>
+#include <dt-bindings/sound/rt5651.h>
/* Info */
#define RT5651_RESET 0x00
@@ -138,6 +135,7 @@
/* Index of Codec Private Register definition */
#define RT5651_BIAS_CUR1 0x12
#define RT5651_BIAS_CUR3 0x14
+#define RT5651_BIAS_CUR4 0x15
#define RT5651_CLSD_INT_REG1 0x1c
#define RT5651_CHPUMP_INT_REG1 0x24
#define RT5651_MAMP_INT_REG2 0x37
@@ -1966,6 +1964,15 @@
#define RT5651_D_GATE_EN_SFT 0
/* Codec Private Register definition */
+
+/* MIC Over current threshold scale factor (0x15) */
+#define RT5651_MIC_OVCD_SF_MASK (0x3 << 8)
+#define RT5651_MIC_OVCD_SF_SFT 8
+#define RT5651_MIC_OVCD_SF_0P5 (0x0 << 8)
+#define RT5651_MIC_OVCD_SF_0P75 (0x1 << 8)
+#define RT5651_MIC_OVCD_SF_1P0 (0x2 << 8)
+#define RT5651_MIC_OVCD_SF_1P5 (0x3 << 8)
+
/* 3D Speaker Control (0x63) */
#define RT5651_3D_SPK_MASK (0x1 << 15)
#define RT5651_3D_SPK_SFT 15
@@ -2059,12 +2066,25 @@ struct rt5651_pll_code {
};
struct rt5651_priv {
- struct snd_soc_codec *codec;
- struct rt5651_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
+ /* Jack and button detect data */
struct snd_soc_jack *hp_jack;
- struct delayed_work jack_detect_work;
-
+ struct gpio_desc *gpiod_hp_det;
+ struct work_struct jack_detect_work;
+ struct delayed_work bp_work;
+ bool ovcd_irq_enabled;
+ bool pressed;
+ bool press_reported;
+ int press_count;
+ int release_count;
+ int poll_count;
+ unsigned int jd_src;
+ bool jd_active_high;
+ unsigned int ovcd_th;
+ unsigned int ovcd_sf;
+
+ int irq;
int sysclk;
int sysclk_src;
int lrck[RT5651_AIFS];
@@ -2079,6 +2099,4 @@ struct rt5651_priv {
bool hp_mute;
};
-int rt5651_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hp_jack);
#endif /* __RT5651_H__ */
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 07e7757417bc..31b47db7b4f7 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5659.c -- RT5659/RT5658 ALSA SoC audio codec driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@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.
*/
#include <linux/clk.h>
@@ -19,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -1198,66 +1194,29 @@ static const struct snd_kcontrol_new rt5659_if3_dac_swap_mux =
static const struct snd_kcontrol_new rt5659_if3_adc_swap_mux =
SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5659_if3_adc_enum);
-static const char * const rt5659_asrc_clk_src[] = {
- "clk_sysy_div_out", "clk_i2s1_track", "clk_i2s2_track",
- "clk_i2s3_track", "clk_sys2", "clk_sys3"
-};
-
-static unsigned int rt5659_asrc_clk_map_values[] = {
- 0, 1, 2, 3, 5, 6,
-};
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_sto_asrc_enum, RT5659_ASRC_2, RT5659_DA_STO_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_monol_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_L_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_monor_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_R_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_sto1_asrc_enum, RT5659_ASRC_2, RT5659_AD_STO1_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_sto2_asrc_enum, RT5659_ASRC_3, RT5659_AD_STO2_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_monol_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_L_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_monor_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_R_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
static int rt5659_hp_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int ret = snd_soc_put_volsw(kcontrol, ucontrol);
- if (snd_soc_read(codec, RT5659_STO_NG2_CTRL_1) & RT5659_NG2_EN) {
- snd_soc_update_bits(codec, RT5659_STO_NG2_CTRL_1,
+ if (snd_soc_component_read(component, RT5659_STO_NG2_CTRL_1) & RT5659_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1,
RT5659_NG2_EN_MASK, RT5659_NG2_DIS);
- snd_soc_update_bits(codec, RT5659_STO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1,
RT5659_NG2_EN_MASK, RT5659_NG2_EN);
}
return ret;
}
-static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5659_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
if (enable) {
- snd_soc_write(codec, RT5659_4BTN_IL_CMD_1, 0x000b);
+ snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, 0x000b);
/* MICBIAS1 and Mic Det Power for button detect*/
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
@@ -1265,19 +1224,19 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
"Mic Det Power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_2,
RT5659_PWR_MB1, RT5659_PWR_MB1);
- snd_soc_update_bits(codec, RT5659_PWR_VOL,
+ snd_soc_component_update_bits(component, RT5659_PWR_VOL,
RT5659_PWR_MIC_DET, RT5659_PWR_MIC_DET);
- snd_soc_update_bits(codec, RT5659_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2,
RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
- snd_soc_update_bits(codec, RT5659_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2,
RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN);
} else {
- snd_soc_update_bits(codec, RT5659_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2,
RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_DIS);
- snd_soc_update_bits(codec, RT5659_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2,
RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_DIS);
/* MICBIAS1 and Mic Det Power for button detect*/
snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
@@ -1288,7 +1247,7 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
/**
* rt5659_headset_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
@@ -1296,37 +1255,37 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
* Returns detect status.
*/
-static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5659_headset_detect(struct snd_soc_component *component, int jack_insert)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
int reg_63;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
if (jack_insert) {
snd_soc_dapm_force_enable_pin(dapm,
"Mic Det Power");
snd_soc_dapm_sync(dapm);
- reg_63 = snd_soc_read(codec, RT5659_PWR_ANLG_1);
+ reg_63 = snd_soc_component_read(component, RT5659_PWR_ANLG_1);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1,
RT5659_PWR_VREF2 | RT5659_PWR_MB,
RT5659_PWR_VREF2 | RT5659_PWR_MB);
msleep(20);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1,
RT5659_PWR_FV2, RT5659_PWR_FV2);
- snd_soc_write(codec, RT5659_EJD_CTRL_2, 0x4160);
- snd_soc_update_bits(codec, RT5659_EJD_CTRL_1,
+ snd_soc_component_write(component, RT5659_EJD_CTRL_2, 0x4160);
+ snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1,
0x20, 0x0);
msleep(20);
- snd_soc_update_bits(codec, RT5659_EJD_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1,
0x20, 0x20);
while (i < 5) {
msleep(sleep_time[i]);
- val = snd_soc_read(codec, RT5659_EJD_CTRL_2) & 0x0003;
+ val = snd_soc_component_read(component, RT5659_EJD_CTRL_2) & 0x0003;
i++;
if (val == 0x1 || val == 0x2 || val == 0x3)
break;
@@ -1335,10 +1294,10 @@ static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
switch (val) {
case 1:
rt5659->jack_type = SND_JACK_HEADSET;
- rt5659_enable_push_button_irq(codec, true);
+ rt5659_enable_push_button_irq(component, true);
break;
default:
- snd_soc_write(codec, RT5659_PWR_ANLG_1, reg_63);
+ snd_soc_component_write(component, RT5659_PWR_ANLG_1, reg_63);
rt5659->jack_type = SND_JACK_HEADPHONE;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
@@ -1348,21 +1307,21 @@ static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
if (rt5659->jack_type == SND_JACK_HEADSET)
- rt5659_enable_push_button_irq(codec, false);
+ rt5659_enable_push_button_irq(component, false);
rt5659->jack_type = 0;
}
- dev_dbg(codec->dev, "jack_type = %d\n", rt5659->jack_type);
+ dev_dbg(component->dev, "jack_type = %d\n", rt5659->jack_type);
return rt5659->jack_type;
}
-static int rt5659_button_detect(struct snd_soc_codec *codec)
+static int rt5659_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5659_4BTN_IL_CMD_1);
+ val = snd_soc_component_read(component, RT5659_4BTN_IL_CMD_1);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5659_4BTN_IL_CMD_1, val);
+ snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, val);
return btn_type;
}
@@ -1377,10 +1336,10 @@ static irqreturn_t rt5659_irq(int irq, void *data)
return IRQ_HANDLED;
}
-int rt5659_set_jack_detect(struct snd_soc_codec *codec,
+int rt5659_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
rt5659->hs_jack = hs_jack;
@@ -1396,19 +1355,19 @@ static void rt5659_jack_detect_work(struct work_struct *work)
container_of(work, struct rt5659_priv, jack_detect_work.work);
int val, btn_type, report = 0;
- if (!rt5659->codec)
+ if (!rt5659->component)
return;
- val = snd_soc_read(rt5659->codec, RT5659_INT_ST_1) & 0x0080;
+ val = snd_soc_component_read(rt5659->component, RT5659_INT_ST_1) & 0x0080;
if (!val) {
/* jack in */
if (rt5659->jack_type == 0) {
/* jack was out, report jack type */
- report = rt5659_headset_detect(rt5659->codec, 1);
+ report = rt5659_headset_detect(rt5659->component, 1);
} else {
/* jack is already in, report button event */
report = SND_JACK_HEADSET;
- btn_type = rt5659_button_detect(rt5659->codec);
+ btn_type = rt5659_button_detect(rt5659->component);
/**
* rt5659 can report three kinds of button behavior,
* one click, double click and hold. However,
@@ -1441,7 +1400,7 @@ static void rt5659_jack_detect_work(struct work_struct *work)
break;
default:
btn_type = 0;
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -1453,7 +1412,7 @@ static void rt5659_jack_detect_work(struct work_struct *work)
}
} else {
/* jack out */
- report = rt5659_headset_detect(rt5659->codec, 0);
+ report = rt5659_headset_detect(rt5659->component, 0);
}
snd_soc_jack_report(rt5659->hs_jack, report, SND_JACK_HEADSET |
@@ -1461,6 +1420,61 @@ static void rt5659_jack_detect_work(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
+static void rt5659_jack_detect_intel_hd_header(struct work_struct *work)
+{
+ struct rt5659_priv *rt5659 =
+ container_of(work, struct rt5659_priv, jack_detect_work.work);
+ unsigned int value;
+ bool hp_flag, mic_flag;
+
+ if (!rt5659->hs_jack)
+ return;
+
+ /* headphone jack */
+ regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value);
+ hp_flag = (!(value & 0x8)) ? true : false;
+
+ if (hp_flag != rt5659->hda_hp_plugged) {
+ rt5659->hda_hp_plugged = hp_flag;
+
+ if (hp_flag) {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x0);
+ rt5659->jack_type |= SND_JACK_HEADPHONE;
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x10);
+ rt5659->jack_type = rt5659->jack_type &
+ (~SND_JACK_HEADPHONE);
+ }
+
+ snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type,
+ SND_JACK_HEADPHONE);
+ }
+
+ /* mic jack */
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+ regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value);
+ mic_flag = (value & 0x2000) ? true : false;
+
+ if (mic_flag != rt5659->hda_mic_plugged) {
+ rt5659->hda_mic_plugged = mic_flag;
+ if (mic_flag) {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x2);
+ rt5659->jack_type |= SND_JACK_MICROPHONE;
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x0);
+ rt5659->jack_type = rt5659->jack_type
+ & (~SND_JACK_MICROPHONE);
+ }
+
+ snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type,
+ SND_JACK_MICROPHONE);
+ }
+}
+
static const struct snd_kcontrol_new rt5659_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5659_SPO_VOL,
@@ -1550,38 +1564,64 @@ static const struct snd_kcontrol_new rt5659_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
- int pd, idx = -EINVAL;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ int pd, idx;
pd = rl6231_get_pre_div(rt5659->regmap,
RT5659_ADDA_CLK_1, RT5659_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rt5659->sysclk / pd);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else {
- snd_soc_update_bits(codec, RT5659_DMIC_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_DMIC_CTRL_1,
RT5659_DMIC_CLK_MASK, idx << RT5659_DMIC_CLK_SFT);
}
return idx;
}
-static int set_adc_clk(struct snd_soc_dapm_widget *w,
+static int set_adc1_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_POST_PMU:
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int set_adc2_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5659_CHOP_ADC,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK);
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5659_CHOP_ADC,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK, 0);
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK, 0);
break;
default:
@@ -1595,15 +1635,15 @@ static int set_adc_clk(struct snd_soc_dapm_widget *w,
static int rt5659_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Depop */
- snd_soc_write(codec, RT5659_DEPOP_1, 0x0009);
+ snd_soc_component_write(component, RT5659_DEPOP_1, 0x0009);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
+ snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
break;
default:
return 0;
@@ -1616,9 +1656,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- val = snd_soc_read(codec, RT5659_GLB_CLK);
+ val = snd_soc_component_read(component, RT5659_GLB_CLK);
val &= RT5659_SCLK_SRC_MASK;
if (val == RT5659_SCLK_SRC_PLL1)
return 1;
@@ -1630,7 +1670,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int reg, shift, val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (w->shift) {
case RT5659_ADC_MONO_R_ASRC_SFT:
@@ -1661,13 +1701,13 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
case 3:
/* I2S_Pre_Div1 should be 1 in asrc mode */
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_PD1_MASK, RT5659_I2S_PD1_2);
return 1;
default:
@@ -2303,24 +2343,24 @@ static const struct snd_kcontrol_new pdm_r_switch =
static int rt5659_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5659_CLASSD_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1,
RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_EN);
- snd_soc_update_bits(codec, RT5659_CLASSD_2,
+ snd_soc_component_update_bits(component, RT5659_CLASSD_2,
RT5659_M_RI_DIG, RT5659_M_RI_DIG);
- snd_soc_write(codec, RT5659_CLASSD_1, 0x0803);
- snd_soc_write(codec, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000);
+ snd_soc_component_write(component, RT5659_CLASSD_1, 0x0803);
+ snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_CLASSD_1, 0x0011);
- snd_soc_update_bits(codec, RT5659_CLASSD_2,
+ snd_soc_component_write(component, RT5659_CLASSD_1, 0x0011);
+ snd_soc_component_update_bits(component, RT5659_CLASSD_2,
RT5659_M_RI_DIG, 0x0);
- snd_soc_write(codec, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003);
- snd_soc_update_bits(codec, RT5659_CLASSD_CTRL_1,
+ snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003);
+ snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1,
RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_DIS);
break;
@@ -2335,15 +2375,15 @@ static int rt5659_spk_event(struct snd_soc_dapm_widget *w,
static int rt5659_mono_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00);
+ snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04);
+ snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04);
break;
default:
@@ -2357,16 +2397,16 @@ static int rt5659_mono_event(struct snd_soc_dapm_widget *w,
static int rt5659_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT5659_HP_CHARGE_PUMP_1, 0x0e1e);
- snd_soc_update_bits(codec, RT5659_DEPOP_1, 0x0010, 0x0010);
+ snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0e1e);
+ snd_soc_component_update_bits(component, RT5659_DEPOP_1, 0x0010, 0x0010);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT5659_DEPOP_1, 0x0000);
+ snd_soc_component_write(component, RT5659_DEPOP_1, 0x0000);
break;
default:
@@ -2392,13 +2432,18 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w,
return 0;
}
-static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget rt5659_particular_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5659_PWR_ANLG_3, RT5659_PWR_LDO2_BIT, 0,
NULL, 0),
- SND_SOC_DAPM_SUPPLY("PLL", RT5659_PWR_ANLG_3, RT5659_PWR_PLL_BIT, 0,
- NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5659_PWR_ANLG_2, RT5659_PWR_MB1_BIT,
+ 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5659_PWR_VOL,
RT5659_PWR_MIC_DET_BIT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", RT5659_PWR_ANLG_3, RT5659_PWR_PLL_BIT, 0,
+ NULL, 0),
SND_SOC_DAPM_SUPPLY("Mono Vref", RT5659_PWR_ANLG_1,
RT5659_PWR_VREF3_BIT, 0, NULL, 0),
@@ -2423,8 +2468,6 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
RT5659_ADC_MONO_R_ASRC_SFT, 0, NULL, 0),
/* Input Side */
- SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5659_PWR_ANLG_2, RT5659_PWR_MB1_BIT,
- 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5659_PWR_ANLG_2, RT5659_PWR_MB2_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5659_PWR_ANLG_2, RT5659_PWR_MB3_BIT,
@@ -2500,13 +2543,13 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
RT5659_PWR_ADC_L1_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_R1_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5659_PWR_DIG_2,
+ SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_L2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5659_PWR_DIG_2,
+ SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_R2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC1 clock", SND_SOC_NOPM, 0, 0, set_adc_clk,
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", SND_SOC_NOPM, 0, 0, set_adc1_clk,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_SUPPLY("ADC2 clock", SND_SOC_NOPM, 0, 0, set_adc_clk,
+ SND_SOC_DAPM_SUPPLY("ADC2 clock", SND_SOC_NOPM, 0, 0, set_adc2_clk,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
/* ADC Mux */
@@ -2818,11 +2861,6 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
{ "I2S2", NULL, "I2S2 ASRC" },
{ "I2S3", NULL, "I2S3 ASRC" },
- { "IN1P", NULL, "LDO2" },
- { "IN2P", NULL, "LDO2" },
- { "IN3P", NULL, "LDO2" },
- { "IN4P", NULL, "LDO2" },
-
{ "DMIC1", NULL, "DMIC L1" },
{ "DMIC1", NULL, "DMIC R1" },
{ "DMIC2", NULL, "DMIC L2" },
@@ -3237,21 +3275,21 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
static int rt5659_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 rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, frame_size;
rt5659->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5659->sysclk, rt5659->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5659->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -3278,39 +3316,39 @@ static int rt5659_hw_params(struct snd_pcm_substream *substream,
case RT5659_AIF1:
mask_clk = RT5659_I2S_PD1_MASK;
val_clk = pre_div << RT5659_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5659_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S1_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
case RT5659_AIF2:
mask_clk = RT5659_I2S_PD2_MASK;
val_clk = pre_div << RT5659_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5659_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S2_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
case RT5659_AIF3:
mask_clk = RT5659_I2S_PD3_MASK;
val_clk = pre_div << RT5659_I2S_PD3_SFT;
- snd_soc_update_bits(codec, RT5659_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S3_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, mask_clk, val_clk);
switch (rt5659->lrck[dai->id]) {
case 192000:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_32);
break;
case 96000:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_64);
break;
default:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_128);
break;
}
@@ -3320,15 +3358,15 @@ static int rt5659_hw_params(struct snd_pcm_substream *substream,
static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5659->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5659_I2S_MS_S;
rt5659->master[dai->id] = 0;
break;
@@ -3364,38 +3402,43 @@ static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5659_AIF1:
- snd_soc_update_bits(codec, RT5659_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S1_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
case RT5659_AIF2:
- snd_soc_update_bits(codec, RT5659_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S2_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
case RT5659_AIF3:
- snd_soc_update_bits(codec, RT5659_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S3_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5659_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int rt5659_set_component_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
+ int ret;
if (freq == rt5659->sysclk && clk_id == rt5659->sysclk_src)
return 0;
switch (clk_id) {
case RT5659_SCLK_S_MCLK:
+ ret = clk_set_rate(rt5659->mclk, freq);
+ if (ret)
+ return ret;
+
reg_val |= RT5659_SCLK_SRC_MCLK;
break;
case RT5659_SCLK_S_PLL1:
@@ -3405,25 +3448,25 @@ static int rt5659_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
reg_val |= RT5659_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_SCLK_SRC_MASK, reg_val);
rt5659->sysclk = freq;
rt5659->sysclk_src = clk_id;
- dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n",
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
freq, clk_id);
return 0;
}
-static int rt5659_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
+static int rt5659_set_component_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -3432,52 +3475,52 @@ static int rt5659_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5659->pll_in = 0;
rt5659->pll_out = 0;
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_SCLK_SRC_MASK, RT5659_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5659_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_MCLK);
break;
case RT5659_PLL1_S_BCLK1:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK1);
break;
case RT5659_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK2);
break;
case RT5659_PLL1_S_BCLK3:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK3);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5659_PLL_CTRL_1,
+ snd_soc_component_write(component, RT5659_PLL_CTRL_1,
pll_code.n_code << RT5659_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5659_PLL_CTRL_2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5659_PLL_M_SFT |
- pll_code.m_bp << RT5659_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5659_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5659_PLL_M_SFT) |
+ (pll_code.m_bp << RT5659_PLL_M_BP_SFT));
rt5659->pll_in = freq_in;
rt5659->pll_out = freq_out;
@@ -3489,7 +3532,7 @@ static int rt5659_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
static int rt5659_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -3533,29 +3576,29 @@ static int rt5659_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_TDM_CTRL_1, 0x8ff0, val);
+ snd_soc_component_update_bits(component, RT5659_TDM_CTRL_1, 0x8ff0, val);
return 0;
}
static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
rt5659->bclk[dai->id] = ratio;
if (ratio == 64) {
switch (dai->id) {
case RT5659_AIF2:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_BCLK_MS2_MASK,
RT5659_I2S_BCLK_MS2_64);
break;
case RT5659_AIF3:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_BCLK_MS3_MASK,
RT5659_I2S_BCLK_MS3_64);
break;
@@ -3565,11 +3608,11 @@ static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
return 0;
}
-static int rt5659_set_bias_level(struct snd_soc_codec *codec,
+static int rt5659_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -3591,7 +3634,7 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec,
if (dapm->bias_level == SND_SOC_BIAS_OFF) {
ret = clk_prepare_enable(rt5659->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to enable MCLK: %d\n", ret);
return ret;
}
@@ -3617,37 +3660,48 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5659_probe(struct snd_soc_codec *codec)
+static int rt5659_probe(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
- rt5659->codec = codec;
+ rt5659->component = component;
+
+ switch (rt5659->pdata.jd_src) {
+ case RT5659_JD_HDA_HEADER:
+ break;
+
+ default:
+ snd_soc_dapm_new_controls(dapm,
+ rt5659_particular_dapm_widgets,
+ ARRAY_SIZE(rt5659_particular_dapm_widgets));
+ break;
+ }
return 0;
}
-static int rt5659_remove(struct snd_soc_codec *codec)
+static void rt5659_remove(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regmap_write(rt5659->regmap, RT5659_RESET, 0);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5659_suspend(struct snd_soc_codec *codec)
+static int rt5659_suspend(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5659->regmap, true);
regcache_mark_dirty(rt5659->regmap);
return 0;
}
-static int rt5659_resume(struct snd_soc_codec *codec)
+static int rt5659_resume(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5659->regmap, false);
regcache_sync(rt5659->regmap);
@@ -3730,23 +3784,22 @@ static struct snd_soc_dai_driver rt5659_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5659 = {
- .probe = rt5659_probe,
- .remove = rt5659_remove,
- .suspend = rt5659_suspend,
- .resume = rt5659_resume,
- .set_bias_level = rt5659_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5659_snd_controls,
- .num_controls = ARRAY_SIZE(rt5659_snd_controls),
- .dapm_widgets = rt5659_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets),
- .dapm_routes = rt5659_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes),
- },
- .set_sysclk = rt5659_set_codec_sysclk,
- .set_pll = rt5659_set_codec_pll,
+static const struct snd_soc_component_driver soc_component_dev_rt5659 = {
+ .probe = rt5659_probe,
+ .remove = rt5659_remove,
+ .suspend = rt5659_suspend,
+ .resume = rt5659_resume,
+ .set_bias_level = rt5659_set_bias_level,
+ .controls = rt5659_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5659_snd_controls),
+ .dapm_widgets = rt5659_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets),
+ .dapm_routes = rt5659_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes),
+ .set_sysclk = rt5659_set_component_sysclk,
+ .set_pll = rt5659_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
@@ -3762,8 +3815,8 @@ static const struct regmap_config rt5659_regmap = {
};
static const struct i2c_device_id rt5659_i2c_id[] = {
- { "rt5658", 0 },
- { "rt5659", 0 },
+ { "rt5658" },
+ { "rt5659" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5659_i2c_id);
@@ -3849,7 +3902,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 30) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"HP Calibration 1 Failure\n");
return;
}
@@ -3874,7 +3927,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 85) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"HP Calibration 2 Failure\n");
return;
}
@@ -3922,7 +3975,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 10) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"SPK Calibration Failure\n");
return;
}
@@ -3955,7 +4008,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 35) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"Mono Calibration Failure\n");
return;
}
@@ -3990,8 +4043,55 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
regmap_write(rt5659->regmap, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
}
-static int rt5659_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659)
+{
+ int value;
+
+ regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value);
+ if (!(value & 0x8)) {
+ rt5659->hda_hp_plugged = true;
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x0);
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x10);
+ }
+
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1,
+ RT5659_PWR_VREF2 | RT5659_PWR_MB,
+ RT5659_PWR_VREF2 | RT5659_PWR_MB);
+ msleep(20);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1,
+ RT5659_PWR_FV2, RT5659_PWR_FV2);
+
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_3, RT5659_PWR_LDO2,
+ RT5659_PWR_LDO2);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_2, RT5659_PWR_MB1,
+ RT5659_PWR_MB1);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_VOL, RT5659_PWR_MIC_DET,
+ RT5659_PWR_MIC_DET);
+ msleep(20);
+
+ regmap_update_bits(rt5659->regmap, RT5659_4BTN_IL_CMD_2,
+ RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN);
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+ regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value);
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+
+ if (value & 0x2000) {
+ rt5659->hda_mic_plugged = true;
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x2);
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x0);
+ }
+
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
+}
+
+static int rt5659_i2c_probe(struct i2c_client *i2c)
{
struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5659_priv *rt5659;
@@ -4040,13 +4140,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
regmap_write(rt5659->regmap, RT5659_RESET, 0);
/* Check if MCLK provided */
- rt5659->mclk = devm_clk_get(&i2c->dev, "mclk");
- if (IS_ERR(rt5659->mclk)) {
- if (PTR_ERR(rt5659->mclk) != -ENOENT)
- return PTR_ERR(rt5659->mclk);
- /* Otherwise mark the mclk pointer to NULL */
- rt5659->mclk = NULL;
- }
+ rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5659->mclk))
+ return PTR_ERR(rt5659->mclk);
rt5659_calibrate(rt5659);
@@ -4174,39 +4270,40 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
RT5659_PWR_MB, RT5659_PWR_MB);
regmap_write(rt5659->regmap, RT5659_PWR_ANLG_2, 0x0001);
regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_2, 0x0040);
+ INIT_DELAYED_WORK(&rt5659->jack_detect_work,
+ rt5659_jack_detect_work);
break;
- case RT5659_JD_NULL:
+ case RT5659_JD_HDA_HEADER:
+ regmap_write(rt5659->regmap, RT5659_GPIO_CTRL_3, 0x8000);
+ regmap_write(rt5659->regmap, RT5659_RC_CLK_CTRL, 0x0900);
+ regmap_write(rt5659->regmap, RT5659_EJD_CTRL_1, 0x70c0);
+ regmap_write(rt5659->regmap, RT5659_JD_CTRL_1, 0x2000);
+ regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_1, 0x0040);
+ INIT_DELAYED_WORK(&rt5659->jack_detect_work,
+ rt5659_jack_detect_intel_hd_header);
+ rt5659_intel_hd_header_probe_setup(rt5659);
break;
default:
- dev_warn(&i2c->dev, "Currently, support JD3 only\n");
break;
}
- INIT_DELAYED_WORK(&rt5659->jack_detect_work, rt5659_jack_detect_work);
-
if (i2c->irq) {
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
if (ret)
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
/* Enable IRQ output for GPIO1 pin any way */
regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1,
RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ);
}
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5659,
rt5659_dai, ARRAY_SIZE(rt5659_dai));
}
-static int rt5659_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static void rt5659_i2c_shutdown(struct i2c_client *client)
{
struct rt5659_priv *rt5659 = i2c_get_clientdata(client);
@@ -4218,16 +4315,16 @@ static void rt5659_i2c_shutdown(struct i2c_client *client)
static const struct of_device_id rt5659_of_match[] = {
{ .compatible = "realtek,rt5658", },
{ .compatible = "realtek,rt5659", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, rt5659_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5659_acpi_match[] = {
- { "10EC5658", 0, },
- { "10EC5659", 0, },
- { },
+ { "10EC5658" },
+ { "10EC5659" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match);
#endif
@@ -4239,7 +4336,6 @@ static struct i2c_driver rt5659_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5659_acpi_match),
},
.probe = rt5659_i2c_probe,
- .remove = rt5659_i2c_remove,
.shutdown = rt5659_i2c_shutdown,
.id_table = rt5659_i2c_id,
};
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index 8f1aeef08489..b49fd8baf4e7 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5659.h -- RT5659/RT5658 ALSA SoC audio driver
*
* Copyright 2015 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT5659_H__
@@ -1008,7 +1005,7 @@
#define RT5659_PWR_ADC_R1 (0x1 << 3)
#define RT5659_PWR_ADC_R1_BIT 3
#define RT5659_PWR_ADC_L2 (0x1 << 2)
-#define RT5659_PWR_ADC_L2_BIT 4
+#define RT5659_PWR_ADC_L2_BIT 2
#define RT5659_PWR_ADC_R2 (0x1 << 1)
#define RT5659_PWR_ADC_R2_BIT 1
#define RT5659_PWR_CLS_D (0x1)
@@ -1743,10 +1740,14 @@
#define RT5659_CKGEN_DAC2_SFT 4
/* Chopper and Clock control for ADC (0x013b)*/
-#define RT5659_CKXEN_ADCC_MASK (0x1 << 13)
-#define RT5659_CKXEN_ADCC_SFT 13
-#define RT5659_CKGEN_ADCC_MASK (0x1 << 12)
-#define RT5659_CKGEN_ADCC_SFT 12
+#define RT5659_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5659_CKXEN_ADC1_SFT 13
+#define RT5659_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5659_CKGEN_ADC1_SFT 12
+#define RT5659_CKXEN_ADC2_MASK (0x1 << 5)
+#define RT5659_CKXEN_ADC2_SFT 5
+#define RT5659_CKGEN_ADC2_MASK (0x1 << 4)
+#define RT5659_CKGEN_ADC2_SFT 4
/* Test Mode Control 1 (0x0145) */
#define RT5659_AD2DA_LB_MASK (0x1 << 9)
@@ -1789,7 +1790,7 @@ struct rt5659_pll_code {
};
struct rt5659_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5659_platform_data pdata;
struct regmap *regmap;
struct gpio_desc *gpiod_ldo1_en;
@@ -1810,10 +1811,11 @@ struct rt5659_priv {
int pll_out;
int jack_type;
-
+ bool hda_hp_plugged;
+ bool hda_mic_plugged;
};
-int rt5659_set_jack_detect(struct snd_soc_codec *codec,
+int rt5659_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack);
#endif /* __RT5659_H__ */
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index d22ef00e0d96..44c3a3b92f98 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5660.c -- RT5660 ALSA SoC audio codec driver
*
* Copyright 2016 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.
*/
#include <linux/module.h>
@@ -14,11 +11,9 @@
#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>
@@ -354,17 +349,17 @@ static const struct snd_kcontrol_new rt5660_snd_controls[] = {
static int rt5660_set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5660->sysclk / rl6231_get_pre_div(rt5660->regmap,
RT5660_ADDA_CLK1, RT5660_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5660_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5660_DMIC_CTRL1,
RT5660_DMIC_CLK_MASK, idx << RT5660_DMIC_CLK_SFT);
return idx;
@@ -373,10 +368,10 @@ static int rt5660_set_dmic_clk(struct snd_soc_dapm_widget *w,
static int rt5660_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int val;
- val = snd_soc_read(codec, RT5660_GLB_CLK);
+ val = snd_soc_component_read(component, RT5660_GLB_CLK);
val &= RT5660_SCLK_SRC_MASK;
if (val == RT5660_SCLK_SRC_PLL1)
return 1;
@@ -541,17 +536,17 @@ static const struct snd_kcontrol_new rt5660_if1_adc_swap_mux =
static int rt5660_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5660_LOUT_AMP_CTRL,
+ snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL,
RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK,
RT5660_LOUT_CO_EN | RT5660_LOUT_CB_PU);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5660_LOUT_AMP_CTRL,
+ snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL,
RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK,
RT5660_LOUT_CO_DIS | RT5660_LOUT_CB_PD);
break;
@@ -838,22 +833,22 @@ static const struct snd_soc_dapm_route rt5660_dapm_routes[] = {
static int rt5660_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 rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
rt5660->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5660->sysclk, rt5660->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5660->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return frame_size;
}
@@ -890,13 +885,13 @@ static int rt5660_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5660_I2S_BCLK_MS1_MASK | RT5660_I2S_PD1_MASK;
val_clk = bclk_ms << RT5660_I2S_BCLK_MS1_SFT |
pre_div << RT5660_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK,
+ snd_soc_component_update_bits(component, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK,
val_len);
- snd_soc_update_bits(codec, RT5660_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5660_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -905,16 +900,16 @@ static int rt5660_hw_params(struct snd_pcm_substream *substream,
static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5660->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5660_I2S_MS_S;
rt5660->master[dai->id] = 0;
break;
@@ -957,13 +952,13 @@ static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5660_AIF1:
- snd_soc_update_bits(codec, RT5660_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5660_I2S1_SDP,
RT5660_I2S_MS_MASK | RT5660_I2S_BP_MASK |
RT5660_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -973,8 +968,8 @@ static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5660->sysclk && clk_id == rt5660->sysclk_src)
@@ -994,11 +989,11 @@ static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai,
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5660_GLB_CLK, RT5660_SCLK_SRC_MASK,
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK, RT5660_SCLK_SRC_MASK,
reg_val);
rt5660->sysclk = freq;
@@ -1012,8 +1007,8 @@ static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -1022,46 +1017,46 @@ static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5660->pll_in = 0;
rt5660->pll_out = 0;
- snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
RT5660_SCLK_SRC_MASK, RT5660_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5660_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_MCLK);
break;
case RT5660_PLL1_S_BCLK:
- snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_BCLK1);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5660_PLL_CTRL1,
+ snd_soc_component_write(component, RT5660_PLL_CTRL1,
pll_code.n_code << RT5660_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5660_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5660_PLL_M_SFT |
- pll_code.m_bp << RT5660_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5660_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5660_PLL_M_SFT) |
+ (pll_code.m_bp << RT5660_PLL_M_BP_SFT));
rt5660->pll_in = freq_in;
rt5660->pll_out = freq_out;
@@ -1070,10 +1065,10 @@ static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5660_set_bias_level(struct snd_soc_codec *codec,
+static int rt5660_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1081,13 +1076,10 @@ static int rt5660_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, RT5660_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5660_GEN_CTRL1,
RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL);
- if (IS_ERR(rt5660->mclk))
- break;
-
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(rt5660->mclk);
} else {
ret = clk_prepare_enable(rt5660->mclk);
@@ -1097,21 +1089,21 @@ static int rt5660_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5660_PWR_ANLG1,
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5660_PWR_ANLG1,
RT5660_PWR_VREF1 | RT5660_PWR_MB |
RT5660_PWR_BG | RT5660_PWR_VREF2,
RT5660_PWR_VREF1 | RT5660_PWR_MB |
RT5660_PWR_BG | RT5660_PWR_VREF2);
usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5660_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5660_PWR_ANLG1,
RT5660_PWR_FV1 | RT5660_PWR_FV2,
RT5660_PWR_FV1 | RT5660_PWR_FV2);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, RT5660_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5660_GEN_CTRL1,
RT5660_DIG_GATE_CTRL, 0);
break;
@@ -1122,24 +1114,24 @@ static int rt5660_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5660_probe(struct snd_soc_codec *codec)
+static int rt5660_probe(struct snd_soc_component *component)
{
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
- rt5660->codec = codec;
+ rt5660->component = component;
return 0;
}
-static int rt5660_remove(struct snd_soc_codec *codec)
+static void rt5660_remove(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5660_RESET, 0);
+ snd_soc_component_write(component, RT5660_RESET, 0);
}
#ifdef CONFIG_PM
-static int rt5660_suspend(struct snd_soc_codec *codec)
+static int rt5660_suspend(struct snd_soc_component *component)
{
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5660->regmap, true);
regcache_mark_dirty(rt5660->regmap);
@@ -1147,9 +1139,9 @@ static int rt5660_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5660_resume(struct snd_soc_codec *codec)
+static int rt5660_resume(struct snd_soc_component *component)
{
- struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
if (rt5660->pdata.poweroff_codec_in_suspend)
msleep(350);
@@ -1197,34 +1189,34 @@ static struct snd_soc_dai_driver rt5660_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5660 = {
- .probe = rt5660_probe,
- .remove = rt5660_remove,
- .suspend = rt5660_suspend,
- .resume = rt5660_resume,
- .set_bias_level = rt5660_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5660_snd_controls,
- .num_controls = ARRAY_SIZE(rt5660_snd_controls),
- .dapm_widgets = rt5660_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5660_dapm_widgets),
- .dapm_routes = rt5660_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5660_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_rt5660 = {
+ .probe = rt5660_probe,
+ .remove = rt5660_remove,
+ .suspend = rt5660_suspend,
+ .resume = rt5660_resume,
+ .set_bias_level = rt5660_set_bias_level,
+ .controls = rt5660_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5660_snd_controls),
+ .dapm_widgets = rt5660_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5660_dapm_widgets),
+ .dapm_routes = rt5660_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5660_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5660_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5660_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5660_ranges) *
RT5660_PR_SPACING),
.volatile_reg = rt5660_volatile_register,
.readable_reg = rt5660_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5660_reg,
.num_reg_defaults = ARRAY_SIZE(rt5660_reg),
.ranges = rt5660_ranges,
@@ -1232,22 +1224,27 @@ static const struct regmap_config rt5660_regmap = {
};
static const struct i2c_device_id rt5660_i2c_id[] = {
- { "rt5660", 0 },
+ { "rt5660" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id);
+#ifdef CONFIG_OF
static const struct of_device_id rt5660_of_match[] = {
{ .compatible = "realtek,rt5660", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5660_of_match);
+#endif
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5660_acpi_match[] = {
- { "10EC5660", 0 },
- { },
+ { "10EC3277" },
+ { "10EC5660" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match);
+#endif
static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev)
{
@@ -1263,8 +1260,7 @@ static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev)
return 0;
}
-static int rt5660_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5660_i2c_probe(struct i2c_client *i2c)
{
struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5660_priv *rt5660;
@@ -1278,9 +1274,9 @@ static int rt5660_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
/* Check if MCLK provided */
- rt5660->mclk = devm_clk_get(&i2c->dev, "mclk");
- if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5660->mclk))
+ return PTR_ERR(rt5660->mclk);
i2c_set_clientdata(i2c, rt5660);
@@ -1319,27 +1315,24 @@ static int rt5660_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL);
- if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2)
+ if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) {
regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
RT5660_SEL_DMIC_DATA_MASK,
RT5660_SEL_DMIC_DATA_GPIO2);
- else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P)
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP2_PIN_MASK, RT5660_GP2_PIN_DMIC1_SDA);
+ } else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) {
regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
RT5660_SEL_DMIC_DATA_MASK,
RT5660_SEL_DMIC_DATA_IN1P);
+ }
}
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5660,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5660,
rt5660_dai, ARRAY_SIZE(rt5660_dai));
}
-static int rt5660_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5660_i2c_driver = {
.driver = {
.name = "rt5660",
@@ -1347,7 +1340,6 @@ static struct i2c_driver rt5660_i2c_driver = {
.of_match_table = of_match_ptr(rt5660_of_match),
},
.probe = rt5660_i2c_probe,
- .remove = rt5660_i2c_remove,
.id_table = rt5660_i2c_id,
};
module_i2c_driver(rt5660_i2c_driver);
diff --git a/sound/soc/codecs/rt5660.h b/sound/soc/codecs/rt5660.h
index bba18fb66b6f..a33025c920e1 100644
--- a/sound/soc/codecs/rt5660.h
+++ b/sound/soc/codecs/rt5660.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5660.h -- RT5660 ALSA SoC audio driver
*
* Copyright 2016 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 _RT5660_H
@@ -831,7 +828,7 @@ enum {
};
struct rt5660_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5660_platform_data pdata;
struct regmap *regmap;
struct clk *mclk;
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index d329bf719d80..45057562c0c8 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5663.c -- RT5663 ALSA SoC audio codec driver
*
* Copyright 2016 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.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -17,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -33,6 +31,9 @@
#define RT5663_DEVICE_ID_2 0x6451
#define RT5663_DEVICE_ID_1 0x6406
+#define RT5663_POWER_ON_DELAY_MS 300
+#define RT5663_SUPPLY_CURRENT_UA 500000
+
enum {
CODEC_VER_1,
CODEC_VER_0,
@@ -48,14 +49,20 @@ struct impedance_mapping_table {
unsigned int dc_offset_r_manual_mic;
};
+static const char *const rt5663_supply_names[] = {
+ "avdd",
+ "cpvdd",
+};
+
struct rt5663_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5663_platform_data pdata;
struct regmap *regmap;
struct delayed_work jack_detect_work, jd_unplug_work;
struct snd_soc_jack *hs_jack;
struct timer_list btn_check_timer;
struct impedance_mapping_table *imp_table;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(rt5663_supply_names)];
int codec_ver;
int sysclk;
@@ -67,11 +74,15 @@ struct rt5663_priv {
int pll_out;
int jack_type;
+ unsigned int irq;
};
static const struct reg_sequence rt5663_patch_list[] = {
{ 0x002a, 0x8020 },
{ 0x0086, 0x0028 },
+ { 0x0100, 0xa020 },
+ { 0x0117, 0x0f28 },
+ { 0x02fb, 0x8089 },
};
static const struct reg_default rt5663_v2_reg[] = {
@@ -578,7 +589,7 @@ static const struct reg_default rt5663_reg[] = {
{ 0x00fd, 0x0001 },
{ 0x00fe, 0x10ec },
{ 0x00ff, 0x6406 },
- { 0x0100, 0xa0a0 },
+ { 0x0100, 0xa020 },
{ 0x0108, 0x4444 },
{ 0x0109, 0x4444 },
{ 0x010a, 0xaaaa },
@@ -593,7 +604,7 @@ static const struct reg_default rt5663_reg[] = {
{ 0x0113, 0x2000 },
{ 0x0114, 0x0000 },
{ 0x0116, 0x0000 },
- { 0x0117, 0x0f00 },
+ { 0x0117, 0x0f28 },
{ 0x0118, 0x0006 },
{ 0x0125, 0x2424 },
{ 0x0126, 0x5550 },
@@ -693,7 +704,7 @@ static const struct reg_default rt5663_reg[] = {
{ 0x0251, 0x0000 },
{ 0x0252, 0x028a },
{ 0x02fa, 0x0000 },
- { 0x02fb, 0x00a4 },
+ { 0x02fb, 0x8089 },
{ 0x02fc, 0x0300 },
{ 0x0300, 0x0000 },
{ 0x03d0, 0x0000 },
@@ -1384,57 +1395,57 @@ static const char * const rt5663_if1_adc_data_select[] = {
static SOC_ENUM_SINGLE_DECL(rt5663_if1_adc_enum, RT5663_TDM_2,
RT5663_DATA_SWAP_ADCDAT1_SHIFT, rt5663_if1_adc_data_select);
-static void rt5663_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5663_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
if (enable) {
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_EN);
/* reset in-line command */
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_RESET_4BTN_INL_MASK,
RT5663_RESET_4BTN_INL_RESET);
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_RESET_4BTN_INL_MASK,
RT5663_RESET_4BTN_INL_NOR);
switch (rt5663->codec_ver) {
case CODEC_VER_1:
- snd_soc_update_bits(codec, RT5663_IRQ_3,
+ snd_soc_component_update_bits(component, RT5663_IRQ_3,
RT5663_V2_EN_IRQ_INLINE_MASK,
RT5663_V2_EN_IRQ_INLINE_NOR);
break;
case CODEC_VER_0:
- snd_soc_update_bits(codec, RT5663_IRQ_2,
+ snd_soc_component_update_bits(component, RT5663_IRQ_2,
RT5663_EN_IRQ_INLINE_MASK,
RT5663_EN_IRQ_INLINE_NOR);
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
} else {
switch (rt5663->codec_ver) {
case CODEC_VER_1:
- snd_soc_update_bits(codec, RT5663_IRQ_3,
+ snd_soc_component_update_bits(component, RT5663_IRQ_3,
RT5663_V2_EN_IRQ_INLINE_MASK,
RT5663_V2_EN_IRQ_INLINE_BYP);
break;
case CODEC_VER_0:
- snd_soc_update_bits(codec, RT5663_IRQ_2,
+ snd_soc_component_update_bits(component, RT5663_IRQ_2,
RT5663_EN_IRQ_INLINE_MASK,
RT5663_EN_IRQ_INLINE_BYP);
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_DIS);
/* reset in-line command */
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_RESET_4BTN_INL_MASK,
RT5663_RESET_4BTN_INL_RESET);
- snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
RT5663_RESET_4BTN_INL_MASK,
RT5663_RESET_4BTN_INL_NOR);
}
@@ -1442,7 +1453,7 @@ static void rt5663_enable_push_button_irq(struct snd_soc_codec *codec,
/**
* rt5663_v2_jack_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
@@ -1450,41 +1461,41 @@ static void rt5663_enable_push_button_irq(struct snd_soc_codec *codec,
* Returns detect status.
*/
-static int rt5663_v2_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5663_v2_jack_detect(struct snd_soc_component *component, int jack_insert)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
- dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+ dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert);
if (jack_insert) {
- snd_soc_write(codec, RT5663_CBJ_TYPE_2, 0x8040);
- snd_soc_write(codec, RT5663_CBJ_TYPE_3, 0x1484);
+ snd_soc_component_write(component, RT5663_CBJ_TYPE_2, 0x8040);
+ snd_soc_component_write(component, RT5663_CBJ_TYPE_3, 0x1484);
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2");
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_force_enable_pin(dapm, "CBJ Power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5663_RC_CLK,
+ snd_soc_component_update_bits(component, RT5663_RC_CLK,
RT5663_DIG_1M_CLK_MASK, RT5663_DIG_1M_CLK_EN);
- snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x8);
+ snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x8);
while (i < 5) {
msleep(sleep_time[i]);
- val = snd_soc_read(codec, RT5663_CBJ_TYPE_2) & 0x0003;
+ val = snd_soc_component_read(component, RT5663_CBJ_TYPE_2) & 0x0003;
if (val == 0x1 || val == 0x2 || val == 0x3)
break;
- dev_dbg(codec->dev, "%s: MX-0011 val=%x sleep %d\n",
+ dev_dbg(component->dev, "%s: MX-0011 val=%x sleep %d\n",
__func__, val, sleep_time[i]);
i++;
}
- dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
+ dev_dbg(component->dev, "%s val = %d\n", __func__, val);
switch (val) {
case 1:
case 2:
rt5663->jack_type = SND_JACK_HEADSET;
- rt5663_enable_push_button_irq(codec, true);
+ rt5663_enable_push_button_irq(component, true);
break;
default:
snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
@@ -1496,10 +1507,10 @@ static int rt5663_v2_jack_detect(struct snd_soc_codec *codec, int jack_insert)
break;
}
} else {
- snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x0);
+ snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x0);
if (rt5663->jack_type == SND_JACK_HEADSET) {
- rt5663_enable_push_button_irq(codec, false);
+ rt5663_enable_push_button_irq(component, false);
snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
@@ -1509,60 +1520,68 @@ static int rt5663_v2_jack_detect(struct snd_soc_codec *codec, int jack_insert)
rt5663->jack_type = 0;
}
- dev_dbg(codec->dev, "jack_type = %d\n", rt5663->jack_type);
+ dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type);
return rt5663->jack_type;
}
/**
* rt5663_jack_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
*
* Returns detect status.
*/
-static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5663_jack_detect(struct snd_soc_component *component, int jack_insert)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
int val, i = 0;
- dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+ dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert);
if (jack_insert) {
- snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
- snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
RT5663_SI_HP_MASK | RT5663_OSW_HP_L_MASK |
RT5663_OSW_HP_R_MASK, RT5663_SI_HP_EN |
RT5663_OSW_HP_L_DIS | RT5663_OSW_HP_R_DIS);
- snd_soc_update_bits(codec, RT5663_DUMMY_1,
+ snd_soc_component_update_bits(component, RT5663_DUMMY_1,
RT5663_EMB_CLK_MASK | RT5663_HPA_CPL_BIAS_MASK |
RT5663_HPA_CPR_BIAS_MASK, RT5663_EMB_CLK_EN |
RT5663_HPA_CPL_BIAS_1 | RT5663_HPA_CPR_BIAS_1);
- snd_soc_update_bits(codec, RT5663_CBJ_1,
+ snd_soc_component_update_bits(component, RT5663_CBJ_1,
RT5663_INBUF_CBJ_BST1_MASK | RT5663_CBJ_SENSE_BST1_MASK,
RT5663_INBUF_CBJ_BST1_ON | RT5663_CBJ_SENSE_BST1_L);
- snd_soc_update_bits(codec, RT5663_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_2,
RT5663_PWR_MIC_DET_MASK, RT5663_PWR_MIC_DET_ON);
/* BST1 power on for JD */
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
RT5663_PWR_BST1_MASK, RT5663_PWR_BST1_ON);
- snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1,
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
RT5663_CBJ_DET_MASK | RT5663_EXT_JD_MASK |
RT5663_POL_EXT_JD_MASK, RT5663_CBJ_DET_EN |
RT5663_EXT_JD_EN | RT5663_POL_EXT_JD_EN);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_MB_MASK | RT5663_LDO1_DVO_MASK |
RT5663_AMP_HP_MASK, RT5663_PWR_MB |
RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
- snd_soc_update_bits(codec, RT5663_AUTO_1MRC_CLK,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_VREF1 | RT5663_PWR_VREF2);
+ msleep(20);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5663_AUTO_1MRC_CLK,
RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN);
- snd_soc_update_bits(codec, RT5663_IRQ_1,
+ snd_soc_component_update_bits(component, RT5663_IRQ_1,
RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN);
- snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1,
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
RT5663_EM_JD_MASK, RT5663_EM_JD_RST);
- snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1,
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
RT5663_EM_JD_MASK, RT5663_EM_JD_NOR);
while (true) {
@@ -1577,10 +1596,10 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
i++;
}
- val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) & 0x0003;
- dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
+ val = snd_soc_component_read(component, RT5663_EM_JACK_TYPE_2) & 0x0003;
+ dev_dbg(component->dev, "%s val = %d\n", __func__, val);
- snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
RT5663_OSW_HP_L_MASK | RT5663_OSW_HP_R_MASK,
RT5663_OSW_HP_L_EN | RT5663_OSW_HP_R_EN);
@@ -1588,7 +1607,7 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
case 1:
case 2:
rt5663->jack_type = SND_JACK_HEADSET;
- rt5663_enable_push_button_irq(codec, true);
+ rt5663_enable_push_button_irq(component, true);
if (rt5663->pdata.impedance_sensing_num)
break;
@@ -1613,7 +1632,10 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
break;
default:
rt5663->jack_type = SND_JACK_HEADPHONE;
-
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK |
+ RT5663_PWR_VREF2_MASK, 0);
if (rt5663->pdata.impedance_sensing_num)
break;
@@ -1636,17 +1658,20 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
}
} else {
if (rt5663->jack_type == SND_JACK_HEADSET)
- rt5663_enable_push_button_irq(codec, false);
+ rt5663_enable_push_button_irq(component, false);
rt5663->jack_type = 0;
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK |
+ RT5663_PWR_VREF2_MASK, 0);
}
- dev_dbg(codec->dev, "jack_type = %d\n", rt5663->jack_type);
+ dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type);
return rt5663->jack_type;
}
-static int rt5663_impedance_sensing(struct snd_soc_codec *codec)
+static int rt5663_impedance_sensing(struct snd_soc_component *component)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80;
for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
@@ -1655,59 +1680,59 @@ static int rt5663_impedance_sensing(struct snd_soc_codec *codec)
}
if (rt5663->jack_type == SND_JACK_HEADSET) {
- snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
- snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
} else {
- snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
rt5663->imp_table[i].dc_offset_l_manual >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
- snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
rt5663->imp_table[i].dc_offset_r_manual >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
}
- reg84 = snd_soc_read(codec, RT5663_ASRC_2);
- reg26 = snd_soc_read(codec, RT5663_STO1_ADC_MIXER);
- reg2fa = snd_soc_read(codec, RT5663_DUMMY_1);
- reg91 = snd_soc_read(codec, RT5663_HP_CHARGE_PUMP_1);
- reg10 = snd_soc_read(codec, RT5663_RECMIX);
- reg80 = snd_soc_read(codec, RT5663_GLB_CLK);
-
- snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000, 0);
- snd_soc_write(codec, RT5663_ASRC_2, 0);
- snd_soc_write(codec, RT5663_STO1_ADC_MIXER, 0x4040);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ reg84 = snd_soc_component_read(component, RT5663_ASRC_2);
+ reg26 = snd_soc_component_read(component, RT5663_STO1_ADC_MIXER);
+ reg2fa = snd_soc_component_read(component, RT5663_DUMMY_1);
+ reg91 = snd_soc_component_read(component, RT5663_HP_CHARGE_PUMP_1);
+ reg10 = snd_soc_component_read(component, RT5663_RECMIX);
+ reg80 = snd_soc_component_read(component, RT5663_GLB_CLK);
+
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000, 0);
+ snd_soc_component_write(component, RT5663_ASRC_2, 0);
+ snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, 0x4040);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
RT5663_PWR_VREF1 | RT5663_PWR_VREF2);
usleep_range(10000, 10005);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
RT5663_PWR_FV1 | RT5663_PWR_FV2);
- snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
RT5663_SCLK_SRC_RCCLK);
- snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
RT5663_DIG_25M_CLK_EN);
- snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0);
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00);
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc);
- snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, 0x1232);
- snd_soc_write(codec, RT5663_HP_LOGIC_2, 0x0005);
- snd_soc_write(codec, RT5663_DEPOP_2, 0x3003);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0030);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0x0003);
- snd_soc_update_bits(codec, RT5663_PWR_DIG_2,
+ snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, 0x1232);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0x0005);
+ snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0x0030);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0x0003);
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_2,
RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F,
RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F);
- snd_soc_update_bits(codec, RT5663_PWR_DIG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_1,
RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
RT5663_PWR_ADC_R1,
@@ -1715,71 +1740,71 @@ static int rt5663_impedance_sensing(struct snd_soc_codec *codec)
RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 |
RT5663_PWR_ADC_R1);
msleep(40);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2,
RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2);
msleep(30);
- snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371);
- snd_soc_write(codec, RT5663_STO_DAC_MIXER, 0);
- snd_soc_write(codec, RT5663_BYPASS_STO_DAC, 0x000c);
- snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa);
- snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224);
- snd_soc_write(codec, RT5663_HP_OUT_EN, 0x8088);
- snd_soc_write(codec, RT5663_CHOP_ADC, 0x3000);
- snd_soc_write(codec, RT5663_ADDA_RST, 0xc000);
- snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0x3320);
- snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c9);
- snd_soc_write(codec, RT5663_DUMMY_1, 0x004c);
- snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7733);
- snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777);
- snd_soc_write(codec, RT5663_STO_DRE_9, 0x0007);
- snd_soc_write(codec, RT5663_STO_DRE_10, 0x0007);
- snd_soc_write(codec, RT5663_DUMMY_2, 0x02a4);
- snd_soc_write(codec, RT5663_RECMIX, 0x0005);
- snd_soc_write(codec, RT5663_HP_IMP_SEN_1, 0x4334);
- snd_soc_update_bits(codec, RT5663_IRQ_3, 0x0004, 0x0004);
- snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x2200);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x3000);
- snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x6200);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_component_write(component, RT5663_STO_DAC_MIXER, 0);
+ snd_soc_component_write(component, RT5663_BYPASS_STO_DAC, 0x000c);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x8088);
+ snd_soc_component_write(component, RT5663_CHOP_ADC, 0x3000);
+ snd_soc_component_write(component, RT5663_ADDA_RST, 0xc000);
+ snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0x3320);
+ snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c9);
+ snd_soc_component_write(component, RT5663_DUMMY_1, 0x004c);
+ snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7733);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_component_write(component, RT5663_STO_DRE_9, 0x0007);
+ snd_soc_component_write(component, RT5663_STO_DRE_10, 0x0007);
+ snd_soc_component_write(component, RT5663_DUMMY_2, 0x02a4);
+ snd_soc_component_write(component, RT5663_RECMIX, 0x0005);
+ snd_soc_component_write(component, RT5663_HP_IMP_SEN_1, 0x4334);
+ snd_soc_component_update_bits(component, RT5663_IRQ_3, 0x0004, 0x0004);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x2200);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x3000);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x6200);
for (i = 0; i < 100; i++) {
msleep(20);
- if (snd_soc_read(codec, RT5663_INT_ST_1) & 0x2)
+ if (snd_soc_component_read(component, RT5663_INT_ST_1) & 0x2)
break;
}
- value = snd_soc_read(codec, RT5663_HP_IMP_SEN_4);
+ value = snd_soc_component_read(component, RT5663_HP_IMP_SEN_4);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0);
- snd_soc_write(codec, RT5663_INT_ST_1, 0);
- snd_soc_write(codec, RT5663_HP_LOGIC_1, 0);
- snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0);
+ snd_soc_component_write(component, RT5663_INT_ST_1, 0);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0);
+ snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
RT5663_DIG_25M_CLK_DIS);
- snd_soc_write(codec, RT5663_GLB_CLK, reg80);
- snd_soc_write(codec, RT5663_RECMIX, reg10);
- snd_soc_write(codec, RT5663_DUMMY_2, 0x00a4);
- snd_soc_write(codec, RT5663_DUMMY_1, reg2fa);
- snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c8);
- snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0xb320);
- snd_soc_write(codec, RT5663_ADDA_RST, 0xe400);
- snd_soc_write(codec, RT5663_CHOP_ADC, 0x2000);
- snd_soc_write(codec, RT5663_HP_OUT_EN, 0x0008);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ snd_soc_component_write(component, RT5663_GLB_CLK, reg80);
+ snd_soc_component_write(component, RT5663_RECMIX, reg10);
+ snd_soc_component_write(component, RT5663_DUMMY_2, 0x00a4);
+ snd_soc_component_write(component, RT5663_DUMMY_1, reg2fa);
+ snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c8);
+ snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0xb320);
+ snd_soc_component_write(component, RT5663_ADDA_RST, 0xe400);
+ snd_soc_component_write(component, RT5663_CHOP_ADC, 0x2000);
+ snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x0008);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0);
- snd_soc_update_bits(codec, RT5663_PWR_DIG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_1,
RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
RT5663_PWR_ADC_R1, 0);
- snd_soc_update_bits(codec, RT5663_PWR_DIG_2,
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_2,
RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0);
- snd_soc_write(codec, RT5663_HP_LOGIC_2, 0);
- snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, reg91);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, reg91);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0);
- snd_soc_write(codec, RT5663_STO1_ADC_MIXER, reg26);
- snd_soc_write(codec, RT5663_ASRC_2, reg84);
+ snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, reg26);
+ snd_soc_component_write(component, RT5663_ASRC_2, reg84);
for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
if (value >= rt5663->imp_table[i].imp_min &&
@@ -1787,42 +1812,42 @@ static int rt5663_impedance_sensing(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK,
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK,
rt5663->imp_table[i].vol);
- snd_soc_update_bits(codec, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK,
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK,
rt5663->imp_table[i].vol);
if (rt5663->jack_type == SND_JACK_HEADSET) {
- snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
- snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
} else {
- snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
rt5663->imp_table[i].dc_offset_l_manual >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
- snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
rt5663->imp_table[i].dc_offset_r_manual >> 16);
- snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
}
return 0;
}
-static int rt5663_button_detect(struct snd_soc_codec *codec)
+static int rt5663_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5663_IL_CMD_5);
- dev_dbg(codec->dev, "%s: val=0x%x\n", __func__, val);
+ val = snd_soc_component_read(component, RT5663_IL_CMD_5);
+ dev_dbg(component->dev, "%s: val=0x%x\n", __func__, val);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5663_IL_CMD_5, val);
+ snd_soc_component_write(component, RT5663_IL_CMD_5, val);
return btn_type;
}
@@ -1840,10 +1865,10 @@ static irqreturn_t rt5663_irq(int irq, void *data)
return IRQ_HANDLED;
}
-int rt5663_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack)
+static int rt5663_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
rt5663->hs_jack = hs_jack;
@@ -1851,14 +1876,13 @@ int rt5663_set_jack_detect(struct snd_soc_codec *codec,
return 0;
}
-EXPORT_SYMBOL_GPL(rt5663_set_jack_detect);
-static bool rt5663_check_jd_status(struct snd_soc_codec *codec)
+static bool rt5663_check_jd_status(struct snd_soc_component *component)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
- int val = snd_soc_read(codec, RT5663_INT_ST_1);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ int val = snd_soc_component_read(component, RT5663_INT_ST_1);
- dev_dbg(codec->dev, "%s val=%x\n", __func__, val);
+ dev_dbg(component->dev, "%s val=%x\n", __func__, val);
/* JD1 */
switch (rt5663->codec_ver) {
@@ -1867,7 +1891,7 @@ static bool rt5663_check_jd_status(struct snd_soc_codec *codec)
case CODEC_VER_0:
return !(val & 0x1000);
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
return false;
@@ -1877,28 +1901,28 @@ static void rt5663_jack_detect_work(struct work_struct *work)
{
struct rt5663_priv *rt5663 =
container_of(work, struct rt5663_priv, jack_detect_work.work);
- struct snd_soc_codec *codec = rt5663->codec;
+ struct snd_soc_component *component = rt5663->component;
int btn_type, report = 0;
- if (!codec)
+ if (!component)
return;
- if (rt5663_check_jd_status(codec)) {
+ if (rt5663_check_jd_status(component)) {
/* jack in */
if (rt5663->jack_type == 0) {
/* jack was out, report jack type */
switch (rt5663->codec_ver) {
case CODEC_VER_1:
report = rt5663_v2_jack_detect(
- rt5663->codec, 1);
+ rt5663->component, 1);
break;
case CODEC_VER_0:
- report = rt5663_jack_detect(rt5663->codec, 1);
+ report = rt5663_jack_detect(rt5663->component, 1);
if (rt5663->pdata.impedance_sensing_num)
- rt5663_impedance_sensing(rt5663->codec);
+ rt5663_impedance_sensing(rt5663->component);
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
/* Delay the jack insert report to avoid pop noise */
@@ -1906,7 +1930,7 @@ static void rt5663_jack_detect_work(struct work_struct *work)
} else {
/* jack is already in, report button event */
report = SND_JACK_HEADSET;
- btn_type = rt5663_button_detect(rt5663->codec);
+ btn_type = rt5663_button_detect(rt5663->component);
/**
* rt5663 can report three kinds of button behavior,
* one click, double click and hold. However,
@@ -1939,7 +1963,7 @@ static void rt5663_jack_detect_work(struct work_struct *work)
break;
default:
btn_type = 0;
- dev_err(rt5663->codec->dev,
+ dev_err(rt5663->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -1959,16 +1983,16 @@ static void rt5663_jack_detect_work(struct work_struct *work)
/* jack out */
switch (rt5663->codec_ver) {
case CODEC_VER_1:
- report = rt5663_v2_jack_detect(rt5663->codec, 0);
+ report = rt5663_v2_jack_detect(rt5663->component, 0);
break;
case CODEC_VER_0:
- report = rt5663_jack_detect(rt5663->codec, 0);
+ report = rt5663_jack_detect(rt5663->component, 0);
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
}
- dev_dbg(codec->dev, "%s jack report: 0x%04x\n", __func__, report);
+ dev_dbg(component->dev, "%s jack report: 0x%04x\n", __func__, report);
snd_soc_jack_report(rt5663->hs_jack, report, SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3);
@@ -1978,22 +2002,22 @@ static void rt5663_jd_unplug_work(struct work_struct *work)
{
struct rt5663_priv *rt5663 =
container_of(work, struct rt5663_priv, jd_unplug_work.work);
- struct snd_soc_codec *codec = rt5663->codec;
+ struct snd_soc_component *component = rt5663->component;
- if (!codec)
+ if (!component)
return;
- if (!rt5663_check_jd_status(codec)) {
+ if (!rt5663_check_jd_status(component)) {
/* jack out */
switch (rt5663->codec_ver) {
case CODEC_VER_1:
- rt5663_v2_jack_detect(rt5663->codec, 0);
+ rt5663_v2_jack_detect(rt5663->component, 0);
break;
case CODEC_VER_0:
- rt5663_jack_detect(rt5663->codec, 0);
+ rt5663_jack_detect(rt5663->component, 0);
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET |
@@ -2047,9 +2071,9 @@ static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- val = snd_soc_read(codec, RT5663_GLB_CLK);
+ val = snd_soc_component_read(component, RT5663_GLB_CLK);
val &= RT5663_SCLK_SRC_MASK;
if (val == RT5663_SCLK_SRC_PLL1)
return 1;
@@ -2061,8 +2085,8 @@ static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int reg, shift, val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
if (rt5663->codec_ver == CODEC_VER_1) {
switch (w->shift) {
@@ -2092,7 +2116,7 @@ static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w,
}
}
- val = (snd_soc_read(codec, reg) >> shift) & 0x7;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0x7;
if (val)
return 1;
@@ -2103,23 +2127,23 @@ static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w,
static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
int da_asrc_en, ad_asrc_en;
- da_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) &
+ da_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) &
RT5663_DA_STO1_TRACK_MASK) ? 1 : 0;
switch (rt5663->codec_ver) {
case CODEC_VER_1:
- ad_asrc_en = (snd_soc_read(codec, RT5663_ASRC_3) &
+ ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_3) &
RT5663_V2_AD_STO1_TRACK_MASK) ? 1 : 0;
break;
case CODEC_VER_0:
- ad_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) &
+ ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) &
RT5663_AD_STO1_TRACK_MASK) ? 1 : 0;
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
return 1;
}
@@ -2127,14 +2151,14 @@ static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source,
if (rt5663->sysclk > rt5663->lrck * 384)
return 1;
- dev_err(codec->dev, "sysclk < 384 x fs, disable i2s asrc\n");
+ dev_err(component->dev, "sysclk < 384 x fs, disable i2s asrc\n");
return 0;
}
/**
* rt5663_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -2146,10 +2170,10 @@ static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source,
* 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 rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5663_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int asrc2_mask = 0;
unsigned int asrc2_value = 0;
unsigned int asrc3_mask = 0;
@@ -2180,16 +2204,16 @@ int rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec,
asrc2_value |= clk_src << RT5663_AD_STO1_TRACK_SHIFT;
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
}
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5663_ASRC_2, asrc2_mask,
+ snd_soc_component_update_bits(component, RT5663_ASRC_2, asrc2_mask,
asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5663_ASRC_3, asrc3_mask,
+ snd_soc_component_update_bits(component, RT5663_ASRC_3, asrc3_mask,
asrc3_value);
return 0;
@@ -2295,43 +2319,51 @@ static const struct snd_kcontrol_new rt5663_alg_dacr_mux =
static int rt5663_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (rt5663->codec_ver == CODEC_VER_1) {
- snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
RT5663_SEL_PM_HP_SHIFT, RT5663_SEL_PM_HP_HIGH);
- snd_soc_update_bits(codec, RT5663_HP_LOGIC_2,
+ snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2,
RT5663_HP_SIG_SRC1_MASK,
RT5663_HP_SIG_SRC1_SILENCE);
} else {
- snd_soc_write(codec, RT5663_DEPOP_2, 0x3003);
- snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component,
+ RT5663_DACREF_LDO, 0x3e0e, 0x3a0a);
+ snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003);
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_DIS);
- snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371);
- snd_soc_write(codec, RT5663_HP_BIAS, 0xabba);
- snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224);
- snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7766);
- snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa);
- snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777);
- snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000,
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xabba);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7766);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000,
0x8000);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000,
0x3000);
+ snd_soc_component_update_bits(component,
+ RT5663_DIG_VOL_ZCD, 0x00c0, 0x0080);
}
break;
case SND_SOC_DAPM_PRE_PMD:
if (rt5663->codec_ver == CODEC_VER_1) {
- snd_soc_update_bits(codec, RT5663_HP_LOGIC_2,
+ snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2,
RT5663_HP_SIG_SRC1_MASK,
RT5663_HP_SIG_SRC1_REG);
} else {
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x0);
- snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x0);
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN);
+ snd_soc_component_update_bits(component,
+ RT5663_DACREF_LDO, 0x3e0e, 0);
+ snd_soc_component_update_bits(component,
+ RT5663_DIG_VOL_ZCD, 0x00c0, 0);
}
break;
@@ -2345,23 +2377,23 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w,
static int rt5663_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (rt5663->codec_ver == CODEC_VER_0) {
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030,
0x0030);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003,
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003,
0x0003);
}
break;
case SND_SOC_DAPM_POST_PMD:
if (rt5663->codec_ver == CODEC_VER_0) {
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0);
- snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0);
}
break;
@@ -2375,17 +2407,17 @@ static int rt5663_charge_pump_event(struct snd_soc_dapm_widget *w,
static int rt5663_bst2_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK,
RT5663_PWR_BST2 | RT5663_PWR_BST2_OP);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, 0);
break;
@@ -2399,17 +2431,17 @@ static int rt5663_bst2_power(struct snd_soc_dapm_widget *w,
static int rt5663_pre_div_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00);
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0x0000);
- snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0x0000);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0x0000);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0x0000);
break;
default:
@@ -2731,8 +2763,8 @@ static const struct snd_soc_dapm_route rt5663_specific_dapm_routes[] = {
static int rt5663_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 rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0;
int pre_div;
@@ -2743,7 +2775,7 @@ static int rt5663_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5663->sysclk, rt5663->lrck);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5663->lrck, dai->id);
return -EINVAL;
}
@@ -2767,10 +2799,10 @@ static int rt5663_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5663_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5663_I2S1_SDP,
RT5663_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5663_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1,
RT5663_I2S_PD1_MASK, pre_div << RT5663_I2S_PD1_SHIFT);
return 0;
@@ -2778,13 +2810,13 @@ static int rt5663_hw_params(struct snd_pcm_substream *substream,
static int rt5663_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;
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5663_I2S_MS_S;
break;
default:
@@ -2817,7 +2849,7 @@ static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5663_I2S1_SDP, RT5663_I2S_MS_MASK |
+ snd_soc_component_update_bits(component, RT5663_I2S1_SDP, RT5663_I2S_MS_MASK |
RT5663_I2S_BP_MASK | RT5663_I2S_DF_MASK, reg_val);
return 0;
@@ -2826,8 +2858,8 @@ static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5663->sysclk && clk_id == rt5663->sysclk_src)
@@ -2844,15 +2876,15 @@ static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
reg_val |= RT5663_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
reg_val);
rt5663->sysclk = freq;
rt5663->sysclk_src = clk_id;
- dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n",
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
freq, clk_id);
return 0;
@@ -2861,8 +2893,8 @@ static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
int mask, shift, val;
@@ -2872,11 +2904,11 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5663->pll_in = 0;
rt5663->pll_out = 0;
- snd_soc_update_bits(codec, RT5663_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK,
RT5663_SCLK_SRC_MASK, RT5663_SCLK_SRC_MCLK);
return 0;
}
@@ -2891,7 +2923,7 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
shift = RT5663_PLL1_SRC_SHIFT;
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
return -EINVAL;
}
@@ -2903,26 +2935,26 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
val = 0x1;
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5663_GLB_CLK, mask, (val << shift));
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, mask, (val << shift));
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", pll_code.m_bp,
+ 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_write(codec, RT5663_PLL_1,
+ snd_soc_component_write(component, RT5663_PLL_1,
pll_code.n_code << RT5663_PLL_N_SHIFT | pll_code.k_code);
- snd_soc_write(codec, RT5663_PLL_2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5663_PLL_M_SHIFT |
- pll_code.m_bp << RT5663_PLL_M_BP_SHIFT);
+ snd_soc_component_write(component, RT5663_PLL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5663_PLL_M_SHIFT) |
+ (pll_code.m_bp << RT5663_PLL_M_BP_SHIFT));
rt5663->pll_in = freq_in;
rt5663->pll_out = freq_out;
@@ -2934,8 +2966,8 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5663_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int val = 0, reg;
if (rx_mask || tx_mask)
@@ -2987,11 +3019,11 @@ static int rt5663_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
reg = RT5663_TDM_1;
break;
default:
- dev_err(codec->dev, "Unknown CODEC Version\n");
+ dev_err(component->dev, "Unknown CODEC Version\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, reg, RT5663_TDM_MODE_MASK |
+ snd_soc_component_update_bits(component, reg, RT5663_TDM_MODE_MASK |
RT5663_TDM_IN_CH_MASK | RT5663_TDM_OUT_CH_MASK |
RT5663_TDM_IN_LEN_MASK | RT5663_TDM_OUT_LEN_MASK, val);
@@ -3000,11 +3032,11 @@ static int rt5663_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
unsigned int reg;
- dev_dbg(codec->dev, "%s ratio = %d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio = %d\n", __func__, ratio);
if (rt5663->codec_ver == CODEC_VER_1)
reg = RT5663_TDM_9;
@@ -3013,51 +3045,51 @@ static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
switch (ratio) {
case 32:
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
RT5663_TDM_LENGTN_MASK,
RT5663_TDM_LENGTN_16);
break;
case 40:
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
RT5663_TDM_LENGTN_MASK,
RT5663_TDM_LENGTN_20);
break;
case 48:
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
RT5663_TDM_LENGTN_MASK,
RT5663_TDM_LENGTN_24);
break;
case 64:
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
RT5663_TDM_LENGTN_MASK,
RT5663_TDM_LENGTN_32);
break;
default:
- dev_err(codec->dev, "Invalid ratio!\n");
+ dev_err(component->dev, "Invalid ratio!\n");
return -EINVAL;
}
return 0;
}
-static int rt5663_set_bias_level(struct snd_soc_codec *codec,
+static int rt5663_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
RT5663_PWR_FV1 | RT5663_PWR_FV2);
break;
case SND_SOC_BIAS_PREPARE:
if (rt5663->codec_ver == CODEC_VER_1) {
- snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
RT5663_DIG_GATE_CTRL_MASK,
RT5663_DIG_GATE_CTRL_EN);
- snd_soc_update_bits(codec, RT5663_SIG_CLK_DET,
+ snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET,
RT5663_EN_ANA_CLK_DET_MASK |
RT5663_PWR_CLK_DET_MASK,
RT5663_EN_ANA_CLK_DET_AUTO |
@@ -3067,17 +3099,17 @@ static int rt5663_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (rt5663->codec_ver == CODEC_VER_1)
- snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
RT5663_DIG_GATE_CTRL_MASK,
RT5663_DIG_GATE_CTRL_DIS);
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK |
RT5663_PWR_MB_MASK, RT5663_PWR_VREF1 |
RT5663_PWR_VREF2 | RT5663_PWR_MB);
usleep_range(10000, 10005);
if (rt5663->codec_ver == CODEC_VER_1) {
- snd_soc_update_bits(codec, RT5663_SIG_CLK_DET,
+ snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET,
RT5663_EN_ANA_CLK_DET_MASK |
RT5663_PWR_CLK_DET_MASK,
RT5663_EN_ANA_CLK_DET_DIS |
@@ -3086,9 +3118,17 @@ static int rt5663_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
- RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
- RT5663_PWR_FV1 | RT5663_PWR_FV2, 0x0);
+ if (rt5663->jack_type != SND_JACK_HEADSET)
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1 | RT5663_PWR_FV2 |
+ RT5663_PWR_MB_MASK, 0);
+ else
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
break;
default:
@@ -3098,12 +3138,12 @@ static int rt5663_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5663_probe(struct snd_soc_codec *codec)
+static int rt5663_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
- rt5663->codec = codec;
+ rt5663->component = component;
switch (rt5663->codec_ver) {
case CODEC_VER_1:
@@ -3113,7 +3153,7 @@ static int rt5663_probe(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm,
rt5663_v2_specific_dapm_routes,
ARRAY_SIZE(rt5663_v2_specific_dapm_routes));
- snd_soc_add_codec_controls(codec, rt5663_v2_specific_controls,
+ snd_soc_add_component_controls(component, rt5663_v2_specific_controls,
ARRAY_SIZE(rt5663_v2_specific_controls));
break;
case CODEC_VER_0:
@@ -3123,11 +3163,11 @@ static int rt5663_probe(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm,
rt5663_specific_dapm_routes,
ARRAY_SIZE(rt5663_specific_dapm_routes));
- snd_soc_add_codec_controls(codec, rt5663_specific_controls,
+ snd_soc_add_component_controls(component, rt5663_specific_controls,
ARRAY_SIZE(rt5663_specific_controls));
if (!rt5663->imp_table)
- snd_soc_add_codec_controls(codec, rt5663_hpvol_controls,
+ snd_soc_add_component_controls(component, rt5663_hpvol_controls,
ARRAY_SIZE(rt5663_hpvol_controls));
break;
}
@@ -3135,19 +3175,23 @@ static int rt5663_probe(struct snd_soc_codec *codec)
return 0;
}
-static int rt5663_remove(struct snd_soc_codec *codec)
+static void rt5663_remove(struct snd_soc_component *component)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
regmap_write(rt5663->regmap, RT5663_RESET, 0);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5663_suspend(struct snd_soc_codec *codec)
+static int rt5663_suspend(struct snd_soc_component *component)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ if (rt5663->irq)
+ disable_irq(rt5663->irq);
+
+ cancel_delayed_work_sync(&rt5663->jack_detect_work);
+ cancel_delayed_work_sync(&rt5663->jd_unplug_work);
regcache_cache_only(rt5663->regmap, true);
regcache_mark_dirty(rt5663->regmap);
@@ -3155,15 +3199,18 @@ static int rt5663_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5663_resume(struct snd_soc_codec *codec)
+static int rt5663_resume(struct snd_soc_component *component)
{
- struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5663->regmap, false);
regcache_sync(rt5663->regmap);
rt5663_irq(0, rt5663);
+ if (rt5663->irq)
+ enable_irq(rt5663->irq);
+
return 0;
}
#else
@@ -3206,31 +3253,32 @@ static struct snd_soc_dai_driver rt5663_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5663 = {
- .probe = rt5663_probe,
- .remove = rt5663_remove,
- .suspend = rt5663_suspend,
- .resume = rt5663_resume,
- .set_bias_level = rt5663_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5663_snd_controls,
- .num_controls = ARRAY_SIZE(rt5663_snd_controls),
- .dapm_widgets = rt5663_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets),
- .dapm_routes = rt5663_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes),
- }
+static const struct snd_soc_component_driver soc_component_dev_rt5663 = {
+ .probe = rt5663_probe,
+ .remove = rt5663_remove,
+ .suspend = rt5663_suspend,
+ .resume = rt5663_resume,
+ .set_bias_level = rt5663_set_bias_level,
+ .controls = rt5663_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5663_snd_controls),
+ .dapm_widgets = rt5663_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets),
+ .dapm_routes = rt5663_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes),
+ .set_jack = rt5663_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5663_v2_regmap = {
.reg_bits = 16,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = 0x07fa,
.volatile_reg = rt5663_v2_volatile_register,
.readable_reg = rt5663_v2_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5663_v2_reg,
.num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg),
};
@@ -3238,11 +3286,12 @@ static const struct regmap_config rt5663_v2_regmap = {
static const struct regmap_config rt5663_regmap = {
.reg_bits = 16,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = 0x03f3,
.volatile_reg = rt5663_volatile_register,
.readable_reg = rt5663_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5663_reg,
.num_reg_defaults = ARRAY_SIZE(rt5663_reg),
};
@@ -3251,13 +3300,14 @@ static const struct regmap_config temp_regmap = {
.name = "nocache",
.reg_bits = 16,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = 0x03f3,
.cache_type = REGCACHE_NONE,
};
static const struct i2c_device_id rt5663_i2c_id[] = {
- { "rt5663", 0 },
+ { "rt5663" },
{}
};
MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id);
@@ -3265,15 +3315,15 @@ MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id);
#if defined(CONFIG_OF)
static const struct of_device_id rt5663_of_match[] = {
{ .compatible = "realtek,rt5663", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5663_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5663_acpi_match[] = {
- { "10EC5663", 0},
- {},
+ { "10EC5663" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match);
#endif
@@ -3311,6 +3361,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663)
regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x000c);
regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x0324);
regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x8001);
+ regmap_write(rt5663->regmap, RT5663_VREFADJ_OP, 0x0f28);
regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23b);
msleep(30);
regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23b);
@@ -3345,6 +3396,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663)
regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8003);
regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x018c);
regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32);
+ regmap_write(rt5663->regmap, RT5663_DUMMY_2, 0x8089);
regmap_write(rt5663->regmap, RT5663_DACREF_LDO, 0x3b0b);
msleep(40);
regmap_write(rt5663->regmap, RT5663_STO_DAC_MIXER, 0x0000);
@@ -3418,6 +3470,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663)
static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
{
int table_size;
+ int ret;
device_property_read_u32(dev, "realtek,dc_offset_l_manual",
&rt5663->pdata.dc_offset_l_manual);
@@ -3434,20 +3487,23 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
table_size = sizeof(struct impedance_mapping_table) *
rt5663->pdata.impedance_sensing_num;
rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL);
- device_property_read_u32_array(dev,
+ if (!rt5663->imp_table)
+ return -ENOMEM;
+ ret = device_property_read_u32_array(dev,
"realtek,impedance_sensing_table",
(u32 *)rt5663->imp_table, table_size);
+ if (ret)
+ return ret;
}
return 0;
}
-static int rt5663_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5663_i2c_probe(struct i2c_client *i2c)
{
struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5663_priv *rt5663;
- int ret;
+ int ret, i;
unsigned int val;
struct regmap *regmap;
@@ -3461,15 +3517,50 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5663->pdata = *pdata;
- else
- rt5663_parse_dp(rt5663, &i2c->dev);
+ else {
+ ret = rt5663_parse_dp(rt5663, &i2c->dev);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++)
+ rt5663->supplies[i].supply = rt5663_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5663->supplies),
+ rt5663->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Set load for regulator. */
+ for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) {
+ ret = regulator_set_load(rt5663->supplies[i].consumer,
+ RT5663_SUPPLY_CURRENT_UA);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to set regulator load on %s, ret: %d\n",
+ rt5663->supplies[i].supply, ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5663->supplies),
+ rt5663->supplies);
+
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+ msleep(RT5663_POWER_ON_DELAY_MS);
regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
@@ -3494,14 +3585,15 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5663\n",
val);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_enable;
}
if (IS_ERR(rt5663->regmap)) {
ret = PTR_ERR(rt5663->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
/* reset and calibrate */
@@ -3568,7 +3660,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1,
RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK,
RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
- break;
+ break;
case CODEC_VER_0:
regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC,
RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
@@ -3579,21 +3671,15 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5663->regmap, RT5663_GPIO_1,
RT5663_GPIO1_TYPE_MASK, RT5663_GPIO1_TYPE_EN);
regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032);
- regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa2be);
- msleep(20);
- regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf2be);
regmap_update_bits(rt5663->regmap, RT5663_GPIO_2,
RT5663_GP1_PIN_CONF_MASK | RT5663_SEL_GPIO1_MASK,
RT5663_GP1_PIN_CONF_OUTPUT | RT5663_SEL_GPIO1_EN);
- /* DACREF LDO control */
- regmap_update_bits(rt5663->regmap, RT5663_DACREF_LDO, 0x3e0e,
- 0x3a0a);
regmap_update_bits(rt5663->regmap, RT5663_RECMIX,
RT5663_RECMIX1_BST1_MASK, RT5663_RECMIX1_BST1_ON);
regmap_update_bits(rt5663->regmap, RT5663_TDM_2,
RT5663_DATA_SWAP_ADCDAT1_MASK,
RT5663_DATA_SWAP_ADCDAT1_LL);
- break;
+ break;
default:
dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
}
@@ -3605,32 +3691,44 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
ret = request_irq(i2c->irq, rt5663_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5663", rt5663);
- if (ret)
- dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n",
+ if (ret) {
+ dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n",
__func__, ret);
+ goto err_enable;
+ }
+ rt5663->irq = i2c->irq;
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5663,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5663,
rt5663_dai, ARRAY_SIZE(rt5663_dai));
- if (ret) {
- if (i2c->irq)
- free_irq(i2c->irq, rt5663);
- }
+ if (ret)
+ goto err_enable;
+
+ return 0;
+
+ /*
+ * Error after enabling regulators should goto err_enable
+ * to disable regulators.
+ */
+err_enable:
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5663);
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies);
return ret;
}
-static int rt5663_i2c_remove(struct i2c_client *i2c)
+static void rt5663_i2c_remove(struct i2c_client *i2c)
{
struct rt5663_priv *rt5663 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt5663);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
+ regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies);
}
static void rt5663_i2c_shutdown(struct i2c_client *client)
diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h
index 03adc8004ba9..2c485d0655b5 100644
--- a/sound/soc/codecs/rt5663.h
+++ b/sound/soc/codecs/rt5663.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5663.h -- RT5663 ALSA SoC audio driver
*
* Copyright 2016 Realtek Microelectronics
* 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 __RT5663_H__
@@ -1125,9 +1122,7 @@ enum {
RT5663_AD_STEREO_FILTER = 0x2,
};
-int rt5663_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack);
-int rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5663_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
#endif /* __RT5663_H__ */
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index f05d362c4e23..b16b2c66e754 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5665.c -- RT5665/RT5658 ALSA SoC audio codec driver
*
* Copyright 2016 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@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.
*/
#include <linux/module.h>
@@ -18,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -35,22 +31,19 @@
#include "rl6231.h"
#include "rt5665.h"
-#define RT5665_NUM_SUPPLIES 3
-
-static const char *rt5665_supply_names[RT5665_NUM_SUPPLIES] = {
+static const char * const rt5665_supply_names[] = {
"AVDD",
"MICVDD",
"VBAT",
};
struct rt5665_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5665_platform_data pdata;
struct regmap *regmap;
struct gpio_desc *gpiod_ldo1_en;
struct gpio_desc *gpiod_reset;
struct snd_soc_jack *hs_jack;
- struct regulator_bulk_data supplies[RT5665_NUM_SUPPLIES];
struct delayed_work jack_detect_work;
struct delayed_work calibrate_work;
struct delayed_work jd_check_work;
@@ -1000,13 +993,13 @@ static const struct snd_kcontrol_new rt5665_if3_adc_swap_mux =
static int rt5665_hp_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int ret = snd_soc_put_volsw(kcontrol, ucontrol);
- if (snd_soc_read(codec, RT5665_STO_NG2_CTRL_1) & RT5665_NG2_EN) {
- snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1,
+ if (snd_soc_component_read(component, RT5665_STO_NG2_CTRL_1) & RT5665_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
- snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_EN);
}
@@ -1016,161 +1009,65 @@ static int rt5665_hp_vol_put(struct snd_kcontrol *kcontrol,
static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int ret = snd_soc_put_volsw(kcontrol, ucontrol);
- if (snd_soc_read(codec, RT5665_MONO_NG2_CTRL_1) & RT5665_NG2_EN) {
- snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1,
+ if (snd_soc_component_read(component, RT5665_MONO_NG2_CTRL_1) & RT5665_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
- snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_EN);
}
return ret;
}
-/**
- * rt5665_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
- * @filter_mask: mask of filters.
- * @clk_src: clock source
- *
- * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5665 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 rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec,
- 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 RT5665_CLK_SEL_SYS:
- case RT5665_CLK_SEL_I2S1_ASRC:
- case RT5665_CLK_SEL_I2S2_ASRC:
- case RT5665_CLK_SEL_I2S3_ASRC:
- case RT5665_CLK_SEL_SYS2:
- case RT5665_CLK_SEL_SYS3:
- case RT5665_CLK_SEL_SYS4:
- break;
-
- default:
- return -EINVAL;
- }
-
- if (filter_mask & RT5665_DA_STEREO1_FILTER) {
- asrc2_mask |= RT5665_DA_STO1_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_STO1_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_STO1_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_STEREO2_FILTER) {
- asrc2_mask |= RT5665_DA_STO2_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_STO2_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_STO2_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_MONO_L_FILTER) {
- asrc2_mask |= RT5665_DA_MONOL_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_MONOL_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_MONOL_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_MONO_R_FILTER) {
- asrc2_mask |= RT5665_DA_MONOR_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_MONOR_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_MONOR_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_STEREO1_FILTER) {
- asrc3_mask |= RT5665_AD_STO1_CLK_SEL_MASK;
- asrc3_value = (asrc2_value & ~RT5665_AD_STO1_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_STO1_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_STEREO2_FILTER) {
- asrc3_mask |= RT5665_AD_STO2_CLK_SEL_MASK;
- asrc3_value = (asrc2_value & ~RT5665_AD_STO2_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_STO2_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_MONO_L_FILTER) {
- asrc3_mask |= RT5665_AD_MONOL_CLK_SEL_MASK;
- asrc3_value = (asrc3_value & ~RT5665_AD_MONOL_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_MONOL_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_MONO_R_FILTER) {
- asrc3_mask |= RT5665_AD_MONOR_CLK_SEL_MASK;
- asrc3_value = (asrc3_value & ~RT5665_AD_MONOR_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_MONOR_CLK_SEL_SFT);
- }
-
- if (asrc2_mask)
- snd_soc_update_bits(codec, RT5665_ASRC_2,
- asrc2_mask, asrc2_value);
-
- if (asrc3_mask)
- snd_soc_update_bits(codec, RT5665_ASRC_3,
- asrc3_mask, asrc3_value);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rt5665_sel_asrc_clk_src);
-
-static int rt5665_button_detect(struct snd_soc_codec *codec)
+static int rt5665_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5665_4BTN_IL_CMD_1);
+ val = snd_soc_component_read(component, RT5665_4BTN_IL_CMD_1);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, val);
+ snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, val);
return btn_type;
}
-static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5665_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
if (enable) {
- snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x0003);
- snd_soc_update_bits(codec, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
- snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048);
- snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
+ snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, 0x0003);
+ snd_soc_component_update_bits(component, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
+ snd_soc_component_write(component, RT5665_IL_CMD_1, 0x0048);
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK,
RT5665_4BTN_IL_EN | RT5665_4BTN_IL_NOR);
- snd_soc_update_bits(codec, RT5665_IRQ_CTRL_3,
+ snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3,
RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_EN);
} else {
- snd_soc_update_bits(codec, RT5665_IRQ_CTRL_3,
+ snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3,
RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_DIS);
- snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
RT5665_4BTN_IL_MASK, RT5665_4BTN_IL_DIS);
- snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
RT5665_4BTN_IL_RST_MASK, RT5665_4BTN_IL_RST);
}
}
/**
* rt5665_headset_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
*
* Returns detect status.
*/
-static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5665_headset_detect(struct snd_soc_component *component, int jack_insert)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
unsigned int sar_hs_type, val;
if (jack_insert) {
@@ -1201,7 +1098,7 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
usleep_range(10000, 15000);
- rt5665->sar_adc_value = snd_soc_read(rt5665->codec,
+ rt5665->sar_adc_value = snd_soc_component_read(rt5665->component,
RT5665_SAR_IL_CMD_4) & 0x7ff;
sar_hs_type = rt5665->pdata.sar_hs_type ?
@@ -1209,7 +1106,7 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
if (rt5665->sar_adc_value > sar_hs_type) {
rt5665->jack_type = SND_JACK_HEADSET;
- rt5665_enable_push_button_irq(codec, true);
+ rt5665_enable_push_button_irq(component, true);
} else {
rt5665->jack_type = SND_JACK_HEADPHONE;
regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1,
@@ -1225,11 +1122,11 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
snd_soc_dapm_sync(dapm);
if (rt5665->jack_type == SND_JACK_HEADSET)
- rt5665_enable_push_button_irq(codec, false);
+ rt5665_enable_push_button_irq(component, false);
rt5665->jack_type = 0;
}
- dev_dbg(codec->dev, "jack_type = %d\n", rt5665->jack_type);
+ dev_dbg(component->dev, "jack_type = %d\n", rt5665->jack_type);
return rt5665->jack_type;
}
@@ -1248,9 +1145,9 @@ static void rt5665_jd_check_handler(struct work_struct *work)
struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
jd_check_work.work);
- if (snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010) {
+ if (snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010) {
/* jack out */
- rt5665->jack_type = rt5665_headset_detect(rt5665->codec, 0);
+ rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0);
snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type,
SND_JACK_HEADSET |
@@ -1261,10 +1158,10 @@ static void rt5665_jd_check_handler(struct work_struct *work)
}
}
-static int rt5665_set_jack_detect(struct snd_soc_codec *codec,
+static int rt5665_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack, void *data)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
switch (rt5665->pdata.jd_src) {
case RT5665_JD1:
@@ -1281,7 +1178,7 @@ static int rt5665_set_jack_detect(struct snd_soc_codec *codec,
break;
default:
- dev_warn(codec->dev, "Wrong JD source\n");
+ dev_warn(component->dev, "Wrong JD source\n");
break;
}
@@ -1296,12 +1193,12 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
container_of(work, struct rt5665_priv, jack_detect_work.work);
int val, btn_type;
- while (!rt5665->codec) {
+ while (!rt5665->component) {
pr_debug("%s codec = null\n", __func__);
usleep_range(10000, 15000);
}
- while (!rt5665->codec->component.card->instantiated) {
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
pr_debug("%s\n", __func__);
usleep_range(10000, 15000);
}
@@ -1313,17 +1210,17 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
mutex_lock(&rt5665->calibrate_mutex);
- val = snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010;
+ val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010;
if (!val) {
/* jack in */
if (rt5665->jack_type == 0) {
/* jack was out, report jack type */
rt5665->jack_type =
- rt5665_headset_detect(rt5665->codec, 1);
+ rt5665_headset_detect(rt5665->component, 1);
} else {
/* jack is already in, report button event */
rt5665->jack_type = SND_JACK_HEADSET;
- btn_type = rt5665_button_detect(rt5665->codec);
+ btn_type = rt5665_button_detect(rt5665->component);
/**
* rt5665 can report three kinds of button behavior,
* one click, double click and hold. However,
@@ -1356,7 +1253,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
break;
default:
btn_type = 0;
- dev_err(rt5665->codec->dev,
+ dev_err(rt5665->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -1364,7 +1261,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
}
} else {
/* jack out */
- rt5665->jack_type = rt5665_headset_detect(rt5665->codec, 0);
+ rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0);
}
snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type,
@@ -1479,18 +1376,18 @@ static const struct snd_kcontrol_new rt5665_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
- int pd, idx = -EINVAL;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ int pd, idx;
pd = rl6231_get_pre_div(rt5665->regmap,
RT5665_ADDA_CLK_1, RT5665_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rt5665->sysclk / pd);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else {
- snd_soc_update_bits(codec, RT5665_DMIC_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_DMIC_CTRL_1,
RT5665_DMIC_CLK_MASK, idx << RT5665_DMIC_CLK_SFT);
}
return idx;
@@ -1499,16 +1396,16 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int rt5665_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5665_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1,
RT5665_PM_HP_MASK | RT5665_OSW_L_MASK,
RT5665_PM_HP_HV | RT5665_OSW_L_EN);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, RT5665_HP_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1,
RT5665_PM_HP_MASK | RT5665_OSW_L_MASK,
RT5665_PM_HP_LV | RT5665_OSW_L_DIS);
break;
@@ -1523,9 +1420,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- val = snd_soc_read(codec, RT5665_GLB_CLK);
+ val = snd_soc_component_read(component, RT5665_GLB_CLK);
val &= RT5665_SCLK_SRC_MASK;
if (val == RT5665_SCLK_SRC_PLL1)
return 1;
@@ -1537,7 +1434,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int reg, shift, val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (w->shift) {
case RT5665_ADC_MONO_R_ASRC_SFT:
@@ -1576,13 +1473,13 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case RT5665_CLK_SEL_I2S1_ASRC:
case RT5665_CLK_SEL_I2S2_ASRC:
case RT5665_CLK_SEL_I2S3_ASRC:
/* I2S_Pre_Div1 should be 1 in asrc mode */
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
RT5665_I2S_PD1_MASK, RT5665_I2S_PD1_2);
return 1;
default:
@@ -2474,24 +2371,24 @@ static const struct snd_kcontrol_new pdm_r_switch =
static int rt5665_mono_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_EN);
- snd_soc_update_bits(codec, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
+ snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
0x0);
- snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x10, 0x10);
- snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x20, 0x20);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0x10);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0x20);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x20, 0);
- snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x10, 0);
- snd_soc_update_bits(codec, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0);
+ snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
0x40);
- snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
break;
@@ -2506,18 +2403,18 @@ static int rt5665_mono_event(struct snd_soc_dapm_widget *w,
static int rt5665_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_EN);
- snd_soc_write(codec, RT5665_HP_LOGIC_CTRL_2, 0x0003);
+ snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0003);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5665_HP_LOGIC_CTRL_2, 0x0002);
- snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1,
+ snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0002);
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
break;
@@ -2532,16 +2429,16 @@ static int rt5665_hp_event(struct snd_soc_dapm_widget *w,
static int rt5665_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5665_DEPOP_1,
+ snd_soc_component_update_bits(component, RT5665_DEPOP_1,
RT5665_PUMP_EN, RT5665_PUMP_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5665_DEPOP_1,
+ snd_soc_component_update_bits(component, RT5665_DEPOP_1,
RT5665_PUMP_EN, 0);
break;
@@ -2569,26 +2466,26 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
+static int rt5665_set_verf(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
switch (w->shift) {
case RT5665_PWR_VREF1_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV1, 0);
break;
case RT5665_PWR_VREF2_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV2, 0);
break;
case RT5665_PWR_VREF3_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV3, 0);
break;
@@ -2601,17 +2498,17 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
usleep_range(15000, 20000);
switch (w->shift) {
case RT5665_PWR_VREF1_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV1, RT5665_PWR_FV1);
break;
case RT5665_PWR_VREF2_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV2, RT5665_PWR_FV2);
break;
case RT5665_PWR_VREF3_BIT:
- snd_soc_update_bits(codec, RT5665_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
RT5665_PWR_FV3, RT5665_PWR_FV3);
break;
@@ -2630,7 +2527,7 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int val1, val2, mask1 = 0, mask2 = 0;
switch (w->shift) {
@@ -2660,18 +2557,18 @@ static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (mask1)
- snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1,
mask1, val1);
if (mask2)
- snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2,
mask2, val2);
break;
case SND_SOC_DAPM_POST_PMD:
if (mask1)
- snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1,
mask1, 0);
if (mask2)
- snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2,
mask2, 0);
break;
default:
@@ -2689,11 +2586,11 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5665_PWR_VOL,
RT5665_PWR_MIC_DET_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Vref1", RT5665_PWR_ANLG_1, RT5665_PWR_VREF1_BIT, 0,
- rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("Vref2", RT5665_PWR_ANLG_1, RT5665_PWR_VREF2_BIT, 0,
- rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("Vref3", RT5665_PWR_ANLG_1, RT5665_PWR_VREF3_BIT, 0,
- rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
/* ASRC */
SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5665_ASRC_1,
@@ -4052,7 +3949,7 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -4096,7 +3993,7 @@ static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_TDM_CTRL_1,
RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
RT5665_TDM_OUT_LEN_MASK, val);
@@ -4108,24 +4005,24 @@ static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int rt5665_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 rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100;
int pre_div, frame_size;
rt5665->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5665->sysclk, rt5665->lrck[dai->id]);
if (pre_div < 0) {
- dev_warn(codec->dev, "Force using PLL");
- snd_soc_codec_set_pll(codec, 0, RT5665_PLL1_S_MCLK,
+ dev_warn(component->dev, "Force using PLL");
+ snd_soc_component_set_pll(component, 0, RT5665_PLL1_S_MCLK,
rt5665->sysclk, rt5665->lrck[dai->id] * 512);
- snd_soc_codec_set_sysclk(codec, RT5665_SCLK_S_PLL1, 0,
+ snd_soc_component_set_sysclk(component, RT5665_SCLK_S_PLL1, 0,
rt5665->lrck[dai->id] * 512, 0);
pre_div = 1;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -4160,7 +4057,7 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
reg_clk = RT5665_ADDA_CLK_1;
mask_clk = RT5665_I2S_PD1_MASK;
val_clk = pre_div << RT5665_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5665_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S1_SDP,
RT5665_I2S_DL_MASK, val_len);
break;
case RT5665_AIF2_1:
@@ -4168,48 +4065,48 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD2_MASK;
val_clk = pre_div << RT5665_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5665_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S2_SDP,
RT5665_I2S_DL_MASK, val_len);
break;
case RT5665_AIF3:
reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD3_MASK;
val_clk = pre_div << RT5665_I2S_PD3_SFT;
- snd_soc_update_bits(codec, RT5665_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S3_SDP,
RT5665_I2S_DL_MASK, val_len);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
- snd_soc_update_bits(codec, reg_clk, mask_clk, val_clk);
- snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
+ snd_soc_component_update_bits(component, reg_clk, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
switch (rt5665->lrck[dai->id]) {
case 192000:
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
RT5665_DAC_OSR_32 | RT5665_ADC_OSR_32);
break;
case 96000:
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
RT5665_DAC_OSR_64 | RT5665_ADC_OSR_64);
break;
default:
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
RT5665_DAC_OSR_128 | RT5665_ADC_OSR_128);
break;
}
if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) {
- snd_soc_update_bits(codec, RT5665_I2S_M_CLK_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
RT5665_I2S2_M_PD_MASK, pre_div << RT5665_I2S2_M_PD_SFT);
}
if (rt5665->master[RT5665_AIF3]) {
- snd_soc_update_bits(codec, RT5665_I2S_M_CLK_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
RT5665_I2S3_M_PD_MASK, pre_div << RT5665_I2S3_M_PD_SFT);
}
@@ -4218,15 +4115,15 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5665->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5665_I2S_MS_S;
rt5665->master[dai->id] = 0;
break;
@@ -4263,32 +4160,32 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5665_AIF1_1:
case RT5665_AIF1_2:
- snd_soc_update_bits(codec, RT5665_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S1_SDP,
RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
RT5665_I2S_DF_MASK, reg_val);
break;
case RT5665_AIF2_1:
case RT5665_AIF2_2:
- snd_soc_update_bits(codec, RT5665_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S2_SDP,
RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
RT5665_I2S_DF_MASK, reg_val);
break;
case RT5665_AIF3:
- snd_soc_update_bits(codec, RT5665_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5665_I2S3_SDP,
RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
RT5665_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int rt5665_set_component_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0, src = 0;
if (freq == rt5665->sysclk && clk_id == rt5665->sysclk_src)
@@ -4308,34 +4205,34 @@ static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
src = RT5665_CLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_SCLK_SRC_MASK, reg_val);
if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) {
- snd_soc_update_bits(codec, RT5665_I2S_M_CLK_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
RT5665_I2S2_SRC_MASK, src << RT5665_I2S2_SRC_SFT);
}
if (rt5665->master[RT5665_AIF3]) {
- snd_soc_update_bits(codec, RT5665_I2S_M_CLK_CTRL_1,
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
RT5665_I2S3_SRC_MASK, src << RT5665_I2S3_SRC_SFT);
}
rt5665->sysclk = freq;
rt5665->sysclk_src = clk_id;
- dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
return 0;
}
-static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
+static int rt5665_set_component_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -4344,52 +4241,52 @@ static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5665->pll_in = 0;
rt5665->pll_out = 0;
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_SCLK_SRC_MASK, RT5665_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5665_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK);
break;
case RT5665_PLL1_S_BCLK1:
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK1);
break;
case RT5665_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK2);
break;
case RT5665_PLL1_S_BCLK3:
- snd_soc_update_bits(codec, RT5665_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3);
break;
default:
- dev_err(codec->dev, "Unknown PLL Source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5665_PLL_CTRL_1,
+ snd_soc_component_write(component, RT5665_PLL_CTRL_1,
pll_code.n_code << RT5665_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5665_PLL_CTRL_2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5665_PLL_M_SFT |
- pll_code.m_bp << RT5665_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5665_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5665_PLL_M_SFT) |
+ (pll_code.m_bp << RT5665_PLL_M_BP_SFT));
rt5665->pll_in = freq_in;
rt5665->pll_out = freq_out;
@@ -4400,10 +4297,10 @@ static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
rt5665->bclk[dai->id] = ratio;
@@ -4411,12 +4308,12 @@ static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
switch (dai->id) {
case RT5665_AIF2_1:
case RT5665_AIF2_2:
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_2,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2,
RT5665_I2S_BCLK_MS2_MASK,
RT5665_I2S_BCLK_MS2_64);
break;
case RT5665_AIF3:
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_2,
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2,
RT5665_I2S_BCLK_MS3_MASK,
RT5665_I2S_BCLK_MS3_64);
break;
@@ -4426,10 +4323,10 @@ static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
return 0;
}
-static int rt5665_set_bias_level(struct snd_soc_codec *codec,
+static int rt5665_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
@@ -4459,39 +4356,37 @@ static int rt5665_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5665_probe(struct snd_soc_codec *codec)
+static int rt5665_probe(struct snd_soc_component *component)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
- rt5665->codec = codec;
+ rt5665->component = component;
schedule_delayed_work(&rt5665->calibrate_work, msecs_to_jiffies(100));
return 0;
}
-static int rt5665_remove(struct snd_soc_codec *codec)
+static void rt5665_remove(struct snd_soc_component *component)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
regmap_write(rt5665->regmap, RT5665_RESET, 0);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5665_suspend(struct snd_soc_codec *codec)
+static int rt5665_suspend(struct snd_soc_component *component)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5665->regmap, true);
regcache_mark_dirty(rt5665->regmap);
return 0;
}
-static int rt5665_resume(struct snd_soc_codec *codec)
+static int rt5665_resume(struct snd_soc_component *component)
{
- struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5665->regmap, false);
regcache_sync(rt5665->regmap);
@@ -4605,24 +4500,23 @@ static struct snd_soc_dai_driver rt5665_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5665 = {
- .probe = rt5665_probe,
- .remove = rt5665_remove,
- .suspend = rt5665_suspend,
- .resume = rt5665_resume,
- .set_bias_level = rt5665_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5665_snd_controls,
- .num_controls = ARRAY_SIZE(rt5665_snd_controls),
- .dapm_widgets = rt5665_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
- .dapm_routes = rt5665_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
- },
- .set_sysclk = rt5665_set_codec_sysclk,
- .set_pll = rt5665_set_codec_pll,
- .set_jack = rt5665_set_jack_detect,
+static const struct snd_soc_component_driver soc_component_dev_rt5665 = {
+ .probe = rt5665_probe,
+ .remove = rt5665_remove,
+ .suspend = rt5665_suspend,
+ .resume = rt5665_resume,
+ .set_bias_level = rt5665_set_bias_level,
+ .controls = rt5665_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5665_snd_controls),
+ .dapm_widgets = rt5665_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
+ .dapm_routes = rt5665_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
+ .set_sysclk = rt5665_set_component_sysclk,
+ .set_pll = rt5665_set_component_pll,
+ .set_jack = rt5665_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
@@ -4632,14 +4526,15 @@ static const struct regmap_config rt5665_regmap = {
.max_register = 0x0400,
.volatile_reg = rt5665_volatile_register,
.readable_reg = rt5665_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5665_reg,
.num_reg_defaults = ARRAY_SIZE(rt5665_reg),
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
};
static const struct i2c_device_id rt5665_i2c_id[] = {
- {"rt5665", 0},
+ {"rt5665"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id);
@@ -4662,9 +4557,6 @@ static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev)
of_property_read_u32(dev->of_node, "realtek,jd-src",
&rt5665->pdata.jd_src);
- rt5665->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
- "realtek,ldo1-en-gpios", 0);
-
return 0;
}
@@ -4753,7 +4645,7 @@ static void rt5665_calibrate_handler(struct work_struct *work)
struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
calibrate_work.work);
- while (!rt5665->codec->component.card->instantiated) {
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
pr_debug("%s\n", __func__);
usleep_range(10000, 15000);
}
@@ -4761,12 +4653,11 @@ static void rt5665_calibrate_handler(struct work_struct *work)
rt5665_calibrate(rt5665);
}
-static int rt5665_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5665_i2c_probe(struct i2c_client *i2c)
{
struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5665_priv *rt5665;
- int i, ret;
+ int ret;
unsigned int val;
rt5665 = devm_kzalloc(&i2c->dev, sizeof(struct rt5665_priv),
@@ -4782,27 +4673,19 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
else
rt5665_parse_dt(rt5665, &i2c->dev);
- for (i = 0; i < ARRAY_SIZE(rt5665->supplies); i++)
- rt5665->supplies[i].supply = rt5665_supply_names[i];
-
- ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5665->supplies),
- rt5665->supplies);
+ ret = devm_regulator_bulk_get_enable(&i2c->dev, ARRAY_SIZE(rt5665_supply_names),
+ rt5665_supply_names);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
- ret = regulator_bulk_enable(ARRAY_SIZE(rt5665->supplies),
- rt5665->supplies);
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
- return ret;
- }
-
- if (gpio_is_valid(rt5665->pdata.ldo1_en)) {
- if (devm_gpio_request_one(&i2c->dev, rt5665->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH, "rt5665"))
- dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
+ rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5665->gpiod_ldo1_en)) {
+ dev_err(&i2c->dev, "Failed gpio request ldo1_en\n");
+ return PTR_ERR(rt5665->gpiod_ldo1_en);
}
/* Sleep for 300 ms miniumum */
@@ -4828,9 +4711,6 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
case 0x0:
rt5665->id = CODEC_5666;
break;
- case 0x6:
- rt5665->id = CODEC_5668;
- break;
case 0x3:
default:
rt5665->id = CODEC_5665;
@@ -4937,21 +4817,15 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5665", rt5665);
if (ret)
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
}
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5665,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5665,
rt5665_dai, ARRAY_SIZE(rt5665_dai));
}
-static int rt5665_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static void rt5665_i2c_shutdown(struct i2c_client *client)
{
struct rt5665_priv *rt5665 = i2c_get_clientdata(client);
@@ -4963,18 +4837,16 @@ static void rt5665_i2c_shutdown(struct i2c_client *client)
static const struct of_device_id rt5665_of_match[] = {
{.compatible = "realtek,rt5665"},
{.compatible = "realtek,rt5666"},
- {.compatible = "realtek,rt5668"},
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5665_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5665_acpi_match[] = {
- {"10EC5665", 0,},
- {"10EC5666", 0,},
- {"10EC5668", 0,},
- {},
+ { "10EC5665" },
+ { "10EC5666" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match);
#endif
@@ -4986,7 +4858,6 @@ static struct i2c_driver rt5665_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5665_acpi_match),
},
.probe = rt5665_i2c_probe,
- .remove = rt5665_i2c_remove,
.shutdown = rt5665_i2c_shutdown,
.id_table = rt5665_i2c_id,
};
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
index 5ddebd6a4a1b..089e4078d37a 100644
--- a/sound/soc/codecs/rt5665.h
+++ b/sound/soc/codecs/rt5665.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5665.h -- RT5665/RT5658 ALSA SoC audio driver
*
* Copyright 2016 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT5665_H__
@@ -1978,7 +1975,6 @@ enum {
enum {
CODEC_5665,
CODEC_5666,
- CODEC_5668,
};
/* filter mask */
@@ -2003,7 +1999,4 @@ enum {
RT5665_CLK_SEL_SYS4,
};
-int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec,
- unsigned int filter_mask, unsigned int clk_src);
-
#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
new file mode 100644
index 000000000000..8442dd09cfaf
--- /dev/null
+++ b/sound/soc/codecs/rt5668.c
@@ -0,0 +1,2585 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5668.c -- RT5668B ALSA SoC audio component driver
+ *
+ * Copyright 2018 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/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.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 <sound/rt5668.h>
+
+#include "rl6231.h"
+#include "rt5668.h"
+
+#define RT5668_NUM_SUPPLIES 3
+
+static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = {
+ "AVDD",
+ "MICVDD",
+ "VBAT",
+};
+
+struct rt5668_priv {
+ struct snd_soc_component *component;
+ struct rt5668_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5668_AIFS];
+ int bclk[RT5668_AIFS];
+ int master[RT5668_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+};
+
+static const struct reg_default rt5668_reg[] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x8000},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x800f},
+ {0x000b, 0x0000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x1404},
+ {0x0013, 0x1000},
+ {0x0014, 0xa00a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0019, 0xafaf},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x0080},
+ {0x0044, 0x0c0c},
+ {0x0049, 0x0000},
+ {0x0061, 0x0000},
+ {0x0062, 0x0000},
+ {0x0063, 0x003f},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0a10},
+ {0x0070, 0x8000},
+ {0x0071, 0x8000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x0080, 0x0000},
+ {0x0081, 0x0000},
+ {0x0082, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008d, 0x0000},
+ {0x008e, 0x0060},
+ {0x008f, 0x1000},
+ {0x0091, 0x0c26},
+ {0x0092, 0x0073},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0098, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x100c},
+ {0x009f, 0x0000},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00ae, 0x2040},
+ {0x00af, 0x0000},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d1, 0x2244},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0009},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f1, 0x00d0},
+ {0x00f2, 0x00d0},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6530},
+ {0x0100, 0xa0a0},
+ {0x010b, 0x0000},
+ {0x010c, 0xae00},
+ {0x010d, 0xaaa0},
+ {0x010e, 0x8aa2},
+ {0x010f, 0x02a2},
+ {0x0110, 0xc000},
+ {0x0111, 0x04a2},
+ {0x0112, 0x2800},
+ {0x0113, 0x0000},
+ {0x0117, 0x0100},
+ {0x0125, 0x0410},
+ {0x0132, 0x6026},
+ {0x0136, 0x5555},
+ {0x0138, 0x3700},
+ {0x013a, 0x2000},
+ {0x013b, 0x2000},
+ {0x013c, 0x2005},
+ {0x013f, 0x0000},
+ {0x0142, 0x0000},
+ {0x0145, 0x0002},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0149, 0x0000},
+ {0x0150, 0x79a1},
+ {0x0151, 0x0000},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0021},
+ {0x0190, 0x413d},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01af, 0x0000},
+ {0x01b0, 0x0400},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0000},
+ {0x01b4, 0x0000},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x01c3},
+ {0x01b7, 0x02a0},
+ {0x01b8, 0x03e9},
+ {0x01b9, 0x1389},
+ {0x01ba, 0xc351},
+ {0x01bb, 0x0009},
+ {0x01bc, 0x0018},
+ {0x01bd, 0x002a},
+ {0x01be, 0x004c},
+ {0x01bf, 0x0097},
+ {0x01c0, 0x433d},
+ {0x01c1, 0x2800},
+ {0x01c2, 0x0000},
+ {0x01c3, 0x0000},
+ {0x01c4, 0x0000},
+ {0x01c5, 0x0000},
+ {0x01c6, 0x0000},
+ {0x01c7, 0x0000},
+ {0x01c8, 0x40af},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0008},
+ {0x01d8, 0x0029},
+ {0x01d9, 0x3333},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0320},
+ {0x01e0, 0x06a1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01ea, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa005},
+ {0x0212, 0x824c},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x0250, 0x4500},
+ {0x0251, 0x40b3},
+ {0x0252, 0x0000},
+ {0x0253, 0x0000},
+ {0x0254, 0x0000},
+ {0x0255, 0x0000},
+ {0x0256, 0x0000},
+ {0x0257, 0x0000},
+ {0x0258, 0x0000},
+ {0x0259, 0x0000},
+ {0x025a, 0x0005},
+ {0x0270, 0x0000},
+ {0x02ff, 0x0110},
+ {0x0300, 0x001f},
+ {0x0301, 0x032c},
+ {0x0302, 0x5f21},
+ {0x0303, 0x4000},
+ {0x0304, 0x4000},
+ {0x0305, 0x06d5},
+ {0x0306, 0x8000},
+ {0x0307, 0x0700},
+ {0x0310, 0x4560},
+ {0x0311, 0xa4a8},
+ {0x0312, 0x7418},
+ {0x0313, 0x0000},
+ {0x0314, 0x0006},
+ {0x0315, 0xffff},
+ {0x0316, 0xc400},
+ {0x0317, 0x0000},
+ {0x03c0, 0x7e00},
+ {0x03c1, 0x8000},
+ {0x03c2, 0x8000},
+ {0x03c3, 0x8000},
+ {0x03c4, 0x8000},
+ {0x03c5, 0x8000},
+ {0x03c6, 0x8000},
+ {0x03c7, 0x8000},
+ {0x03c8, 0x8000},
+ {0x03c9, 0x8000},
+ {0x03ca, 0x8000},
+ {0x03cb, 0x8000},
+ {0x03cc, 0x8000},
+ {0x03d0, 0x0000},
+ {0x03d1, 0x0000},
+ {0x03d2, 0x0000},
+ {0x03d3, 0x0000},
+ {0x03d4, 0x2000},
+ {0x03d5, 0x2000},
+ {0x03d6, 0x0000},
+ {0x03d7, 0x0000},
+ {0x03d8, 0x2000},
+ {0x03d9, 0x2000},
+ {0x03da, 0x2000},
+ {0x03db, 0x2000},
+ {0x03dc, 0x0000},
+ {0x03dd, 0x0000},
+ {0x03de, 0x0000},
+ {0x03df, 0x2000},
+ {0x03e0, 0x0000},
+ {0x03e1, 0x0000},
+ {0x03e2, 0x0000},
+ {0x03e3, 0x0000},
+ {0x03e4, 0x0000},
+ {0x03e5, 0x0000},
+ {0x03e6, 0x0000},
+ {0x03e7, 0x0000},
+ {0x03e8, 0x0000},
+ {0x03e9, 0x0000},
+ {0x03ea, 0x0000},
+ {0x03eb, 0x0000},
+ {0x03ec, 0x0000},
+ {0x03ed, 0x0000},
+ {0x03ee, 0x0000},
+ {0x03ef, 0x0000},
+ {0x03f0, 0x0800},
+ {0x03f1, 0x0800},
+ {0x03f2, 0x0800},
+ {0x03f3, 0x0800},
+};
+
+static bool rt5668_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5668_RESET:
+ case RT5668_CBJ_CTRL_2:
+ case RT5668_INT_ST_1:
+ case RT5668_4BTN_IL_CMD_1:
+ case RT5668_AJD1_CTRL:
+ case RT5668_HP_CALIB_CTRL_1:
+ case RT5668_DEVICE_ID:
+ case RT5668_I2C_MODE:
+ case RT5668_HP_CALIB_CTRL_10:
+ case RT5668_EFUSE_CTRL_2:
+ case RT5668_JD_TOP_VC_VTRL:
+ case RT5668_HP_IMP_SENS_CTRL_19:
+ case RT5668_IL_CMD_1:
+ case RT5668_SAR_IL_CMD_2:
+ case RT5668_SAR_IL_CMD_4:
+ case RT5668_SAR_IL_CMD_10:
+ case RT5668_SAR_IL_CMD_11:
+ case RT5668_EFUSE_CTRL_6...RT5668_EFUSE_CTRL_11:
+ case RT5668_HP_CALIB_STA_1...RT5668_HP_CALIB_STA_11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5668_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5668_RESET:
+ case RT5668_VERSION_ID:
+ case RT5668_VENDOR_ID:
+ case RT5668_DEVICE_ID:
+ case RT5668_HP_CTRL_1:
+ case RT5668_HP_CTRL_2:
+ case RT5668_HPL_GAIN:
+ case RT5668_HPR_GAIN:
+ case RT5668_I2C_CTRL:
+ case RT5668_CBJ_BST_CTRL:
+ case RT5668_CBJ_CTRL_1:
+ case RT5668_CBJ_CTRL_2:
+ case RT5668_CBJ_CTRL_3:
+ case RT5668_CBJ_CTRL_4:
+ case RT5668_CBJ_CTRL_5:
+ case RT5668_CBJ_CTRL_6:
+ case RT5668_CBJ_CTRL_7:
+ case RT5668_DAC1_DIG_VOL:
+ case RT5668_STO1_ADC_DIG_VOL:
+ case RT5668_STO1_ADC_BOOST:
+ case RT5668_HP_IMP_GAIN_1:
+ case RT5668_HP_IMP_GAIN_2:
+ case RT5668_SIDETONE_CTRL:
+ case RT5668_STO1_ADC_MIXER:
+ case RT5668_AD_DA_MIXER:
+ case RT5668_STO1_DAC_MIXER:
+ case RT5668_A_DAC1_MUX:
+ case RT5668_DIG_INF2_DATA:
+ case RT5668_REC_MIXER:
+ case RT5668_CAL_REC:
+ case RT5668_ALC_BACK_GAIN:
+ case RT5668_PWR_DIG_1:
+ case RT5668_PWR_DIG_2:
+ case RT5668_PWR_ANLG_1:
+ case RT5668_PWR_ANLG_2:
+ case RT5668_PWR_ANLG_3:
+ case RT5668_PWR_MIXER:
+ case RT5668_PWR_VOL:
+ case RT5668_CLK_DET:
+ case RT5668_RESET_LPF_CTRL:
+ case RT5668_RESET_HPF_CTRL:
+ case RT5668_DMIC_CTRL_1:
+ case RT5668_I2S1_SDP:
+ case RT5668_I2S2_SDP:
+ case RT5668_ADDA_CLK_1:
+ case RT5668_ADDA_CLK_2:
+ case RT5668_I2S1_F_DIV_CTRL_1:
+ case RT5668_I2S1_F_DIV_CTRL_2:
+ case RT5668_TDM_CTRL:
+ case RT5668_TDM_ADDA_CTRL_1:
+ case RT5668_TDM_ADDA_CTRL_2:
+ case RT5668_DATA_SEL_CTRL_1:
+ case RT5668_TDM_TCON_CTRL:
+ case RT5668_GLB_CLK:
+ case RT5668_PLL_CTRL_1:
+ case RT5668_PLL_CTRL_2:
+ case RT5668_PLL_TRACK_1:
+ case RT5668_PLL_TRACK_2:
+ case RT5668_PLL_TRACK_3:
+ case RT5668_PLL_TRACK_4:
+ case RT5668_PLL_TRACK_5:
+ case RT5668_PLL_TRACK_6:
+ case RT5668_PLL_TRACK_11:
+ case RT5668_SDW_REF_CLK:
+ case RT5668_DEPOP_1:
+ case RT5668_DEPOP_2:
+ case RT5668_HP_CHARGE_PUMP_1:
+ case RT5668_HP_CHARGE_PUMP_2:
+ case RT5668_MICBIAS_1:
+ case RT5668_MICBIAS_2:
+ case RT5668_PLL_TRACK_12:
+ case RT5668_PLL_TRACK_14:
+ case RT5668_PLL2_CTRL_1:
+ case RT5668_PLL2_CTRL_2:
+ case RT5668_PLL2_CTRL_3:
+ case RT5668_PLL2_CTRL_4:
+ case RT5668_RC_CLK_CTRL:
+ case RT5668_I2S_M_CLK_CTRL_1:
+ case RT5668_I2S2_F_DIV_CTRL_1:
+ case RT5668_I2S2_F_DIV_CTRL_2:
+ case RT5668_EQ_CTRL_1:
+ case RT5668_EQ_CTRL_2:
+ case RT5668_IRQ_CTRL_1:
+ case RT5668_IRQ_CTRL_2:
+ case RT5668_IRQ_CTRL_3:
+ case RT5668_IRQ_CTRL_4:
+ case RT5668_INT_ST_1:
+ case RT5668_GPIO_CTRL_1:
+ case RT5668_GPIO_CTRL_2:
+ case RT5668_GPIO_CTRL_3:
+ case RT5668_HP_AMP_DET_CTRL_1:
+ case RT5668_HP_AMP_DET_CTRL_2:
+ case RT5668_MID_HP_AMP_DET:
+ case RT5668_LOW_HP_AMP_DET:
+ case RT5668_DELAY_BUF_CTRL:
+ case RT5668_SV_ZCD_1:
+ case RT5668_SV_ZCD_2:
+ case RT5668_IL_CMD_1:
+ case RT5668_IL_CMD_2:
+ case RT5668_IL_CMD_3:
+ case RT5668_IL_CMD_4:
+ case RT5668_IL_CMD_5:
+ case RT5668_IL_CMD_6:
+ case RT5668_4BTN_IL_CMD_1:
+ case RT5668_4BTN_IL_CMD_2:
+ case RT5668_4BTN_IL_CMD_3:
+ case RT5668_4BTN_IL_CMD_4:
+ case RT5668_4BTN_IL_CMD_5:
+ case RT5668_4BTN_IL_CMD_6:
+ case RT5668_4BTN_IL_CMD_7:
+ case RT5668_ADC_STO1_HP_CTRL_1:
+ case RT5668_ADC_STO1_HP_CTRL_2:
+ case RT5668_AJD1_CTRL:
+ case RT5668_JD1_THD:
+ case RT5668_JD2_THD:
+ case RT5668_JD_CTRL_1:
+ case RT5668_DUMMY_1:
+ case RT5668_DUMMY_2:
+ case RT5668_DUMMY_3:
+ case RT5668_DAC_ADC_DIG_VOL1:
+ case RT5668_BIAS_CUR_CTRL_2:
+ case RT5668_BIAS_CUR_CTRL_3:
+ case RT5668_BIAS_CUR_CTRL_4:
+ case RT5668_BIAS_CUR_CTRL_5:
+ case RT5668_BIAS_CUR_CTRL_6:
+ case RT5668_BIAS_CUR_CTRL_7:
+ case RT5668_BIAS_CUR_CTRL_8:
+ case RT5668_BIAS_CUR_CTRL_9:
+ case RT5668_BIAS_CUR_CTRL_10:
+ case RT5668_VREF_REC_OP_FB_CAP_CTRL:
+ case RT5668_CHARGE_PUMP_1:
+ case RT5668_DIG_IN_CTRL_1:
+ case RT5668_PAD_DRIVING_CTRL:
+ case RT5668_SOFT_RAMP_DEPOP:
+ case RT5668_CHOP_DAC:
+ case RT5668_CHOP_ADC:
+ case RT5668_CALIB_ADC_CTRL:
+ case RT5668_VOL_TEST:
+ case RT5668_SPKVDD_DET_STA:
+ case RT5668_TEST_MODE_CTRL_1:
+ case RT5668_TEST_MODE_CTRL_2:
+ case RT5668_TEST_MODE_CTRL_3:
+ case RT5668_TEST_MODE_CTRL_4:
+ case RT5668_TEST_MODE_CTRL_5:
+ case RT5668_PLL1_INTERNAL:
+ case RT5668_PLL2_INTERNAL:
+ case RT5668_STO_NG2_CTRL_1:
+ case RT5668_STO_NG2_CTRL_2:
+ case RT5668_STO_NG2_CTRL_3:
+ case RT5668_STO_NG2_CTRL_4:
+ case RT5668_STO_NG2_CTRL_5:
+ case RT5668_STO_NG2_CTRL_6:
+ case RT5668_STO_NG2_CTRL_7:
+ case RT5668_STO_NG2_CTRL_8:
+ case RT5668_STO_NG2_CTRL_9:
+ case RT5668_STO_NG2_CTRL_10:
+ case RT5668_STO1_DAC_SIL_DET:
+ case RT5668_SIL_PSV_CTRL1:
+ case RT5668_SIL_PSV_CTRL2:
+ case RT5668_SIL_PSV_CTRL3:
+ case RT5668_SIL_PSV_CTRL4:
+ case RT5668_SIL_PSV_CTRL5:
+ case RT5668_HP_IMP_SENS_CTRL_01:
+ case RT5668_HP_IMP_SENS_CTRL_02:
+ case RT5668_HP_IMP_SENS_CTRL_03:
+ case RT5668_HP_IMP_SENS_CTRL_04:
+ case RT5668_HP_IMP_SENS_CTRL_05:
+ case RT5668_HP_IMP_SENS_CTRL_06:
+ case RT5668_HP_IMP_SENS_CTRL_07:
+ case RT5668_HP_IMP_SENS_CTRL_08:
+ case RT5668_HP_IMP_SENS_CTRL_09:
+ case RT5668_HP_IMP_SENS_CTRL_10:
+ case RT5668_HP_IMP_SENS_CTRL_11:
+ case RT5668_HP_IMP_SENS_CTRL_12:
+ case RT5668_HP_IMP_SENS_CTRL_13:
+ case RT5668_HP_IMP_SENS_CTRL_14:
+ case RT5668_HP_IMP_SENS_CTRL_15:
+ case RT5668_HP_IMP_SENS_CTRL_16:
+ case RT5668_HP_IMP_SENS_CTRL_17:
+ case RT5668_HP_IMP_SENS_CTRL_18:
+ case RT5668_HP_IMP_SENS_CTRL_19:
+ case RT5668_HP_IMP_SENS_CTRL_20:
+ case RT5668_HP_IMP_SENS_CTRL_21:
+ case RT5668_HP_IMP_SENS_CTRL_22:
+ case RT5668_HP_IMP_SENS_CTRL_23:
+ case RT5668_HP_IMP_SENS_CTRL_24:
+ case RT5668_HP_IMP_SENS_CTRL_25:
+ case RT5668_HP_IMP_SENS_CTRL_26:
+ case RT5668_HP_IMP_SENS_CTRL_27:
+ case RT5668_HP_IMP_SENS_CTRL_28:
+ case RT5668_HP_IMP_SENS_CTRL_29:
+ case RT5668_HP_IMP_SENS_CTRL_30:
+ case RT5668_HP_IMP_SENS_CTRL_31:
+ case RT5668_HP_IMP_SENS_CTRL_32:
+ case RT5668_HP_IMP_SENS_CTRL_33:
+ case RT5668_HP_IMP_SENS_CTRL_34:
+ case RT5668_HP_IMP_SENS_CTRL_35:
+ case RT5668_HP_IMP_SENS_CTRL_36:
+ case RT5668_HP_IMP_SENS_CTRL_37:
+ case RT5668_HP_IMP_SENS_CTRL_38:
+ case RT5668_HP_IMP_SENS_CTRL_39:
+ case RT5668_HP_IMP_SENS_CTRL_40:
+ case RT5668_HP_IMP_SENS_CTRL_41:
+ case RT5668_HP_IMP_SENS_CTRL_42:
+ case RT5668_HP_IMP_SENS_CTRL_43:
+ case RT5668_HP_LOGIC_CTRL_1:
+ case RT5668_HP_LOGIC_CTRL_2:
+ case RT5668_HP_LOGIC_CTRL_3:
+ case RT5668_HP_CALIB_CTRL_1:
+ case RT5668_HP_CALIB_CTRL_2:
+ case RT5668_HP_CALIB_CTRL_3:
+ case RT5668_HP_CALIB_CTRL_4:
+ case RT5668_HP_CALIB_CTRL_5:
+ case RT5668_HP_CALIB_CTRL_6:
+ case RT5668_HP_CALIB_CTRL_7:
+ case RT5668_HP_CALIB_CTRL_9:
+ case RT5668_HP_CALIB_CTRL_10:
+ case RT5668_HP_CALIB_CTRL_11:
+ case RT5668_HP_CALIB_STA_1:
+ case RT5668_HP_CALIB_STA_2:
+ case RT5668_HP_CALIB_STA_3:
+ case RT5668_HP_CALIB_STA_4:
+ case RT5668_HP_CALIB_STA_5:
+ case RT5668_HP_CALIB_STA_6:
+ case RT5668_HP_CALIB_STA_7:
+ case RT5668_HP_CALIB_STA_8:
+ case RT5668_HP_CALIB_STA_9:
+ case RT5668_HP_CALIB_STA_10:
+ case RT5668_HP_CALIB_STA_11:
+ case RT5668_SAR_IL_CMD_1:
+ case RT5668_SAR_IL_CMD_2:
+ case RT5668_SAR_IL_CMD_3:
+ case RT5668_SAR_IL_CMD_4:
+ case RT5668_SAR_IL_CMD_5:
+ case RT5668_SAR_IL_CMD_6:
+ case RT5668_SAR_IL_CMD_7:
+ case RT5668_SAR_IL_CMD_8:
+ case RT5668_SAR_IL_CMD_9:
+ case RT5668_SAR_IL_CMD_10:
+ case RT5668_SAR_IL_CMD_11:
+ case RT5668_SAR_IL_CMD_12:
+ case RT5668_SAR_IL_CMD_13:
+ case RT5668_EFUSE_CTRL_1:
+ case RT5668_EFUSE_CTRL_2:
+ case RT5668_EFUSE_CTRL_3:
+ case RT5668_EFUSE_CTRL_4:
+ case RT5668_EFUSE_CTRL_5:
+ case RT5668_EFUSE_CTRL_6:
+ case RT5668_EFUSE_CTRL_7:
+ case RT5668_EFUSE_CTRL_8:
+ case RT5668_EFUSE_CTRL_9:
+ case RT5668_EFUSE_CTRL_10:
+ case RT5668_EFUSE_CTRL_11:
+ case RT5668_JD_TOP_VC_VTRL:
+ case RT5668_DRC1_CTRL_0:
+ case RT5668_DRC1_CTRL_1:
+ case RT5668_DRC1_CTRL_2:
+ case RT5668_DRC1_CTRL_3:
+ case RT5668_DRC1_CTRL_4:
+ case RT5668_DRC1_CTRL_5:
+ case RT5668_DRC1_CTRL_6:
+ case RT5668_DRC1_HARD_LMT_CTRL_1:
+ case RT5668_DRC1_HARD_LMT_CTRL_2:
+ case RT5668_DRC1_PRIV_1:
+ case RT5668_DRC1_PRIV_2:
+ case RT5668_DRC1_PRIV_3:
+ case RT5668_DRC1_PRIV_4:
+ case RT5668_DRC1_PRIV_5:
+ case RT5668_DRC1_PRIV_6:
+ case RT5668_DRC1_PRIV_7:
+ case RT5668_DRC1_PRIV_8:
+ case RT5668_EQ_AUTO_RCV_CTRL1:
+ case RT5668_EQ_AUTO_RCV_CTRL2:
+ case RT5668_EQ_AUTO_RCV_CTRL3:
+ case RT5668_EQ_AUTO_RCV_CTRL4:
+ case RT5668_EQ_AUTO_RCV_CTRL5:
+ case RT5668_EQ_AUTO_RCV_CTRL6:
+ case RT5668_EQ_AUTO_RCV_CTRL7:
+ case RT5668_EQ_AUTO_RCV_CTRL8:
+ case RT5668_EQ_AUTO_RCV_CTRL9:
+ case RT5668_EQ_AUTO_RCV_CTRL10:
+ case RT5668_EQ_AUTO_RCV_CTRL11:
+ case RT5668_EQ_AUTO_RCV_CTRL12:
+ case RT5668_EQ_AUTO_RCV_CTRL13:
+ case RT5668_ADC_L_EQ_LPF1_A1:
+ case RT5668_R_EQ_LPF1_A1:
+ case RT5668_L_EQ_LPF1_H0:
+ case RT5668_R_EQ_LPF1_H0:
+ case RT5668_L_EQ_BPF1_A1:
+ case RT5668_R_EQ_BPF1_A1:
+ case RT5668_L_EQ_BPF1_A2:
+ case RT5668_R_EQ_BPF1_A2:
+ case RT5668_L_EQ_BPF1_H0:
+ case RT5668_R_EQ_BPF1_H0:
+ case RT5668_L_EQ_BPF2_A1:
+ case RT5668_R_EQ_BPF2_A1:
+ case RT5668_L_EQ_BPF2_A2:
+ case RT5668_R_EQ_BPF2_A2:
+ case RT5668_L_EQ_BPF2_H0:
+ case RT5668_R_EQ_BPF2_H0:
+ case RT5668_L_EQ_BPF3_A1:
+ case RT5668_R_EQ_BPF3_A1:
+ case RT5668_L_EQ_BPF3_A2:
+ case RT5668_R_EQ_BPF3_A2:
+ case RT5668_L_EQ_BPF3_H0:
+ case RT5668_R_EQ_BPF3_H0:
+ case RT5668_L_EQ_BPF4_A1:
+ case RT5668_R_EQ_BPF4_A1:
+ case RT5668_L_EQ_BPF4_A2:
+ case RT5668_R_EQ_BPF4_A2:
+ case RT5668_L_EQ_BPF4_H0:
+ case RT5668_R_EQ_BPF4_H0:
+ case RT5668_L_EQ_HPF1_A1:
+ case RT5668_R_EQ_HPF1_A1:
+ case RT5668_L_EQ_HPF1_H0:
+ case RT5668_R_EQ_HPF1_H0:
+ case RT5668_L_EQ_PRE_VOL:
+ case RT5668_R_EQ_PRE_VOL:
+ case RT5668_L_EQ_POST_VOL:
+ case RT5668_R_EQ_POST_VOL:
+ case RT5668_I2C_MODE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 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 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 rt5668_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if2_adc_enum,
+ RT5668_DIG_INF2_DATA, RT5668_IF2_ADC_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_01_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC1_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_23_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC2_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_45_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC3_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_67_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC4_SEL_SFT, rt5668_data_select);
+
+static const struct snd_kcontrol_new rt5668_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5668_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5668_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5668_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5668_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5668_if1_67_adc_enum);
+
+static void rt5668_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT5668_RESET, 0);
+ regmap_write(regmap, RT5668_I2C_MODE, 1);
+}
+
+static int rt5668_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5668_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5668_4BTN_IL_CMD_1, val);
+ pr_debug("%s btn_type=%x\n", __func__, btn_type);
+
+ return btn_type;
+}
+
+static void rt5668_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_EN);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13,
+ RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_BTN);
+ snd_soc_component_write(component, RT5668_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_MASK | RT5668_4BTN_IL_RST_MASK,
+ RT5668_4BTN_IL_EN | RT5668_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3,
+ RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_EN);
+ } else {
+ snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3,
+ RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_DIS);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_MASK, RT5668_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_RST_MASK, RT5668_4BTN_IL_RST);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13,
+ RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_TYPE);
+ }
+}
+
+/**
+ * rt5668_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5668_headset_detect(struct snd_soc_component *component,
+ int jack_insert)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int val, count;
+
+ if (jack_insert) {
+ snd_soc_dapm_force_enable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1,
+ RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_HIGH);
+
+ count = 0;
+ val = snd_soc_component_read(component, RT5668_CBJ_CTRL_2)
+ & RT5668_JACK_TYPE_MASK;
+ while (val == 0 && count < 50) {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component,
+ RT5668_CBJ_CTRL_2) & RT5668_JACK_TYPE_MASK;
+ count++;
+ }
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ rt5668->jack_type = SND_JACK_HEADSET;
+ rt5668_enable_push_button_irq(component, true);
+ break;
+ default:
+ rt5668->jack_type = SND_JACK_HEADPHONE;
+ }
+
+ } else {
+ rt5668_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1,
+ RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_LOW);
+ snd_soc_dapm_disable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+
+ rt5668->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5668->jack_type);
+ return rt5668->jack_type;
+}
+
+static irqreturn_t rt5668_irq(int irq, void *data)
+{
+ struct rt5668_priv *rt5668 = data;
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5668_jd_check_handler(struct work_struct *work)
+{
+ struct rt5668_priv *rt5668 = container_of(work, struct rt5668_priv,
+ jd_check_work.work);
+
+ if (snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL)
+ & RT5668_JDH_RS_MASK) {
+ /* jack out */
+ rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0);
+
+ snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ schedule_delayed_work(&rt5668->jd_check_work, 500);
+ }
+}
+
+static int rt5668_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ switch (rt5668->pdata.jd_src) {
+ case RT5668_JD1:
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_2,
+ RT5668_EXT_JD_SRC, RT5668_EXT_JD_SRC_MANUAL);
+ snd_soc_component_write(component, RT5668_CBJ_CTRL_1, 0xd002);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_3,
+ RT5668_CBJ_IN_BUF_EN, RT5668_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_POW_MASK, RT5668_SAR_POW_EN);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_IRQ);
+ regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL,
+ RT5668_POW_IRQ | RT5668_POW_JDH |
+ RT5668_POW_ANA, RT5668_POW_IRQ |
+ RT5668_POW_JDH | RT5668_POW_ANA);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_2,
+ RT5668_PWR_JDH | RT5668_PWR_JDL,
+ RT5668_PWR_JDH | RT5668_PWR_JDL);
+ regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2,
+ RT5668_JD1_EN_MASK | RT5668_JD1_POL_MASK,
+ RT5668_JD1_EN | RT5668_JD1_POL_NOR);
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(250));
+ break;
+
+ case RT5668_JD_NULL:
+ regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2,
+ RT5668_JD1_EN_MASK, RT5668_JD1_DIS);
+ regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL,
+ RT5668_POW_JDH | RT5668_POW_JDL, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ rt5668->hs_jack = hs_jack;
+
+ return 0;
+}
+
+static void rt5668_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5668_priv *rt5668 =
+ container_of(work, struct rt5668_priv, jack_detect_work.work);
+ int val, btn_type;
+
+ if (!rt5668->component ||
+ !snd_soc_card_is_instantiated(rt5668->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ mutex_lock(&rt5668->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL)
+ & RT5668_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5668->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5668->jack_type =
+ rt5668_headset_detect(rt5668->component, 1);
+ } else {
+ /* jack is already in, report button event */
+ rt5668->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5668_button_detect(rt5668->component);
+ /**
+ * rt5668 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5668->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5668->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5668->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5668->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ btn_type = 0;
+ dev_err(rt5668->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0);
+ }
+
+ snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5668->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5668->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5668->jd_check_work);
+
+ mutex_unlock(&rt5668->calibrate_mutex);
+}
+
+static const struct snd_kcontrol_new rt5668_snd_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5668_HPL_GAIN,
+ RT5668_HPR_GAIN, RT5668_G_HP_SFT, 15, 1, hp_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5668_DAC1_DIG_VOL,
+ RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 175, 0, dac_vol_tlv),
+
+ /* IN Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5668_CBJ_BST_CTRL,
+ RT5668_BST_CBJ_SFT, 8, 0, bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_MUTE_SFT, RT5668_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 127, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5668_STO1_ADC_BOOST,
+ RT5668_STO1_ADC_L_BST_SFT, RT5668_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+};
+
+
+static int rt5668_div_sel(struct rt5668_priv *rt5668,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5668->sysclk < target) {
+ pr_err("sysclk rate %d is too low\n",
+ rt5668->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ pr_info("div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5668->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5668->sysclk) {
+ pr_err("can't find div for sysclk %d\n",
+ rt5668->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5668->sysclk)
+ pr_err("sysclk rate %d is too high\n",
+ rt5668->sysclk);
+
+ return size - 1;
+
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ int idx;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ idx = rt5668_div_sel(rt5668, 1500000, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_CLK_MASK, idx << RT5668_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+static int set_filter_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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ int ref, val, reg, idx;
+ static const int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ val = snd_soc_component_read(component, RT5668_GPIO_CTRL_1) &
+ RT5668_GP4_PIN_MASK;
+ if (w->shift == RT5668_PWR_ADC_S1F_BIT &&
+ val == RT5668_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5668->lrck[RT5668_AIF2];
+ else
+ ref = 256 * rt5668->lrck[RT5668_AIF1];
+
+ idx = rt5668_div_sel(rt5668, ref, div, ARRAY_SIZE(div));
+
+ if (w->shift == RT5668_PWR_ADC_S1F_BIT)
+ reg = RT5668_PLL_TRACK_3;
+ else
+ reg = RT5668_PLL_TRACK_2;
+
+ snd_soc_component_update_bits(component, reg,
+ RT5668_FILTER_CLK_SEL_MASK, idx << RT5668_FILTER_CLK_SEL_SFT);
+
+ return 0;
+}
+
+static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5668_GLB_CLK);
+ val &= RT5668_SCLK_SRC_MASK;
+ if (val == RT5668_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5668_ADC_STO1_ASRC_SFT:
+ reg = RT5668_PLL_TRACK_3;
+ shift = RT5668_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5668_DAC_STO1_ASRC_SFT:
+ reg = RT5668_PLL_TRACK_2;
+ shift = RT5668_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case RT5668_CLK_SEL_I2S1_ASRC:
+ case RT5668_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5668_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5668_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5668_REC_MIXER,
+ RT5668_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5668_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc1l_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC1L_SRC_SFT, rt5668_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc1r_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC1R_SRC_SFT, rt5668_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5668_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adcl_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADCL_SRC_SFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5668_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adcr_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADCR_SRC_SFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5668_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5668_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc2l_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC2L_SRC_SFT, rt5668_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5668_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc2r_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC2R_SRC_SFT, rt5668_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5668_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5668_if1_adc_slot_values[] = {
+ 0,
+ 2,
+ 4,
+ 6,
+};
+
+static const char * const rt5668_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_if1_adc_slot_enum,
+ RT5668_TDM_CTRL, RT5668_TDM_ADC_LCA_SFT, RT5668_TDM_ADC_LCA_MASK,
+ rt5668_if1_adc_slot_src, rt5668_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5668_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5668_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5668_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_alg_dac_l1_enum, RT5668_A_DAC1_MUX,
+ RT5668_A_DACL1_SFT, rt5668_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5668_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5668_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_alg_dac_r1_enum, RT5668_A_DAC1_MUX,
+ RT5668_A_DACR1_SFT, rt5668_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5668_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5668_alg_dac_r1_enum);
+
+/* Out Switch */
+static const struct snd_kcontrol_new hpol_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1,
+ RT5668_L_MUTE_SFT, 1, 1);
+static const struct snd_kcontrol_new hpor_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1,
+ RT5668_R_MUTE_SFT, 1, 1);
+
+static int rt5668_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_PRE_PMU:
+ snd_soc_component_write(component,
+ RT5668_HP_LOGIC_CTRL_2, 0x0012);
+ snd_soc_component_write(component,
+ RT5668_HP_CTRL_2, 0x6000);
+ snd_soc_component_update_bits(component, RT5668_STO_NG2_CTRL_1,
+ RT5668_NG2_EN_MASK, RT5668_NG2_EN);
+ snd_soc_component_update_bits(component,
+ RT5668_DEPOP_1, 0x60, 0x60);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ RT5668_DEPOP_1, 0x60, 0x0);
+ snd_soc_component_write(component,
+ RT5668_HP_CTRL_2, 0x0000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*Add delay to avoid pop noise*/
+ msleep(150);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5655_set_verf(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:
+ switch (w->shift) {
+ case RT5668_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV1, 0);
+ break;
+
+ case RT5668_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV2, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(15000, 20000);
+ switch (w->shift) {
+ case RT5668_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV1,
+ RT5668_PWR_FV1);
+ break;
+
+ case RT5668_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV2,
+ RT5668_PWR_FV2);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const unsigned int rt5668_adcdat_pin_values[] = {
+ 1,
+ 3,
+};
+
+static const char * const rt5668_adcdat_pin_select[] = {
+ "ADCDAT1",
+ "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_adcdat_pin_enum,
+ RT5668_GPIO_CTRL_1, RT5668_GP4_PIN_SFT, RT5668_GP4_PIN_MASK,
+ rt5668_adcdat_pin_select, rt5668_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5668_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5668_adcdat_pin_enum);
+
+static const struct snd_soc_dapm_widget rt5668_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5668_PWR_ANLG_3, RT5668_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5668_PWR_ANLG_3, RT5668_PWR_PLL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2B_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2F_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref1", RT5668_PWR_ANLG_1, RT5668_PWR_VREF1_BIT, 0,
+ rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref2", RT5668_PWR_ANLG_1, RT5668_PWR_VREF2_BIT, 0,
+ rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5668_PWR_ANLG_2, RT5668_PWR_MB1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5668_PWR_ANLG_2, RT5668_PWR_MB2_BIT,
+ 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ 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", RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("CBJ Power", RT5668_PWR_ANLG_3,
+ RT5668_PWR_CBJ_BIT, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5668_rec1_l_mix,
+ ARRAY_SIZE(rt5668_rec1_l_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5668_PWR_ANLG_2,
+ RT5668_PWR_RM1_L_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5668_PWR_DIG_1,
+ RT5668_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5668_PWR_DIG_1,
+ RT5668_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5668_CHOP_ADC,
+ RT5668_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5668_PWR_DIG_2,
+ RT5668_PWR_ADC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_MUTE_SFT, 1, rt5668_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5668_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_R_MUTE_SFT, 1, rt5668_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5668_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5668_PWR_DIG_1, RT5668_PWR_I2S1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5668_PWR_DIG_1, RT5668_PWR_I2S2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", 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),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_adcdat_pin_ctrl),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ RT5668_I2S1_SDP, RT5668_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ RT5668_I2S2_SDP, RT5668_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5668_dac_l_mix, ARRAY_SIZE(rt5668_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5668_dac_r_mix, ARRAY_SIZE(rt5668_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5668_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5668_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5668_PWR_DIG_2,
+ RT5668_PWR_DAC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5668_sto1_dac_l_mix, ARRAY_SIZE(rt5668_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5668_sto1_dac_r_mix, ARRAY_SIZE(rt5668_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5668_PWR_DIG_1,
+ RT5668_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5668_PWR_DIG_1,
+ RT5668_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5668_CHOP_DAC,
+ RT5668_CKGEN_DAC1_SFT, 0, NULL, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5668_hp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("HP Amp L", RT5668_PWR_ANLG_1,
+ RT5668_PWR_HA_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP Amp R", RT5668_PWR_ANLG_1,
+ RT5668_PWR_HA_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5668_DEPOP_1,
+ RT5668_PUMP_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5668_DEPOP_1,
+ RT5668_CAPLESS_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0,
+ &hpol_switch),
+ SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0,
+ &hpor_switch),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5668_CLK_DET,
+ RT5668_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5668_CLK_DET,
+ RT5668_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5668_CLK_DET,
+ RT5668_PLL2_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET", RT5668_CLK_DET,
+ RT5668_POW_CLK_DET_SFT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+};
+
+static const struct snd_soc_dapm_route rt5668_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+
+ /*Vref*/
+ {"MICBIAS1", NULL, "Vref1"},
+ {"MICBIAS1", NULL, "Vref2"},
+ {"MICBIAS2", NULL, "Vref1"},
+ {"MICBIAS2", NULL, "Vref2"},
+
+ {"CLKDET SYS", NULL, "CLKDET"},
+
+ {"IN1P", NULL, "LDO2"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "CBJ Power"},
+ {"CBJ Power", NULL, "Vref2"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"IF1_ADC Mux", NULL, "I2S1"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"DAC L1", NULL, "DAC 1 Clock"},
+ {"DAC R1", NULL, "DAC 1 Clock"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "HP Amp L"},
+ {"HP Amp", NULL, "HP Amp R"},
+ {"HP Amp", NULL, "Capless"},
+ {"HP Amp", NULL, "Charge Pump"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "CBJ Power"},
+ {"HP Amp", NULL, "Vref2"},
+ {"HPOL Playback", "Switch", "HP Amp"},
+ {"HPOR Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HPOL Playback"},
+ {"HPOR", NULL, "HPOR Playback"},
+};
+
+static int rt5668_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;
+
+ switch (slots) {
+ case 4:
+ val |= RT5668_TDM_TX_CH_4;
+ val |= RT5668_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5668_TDM_TX_CH_6;
+ val |= RT5668_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5668_TDM_TX_CH_8;
+ val |= RT5668_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5668_TDM_CTRL,
+ RT5668_TDM_TX_CH_MASK | RT5668_TDM_RX_CH_MASK, val);
+
+ switch (slot_width) {
+ case 16:
+ val = RT5668_TDM_CL_16;
+ break;
+ case 20:
+ val = RT5668_TDM_CL_20;
+ break;
+ case 24:
+ val = RT5668_TDM_CL_24;
+ break;
+ case 32:
+ val = RT5668_TDM_CL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL,
+ RT5668_TDM_CL_MASK, val);
+
+ return 0;
+}
+
+
+static int rt5668_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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int pre_div, frame_size;
+
+ rt5668->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5668->sysclk, rt5668->lrck[dai->id]);
+
+ 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(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt5668->lrck[dai->id], pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5668_I2S1_DL_20;
+ len_2 |= RT5668_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5668_I2S1_DL_24;
+ len_2 |= RT5668_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5668_I2S1_DL_32;
+ len_2 |= RT5668_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5668_I2S2_DL_8;
+ len_2 |= RT5668_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5668_AIF1:
+ snd_soc_component_update_bits(component, RT5668_I2S1_SDP,
+ RT5668_I2S1_DL_MASK, len_1);
+ if (rt5668->master[RT5668_AIF1]) {
+ snd_soc_component_update_bits(component,
+ RT5668_ADDA_CLK_1, RT5668_I2S_M_DIV_MASK,
+ pre_div << RT5668_I2S_M_DIV_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK,
+ RT5668_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK,
+ RT5668_I2S1_MONO_DIS);
+ break;
+ case RT5668_AIF2:
+ snd_soc_component_update_bits(component, RT5668_I2S2_SDP,
+ RT5668_I2S2_DL_MASK, len_2);
+ if (rt5668->master[RT5668_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_M_PD_MASK,
+ pre_div << RT5668_I2S2_M_PD_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK,
+ RT5668_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK,
+ RT5668_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5668_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5668->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5668->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 |= RT5668_I2S_BP_INV;
+ tdm_ctrl |= RT5668_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5668_AIF1)
+ tdm_ctrl |= RT5668_TDM_S_LP_INV | RT5668_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5668_AIF1)
+ tdm_ctrl |= RT5668_TDM_S_BP_INV | RT5668_TDM_S_LP_INV |
+ RT5668_TDM_M_BP_INV | RT5668_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ 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 |= RT5668_I2S_DF_LEFT;
+ tdm_ctrl |= RT5668_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5668_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5668_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5668_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5668_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5668_AIF1:
+ snd_soc_component_update_bits(component, RT5668_I2S1_SDP,
+ RT5668_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL,
+ RT5668_TDM_MS_MASK | RT5668_TDM_S_BP_MASK |
+ RT5668_TDM_DF_MASK | RT5668_TDM_M_BP_MASK |
+ RT5668_TDM_M_LP_MASK | RT5668_TDM_S_LP_MASK,
+ tdm_ctrl | rt5668->master[dai->id]);
+ break;
+ case RT5668_AIF2:
+ if (rt5668->master[dai->id] == 0)
+ reg_val |= RT5668_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5668_I2S2_SDP,
+ RT5668_I2S2_MS_MASK | RT5668_I2S_BP_MASK |
+ RT5668_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5668_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, src = 0;
+
+ if (freq == rt5668->sysclk && clk_id == rt5668->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5668_SCLK_S_MCLK:
+ reg_val |= RT5668_SCLK_SRC_MCLK;
+ src = RT5668_CLK_SRC_MCLK;
+ break;
+ case RT5668_SCLK_S_PLL1:
+ reg_val |= RT5668_SCLK_SRC_PLL1;
+ src = RT5668_CLK_SRC_PLL1;
+ break;
+ case RT5668_SCLK_S_PLL2:
+ reg_val |= RT5668_SCLK_SRC_PLL2;
+ src = RT5668_CLK_SRC_PLL2;
+ break;
+ case RT5668_SCLK_S_RCCLK:
+ reg_val |= RT5668_SCLK_SRC_RCCLK;
+ src = RT5668_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_SCLK_SRC_MASK, reg_val);
+
+ if (rt5668->master[RT5668_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_SRC_MASK,
+ src << RT5668_I2S2_SRC_SFT);
+ }
+
+ rt5668->sysclk = freq;
+ rt5668->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt5668_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5668->pll_src && freq_in == rt5668->pll_in &&
+ freq_out == rt5668->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5668->pll_in = 0;
+ rt5668->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_SCLK_SRC_MASK, RT5668_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5668_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_MCLK);
+ break;
+ case RT5668_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_PLL1_SRC_MASK, RT5668_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, RT5668_PLL_CTRL_1,
+ pll_code.n_code << RT5668_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5668_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5668_PLL_M_SFT) |
+ (pll_code.m_bp << RT5668_PLL_M_BP_SFT));
+
+ rt5668->pll_in = freq_in;
+ rt5668->pll_out = freq_out;
+ rt5668->pll_src = source;
+
+ return 0;
+}
+
+static int rt5668_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2,
+ RT5668_I2S2_BCLK_MS2_MASK,
+ RT5668_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2,
+ RT5668_I2S2_BCLK_MS2_MASK,
+ RT5668_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5668_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB | RT5668_PWR_BG,
+ RT5668_PWR_MB | RT5668_PWR_BG);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB, RT5668_PWR_MB);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL, RT5668_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, 0);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB | RT5668_PWR_BG, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5668_probe(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668->component = component;
+
+ return 0;
+}
+
+static void rt5668_remove(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668_reset(rt5668->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt5668_suspend(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5668->regmap, true);
+ regcache_mark_dirty(rt5668->regmap);
+ return 0;
+}
+
+static int rt5668_resume(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5668->regmap, false);
+ regcache_sync(rt5668->regmap);
+
+ return 0;
+}
+#else
+#define rt5668_suspend NULL
+#define rt5668_resume NULL
+#endif
+
+#define RT5668_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5668_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 rt5668_aif1_dai_ops = {
+ .hw_params = rt5668_hw_params,
+ .set_fmt = rt5668_set_dai_fmt,
+ .set_tdm_slot = rt5668_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops rt5668_aif2_dai_ops = {
+ .hw_params = rt5668_hw_params,
+ .set_fmt = rt5668_set_dai_fmt,
+ .set_bclk_ratio = rt5668_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt5668_dai[] = {
+ {
+ .name = "rt5668-aif1",
+ .id = RT5668_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .ops = &rt5668_aif1_dai_ops,
+ },
+ {
+ .name = "rt5668-aif2",
+ .id = RT5668_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .ops = &rt5668_aif2_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5668 = {
+ .probe = rt5668_probe,
+ .remove = rt5668_remove,
+ .suspend = rt5668_suspend,
+ .resume = rt5668_resume,
+ .set_bias_level = rt5668_set_bias_level,
+ .controls = rt5668_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5668_snd_controls),
+ .dapm_widgets = rt5668_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5668_dapm_widgets),
+ .dapm_routes = rt5668_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5668_dapm_routes),
+ .set_sysclk = rt5668_set_component_sysclk,
+ .set_pll = rt5668_set_component_pll,
+ .set_jack = rt5668_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5668_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5668_I2C_MODE,
+ .volatile_reg = rt5668_volatile_register,
+ .readable_reg = rt5668_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5668_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5668_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt5668_i2c_id[] = {
+ {"rt5668b"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id);
+
+static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev)
+{
+
+ of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin",
+ &rt5668->pdata.dmic1_data_pin);
+ of_property_read_u32(dev->of_node, "realtek,dmic1-clk-pin",
+ &rt5668->pdata.dmic1_clk_pin);
+ of_property_read_u32(dev->of_node, "realtek,jd-src",
+ &rt5668->pdata.jd_src);
+
+ return 0;
+}
+
+static void rt5668_calibrate(struct rt5668_priv *rt5668)
+{
+ int value, count;
+
+ mutex_lock(&rt5668->calibrate_mutex);
+
+ rt5668_reset(rt5668->regmap);
+ regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf);
+ usleep_range(15000, 20000);
+ regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xf2bf);
+ regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8001);
+ regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000);
+ regmap_write(rt5668->regmap, RT5668_STO1_DAC_MIXER, 0x2080);
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x4040);
+ regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0069);
+ regmap_write(rt5668->regmap, RT5668_CHOP_DAC, 0x3000);
+ regmap_write(rt5668->regmap, RT5668_HP_CTRL_2, 0x6000);
+ regmap_write(rt5668->regmap, RT5668_HP_CHARGE_PUMP_1, 0x0f26);
+ regmap_write(rt5668->regmap, RT5668_CALIB_ADC_CTRL, 0x7f05);
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5668->regmap, RT5668_CAL_REC, 0x0d0d);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_9, 0x000f);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8d01);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5668->regmap, RT5668_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_3, 0x06a1);
+ regmap_write(rt5668->regmap, RT5668_A_DAC1_MUX, 0x0311);
+ regmap_write(rt5668->regmap, RT5668_RESET_HPF_CTRL, 0x0000);
+ regmap_write(rt5668->regmap, RT5668_ADC_STO1_HP_CTRL_1, 0x3320);
+
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5668->regmap, RT5668_HP_CALIB_STA_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ pr_err("HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000);
+
+ mutex_unlock(&rt5668->calibrate_mutex);
+
+}
+
+static int rt5668_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5668_priv *rt5668;
+ int i, ret;
+ unsigned int val;
+
+ rt5668 = devm_kzalloc(&i2c->dev, sizeof(struct rt5668_priv),
+ GFP_KERNEL);
+
+ if (rt5668 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5668);
+
+ if (pdata)
+ rt5668->pdata = *pdata;
+ else
+ rt5668_parse_dt(rt5668, &i2c->dev);
+
+ rt5668->regmap = devm_regmap_init_i2c(i2c, &rt5668_regmap);
+ if (IS_ERR(rt5668->regmap)) {
+ ret = PTR_ERR(rt5668->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5668->supplies); i++)
+ rt5668->supplies[i].supply = rt5668_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5668->supplies),
+ rt5668->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5668->supplies),
+ rt5668->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5668->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5668->ldo1_en);
+ }
+
+ /* Sleep for 300 ms miniumum */
+ usleep_range(300000, 350000);
+
+ regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1);
+ usleep_range(10000, 15000);
+
+ regmap_read(rt5668->regmap, RT5668_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ pr_err("Device with ID register %x is not rt5668\n", val);
+ return -ENODEV;
+ }
+
+ rt5668_reset(rt5668->regmap);
+
+ rt5668_calibrate(rt5668);
+
+ regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0000);
+
+ /* DMIC pin*/
+ if (rt5668->pdata.dmic1_data_pin != RT5668_DMIC1_NULL) {
+ switch (rt5668->pdata.dmic1_data_pin) {
+ case RT5668_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP2_PIN_MASK, RT5668_GP2_PIN_DMIC_SDA);
+ break;
+
+ case RT5668_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP5_PIN_MASK, RT5668_GP5_PIN_DMIC_SDA);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ switch (rt5668->pdata.dmic1_clk_pin) {
+ case RT5668_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_DMIC_CLK);
+ break;
+
+ case RT5668_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP3_PIN_MASK, RT5668_GP3_PIN_DMIC_CLK);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+ }
+
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_LDO1_DVO_MASK | RT5668_HP_DRIVER_MASK,
+ RT5668_LDO1_DVO_14 | RT5668_HP_DRIVER_5X);
+ regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP4_PIN_MASK | RT5668_GP5_PIN_MASK,
+ RT5668_GP4_PIN_ADCDAT1 | RT5668_GP5_PIN_DACDAT1);
+ regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000);
+
+ INIT_DELAYED_WORK(&rt5668->jack_detect_work,
+ rt5668_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5668->jd_check_work,
+ rt5668_jd_check_handler);
+
+ mutex_init(&rt5668->calibrate_mutex);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5668", rt5668);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
+ rt5668_dai, ARRAY_SIZE(rt5668_dai));
+}
+
+static void rt5668_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5668_priv *rt5668 = i2c_get_clientdata(client);
+
+ rt5668_reset(rt5668->regmap);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5668_of_match[] = {
+ {.compatible = "realtek,rt5668b"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5668_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5668_acpi_match[] = {
+ { "10EC5668" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5668_acpi_match);
+#endif
+
+static struct i2c_driver rt5668_i2c_driver = {
+ .driver = {
+ .name = "rt5668b",
+ .of_match_table = of_match_ptr(rt5668_of_match),
+ .acpi_match_table = ACPI_PTR(rt5668_acpi_match),
+ },
+ .probe = rt5668_i2c_probe,
+ .shutdown = rt5668_i2c_shutdown,
+ .id_table = rt5668_i2c_id,
+};
+module_i2c_driver(rt5668_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5668B driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h
new file mode 100644
index 000000000000..b34a61d2109c
--- /dev/null
+++ b/sound/soc/codecs/rt5668.h
@@ -0,0 +1,1312 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5668.h -- RT5668/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5668_H__
+#define __RT5668_H__
+
+#include <sound/rt5668.h>
+
+#define DEVICE_ID 0x6530
+
+/* Info */
+#define RT5668_RESET 0x0000
+#define RT5668_VERSION_ID 0x00fd
+#define RT5668_VENDOR_ID 0x00fe
+#define RT5668_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5668_HP_CTRL_1 0x0002
+#define RT5668_HP_CTRL_2 0x0003
+#define RT5668_HPL_GAIN 0x0005
+#define RT5668_HPR_GAIN 0x0006
+
+#define RT5668_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5668_CBJ_BST_CTRL 0x000b
+#define RT5668_CBJ_CTRL_1 0x0010
+#define RT5668_CBJ_CTRL_2 0x0011
+#define RT5668_CBJ_CTRL_3 0x0012
+#define RT5668_CBJ_CTRL_4 0x0013
+#define RT5668_CBJ_CTRL_5 0x0014
+#define RT5668_CBJ_CTRL_6 0x0015
+#define RT5668_CBJ_CTRL_7 0x0016
+/* I/O - ADC/DAC/DMIC */
+#define RT5668_DAC1_DIG_VOL 0x0019
+#define RT5668_STO1_ADC_DIG_VOL 0x001c
+#define RT5668_STO1_ADC_BOOST 0x001f
+#define RT5668_HP_IMP_GAIN_1 0x0022
+#define RT5668_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5668_SIDETONE_CTRL 0x0024
+#define RT5668_STO1_ADC_MIXER 0x0026
+#define RT5668_AD_DA_MIXER 0x0029
+#define RT5668_STO1_DAC_MIXER 0x002a
+#define RT5668_A_DAC1_MUX 0x002b
+#define RT5668_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5668_REC_MIXER 0x003c
+#define RT5668_CAL_REC 0x0044
+#define RT5668_ALC_BACK_GAIN 0x0049
+/* Power */
+#define RT5668_PWR_DIG_1 0x0061
+#define RT5668_PWR_DIG_2 0x0062
+#define RT5668_PWR_ANLG_1 0x0063
+#define RT5668_PWR_ANLG_2 0x0064
+#define RT5668_PWR_ANLG_3 0x0065
+#define RT5668_PWR_MIXER 0x0066
+#define RT5668_PWR_VOL 0x0067
+/* Clock Detect */
+#define RT5668_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5668_RESET_LPF_CTRL 0x006c
+#define RT5668_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5668_DMIC_CTRL_1 0x006e
+/* Format - ADC/DAC */
+#define RT5668_I2S1_SDP 0x0070
+#define RT5668_I2S2_SDP 0x0071
+#define RT5668_ADDA_CLK_1 0x0073
+#define RT5668_ADDA_CLK_2 0x0074
+#define RT5668_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5668_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5668_TDM_CTRL 0x0079
+#define RT5668_TDM_ADDA_CTRL_1 0x007a
+#define RT5668_TDM_ADDA_CTRL_2 0x007b
+#define RT5668_DATA_SEL_CTRL_1 0x007c
+#define RT5668_TDM_TCON_CTRL 0x007e
+/* Function - Analog */
+#define RT5668_GLB_CLK 0x0080
+#define RT5668_PLL_CTRL_1 0x0081
+#define RT5668_PLL_CTRL_2 0x0082
+#define RT5668_PLL_TRACK_1 0x0083
+#define RT5668_PLL_TRACK_2 0x0084
+#define RT5668_PLL_TRACK_3 0x0085
+#define RT5668_PLL_TRACK_4 0x0086
+#define RT5668_PLL_TRACK_5 0x0087
+#define RT5668_PLL_TRACK_6 0x0088
+#define RT5668_PLL_TRACK_11 0x008c
+#define RT5668_SDW_REF_CLK 0x008d
+#define RT5668_DEPOP_1 0x008e
+#define RT5668_DEPOP_2 0x008f
+#define RT5668_HP_CHARGE_PUMP_1 0x0091
+#define RT5668_HP_CHARGE_PUMP_2 0x0092
+#define RT5668_MICBIAS_1 0x0093
+#define RT5668_MICBIAS_2 0x0094
+#define RT5668_PLL_TRACK_12 0x0098
+#define RT5668_PLL_TRACK_14 0x009a
+#define RT5668_PLL2_CTRL_1 0x009b
+#define RT5668_PLL2_CTRL_2 0x009c
+#define RT5668_PLL2_CTRL_3 0x009d
+#define RT5668_PLL2_CTRL_4 0x009e
+#define RT5668_RC_CLK_CTRL 0x009f
+#define RT5668_I2S_M_CLK_CTRL_1 0x00a0
+#define RT5668_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5668_I2S2_F_DIV_CTRL_2 0x00a4
+/* Function - Digital */
+#define RT5668_EQ_CTRL_1 0x00ae
+#define RT5668_EQ_CTRL_2 0x00af
+#define RT5668_IRQ_CTRL_1 0x00b6
+#define RT5668_IRQ_CTRL_2 0x00b7
+#define RT5668_IRQ_CTRL_3 0x00b8
+#define RT5668_IRQ_CTRL_4 0x00b9
+#define RT5668_INT_ST_1 0x00be
+#define RT5668_GPIO_CTRL_1 0x00c0
+#define RT5668_GPIO_CTRL_2 0x00c1
+#define RT5668_GPIO_CTRL_3 0x00c2
+#define RT5668_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5668_HP_AMP_DET_CTRL_2 0x00d1
+#define RT5668_MID_HP_AMP_DET 0x00d2
+#define RT5668_LOW_HP_AMP_DET 0x00d3
+#define RT5668_DELAY_BUF_CTRL 0x00d4
+#define RT5668_SV_ZCD_1 0x00d9
+#define RT5668_SV_ZCD_2 0x00da
+#define RT5668_IL_CMD_1 0x00db
+#define RT5668_IL_CMD_2 0x00dc
+#define RT5668_IL_CMD_3 0x00dd
+#define RT5668_IL_CMD_4 0x00de
+#define RT5668_IL_CMD_5 0x00df
+#define RT5668_IL_CMD_6 0x00e0
+#define RT5668_4BTN_IL_CMD_1 0x00e2
+#define RT5668_4BTN_IL_CMD_2 0x00e3
+#define RT5668_4BTN_IL_CMD_3 0x00e4
+#define RT5668_4BTN_IL_CMD_4 0x00e5
+#define RT5668_4BTN_IL_CMD_5 0x00e6
+#define RT5668_4BTN_IL_CMD_6 0x00e7
+#define RT5668_4BTN_IL_CMD_7 0x00e8
+
+#define RT5668_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5668_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5668_AJD1_CTRL 0x00f0
+#define RT5668_JD1_THD 0x00f1
+#define RT5668_JD2_THD 0x00f2
+#define RT5668_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5668_DUMMY_1 0x00fa
+#define RT5668_DUMMY_2 0x00fb
+#define RT5668_DUMMY_3 0x00fc
+
+#define RT5668_DAC_ADC_DIG_VOL1 0x0100
+#define RT5668_BIAS_CUR_CTRL_2 0x010b
+#define RT5668_BIAS_CUR_CTRL_3 0x010c
+#define RT5668_BIAS_CUR_CTRL_4 0x010d
+#define RT5668_BIAS_CUR_CTRL_5 0x010e
+#define RT5668_BIAS_CUR_CTRL_6 0x010f
+#define RT5668_BIAS_CUR_CTRL_7 0x0110
+#define RT5668_BIAS_CUR_CTRL_8 0x0111
+#define RT5668_BIAS_CUR_CTRL_9 0x0112
+#define RT5668_BIAS_CUR_CTRL_10 0x0113
+#define RT5668_VREF_REC_OP_FB_CAP_CTRL 0x0117
+#define RT5668_CHARGE_PUMP_1 0x0125
+#define RT5668_DIG_IN_CTRL_1 0x0132
+#define RT5668_PAD_DRIVING_CTRL 0x0136
+#define RT5668_SOFT_RAMP_DEPOP 0x0138
+#define RT5668_CHOP_DAC 0x013a
+#define RT5668_CHOP_ADC 0x013b
+#define RT5668_CALIB_ADC_CTRL 0x013c
+#define RT5668_VOL_TEST 0x013f
+#define RT5668_SPKVDD_DET_STA 0x0142
+#define RT5668_TEST_MODE_CTRL_1 0x0145
+#define RT5668_TEST_MODE_CTRL_2 0x0146
+#define RT5668_TEST_MODE_CTRL_3 0x0147
+#define RT5668_TEST_MODE_CTRL_4 0x0148
+#define RT5668_TEST_MODE_CTRL_5 0x0149
+#define RT5668_PLL1_INTERNAL 0x0150
+#define RT5668_PLL2_INTERNAL 0x0151
+#define RT5668_STO_NG2_CTRL_1 0x0160
+#define RT5668_STO_NG2_CTRL_2 0x0161
+#define RT5668_STO_NG2_CTRL_3 0x0162
+#define RT5668_STO_NG2_CTRL_4 0x0163
+#define RT5668_STO_NG2_CTRL_5 0x0164
+#define RT5668_STO_NG2_CTRL_6 0x0165
+#define RT5668_STO_NG2_CTRL_7 0x0166
+#define RT5668_STO_NG2_CTRL_8 0x0167
+#define RT5668_STO_NG2_CTRL_9 0x0168
+#define RT5668_STO_NG2_CTRL_10 0x0169
+#define RT5668_STO1_DAC_SIL_DET 0x0190
+#define RT5668_SIL_PSV_CTRL1 0x0194
+#define RT5668_SIL_PSV_CTRL2 0x0195
+#define RT5668_SIL_PSV_CTRL3 0x0197
+#define RT5668_SIL_PSV_CTRL4 0x0198
+#define RT5668_SIL_PSV_CTRL5 0x0199
+#define RT5668_HP_IMP_SENS_CTRL_01 0x01af
+#define RT5668_HP_IMP_SENS_CTRL_02 0x01b0
+#define RT5668_HP_IMP_SENS_CTRL_03 0x01b1
+#define RT5668_HP_IMP_SENS_CTRL_04 0x01b2
+#define RT5668_HP_IMP_SENS_CTRL_05 0x01b3
+#define RT5668_HP_IMP_SENS_CTRL_06 0x01b4
+#define RT5668_HP_IMP_SENS_CTRL_07 0x01b5
+#define RT5668_HP_IMP_SENS_CTRL_08 0x01b6
+#define RT5668_HP_IMP_SENS_CTRL_09 0x01b7
+#define RT5668_HP_IMP_SENS_CTRL_10 0x01b8
+#define RT5668_HP_IMP_SENS_CTRL_11 0x01b9
+#define RT5668_HP_IMP_SENS_CTRL_12 0x01ba
+#define RT5668_HP_IMP_SENS_CTRL_13 0x01bb
+#define RT5668_HP_IMP_SENS_CTRL_14 0x01bc
+#define RT5668_HP_IMP_SENS_CTRL_15 0x01bd
+#define RT5668_HP_IMP_SENS_CTRL_16 0x01be
+#define RT5668_HP_IMP_SENS_CTRL_17 0x01bf
+#define RT5668_HP_IMP_SENS_CTRL_18 0x01c0
+#define RT5668_HP_IMP_SENS_CTRL_19 0x01c1
+#define RT5668_HP_IMP_SENS_CTRL_20 0x01c2
+#define RT5668_HP_IMP_SENS_CTRL_21 0x01c3
+#define RT5668_HP_IMP_SENS_CTRL_22 0x01c4
+#define RT5668_HP_IMP_SENS_CTRL_23 0x01c5
+#define RT5668_HP_IMP_SENS_CTRL_24 0x01c6
+#define RT5668_HP_IMP_SENS_CTRL_25 0x01c7
+#define RT5668_HP_IMP_SENS_CTRL_26 0x01c8
+#define RT5668_HP_IMP_SENS_CTRL_27 0x01c9
+#define RT5668_HP_IMP_SENS_CTRL_28 0x01ca
+#define RT5668_HP_IMP_SENS_CTRL_29 0x01cb
+#define RT5668_HP_IMP_SENS_CTRL_30 0x01cc
+#define RT5668_HP_IMP_SENS_CTRL_31 0x01cd
+#define RT5668_HP_IMP_SENS_CTRL_32 0x01ce
+#define RT5668_HP_IMP_SENS_CTRL_33 0x01cf
+#define RT5668_HP_IMP_SENS_CTRL_34 0x01d0
+#define RT5668_HP_IMP_SENS_CTRL_35 0x01d1
+#define RT5668_HP_IMP_SENS_CTRL_36 0x01d2
+#define RT5668_HP_IMP_SENS_CTRL_37 0x01d3
+#define RT5668_HP_IMP_SENS_CTRL_38 0x01d4
+#define RT5668_HP_IMP_SENS_CTRL_39 0x01d5
+#define RT5668_HP_IMP_SENS_CTRL_40 0x01d6
+#define RT5668_HP_IMP_SENS_CTRL_41 0x01d7
+#define RT5668_HP_IMP_SENS_CTRL_42 0x01d8
+#define RT5668_HP_IMP_SENS_CTRL_43 0x01d9
+#define RT5668_HP_LOGIC_CTRL_1 0x01da
+#define RT5668_HP_LOGIC_CTRL_2 0x01db
+#define RT5668_HP_LOGIC_CTRL_3 0x01dc
+#define RT5668_HP_CALIB_CTRL_1 0x01de
+#define RT5668_HP_CALIB_CTRL_2 0x01df
+#define RT5668_HP_CALIB_CTRL_3 0x01e0
+#define RT5668_HP_CALIB_CTRL_4 0x01e1
+#define RT5668_HP_CALIB_CTRL_5 0x01e2
+#define RT5668_HP_CALIB_CTRL_6 0x01e3
+#define RT5668_HP_CALIB_CTRL_7 0x01e4
+#define RT5668_HP_CALIB_CTRL_9 0x01e6
+#define RT5668_HP_CALIB_CTRL_10 0x01e7
+#define RT5668_HP_CALIB_CTRL_11 0x01e8
+#define RT5668_HP_CALIB_STA_1 0x01ea
+#define RT5668_HP_CALIB_STA_2 0x01eb
+#define RT5668_HP_CALIB_STA_3 0x01ec
+#define RT5668_HP_CALIB_STA_4 0x01ed
+#define RT5668_HP_CALIB_STA_5 0x01ee
+#define RT5668_HP_CALIB_STA_6 0x01ef
+#define RT5668_HP_CALIB_STA_7 0x01f0
+#define RT5668_HP_CALIB_STA_8 0x01f1
+#define RT5668_HP_CALIB_STA_9 0x01f2
+#define RT5668_HP_CALIB_STA_10 0x01f3
+#define RT5668_HP_CALIB_STA_11 0x01f4
+#define RT5668_SAR_IL_CMD_1 0x0210
+#define RT5668_SAR_IL_CMD_2 0x0211
+#define RT5668_SAR_IL_CMD_3 0x0212
+#define RT5668_SAR_IL_CMD_4 0x0213
+#define RT5668_SAR_IL_CMD_5 0x0214
+#define RT5668_SAR_IL_CMD_6 0x0215
+#define RT5668_SAR_IL_CMD_7 0x0216
+#define RT5668_SAR_IL_CMD_8 0x0217
+#define RT5668_SAR_IL_CMD_9 0x0218
+#define RT5668_SAR_IL_CMD_10 0x0219
+#define RT5668_SAR_IL_CMD_11 0x021a
+#define RT5668_SAR_IL_CMD_12 0x021b
+#define RT5668_SAR_IL_CMD_13 0x021c
+#define RT5668_EFUSE_CTRL_1 0x0250
+#define RT5668_EFUSE_CTRL_2 0x0251
+#define RT5668_EFUSE_CTRL_3 0x0252
+#define RT5668_EFUSE_CTRL_4 0x0253
+#define RT5668_EFUSE_CTRL_5 0x0254
+#define RT5668_EFUSE_CTRL_6 0x0255
+#define RT5668_EFUSE_CTRL_7 0x0256
+#define RT5668_EFUSE_CTRL_8 0x0257
+#define RT5668_EFUSE_CTRL_9 0x0258
+#define RT5668_EFUSE_CTRL_10 0x0259
+#define RT5668_EFUSE_CTRL_11 0x025a
+#define RT5668_JD_TOP_VC_VTRL 0x0270
+#define RT5668_DRC1_CTRL_0 0x02ff
+#define RT5668_DRC1_CTRL_1 0x0300
+#define RT5668_DRC1_CTRL_2 0x0301
+#define RT5668_DRC1_CTRL_3 0x0302
+#define RT5668_DRC1_CTRL_4 0x0303
+#define RT5668_DRC1_CTRL_5 0x0304
+#define RT5668_DRC1_CTRL_6 0x0305
+#define RT5668_DRC1_HARD_LMT_CTRL_1 0x0306
+#define RT5668_DRC1_HARD_LMT_CTRL_2 0x0307
+#define RT5668_DRC1_PRIV_1 0x0310
+#define RT5668_DRC1_PRIV_2 0x0311
+#define RT5668_DRC1_PRIV_3 0x0312
+#define RT5668_DRC1_PRIV_4 0x0313
+#define RT5668_DRC1_PRIV_5 0x0314
+#define RT5668_DRC1_PRIV_6 0x0315
+#define RT5668_DRC1_PRIV_7 0x0316
+#define RT5668_DRC1_PRIV_8 0x0317
+#define RT5668_EQ_AUTO_RCV_CTRL1 0x03c0
+#define RT5668_EQ_AUTO_RCV_CTRL2 0x03c1
+#define RT5668_EQ_AUTO_RCV_CTRL3 0x03c2
+#define RT5668_EQ_AUTO_RCV_CTRL4 0x03c3
+#define RT5668_EQ_AUTO_RCV_CTRL5 0x03c4
+#define RT5668_EQ_AUTO_RCV_CTRL6 0x03c5
+#define RT5668_EQ_AUTO_RCV_CTRL7 0x03c6
+#define RT5668_EQ_AUTO_RCV_CTRL8 0x03c7
+#define RT5668_EQ_AUTO_RCV_CTRL9 0x03c8
+#define RT5668_EQ_AUTO_RCV_CTRL10 0x03c9
+#define RT5668_EQ_AUTO_RCV_CTRL11 0x03ca
+#define RT5668_EQ_AUTO_RCV_CTRL12 0x03cb
+#define RT5668_EQ_AUTO_RCV_CTRL13 0x03cc
+#define RT5668_ADC_L_EQ_LPF1_A1 0x03d0
+#define RT5668_R_EQ_LPF1_A1 0x03d1
+#define RT5668_L_EQ_LPF1_H0 0x03d2
+#define RT5668_R_EQ_LPF1_H0 0x03d3
+#define RT5668_L_EQ_BPF1_A1 0x03d4
+#define RT5668_R_EQ_BPF1_A1 0x03d5
+#define RT5668_L_EQ_BPF1_A2 0x03d6
+#define RT5668_R_EQ_BPF1_A2 0x03d7
+#define RT5668_L_EQ_BPF1_H0 0x03d8
+#define RT5668_R_EQ_BPF1_H0 0x03d9
+#define RT5668_L_EQ_BPF2_A1 0x03da
+#define RT5668_R_EQ_BPF2_A1 0x03db
+#define RT5668_L_EQ_BPF2_A2 0x03dc
+#define RT5668_R_EQ_BPF2_A2 0x03dd
+#define RT5668_L_EQ_BPF2_H0 0x03de
+#define RT5668_R_EQ_BPF2_H0 0x03df
+#define RT5668_L_EQ_BPF3_A1 0x03e0
+#define RT5668_R_EQ_BPF3_A1 0x03e1
+#define RT5668_L_EQ_BPF3_A2 0x03e2
+#define RT5668_R_EQ_BPF3_A2 0x03e3
+#define RT5668_L_EQ_BPF3_H0 0x03e4
+#define RT5668_R_EQ_BPF3_H0 0x03e5
+#define RT5668_L_EQ_BPF4_A1 0x03e6
+#define RT5668_R_EQ_BPF4_A1 0x03e7
+#define RT5668_L_EQ_BPF4_A2 0x03e8
+#define RT5668_R_EQ_BPF4_A2 0x03e9
+#define RT5668_L_EQ_BPF4_H0 0x03ea
+#define RT5668_R_EQ_BPF4_H0 0x03eb
+#define RT5668_L_EQ_HPF1_A1 0x03ec
+#define RT5668_R_EQ_HPF1_A1 0x03ed
+#define RT5668_L_EQ_HPF1_H0 0x03ee
+#define RT5668_R_EQ_HPF1_H0 0x03ef
+#define RT5668_L_EQ_PRE_VOL 0x03f0
+#define RT5668_R_EQ_PRE_VOL 0x03f1
+#define RT5668_L_EQ_POST_VOL 0x03f2
+#define RT5668_R_EQ_POST_VOL 0x03f3
+#define RT5668_I2C_MODE 0xffff
+
+
+/* global definition */
+#define RT5668_L_MUTE (0x1 << 15)
+#define RT5668_L_MUTE_SFT 15
+#define RT5668_VOL_L_MUTE (0x1 << 14)
+#define RT5668_VOL_L_SFT 14
+#define RT5668_R_MUTE (0x1 << 7)
+#define RT5668_R_MUTE_SFT 7
+#define RT5668_VOL_R_MUTE (0x1 << 6)
+#define RT5668_VOL_R_SFT 6
+#define RT5668_L_VOL_MASK (0x3f << 8)
+#define RT5668_L_VOL_SFT 8
+#define RT5668_R_VOL_MASK (0x3f)
+#define RT5668_R_VOL_SFT 0
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5668_G_HP (0xf << 8)
+#define RT5668_G_HP_SFT 8
+#define RT5668_G_STO_DA_DMIX (0xf)
+#define RT5668_G_STO_DA_SFT 0
+
+/* CBJ Control (0x000b) */
+#define RT5668_BST_CBJ_MASK (0xf << 8)
+#define RT5668_BST_CBJ_SFT 8
+
+/* Embeeded Jack and Type Detection Control 1 (0x0010) */
+#define RT5668_EMB_JD_EN (0x1 << 15)
+#define RT5668_EMB_JD_EN_SFT 15
+#define RT5668_EMB_JD_RST (0x1 << 14)
+#define RT5668_JD_MODE (0x1 << 13)
+#define RT5668_JD_MODE_SFT 13
+#define RT5668_DET_TYPE (0x1 << 12)
+#define RT5668_DET_TYPE_SFT 12
+#define RT5668_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5668_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5668_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5668_EXT_JD_DIG (0x1 << 9)
+#define RT5668_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5668_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5668_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5668_FAST_OFF_MASK (0x1 << 7)
+#define RT5668_FAST_OFF_EN (0x1 << 7)
+#define RT5668_FAST_OFF_DIS (0x0 << 7)
+#define RT5668_VREF_POW_MASK (0x1 << 6)
+#define RT5668_VREF_POW_FSM (0x0 << 6)
+#define RT5668_VREF_POW_REG (0x1 << 6)
+#define RT5668_MB1_PATH_MASK (0x1 << 5)
+#define RT5668_CTRL_MB1_REG (0x1 << 5)
+#define RT5668_CTRL_MB1_FSM (0x0 << 5)
+#define RT5668_MB2_PATH_MASK (0x1 << 4)
+#define RT5668_CTRL_MB2_REG (0x1 << 4)
+#define RT5668_CTRL_MB2_FSM (0x0 << 4)
+#define RT5668_TRIG_JD_MASK (0x1 << 3)
+#define RT5668_TRIG_JD_HIGH (0x1 << 3)
+#define RT5668_TRIG_JD_LOW (0x0 << 3)
+#define RT5668_MIC_CAP_MASK (0x1 << 1)
+#define RT5668_MIC_CAP_HS (0x1 << 1)
+#define RT5668_MIC_CAP_HP (0x0 << 1)
+#define RT5668_MIC_CAP_SRC_MASK (0x1)
+#define RT5668_MIC_CAP_SRC_REG (0x1)
+#define RT5668_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 2 (0x0011) */
+#define RT5668_EXT_JD_SRC (0x7 << 4)
+#define RT5668_EXT_JD_SRC_SFT 4
+#define RT5668_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5668_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5668_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5668_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5668_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5668_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 3 (0x0012) */
+#define RT5668_CBJ_IN_BUF_EN (0x1 << 7)
+
+/* Combo Jack and Type Detection Control 4 (0x0013) */
+#define RT5668_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5668_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5668_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5668_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5668_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5668_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5668_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5668_DAC_L1_VOL_SFT 8
+#define RT5668_DAC_R1_VOL_MASK (0xff)
+#define RT5668_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5668_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5668_ADC_L_VOL_SFT 8
+#define RT5668_ADC_R_VOL_MASK (0x7f)
+#define RT5668_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5668_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5668_STO1_ADC_L_BST_SFT 14
+#define RT5668_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5668_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5668_ST_SRC_SEL (0x1 << 8)
+#define RT5668_ST_SRC_SFT 8
+#define RT5668_ST_EN_MASK (0x1 << 6)
+#define RT5668_ST_DIS (0x0 << 6)
+#define RT5668_ST_EN (0x1 << 6)
+#define RT5668_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5668_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5668_M_STO1_ADC_L1_SFT 15
+#define RT5668_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5668_M_STO1_ADC_L2_SFT 14
+#define RT5668_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5668_STO1_ADC1L_SRC_SFT 13
+#define RT5668_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5668_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5668_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5668_STO1_ADC2L_SRC_SFT 12
+#define RT5668_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5668_STO1_ADCL_SRC_SFT 10
+#define RT5668_STO1_DD_L_SRC_MASK (0x1 << 9)
+#define RT5668_STO1_DD_L_SRC_SFT 9
+#define RT5668_STO1_DMIC_SRC_MASK (0x1 << 8)
+#define RT5668_STO1_DMIC_SRC_SFT 8
+#define RT5668_STO1_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5668_STO1_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5668_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5668_M_STO1_ADC_R1_SFT 7
+#define RT5668_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5668_M_STO1_ADC_R2_SFT 6
+#define RT5668_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5668_STO1_ADC1R_SRC_SFT 5
+#define RT5668_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5668_STO1_ADC2R_SRC_SFT 4
+#define RT5668_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5668_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5668_M_ADCMIX_L (0x1 << 15)
+#define RT5668_M_ADCMIX_L_SFT 15
+#define RT5668_M_DAC1_L (0x1 << 14)
+#define RT5668_M_DAC1_L_SFT 14
+#define RT5668_DAC1_R_SEL_MASK (0x1 << 10)
+#define RT5668_DAC1_R_SEL_SFT 10
+#define RT5668_DAC1_L_SEL_MASK (0x1 << 8)
+#define RT5668_DAC1_L_SEL_SFT 8
+#define RT5668_M_ADCMIX_R (0x1 << 7)
+#define RT5668_M_ADCMIX_R_SFT 7
+#define RT5668_M_DAC1_R (0x1 << 6)
+#define RT5668_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5668_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5668_M_DAC_L1_STO_L_SFT 15
+#define RT5668_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5668_G_DAC_L1_STO_L_SFT 14
+#define RT5668_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5668_M_DAC_R1_STO_L_SFT 13
+#define RT5668_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5668_G_DAC_R1_STO_L_SFT 12
+#define RT5668_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5668_M_DAC_L1_STO_R_SFT 7
+#define RT5668_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5668_G_DAC_L1_STO_R_SFT 6
+#define RT5668_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5668_M_DAC_R1_STO_R_SFT 5
+#define RT5668_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5668_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5668_M_ST_STO_L (0x1 << 9)
+#define RT5668_M_ST_STO_L_SFT 9
+#define RT5668_M_ST_STO_R (0x1 << 8)
+#define RT5668_M_ST_STO_R_SFT 8
+#define RT5668_DAC_L1_SRC_MASK (0x3 << 4)
+#define RT5668_A_DACL1_SFT 4
+#define RT5668_DAC_R1_SRC_MASK (0x3)
+#define RT5668_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5668_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5668_IF2_ADC_SEL_SFT 0
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5668_G_CBJ_RM1_L (0x7 << 10)
+#define RT5668_G_CBJ_RM1_L_SFT 10
+#define RT5668_M_CBJ_RM1_L (0x1 << 7)
+#define RT5668_M_CBJ_RM1_L_SFT 7
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5668_PWR_I2S1 (0x1 << 15)
+#define RT5668_PWR_I2S1_BIT 15
+#define RT5668_PWR_I2S2 (0x1 << 14)
+#define RT5668_PWR_I2S2_BIT 14
+#define RT5668_PWR_DAC_L1 (0x1 << 11)
+#define RT5668_PWR_DAC_L1_BIT 11
+#define RT5668_PWR_DAC_R1 (0x1 << 10)
+#define RT5668_PWR_DAC_R1_BIT 10
+#define RT5668_PWR_LDO (0x1 << 8)
+#define RT5668_PWR_LDO_BIT 8
+#define RT5668_PWR_ADC_L1 (0x1 << 4)
+#define RT5668_PWR_ADC_L1_BIT 4
+#define RT5668_PWR_ADC_R1 (0x1 << 3)
+#define RT5668_PWR_ADC_R1_BIT 3
+#define RT5668_DIG_GATE_CTRL (0x1 << 0)
+#define RT5668_DIG_GATE_CTRL_SFT 0
+
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5668_PWR_ADC_S1F (0x1 << 15)
+#define RT5668_PWR_ADC_S1F_BIT 15
+#define RT5668_PWR_DAC_S1F (0x1 << 10)
+#define RT5668_PWR_DAC_S1F_BIT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5668_PWR_VREF1 (0x1 << 15)
+#define RT5668_PWR_VREF1_BIT 15
+#define RT5668_PWR_FV1 (0x1 << 14)
+#define RT5668_PWR_FV1_BIT 14
+#define RT5668_PWR_VREF2 (0x1 << 13)
+#define RT5668_PWR_VREF2_BIT 13
+#define RT5668_PWR_FV2 (0x1 << 12)
+#define RT5668_PWR_FV2_BIT 12
+#define RT5668_LDO1_DBG_MASK (0x3 << 10)
+#define RT5668_PWR_MB (0x1 << 9)
+#define RT5668_PWR_MB_BIT 9
+#define RT5668_PWR_BG (0x1 << 7)
+#define RT5668_PWR_BG_BIT 7
+#define RT5668_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5668_LDO1_BYPASS (0x1 << 6)
+#define RT5668_LDO1_NOT_BYPASS (0x0 << 6)
+#define RT5668_PWR_MA_BIT 6
+#define RT5668_LDO1_DVO_MASK (0x3 << 4)
+#define RT5668_LDO1_DVO_09 (0x0 << 4)
+#define RT5668_LDO1_DVO_10 (0x1 << 4)
+#define RT5668_LDO1_DVO_12 (0x2 << 4)
+#define RT5668_LDO1_DVO_14 (0x3 << 4)
+#define RT5668_HP_DRIVER_MASK (0x3 << 2)
+#define RT5668_HP_DRIVER_1X (0x0 << 2)
+#define RT5668_HP_DRIVER_3X (0x1 << 2)
+#define RT5668_HP_DRIVER_5X (0x3 << 2)
+#define RT5668_PWR_HA_L (0x1 << 1)
+#define RT5668_PWR_HA_L_BIT 1
+#define RT5668_PWR_HA_R (0x1 << 0)
+#define RT5668_PWR_HA_R_BIT 0
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5668_PWR_MB1 (0x1 << 11)
+#define RT5668_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5668_PWR_MB1_BIT 11
+#define RT5668_PWR_MB2 (0x1 << 10)
+#define RT5668_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5668_PWR_MB2_BIT 10
+#define RT5668_PWR_JDH (0x1 << 3)
+#define RT5668_PWR_JDH_BIT 3
+#define RT5668_PWR_JDL (0x1 << 2)
+#define RT5668_PWR_JDL_BIT 2
+#define RT5668_PWR_RM1_L (0x1 << 1)
+#define RT5668_PWR_RM1_L_BIT 1
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5668_PWR_CBJ (0x1 << 9)
+#define RT5668_PWR_CBJ_BIT 9
+#define RT5668_PWR_PLL (0x1 << 6)
+#define RT5668_PWR_PLL_BIT 6
+#define RT5668_PWR_PLL2B (0x1 << 5)
+#define RT5668_PWR_PLL2B_BIT 5
+#define RT5668_PWR_PLL2F (0x1 << 4)
+#define RT5668_PWR_PLL2F_BIT 4
+#define RT5668_PWR_LDO2 (0x1 << 2)
+#define RT5668_PWR_LDO2_BIT 2
+#define RT5668_PWR_DET_SPKVDD (0x1 << 1)
+#define RT5668_PWR_DET_SPKVDD_BIT 1
+
+/* Power Management for Mixer (0x0066) */
+#define RT5668_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5668_PWR_STO1_DAC_L_BIT 5
+#define RT5668_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5668_PWR_STO1_DAC_R_BIT 4
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5668_SYS_CLK_DET (0x1 << 15)
+#define RT5668_SYS_CLK_DET_SFT 15
+#define RT5668_PLL1_CLK_DET (0x1 << 14)
+#define RT5668_PLL1_CLK_DET_SFT 14
+#define RT5668_PLL2_CLK_DET (0x1 << 13)
+#define RT5668_PLL2_CLK_DET_SFT 13
+#define RT5668_POW_CLK_DET2_SFT 8
+#define RT5668_POW_CLK_DET_SFT 0
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5668_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5668_DMIC_1_EN_SFT 15
+#define RT5668_DMIC_1_DIS (0x0 << 15)
+#define RT5668_DMIC_1_EN (0x1 << 15)
+#define RT5668_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5668_DMIC_1_DP_SFT 4
+#define RT5668_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5668_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5668_DMIC_CLK_MASK (0xf << 0)
+#define RT5668_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5668_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5668_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5668_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5668_SEL_ADCDAT_SFT 15
+#define RT5668_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5668_I2S1_TX_CHL_SFT 12
+#define RT5668_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5668_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5668_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5668_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5668_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5668_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5668_I2S1_RX_CHL_SFT 8
+#define RT5668_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5668_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5668_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5668_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5668_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5668_I2S1_MONO_MASK (0x1 << 7)
+#define RT5668_I2S1_MONO_EN (0x1 << 7)
+#define RT5668_I2S1_MONO_DIS (0x0 << 7)
+#define RT5668_I2S2_MONO_MASK (0x1 << 6)
+#define RT5668_I2S2_MONO_EN (0x1 << 6)
+#define RT5668_I2S2_MONO_DIS (0x0 << 6)
+#define RT5668_I2S1_DL_MASK (0x7 << 4)
+#define RT5668_I2S1_DL_SFT 4
+#define RT5668_I2S1_DL_16 (0x0 << 4)
+#define RT5668_I2S1_DL_20 (0x1 << 4)
+#define RT5668_I2S1_DL_24 (0x2 << 4)
+#define RT5668_I2S1_DL_32 (0x3 << 4)
+#define RT5668_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */
+#define RT5668_I2S2_MS_MASK (0x1 << 15)
+#define RT5668_I2S2_MS_SFT 15
+#define RT5668_I2S2_MS_M (0x0 << 15)
+#define RT5668_I2S2_MS_S (0x1 << 15)
+#define RT5668_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5668_I2S2_PIN_CFG_SFT 14
+#define RT5668_I2S2_CLK_SEL_MASK (0x1 << 11)
+#define RT5668_I2S2_CLK_SEL_SFT 11
+#define RT5668_I2S2_OUT_MASK (0x1 << 9)
+#define RT5668_I2S2_OUT_SFT 9
+#define RT5668_I2S2_OUT_UM (0x0 << 9)
+#define RT5668_I2S2_OUT_M (0x1 << 9)
+#define RT5668_I2S_BP_MASK (0x1 << 8)
+#define RT5668_I2S_BP_SFT 8
+#define RT5668_I2S_BP_NOR (0x0 << 8)
+#define RT5668_I2S_BP_INV (0x1 << 8)
+#define RT5668_I2S2_MONO_EN (0x1 << 6)
+#define RT5668_I2S2_MONO_DIS (0x0 << 6)
+#define RT5668_I2S2_DL_MASK (0x3 << 4)
+#define RT5668_I2S2_DL_SFT 4
+#define RT5668_I2S2_DL_16 (0x0 << 4)
+#define RT5668_I2S2_DL_20 (0x1 << 4)
+#define RT5668_I2S2_DL_24 (0x2 << 4)
+#define RT5668_I2S2_DL_8 (0x3 << 4)
+#define RT5668_I2S_DF_MASK (0x7)
+#define RT5668_I2S_DF_SFT 0
+#define RT5668_I2S_DF_I2S (0x0)
+#define RT5668_I2S_DF_LEFT (0x1)
+#define RT5668_I2S_DF_PCM_A (0x2)
+#define RT5668_I2S_DF_PCM_B (0x3)
+#define RT5668_I2S_DF_PCM_A_N (0x6)
+#define RT5668_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5668_ADC_OSR_MASK (0xf << 12)
+#define RT5668_ADC_OSR_SFT 12
+#define RT5668_ADC_OSR_D_1 (0x0 << 12)
+#define RT5668_ADC_OSR_D_2 (0x1 << 12)
+#define RT5668_ADC_OSR_D_4 (0x2 << 12)
+#define RT5668_ADC_OSR_D_6 (0x3 << 12)
+#define RT5668_ADC_OSR_D_8 (0x4 << 12)
+#define RT5668_ADC_OSR_D_12 (0x5 << 12)
+#define RT5668_ADC_OSR_D_16 (0x6 << 12)
+#define RT5668_ADC_OSR_D_24 (0x7 << 12)
+#define RT5668_ADC_OSR_D_32 (0x8 << 12)
+#define RT5668_ADC_OSR_D_48 (0x9 << 12)
+#define RT5668_I2S_M_DIV_MASK (0xf << 12)
+#define RT5668_I2S_M_DIV_SFT 8
+#define RT5668_I2S_M_D_1 (0x0 << 8)
+#define RT5668_I2S_M_D_2 (0x1 << 8)
+#define RT5668_I2S_M_D_3 (0x2 << 8)
+#define RT5668_I2S_M_D_4 (0x3 << 8)
+#define RT5668_I2S_M_D_6 (0x4 << 8)
+#define RT5668_I2S_M_D_8 (0x5 << 8)
+#define RT5668_I2S_M_D_12 (0x6 << 8)
+#define RT5668_I2S_M_D_16 (0x7 << 8)
+#define RT5668_I2S_M_D_24 (0x8 << 8)
+#define RT5668_I2S_M_D_32 (0x9 << 8)
+#define RT5668_I2S_M_D_48 (0x10 << 8)
+#define RT5668_I2S_CLK_SRC_MASK (0x7 << 4)
+#define RT5668_I2S_CLK_SRC_SFT 4
+#define RT5668_I2S_CLK_SRC_MCLK (0x0 << 4)
+#define RT5668_I2S_CLK_SRC_PLL1 (0x1 << 4)
+#define RT5668_I2S_CLK_SRC_PLL2 (0x2 << 4)
+#define RT5668_I2S_CLK_SRC_SDW (0x3 << 4)
+#define RT5668_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */
+#define RT5668_DAC_OSR_MASK (0xf << 0)
+#define RT5668_DAC_OSR_SFT 0
+#define RT5668_DAC_OSR_D_1 (0x0 << 0)
+#define RT5668_DAC_OSR_D_2 (0x1 << 0)
+#define RT5668_DAC_OSR_D_4 (0x2 << 0)
+#define RT5668_DAC_OSR_D_6 (0x3 << 0)
+#define RT5668_DAC_OSR_D_8 (0x4 << 0)
+#define RT5668_DAC_OSR_D_12 (0x5 << 0)
+#define RT5668_DAC_OSR_D_16 (0x6 << 0)
+#define RT5668_DAC_OSR_D_24 (0x7 << 0)
+#define RT5668_DAC_OSR_D_32 (0x8 << 0)
+#define RT5668_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5668_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5668_I2S2_BCLK_MS2_SFT 11
+#define RT5668_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5668_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5668_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5668_TDM_TX_CH_2 (0x0 << 12)
+#define RT5668_TDM_TX_CH_4 (0x1 << 12)
+#define RT5668_TDM_TX_CH_6 (0x2 << 12)
+#define RT5668_TDM_TX_CH_8 (0x3 << 12)
+#define RT5668_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5668_TDM_RX_CH_2 (0x0 << 8)
+#define RT5668_TDM_RX_CH_4 (0x1 << 8)
+#define RT5668_TDM_RX_CH_6 (0x2 << 8)
+#define RT5668_TDM_RX_CH_8 (0x3 << 8)
+#define RT5668_TDM_ADC_LCA_MASK (0xf << 4)
+#define RT5668_TDM_ADC_LCA_SFT 4
+#define RT5668_TDM_ADC_DL_SFT 0
+
+/* TDM control 3 (0x007a) */
+#define RT5668_IF1_ADC1_SEL_SFT 14
+#define RT5668_IF1_ADC2_SEL_SFT 12
+#define RT5668_IF1_ADC3_SEL_SFT 10
+#define RT5668_IF1_ADC4_SEL_SFT 8
+#define RT5668_TDM_ADC_SEL_SFT 4
+
+/* TDM/I2S control (0x007e) */
+#define RT5668_TDM_S_BP_MASK (0x1 << 15)
+#define RT5668_TDM_S_BP_SFT 15
+#define RT5668_TDM_S_BP_NOR (0x0 << 15)
+#define RT5668_TDM_S_BP_INV (0x1 << 15)
+#define RT5668_TDM_S_LP_MASK (0x1 << 14)
+#define RT5668_TDM_S_LP_SFT 14
+#define RT5668_TDM_S_LP_NOR (0x0 << 14)
+#define RT5668_TDM_S_LP_INV (0x1 << 14)
+#define RT5668_TDM_DF_MASK (0x7 << 11)
+#define RT5668_TDM_DF_SFT 11
+#define RT5668_TDM_DF_I2S (0x0 << 11)
+#define RT5668_TDM_DF_LEFT (0x1 << 11)
+#define RT5668_TDM_DF_PCM_A (0x2 << 11)
+#define RT5668_TDM_DF_PCM_B (0x3 << 11)
+#define RT5668_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5668_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5668_TDM_CL_MASK (0x3 << 4)
+#define RT5668_TDM_CL_16 (0x0 << 4)
+#define RT5668_TDM_CL_20 (0x1 << 4)
+#define RT5668_TDM_CL_24 (0x2 << 4)
+#define RT5668_TDM_CL_32 (0x3 << 4)
+#define RT5668_TDM_M_BP_MASK (0x1 << 2)
+#define RT5668_TDM_M_BP_SFT 2
+#define RT5668_TDM_M_BP_NOR (0x0 << 2)
+#define RT5668_TDM_M_BP_INV (0x1 << 2)
+#define RT5668_TDM_M_LP_MASK (0x1 << 1)
+#define RT5668_TDM_M_LP_SFT 1
+#define RT5668_TDM_M_LP_NOR (0x0 << 1)
+#define RT5668_TDM_M_LP_INV (0x1 << 1)
+#define RT5668_TDM_MS_MASK (0x1 << 0)
+#define RT5668_TDM_MS_SFT 0
+#define RT5668_TDM_MS_M (0x0 << 0)
+#define RT5668_TDM_MS_S (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5668_SCLK_SRC_MASK (0x7 << 13)
+#define RT5668_SCLK_SRC_SFT 13
+#define RT5668_SCLK_SRC_MCLK (0x0 << 13)
+#define RT5668_SCLK_SRC_PLL1 (0x1 << 13)
+#define RT5668_SCLK_SRC_PLL2 (0x2 << 13)
+#define RT5668_SCLK_SRC_SDW (0x3 << 13)
+#define RT5668_SCLK_SRC_RCCLK (0x4 << 13)
+#define RT5668_PLL1_SRC_MASK (0x3 << 10)
+#define RT5668_PLL1_SRC_SFT 10
+#define RT5668_PLL1_SRC_MCLK (0x0 << 10)
+#define RT5668_PLL1_SRC_BCLK1 (0x1 << 10)
+#define RT5668_PLL1_SRC_SDW (0x2 << 10)
+#define RT5668_PLL1_SRC_RC (0x3 << 10)
+#define RT5668_PLL2_SRC_MASK (0x3 << 8)
+#define RT5668_PLL2_SRC_SFT 8
+#define RT5668_PLL2_SRC_MCLK (0x0 << 8)
+#define RT5668_PLL2_SRC_BCLK1 (0x1 << 8)
+#define RT5668_PLL2_SRC_SDW (0x2 << 8)
+#define RT5668_PLL2_SRC_RC (0x3 << 8)
+
+
+
+#define RT5668_PLL_INP_MAX 40000000
+#define RT5668_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5668_PLL_N_MAX 0x001ff
+#define RT5668_PLL_N_MASK (RT5668_PLL_N_MAX << 7)
+#define RT5668_PLL_N_SFT 7
+#define RT5668_PLL_K_MAX 0x001f
+#define RT5668_PLL_K_MASK (RT5668_PLL_K_MAX)
+#define RT5668_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5668_PLL_M_MAX 0x00f
+#define RT5668_PLL_M_MASK (RT5668_PLL_M_MAX << 12)
+#define RT5668_PLL_M_SFT 12
+#define RT5668_PLL_M_BP (0x1 << 11)
+#define RT5668_PLL_M_BP_SFT 11
+#define RT5668_PLL_K_BP (0x1 << 10)
+#define RT5668_PLL_K_BP_SFT 10
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5668_DA_ASRC_MASK (0x1 << 13)
+#define RT5668_DA_ASRC_SFT 13
+#define RT5668_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5668_DAC_STO1_ASRC_SFT 12
+#define RT5668_AD_ASRC_MASK (0x1 << 8)
+#define RT5668_AD_ASRC_SFT 8
+#define RT5668_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5668_AD_ASRC_SEL_SFT 4
+#define RT5668_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5668_DMIC_ASRC_SFT 3
+#define RT5668_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5668_ADC_STO1_ASRC_SFT 2
+#define RT5668_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5668_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5668_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5668_FILTER_CLK_SEL_SFT 12
+
+/* ASRC Control 4 (0x0086) */
+#define RT5668_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5668_ASRCIN_FTK_N1_SFT 14
+#define RT5668_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5668_ASRCIN_FTK_N2_SFT 12
+#define RT5668_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5668_ASRCIN_FTK_M1_SFT 8
+#define RT5668_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5668_ASRCIN_FTK_M2_SFT 4
+
+/* SoundWire reference clk (0x008d) */
+#define RT5668_PLL2_OUT_MASK (0x1 << 8)
+#define RT5668_PLL2_OUT_98M (0x0 << 8)
+#define RT5668_PLL2_OUT_49M (0x1 << 8)
+#define RT5668_SDW_REF_2_MASK (0xf << 4)
+#define RT5668_SDW_REF_2_SFT 4
+#define RT5668_SDW_REF_2_48K (0x0 << 4)
+#define RT5668_SDW_REF_2_96K (0x1 << 4)
+#define RT5668_SDW_REF_2_192K (0x2 << 4)
+#define RT5668_SDW_REF_2_32K (0x3 << 4)
+#define RT5668_SDW_REF_2_24K (0x4 << 4)
+#define RT5668_SDW_REF_2_16K (0x5 << 4)
+#define RT5668_SDW_REF_2_12K (0x6 << 4)
+#define RT5668_SDW_REF_2_8K (0x7 << 4)
+#define RT5668_SDW_REF_2_44K (0x8 << 4)
+#define RT5668_SDW_REF_2_88K (0x9 << 4)
+#define RT5668_SDW_REF_2_176K (0xa << 4)
+#define RT5668_SDW_REF_2_353K (0xb << 4)
+#define RT5668_SDW_REF_2_22K (0xc << 4)
+#define RT5668_SDW_REF_2_384K (0xd << 4)
+#define RT5668_SDW_REF_2_11K (0xe << 4)
+#define RT5668_SDW_REF_1_MASK (0xf << 0)
+#define RT5668_SDW_REF_1_SFT 0
+#define RT5668_SDW_REF_1_48K (0x0 << 0)
+#define RT5668_SDW_REF_1_96K (0x1 << 0)
+#define RT5668_SDW_REF_1_192K (0x2 << 0)
+#define RT5668_SDW_REF_1_32K (0x3 << 0)
+#define RT5668_SDW_REF_1_24K (0x4 << 0)
+#define RT5668_SDW_REF_1_16K (0x5 << 0)
+#define RT5668_SDW_REF_1_12K (0x6 << 0)
+#define RT5668_SDW_REF_1_8K (0x7 << 0)
+#define RT5668_SDW_REF_1_44K (0x8 << 0)
+#define RT5668_SDW_REF_1_88K (0x9 << 0)
+#define RT5668_SDW_REF_1_176K (0xa << 0)
+#define RT5668_SDW_REF_1_353K (0xb << 0)
+#define RT5668_SDW_REF_1_22K (0xc << 0)
+#define RT5668_SDW_REF_1_384K (0xd << 0)
+#define RT5668_SDW_REF_1_11K (0xe << 0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5668_PUMP_EN (0x1 << 3)
+#define RT5668_PUMP_EN_SFT 3
+#define RT5668_CAPLESS_EN (0x1 << 0)
+#define RT5668_CAPLESS_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5668_RAMP_MASK (0x1 << 12)
+#define RT5668_RAMP_SFT 12
+#define RT5668_RAMP_DIS (0x0 << 12)
+#define RT5668_RAMP_EN (0x1 << 12)
+#define RT5668_BPS_MASK (0x1 << 11)
+#define RT5668_BPS_SFT 11
+#define RT5668_BPS_DIS (0x0 << 11)
+#define RT5668_BPS_EN (0x1 << 11)
+#define RT5668_FAST_UPDN_MASK (0x1 << 10)
+#define RT5668_FAST_UPDN_SFT 10
+#define RT5668_FAST_UPDN_DIS (0x0 << 10)
+#define RT5668_FAST_UPDN_EN (0x1 << 10)
+#define RT5668_VLO_MASK (0x1 << 7)
+#define RT5668_VLO_SFT 7
+#define RT5668_VLO_3V (0x0 << 7)
+#define RT5668_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5668_OSW_L_MASK (0x1 << 11)
+#define RT5668_OSW_L_SFT 11
+#define RT5668_OSW_L_DIS (0x0 << 11)
+#define RT5668_OSW_L_EN (0x1 << 11)
+#define RT5668_OSW_R_MASK (0x1 << 10)
+#define RT5668_OSW_R_SFT 10
+#define RT5668_OSW_R_DIS (0x0 << 10)
+#define RT5668_OSW_R_EN (0x1 << 10)
+#define RT5668_PM_HP_MASK (0x3 << 8)
+#define RT5668_PM_HP_SFT 8
+#define RT5668_PM_HP_LV (0x0 << 8)
+#define RT5668_PM_HP_MV (0x1 << 8)
+#define RT5668_PM_HP_HV (0x2 << 8)
+#define RT5668_IB_HP_MASK (0x3 << 6)
+#define RT5668_IB_HP_SFT 6
+#define RT5668_IB_HP_125IL (0x0 << 6)
+#define RT5668_IB_HP_25IL (0x1 << 6)
+#define RT5668_IB_HP_5IL (0x2 << 6)
+#define RT5668_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control1 (0x93) */
+#define RT5668_MIC1_OV_MASK (0x3 << 14)
+#define RT5668_MIC1_OV_SFT 14
+#define RT5668_MIC1_OV_2V7 (0x0 << 14)
+#define RT5668_MIC1_OV_2V4 (0x1 << 14)
+#define RT5668_MIC1_OV_2V25 (0x3 << 14)
+#define RT5668_MIC1_OV_1V8 (0x4 << 14)
+#define RT5668_MIC1_CLK_MASK (0x1 << 13)
+#define RT5668_MIC1_CLK_SFT 13
+#define RT5668_MIC1_CLK_DIS (0x0 << 13)
+#define RT5668_MIC1_CLK_EN (0x1 << 13)
+#define RT5668_MIC1_OVCD_MASK (0x1 << 12)
+#define RT5668_MIC1_OVCD_SFT 12
+#define RT5668_MIC1_OVCD_DIS (0x0 << 12)
+#define RT5668_MIC1_OVCD_EN (0x1 << 12)
+#define RT5668_MIC1_OVTH_MASK (0x3 << 10)
+#define RT5668_MIC1_OVTH_SFT 10
+#define RT5668_MIC1_OVTH_768UA (0x0 << 10)
+#define RT5668_MIC1_OVTH_960UA (0x1 << 10)
+#define RT5668_MIC1_OVTH_1152UA (0x2 << 10)
+#define RT5668_MIC1_OVTH_1960UA (0x3 << 10)
+#define RT5668_MIC2_OV_MASK (0x3 << 8)
+#define RT5668_MIC2_OV_SFT 8
+#define RT5668_MIC2_OV_2V7 (0x0 << 8)
+#define RT5668_MIC2_OV_2V4 (0x1 << 8)
+#define RT5668_MIC2_OV_2V25 (0x3 << 8)
+#define RT5668_MIC2_OV_1V8 (0x4 << 8)
+#define RT5668_MIC2_CLK_MASK (0x1 << 7)
+#define RT5668_MIC2_CLK_SFT 7
+#define RT5668_MIC2_CLK_DIS (0x0 << 7)
+#define RT5668_MIC2_CLK_EN (0x1 << 7)
+#define RT5668_MIC2_OVTH_MASK (0x3 << 4)
+#define RT5668_MIC2_OVTH_SFT 4
+#define RT5668_MIC2_OVTH_768UA (0x0 << 4)
+#define RT5668_MIC2_OVTH_960UA (0x1 << 4)
+#define RT5668_MIC2_OVTH_1152UA (0x2 << 4)
+#define RT5668_MIC2_OVTH_1960UA (0x3 << 4)
+#define RT5668_PWR_MB_MASK (0x1 << 3)
+#define RT5668_PWR_MB_SFT 3
+#define RT5668_PWR_MB_PD (0x0 << 3)
+#define RT5668_PWR_MB_PU (0x1 << 3)
+
+/* Micbias Control2 (0x0094) */
+#define RT5668_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5668_PWR_CLK25M_SFT 9
+#define RT5668_PWR_CLK25M_PD (0x0 << 9)
+#define RT5668_PWR_CLK25M_PU (0x1 << 9)
+#define RT5668_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5668_PWR_CLK1M_SFT 8
+#define RT5668_PWR_CLK1M_PD (0x0 << 8)
+#define RT5668_PWR_CLK1M_PU (0x1 << 8)
+
+/* RC Clock Control (0x009f) */
+#define RT5668_POW_IRQ (0x1 << 15)
+#define RT5668_POW_JDH (0x1 << 14)
+#define RT5668_POW_JDL (0x1 << 13)
+#define RT5668_POW_ANA (0x1 << 12)
+
+/* I2S Master Mode Clock Control 1 (0x00a0) */
+#define RT5668_CLK_SRC_MCLK (0x0)
+#define RT5668_CLK_SRC_PLL1 (0x1)
+#define RT5668_CLK_SRC_PLL2 (0x2)
+#define RT5668_CLK_SRC_SDW (0x3)
+#define RT5668_CLK_SRC_RCCLK (0x4)
+#define RT5668_I2S_PD_1 (0x0)
+#define RT5668_I2S_PD_2 (0x1)
+#define RT5668_I2S_PD_3 (0x2)
+#define RT5668_I2S_PD_4 (0x3)
+#define RT5668_I2S_PD_6 (0x4)
+#define RT5668_I2S_PD_8 (0x5)
+#define RT5668_I2S_PD_12 (0x6)
+#define RT5668_I2S_PD_16 (0x7)
+#define RT5668_I2S_PD_24 (0x8)
+#define RT5668_I2S_PD_32 (0x9)
+#define RT5668_I2S_PD_48 (0xa)
+#define RT5668_I2S2_SRC_MASK (0x3 << 4)
+#define RT5668_I2S2_SRC_SFT 4
+#define RT5668_I2S2_M_PD_MASK (0xf << 0)
+#define RT5668_I2S2_M_PD_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5668_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5668_JD1_PULSE_EN_SFT 10
+#define RT5668_JD1_PULSE_DIS (0x0 << 10)
+#define RT5668_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5668_JD1_EN_MASK (0x1 << 15)
+#define RT5668_JD1_EN_SFT 15
+#define RT5668_JD1_DIS (0x0 << 15)
+#define RT5668_JD1_EN (0x1 << 15)
+#define RT5668_JD1_POL_MASK (0x1 << 13)
+#define RT5668_JD1_POL_NOR (0x0 << 13)
+#define RT5668_JD1_POL_INV (0x1 << 13)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5668_IL_IRQ_MASK (0x1 << 7)
+#define RT5668_IL_IRQ_DIS (0x0 << 7)
+#define RT5668_IL_IRQ_EN (0x1 << 7)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5668_GP1_PIN_MASK (0x3 << 14)
+#define RT5668_GP1_PIN_SFT 14
+#define RT5668_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5668_GP1_PIN_IRQ (0x1 << 14)
+#define RT5668_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5668_GP2_PIN_MASK (0x3 << 12)
+#define RT5668_GP2_PIN_SFT 12
+#define RT5668_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5668_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5668_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5668_GP3_PIN_MASK (0x3 << 10)
+#define RT5668_GP3_PIN_SFT 10
+#define RT5668_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5668_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5668_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5668_GP4_PIN_MASK (0x3 << 8)
+#define RT5668_GP4_PIN_SFT 8
+#define RT5668_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5668_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5668_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5668_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5668_GP5_PIN_MASK (0x3 << 6)
+#define RT5668_GP5_PIN_SFT 6
+#define RT5668_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5668_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5668_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5668_GP6_PIN_MASK (0x1 << 5)
+#define RT5668_GP6_PIN_SFT 5
+#define RT5668_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5668_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5668_GP1_PF_MASK (0x1 << 15)
+#define RT5668_GP1_PF_IN (0x0 << 15)
+#define RT5668_GP1_PF_OUT (0x1 << 15)
+#define RT5668_GP1_OUT_MASK (0x1 << 14)
+#define RT5668_GP1_OUT_L (0x0 << 14)
+#define RT5668_GP1_OUT_H (0x1 << 14)
+#define RT5668_GP2_PF_MASK (0x1 << 13)
+#define RT5668_GP2_PF_IN (0x0 << 13)
+#define RT5668_GP2_PF_OUT (0x1 << 13)
+#define RT5668_GP2_OUT_MASK (0x1 << 12)
+#define RT5668_GP2_OUT_L (0x0 << 12)
+#define RT5668_GP2_OUT_H (0x1 << 12)
+#define RT5668_GP3_PF_MASK (0x1 << 11)
+#define RT5668_GP3_PF_IN (0x0 << 11)
+#define RT5668_GP3_PF_OUT (0x1 << 11)
+#define RT5668_GP3_OUT_MASK (0x1 << 10)
+#define RT5668_GP3_OUT_L (0x0 << 10)
+#define RT5668_GP3_OUT_H (0x1 << 10)
+#define RT5668_GP4_PF_MASK (0x1 << 9)
+#define RT5668_GP4_PF_IN (0x0 << 9)
+#define RT5668_GP4_PF_OUT (0x1 << 9)
+#define RT5668_GP4_OUT_MASK (0x1 << 8)
+#define RT5668_GP4_OUT_L (0x0 << 8)
+#define RT5668_GP4_OUT_H (0x1 << 8)
+#define RT5668_GP5_PF_MASK (0x1 << 7)
+#define RT5668_GP5_PF_IN (0x0 << 7)
+#define RT5668_GP5_PF_OUT (0x1 << 7)
+#define RT5668_GP5_OUT_MASK (0x1 << 6)
+#define RT5668_GP5_OUT_L (0x0 << 6)
+#define RT5668_GP5_OUT_H (0x1 << 6)
+#define RT5668_GP6_PF_MASK (0x1 << 5)
+#define RT5668_GP6_PF_IN (0x0 << 5)
+#define RT5668_GP6_PF_OUT (0x1 << 5)
+#define RT5668_GP6_OUT_MASK (0x1 << 4)
+#define RT5668_GP6_OUT_L (0x0 << 4)
+#define RT5668_GP6_OUT_H (0x1 << 4)
+
+
+/* GPIO Status (0x00c2) */
+#define RT5668_GP6_STA (0x1 << 6)
+#define RT5668_GP5_STA (0x1 << 5)
+#define RT5668_GP4_STA (0x1 << 4)
+#define RT5668_GP3_STA (0x1 << 3)
+#define RT5668_GP2_STA (0x1 << 2)
+#define RT5668_GP1_STA (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5668_SV_MASK (0x1 << 15)
+#define RT5668_SV_SFT 15
+#define RT5668_SV_DIS (0x0 << 15)
+#define RT5668_SV_EN (0x1 << 15)
+#define RT5668_ZCD_MASK (0x1 << 10)
+#define RT5668_ZCD_SFT 10
+#define RT5668_ZCD_PD (0x0 << 10)
+#define RT5668_ZCD_PU (0x1 << 10)
+#define RT5668_SV_DLY_MASK (0xf)
+#define RT5668_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0x00da) */
+#define RT5668_ZCD_BST1_CBJ_MASK (0x1 << 7)
+#define RT5668_ZCD_BST1_CBJ_SFT 7
+#define RT5668_ZCD_BST1_CBJ_DIS (0x0 << 7)
+#define RT5668_ZCD_BST1_CBJ_EN (0x1 << 7)
+#define RT5668_ZCD_RECMIX_MASK (0x1)
+#define RT5668_ZCD_RECMIX_SFT 0
+#define RT5668_ZCD_RECMIX_DIS (0x0)
+#define RT5668_ZCD_RECMIX_EN (0x1)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5668_4BTN_IL_MASK (0x1 << 15)
+#define RT5668_4BTN_IL_EN (0x1 << 15)
+#define RT5668_4BTN_IL_DIS (0x0 << 15)
+#define RT5668_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5668_4BTN_IL_NOR (0x1 << 14)
+#define RT5668_4BTN_IL_RST (0x0 << 14)
+
+/* Analog JD Control (0x00f0) */
+#define RT5668_JDH_RS_MASK (0x1 << 4)
+#define RT5668_JDH_NO_PLUG (0x1 << 4)
+#define RT5668_JDH_PLUG (0x0 << 4)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5668_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5668_CKXEN_DAC1_SFT 13
+#define RT5668_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5668_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5668_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5668_CKXEN_ADC1_SFT 13
+#define RT5668_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5668_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5668_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5668_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5668_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5668_AD2DA_LB_MASK (0x1 << 10)
+#define RT5668_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5668_NG2_EN_MASK (0x1 << 15)
+#define RT5668_NG2_EN (0x1 << 15)
+#define RT5668_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5668_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5668_DEB_80_MS (0x0 << 4)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5668_SAR_BUTT_DET_MASK (0x1 << 15)
+#define RT5668_SAR_BUTT_DET_EN (0x1 << 15)
+#define RT5668_SAR_BUTT_DET_DIS (0x0 << 15)
+#define RT5668_SAR_BUTDET_MODE_MASK (0x1 << 14)
+#define RT5668_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5668_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5668_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5668_SAR_BUTDET_RST_NORMAL (0x1 << 13)
+#define RT5668_SAR_BUTDET_RST (0x0 << 13)
+#define RT5668_SAR_POW_MASK (0x1 << 12)
+#define RT5668_SAR_POW_EN (0x1 << 12)
+#define RT5668_SAR_POW_DIS (0x0 << 12)
+#define RT5668_SAR_RST_MASK (0x1 << 11)
+#define RT5668_SAR_RST_NORMAL (0x1 << 11)
+#define RT5668_SAR_RST (0x0 << 11)
+#define RT5668_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5668_SAR_BYPASS_EN (0x1 << 10)
+#define RT5668_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5668_SAR_SEL_MB1_MASK (0x1 << 9)
+#define RT5668_SAR_SEL_MB1_SEL (0x1 << 9)
+#define RT5668_SAR_SEL_MB1_NOSEL (0x0 << 9)
+#define RT5668_SAR_SEL_MB2_MASK (0x1 << 8)
+#define RT5668_SAR_SEL_MB2_SEL (0x1 << 8)
+#define RT5668_SAR_SEL_MB2_NOSEL (0x0 << 8)
+#define RT5668_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5668_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5668_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5668_SAR_SEL_MB1_MB2_MASK (0x1 << 5)
+#define RT5668_SAR_SEL_MB1_MB2_AUTO (0x1 << 5)
+#define RT5668_SAR_SEL_MB1_MB2_MANU (0x0 << 5)
+#define RT5668_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5668_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5668_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5668_SAR_SOUR_MASK (0x3f)
+#define RT5668_SAR_SOUR_BTN (0x3f)
+#define RT5668_SAR_SOUR_TYPE (0x0)
+
+
+/* System Clock Source */
+enum {
+ RT5668_SCLK_S_MCLK,
+ RT5668_SCLK_S_PLL1,
+ RT5668_SCLK_S_PLL2,
+ RT5668_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5668_PLL1_S_MCLK,
+ RT5668_PLL1_S_BCLK1,
+ RT5668_PLL1_S_RCCLK,
+};
+
+enum {
+ RT5668_AIF1,
+ RT5668_AIF2,
+ RT5668_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5668_DA_STEREO1_FILTER = 0x1,
+ RT5668_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5668_CLK_SEL_SYS,
+ RT5668_CLK_SEL_I2S1_ASRC,
+ RT5668_CLK_SEL_I2S2_ASRC,
+};
+
+#endif /* __RT5668_H__ */
diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h
index a34d0cdb8198..a07b7dfcf501 100644
--- a/sound/soc/codecs/rt5670-dsp.h
+++ b/sound/soc/codecs/rt5670-dsp.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670-dsp.h -- RT5670 ALSA SoC DSP driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT5670_DSP_H__
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index c5094b4399e2..efd26082f19a 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5670.c -- RT5670 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@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.
*/
#include <linux/module.h>
@@ -28,24 +25,24 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/rt5670.h>
#include "rl6231.h"
#include "rt5670.h"
#include "rt5670-dsp.h"
-#define RT5670_DEV_GPIO BIT(0)
-#define RT5670_IN2_DIFF BIT(1)
-#define RT5670_DMIC_EN BIT(2)
-#define RT5670_DMIC1_IN2P BIT(3)
-#define RT5670_DMIC1_GPIO6 BIT(4)
-#define RT5670_DMIC1_GPIO7 BIT(5)
-#define RT5670_DMIC2_INR BIT(6)
-#define RT5670_DMIC2_GPIO8 BIT(7)
-#define RT5670_DMIC3_GPIO5 BIT(8)
-#define RT5670_JD_MODE1 BIT(9)
-#define RT5670_JD_MODE2 BIT(10)
-#define RT5670_JD_MODE3 BIT(11)
+#define RT5670_GPIO1_IS_IRQ BIT(0)
+#define RT5670_IN2_DIFF BIT(1)
+#define RT5670_DMIC_EN BIT(2)
+#define RT5670_DMIC1_IN2P BIT(3)
+#define RT5670_DMIC1_GPIO6 BIT(4)
+#define RT5670_DMIC1_GPIO7 BIT(5)
+#define RT5670_DMIC2_INR BIT(6)
+#define RT5670_DMIC2_GPIO8 BIT(7)
+#define RT5670_DMIC3_GPIO5 BIT(8)
+#define RT5670_JD_MODE1 BIT(9)
+#define RT5670_JD_MODE2 BIT(10)
+#define RT5670_JD_MODE3 BIT(11)
+#define RT5670_GPIO1_IS_EXT_SPK_EN BIT(12)
static unsigned long rt5670_quirk;
static unsigned int quirk_override;
@@ -71,7 +68,7 @@ static const struct regmap_range_cfg rt5670_ranges[] = {
static const struct reg_sequence init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
- { RT5670_PR_BASE + 0x38, 0x3ba1 },
+ { RT5670_PR_BASE + 0x38, 0x1fe1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
{ 0x8a, 0x0123 },
};
@@ -423,7 +420,7 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
/**
* rt5670_headset_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
@@ -431,46 +428,46 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
* Returns detect status.
*/
-static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5670_headset_detect(struct snd_soc_component *component, int jack_insert)
{
int val;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (jack_insert) {
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x0);
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
RT5670_CBJ_MN_JD);
- snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
- snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
+ snd_soc_component_write(component, RT5670_GPIO_CTRL2, 0x0004);
+ snd_soc_component_update_bits(component, RT5670_GPIO_CTRL1,
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL1,
RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN);
- snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ snd_soc_component_write(component, RT5670_JD_CTRL3, 0x00f0);
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, 0);
msleep(300);
- val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
+ val = snd_soc_component_read(component, RT5670_CJ_CTRL3) & 0x7;
if (val == 0x1 || val == 0x2) {
rt5670->jack_type = SND_JACK_HEADSET;
/* for push button */
- snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8);
- snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40);
- snd_soc_read(codec, RT5670_IL_CMD);
+ snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x8);
+ snd_soc_component_update_bits(component, RT5670_IL_CMD, 0x40, 0x40);
+ snd_soc_component_read(component, RT5670_IL_CMD);
} else {
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = SND_JACK_HEADPHONE;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
}
} else {
- snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = 0;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
@@ -479,35 +476,35 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
return rt5670->jack_type;
}
-void rt5670_jack_suspend(struct snd_soc_codec *codec)
+void rt5670_jack_suspend(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
rt5670->jack_type_saved = rt5670->jack_type;
- rt5670_headset_detect(codec, 0);
+ rt5670_headset_detect(component, 0);
}
EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
-void rt5670_jack_resume(struct snd_soc_codec *codec)
+void rt5670_jack_resume(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->jack_type_saved)
- rt5670_headset_detect(codec, 1);
+ rt5670_headset_detect(component, 1);
}
EXPORT_SYMBOL_GPL(rt5670_jack_resume);
-static int rt5670_button_detect(struct snd_soc_codec *codec)
+static int rt5670_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5670_IL_CMD);
+ val = snd_soc_component_read(component, RT5670_IL_CMD);
btn_type = val & 0xff80;
- snd_soc_write(codec, RT5670_IL_CMD, val);
+ snd_soc_component_write(component, RT5670_IL_CMD, val);
if (btn_type != 0) {
msleep(20);
- val = snd_soc_read(codec, RT5670_IL_CMD);
- snd_soc_write(codec, RT5670_IL_CMD, val);
+ val = snd_soc_component_read(component, RT5670_IL_CMD);
+ snd_soc_component_write(component, RT5670_IL_CMD, val);
}
return btn_type;
@@ -520,26 +517,26 @@ static int rt5670_irq_detection(void *data)
struct snd_soc_jack *jack = rt5670->jack;
int val, btn_type, report = jack->status;
- if (rt5670->pdata.jd_mode == 1) /* 2 port */
- val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070;
+ if (rt5670->jd_mode == 1) /* 2 port */
+ val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0070;
else
- val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020;
+ val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0020;
switch (val) {
/* jack in */
case 0x30: /* 2 port */
case 0x0: /* 1 port or 2 port */
if (rt5670->jack_type == 0) {
- report = rt5670_headset_detect(rt5670->codec, 1);
+ report = rt5670_headset_detect(rt5670->component, 1);
/* for push button and jack out */
gpio->debounce_time = 25;
break;
}
btn_type = 0;
- if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) {
+ if (snd_soc_component_read(rt5670->component, RT5670_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
- btn_type = rt5670_button_detect(rt5670->codec);
+ btn_type = rt5670_button_detect(rt5670->component);
switch (btn_type) {
case 0x2000: /* up */
report |= SND_JACK_BTN_1;
@@ -551,7 +548,7 @@ static int rt5670_irq_detection(void *data)
report |= SND_JACK_BTN_2;
break;
default:
- dev_err(rt5670->codec->dev,
+ dev_err(rt5670->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -566,8 +563,8 @@ static int rt5670_irq_detection(void *data)
case 0x10: /* 2 port */
case 0x20: /* 1 port */
report = 0;
- snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0);
- rt5670_headset_detect(rt5670->codec, 0);
+ snd_soc_component_update_bits(rt5670->component, RT5670_INT_IRQ_ST, 0x1, 0x0);
+ rt5670_headset_detect(rt5670->component, 0);
gpio->debounce_time = 150; /* for jack in */
break;
default:
@@ -577,14 +574,14 @@ static int rt5670_irq_detection(void *data)
return report;
}
-int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+int rt5670_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
int ret;
rt5670->jack = jack;
- rt5670->hp_gpio.gpiod_dev = codec->dev;
+ rt5670->hp_gpio.gpiod_dev = component->dev;
rt5670->hp_gpio.name = "headset";
rt5670->hp_gpio.report = SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2;
@@ -596,7 +593,7 @@ int rt5670_set_jack_detect(struct snd_soc_codec *codec,
ret = snd_soc_jack_add_gpios(rt5670->jack, 1,
&rt5670->hp_gpio);
if (ret) {
- dev_err(codec->dev, "Adding jack GPIO failed\n");
+ dev_err(component->dev, "Adding jack GPIO failed\n");
return ret;
}
@@ -605,9 +602,9 @@ int rt5670_set_jack_detect(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(rt5670_set_jack_detect);
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_MINMAX(dac_vol_tlv, -6562, 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_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 */
@@ -632,21 +629,69 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA,
static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_enum, RT5670_DIG_INF1_DATA,
RT5670_IF2_ADC_SEL_SFT, rt5670_data_select);
+/*
+ * For reliable output-mute LED control we need a "DAC1 Playback Switch" control.
+ * We emulate this by only clearing the RT5670_M_DAC1_L/_R AD_DA_MIXER register
+ * bits when both our emulated DAC1 Playback Switch control and the DAC1 MIXL/R
+ * DAPM-mixer DAC1 input are enabled.
+ */
+static void rt5670_update_ad_da_mixer_dac1_m_bits(struct rt5670_priv *rt5670)
+{
+ int val = RT5670_M_DAC1_L | RT5670_M_DAC1_R;
+
+ if (rt5670->dac1_mixl_dac1_switch && rt5670->dac1_playback_switch_l)
+ val &= ~RT5670_M_DAC1_L;
+
+ if (rt5670->dac1_mixr_dac1_switch && rt5670->dac1_playback_switch_r)
+ val &= ~RT5670_M_DAC1_R;
+
+ regmap_update_bits(rt5670->regmap, RT5670_AD_DA_MIXER,
+ RT5670_M_DAC1_L | RT5670_M_DAC1_R, val);
+}
+
+static int rt5670_dac1_playback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5670->dac1_playback_switch_l;
+ ucontrol->value.integer.value[1] = rt5670->dac1_playback_switch_r;
+
+ return 0;
+}
+
+static int rt5670_dac1_playback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ if (rt5670->dac1_playback_switch_l == ucontrol->value.integer.value[0] &&
+ rt5670->dac1_playback_switch_r == ucontrol->value.integer.value[1])
+ return 0;
+
+ rt5670->dac1_playback_switch_l = ucontrol->value.integer.value[0];
+ rt5670->dac1_playback_switch_r = ucontrol->value.integer.value[1];
+
+ rt5670_update_ad_da_mixer_dac1_m_bits(rt5670);
+
+ return 1;
+}
+
static const struct snd_kcontrol_new rt5670_snd_controls[] = {
/* Headphone Output Volume */
- SOC_DOUBLE("HP Playback Switch", RT5670_HP_VOL,
- RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
39, 1, out_vol_tlv),
/* OUTPUT Control */
- SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1,
- RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("OUT Playback Volume", RT5670_LOUT1,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, 39, 1, out_vol_tlv),
/* DAC Digital Volume */
SOC_DOUBLE("DAC2 Playback Switch", RT5670_DAC_CTRL,
RT5670_M_DAC_L2_VOL_SFT, RT5670_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_EXT("DAC1 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt5670_dac1_playback_switch_get, rt5670_dac1_playback_switch_put),
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5670_DAC1_DIG_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
175, 0, dac_vol_tlv),
@@ -699,17 +744,17 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5670->sysclk / rl6231_get_pre_div(rt5670->regmap,
RT5670_ADDA_CLK1, RT5670_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5670_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5670_DMIC_CTRL1,
RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT);
return idx;
}
@@ -717,8 +762,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
return 1;
@@ -729,7 +774,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -765,7 +810,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -781,8 +826,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
return 1;
@@ -793,7 +838,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
/**
* rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -805,7 +850,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
* 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 rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5670_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
unsigned int asrc2_mask = 0, asrc2_value = 0;
@@ -863,11 +908,11 @@ int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_2,
+ snd_soc_component_update_bits(component, RT5670_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_3,
+ snd_soc_component_update_bits(component, RT5670_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
}
@@ -916,18 +961,44 @@ static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = {
RT5670_M_MONO_ADC_R2_SFT, 1, 1),
};
+/* See comment above rt5670_update_ad_da_mixer_dac1_m_bits() */
+static int rt5670_put_dac1_mix_dac1_switch(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_dapm_kcontrol_component(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (mc->shift == 0)
+ rt5670->dac1_mixl_dac1_switch = ucontrol->value.integer.value[0];
+ else
+ rt5670->dac1_mixr_dac1_switch = ucontrol->value.integer.value[0];
+
+ /* Apply the update (if any) */
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ if (ret == 0)
+ return 0;
+
+ rt5670_update_ad_da_mixer_dac1_m_bits(rt5670);
+
+ return 1;
+}
+
+#define SOC_DAPM_SINGLE_RT5670_DAC1_SW(name, shift) \
+ SOC_SINGLE_EXT(name, SND_SOC_NOPM, shift, 1, 0, \
+ snd_soc_dapm_get_volsw, rt5670_put_dac1_mix_dac1_switch)
+
static const struct snd_kcontrol_new rt5670_dac_l_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER,
RT5670_M_ADCMIX_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 0),
};
static const struct snd_kcontrol_new rt5670_dac_r_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER,
RT5670_M_ADCMIX_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 1),
};
static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = {
@@ -1057,20 +1128,6 @@ static const struct snd_kcontrol_new rt5670_lout_mix[] = {
RT5670_M_OV_R_LM_SFT, 1, 1),
};
-static const struct snd_kcontrol_new rt5670_hpl_mix[] = {
- SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACL1_HML_SFT, 1, 1),
- SOC_DAPM_SINGLE("INL1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INL1_HML_SFT, 1, 1),
-};
-
-static const struct snd_kcontrol_new rt5670_hpr_mix[] = {
- SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACR1_HMR_SFT, 1, 1),
- SOC_DAPM_SINGLE("INR1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INR1_HMR_SFT, 1, 1),
-};
-
static const struct snd_kcontrol_new lout_l_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1,
RT5670_L_MUTE_SFT, 1, 1);
@@ -1196,24 +1253,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc2_enum, RT5670_STO2_ADC_MIXER,
static const struct snd_kcontrol_new rt5670_sto2_adc_2_mux =
SOC_DAPM_ENUM("Stereo2 ADC 2 Mux", rt5670_stereo2_adc2_enum);
-
-/* MX-27 MX26 [10] */
-static const char * const rt5670_stereo_adc_src[] = {
- "ADC1L ADC2R", "ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc_enum, RT5670_STO1_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_mux =
- SOC_DAPM_ENUM("Stereo1 ADC source", rt5670_stereo1_adc_enum);
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_enum, RT5670_STO2_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto2_adc_mux =
- SOC_DAPM_ENUM("Stereo2 ADC source", rt5670_stereo2_adc_enum);
-
/* MX-27 MX-26 [9:8] */
static const char * const rt5670_stereo_dmic_src[] = {
"DMIC1", "DMIC2", "DMIC3"
@@ -1231,17 +1270,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER,
static const struct snd_kcontrol_new rt5670_sto2_dmic_mux =
SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum);
-/* MX-27 [0] */
-static const char * const rt5670_stereo_dmic3_src[] = {
- "DMIC3", "PDM ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo_dmic3_enum, RT5670_STO1_ADC_MIXER,
- RT5670_DMIC3_SRC_SFT, rt5670_stereo_dmic3_src);
-
-static const struct snd_kcontrol_new rt5670_sto_dmic3_mux =
- SOC_DAPM_ENUM("Stereo DMIC3 source", rt5670_stereo_dmic3_enum);
-
/* Mono ADC source */
/* MX-28 [12] */
static const char * const rt5670_mono_adc_l1_src[] = {
@@ -1334,17 +1362,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA,
static const struct snd_kcontrol_new rt5670_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum);
-/* MX-30 [5:4] */
-static const char * const rt5670_if4_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "IF_ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_if4_adc_in_enum, RT5670_DIG_INF2_DATA,
- RT5670_IF4_ADC_IN_SFT, rt5670_if4_adc_in_src);
-
-static const struct snd_kcontrol_new rt5670_if4_adc_in_mux =
- SOC_DAPM_ENUM("IF4 ADC IN source", rt5670_if4_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5670_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1421,8 +1438,8 @@ static const struct snd_kcontrol_new rt5670_vad_adc_mux =
static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1457,8 +1474,8 @@ static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1504,19 +1521,46 @@ static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5670_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 rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ if (!rt5670->gpio1_is_ext_spk_en)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_HI);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_LO);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST1_P, RT5670_PWR_BST1_P);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST1_P, 0);
break;
@@ -1530,16 +1574,16 @@ static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5670_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST2_P, RT5670_PWR_BST2_P);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST2_P, 0);
break;
@@ -1686,12 +1730,10 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
RT5670_PWR_ADC_S1F_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5670_PWR_DIG2,
RT5670_PWR_ADC_S2F_BIT, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", RT5670_STO1_ADC_DIG_VOL,
- RT5670_L_MUTE_SFT, 1, rt5670_sto1_adc_l_mix,
- ARRAY_SIZE(rt5670_sto1_adc_l_mix)),
- SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", RT5670_STO1_ADC_DIG_VOL,
- RT5670_R_MUTE_SFT, 1, rt5670_sto1_adc_r_mix,
- ARRAY_SIZE(rt5670_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_sto1_adc_l_mix, ARRAY_SIZE(rt5670_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_sto1_adc_r_mix, ARRAY_SIZE(rt5670_sto1_adc_r_mix)),
SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
rt5670_sto2_adc_l_mix,
ARRAY_SIZE(rt5670_sto2_adc_l_mix)),
@@ -1917,7 +1959,9 @@ static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
- SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt5670_spk_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"),
SND_SOC_DAPM_OUTPUT("SPORP"),
@@ -2322,21 +2366,21 @@ static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
static int rt5670_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 rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
rt5670->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5670->sysclk, rt5670->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5670->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -2368,20 +2412,20 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK;
val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT |
pre_div << RT5670_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S1_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5670_AIF2:
mask_clk = RT5670_I2S_BCLK_MS2_MASK | RT5670_I2S_PD2_MASK;
val_clk = bclk_ms << RT5670_I2S_BCLK_MS2_SFT |
pre_div << RT5670_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5670_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S2_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2390,15 +2434,15 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5670->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5670_I2S_MS_S;
rt5670->master[dai->id] = 0;
break;
@@ -2434,26 +2478,26 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S1_SDP,
RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK |
RT5670_I2S_DF_MASK, reg_val);
break;
case RT5670_AIF2:
- snd_soc_update_bits(codec, RT5670_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S2_SDP,
RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK |
RT5670_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5670_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int rt5670_set_codec_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (clk_id) {
@@ -2467,16 +2511,16 @@ static int rt5670_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
reg_val |= RT5670_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, reg_val);
rt5670->sysclk = freq;
if (clk_id != RT5670_SCLK_S_RCCLK)
rt5670->sysclk_src = clk_id;
- dev_dbg(codec->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id);
+ dev_dbg(component->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id);
return 0;
}
@@ -2484,8 +2528,8 @@ static int rt5670_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -2494,18 +2538,18 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5670->pll_in = 0;
rt5670->pll_out = 0;
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5670_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK);
break;
case RT5670_PLL1_S_BCLK1:
@@ -2514,38 +2558,38 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
case RT5670_PLL1_S_BCLK4:
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1);
break;
case RT5670_AIF2:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5670_PLL_CTRL1,
+ snd_soc_component_write(component, RT5670_PLL_CTRL1,
pll_code.n_code << RT5670_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5670_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT |
- pll_code.m_bp << RT5670_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5670_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT) |
+ (pll_code.m_bp << RT5670_PLL_M_BP_SFT));
rt5670->pll_in = freq_in;
rt5670->pll_out = freq_out;
@@ -2557,7 +2601,7 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -2595,75 +2639,75 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_TDM_CTRL_1, 0x7c00, val);
+ snd_soc_component_update_bits(component, RT5670_TDM_CTRL_1, 0x7c00, val);
return 0;
}
static int rt5670_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (dai->id != RT5670_AIF1)
return 0;
if ((ratio % 50) == 0)
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3,
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3,
RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_50FS);
else
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3,
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3,
RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_NOR);
return 0;
}
-static int rt5670_set_bias_level(struct snd_soc_codec *codec,
+static int rt5670_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_FV1 | RT5670_PWR_FV2);
- snd_soc_update_bits(codec, RT5670_CHARGE_PUMP,
+ snd_soc_component_update_bits(component, RT5670_CHARGE_PUMP,
RT5670_OSW_L_MASK | RT5670_OSW_R_MASK,
RT5670_OSW_L_DIS | RT5670_OSW_R_DIS);
- snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x1);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_LDO_SEL_MASK, 0x5);
}
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_LDO_SEL_MASK, 0x3);
break;
case SND_SOC_BIAS_OFF:
- if (rt5670->pdata.jd_mode)
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ if (rt5670->jd_mode)
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_MB | RT5670_PWR_BG);
else
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+ snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x0);
break;
default:
@@ -2673,12 +2717,12 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5670_probe(struct snd_soc_codec *codec)
+static int rt5670_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
- switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+ switch (snd_soc_component_read(component, RT5670_RESET) & RT5670_ID_MASK) {
case RT5670_ID_5670:
case RT5670_ID_5671:
snd_soc_dapm_new_controls(dapm,
@@ -2697,37 +2741,36 @@ static int rt5670_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5672_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5670 RT5671 or RT5672 only\n");
return -ENODEV;
}
- rt5670->codec = codec;
+ rt5670->component = component;
return 0;
}
-static int rt5670_remove(struct snd_soc_codec *codec)
+static void rt5670_remove(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regmap_write(rt5670->regmap, RT5670_RESET, 0);
snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
- return 0;
}
#ifdef CONFIG_PM
-static int rt5670_suspend(struct snd_soc_codec *codec)
+static int rt5670_suspend(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5670->regmap, true);
regcache_mark_dirty(rt5670->regmap);
return 0;
}
-static int rt5670_resume(struct snd_soc_codec *codec)
+static int rt5670_resume(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5670->regmap, false);
regcache_sync(rt5670->regmap);
@@ -2770,7 +2813,7 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt5670-aif2",
@@ -2790,37 +2833,37 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
- .probe = rt5670_probe,
- .remove = rt5670_remove,
- .suspend = rt5670_suspend,
- .resume = rt5670_resume,
- .set_bias_level = rt5670_set_bias_level,
- .idle_bias_off = true,
- .set_sysclk = rt5670_set_codec_sysclk,
- .component_driver = {
- .controls = rt5670_snd_controls,
- .num_controls = ARRAY_SIZE(rt5670_snd_controls),
- .dapm_widgets = rt5670_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets),
- .dapm_routes = rt5670_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_rt5670 = {
+ .probe = rt5670_probe,
+ .remove = rt5670_remove,
+ .suspend = rt5670_suspend,
+ .resume = rt5670_resume,
+ .set_bias_level = rt5670_set_bias_level,
+ .set_sysclk = rt5670_set_codec_sysclk,
+ .controls = rt5670_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5670_snd_controls),
+ .dapm_widgets = rt5670_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets),
+ .dapm_routes = rt5670_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5670_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) *
RT5670_PR_SPACING),
.volatile_reg = rt5670_volatile_register,
.readable_reg = rt5670_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5670_reg,
.num_reg_defaults = ARRAY_SIZE(rt5670_reg),
.ranges = rt5670_ranges,
@@ -2828,19 +2871,19 @@ static const struct regmap_config rt5670_regmap = {
};
static const struct i2c_device_id rt5670_i2c_id[] = {
- { "rt5670", 0 },
- { "rt5671", 0 },
- { "rt5672", 0 },
+ { "rt5670" },
+ { "rt5671" },
+ { "rt5672" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5670_acpi_match[] = {
- { "10EC5670", 0},
- { "10EC5672", 0},
- { "10EC5640", 0}, /* quirk */
- { },
+ { "10EC5640" }, /* quirk */
+ { "10EC5670" },
+ { "10EC5672" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
#endif
@@ -2861,7 +2904,7 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_IRQ |
RT5670_JD_MODE1),
},
{
@@ -2873,7 +2916,19 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Lenovo Thinkpad Tablet 8",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
RT5670_JD_MODE1),
},
{
@@ -2885,7 +2940,7 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_IRQ |
RT5670_JD_MODE1),
},
{
@@ -2897,19 +2952,19 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_IRQ |
RT5670_JD_MODE1),
},
{
.callback = rt5670_quirk_cb,
- .ident = "Lenovo Thinkpad Tablet 10",
+ .ident = "Lenovo Miix 2 10",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_EXT_SPK_EN |
RT5670_JD_MODE2),
},
{
@@ -2921,16 +2976,77 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC2_INR |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE3),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Dell Venue 10 Pro 5055",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Aegex 10 tablet (RU2)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AEGEX"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "RU2"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
RT5670_JD_MODE3),
},
{}
};
-static int rt5670_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+const char *rt5670_components(void)
+{
+ unsigned long quirk;
+ bool dmic1 = false;
+ bool dmic2 = false;
+ bool dmic3 = false;
+
+ if (quirk_override) {
+ quirk = quirk_override;
+ } else {
+ dmi_check_system(dmi_platform_intel_quirks);
+ quirk = rt5670_quirk;
+ }
+
+ if ((quirk & RT5670_DMIC1_IN2P) ||
+ (quirk & RT5670_DMIC1_GPIO6) ||
+ (quirk & RT5670_DMIC1_GPIO7))
+ dmic1 = true;
+
+ if ((quirk & RT5670_DMIC2_INR) ||
+ (quirk & RT5670_DMIC2_GPIO8))
+ dmic2 = true;
+
+ if (quirk & RT5670_DMIC3_GPIO5)
+ dmic3 = true;
+
+ if (dmic1 && dmic2)
+ return "cfg-spk:2 cfg-mic:dmics12";
+ else if (dmic1)
+ return "cfg-spk:2 cfg-mic:dmic1";
+ else if (dmic2)
+ return "cfg-spk:2 cfg-mic:dmic2";
+ else if (dmic3)
+ return "cfg-spk:2 cfg-mic:dmic3";
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rt5670_components);
+
+static int rt5670_i2c_probe(struct i2c_client *i2c)
{
- struct rt5670_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5670_priv *rt5670;
int ret;
unsigned int val;
@@ -2943,9 +3059,6 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5670);
- if (pdata)
- rt5670->pdata = *pdata;
-
dmi_check_system(dmi_platform_intel_quirks);
if (quirk_override) {
dev_info(&i2c->dev, "Overriding quirk 0x%x => 0x%x\n",
@@ -2953,56 +3066,70 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
rt5670_quirk = quirk_override;
}
- if (rt5670_quirk & RT5670_DEV_GPIO) {
- rt5670->pdata.dev_gpio = true;
- dev_info(&i2c->dev, "quirk dev_gpio\n");
+ if (rt5670_quirk & RT5670_GPIO1_IS_IRQ) {
+ rt5670->gpio1_is_irq = true;
+ dev_info(&i2c->dev, "quirk GPIO1 is IRQ\n");
+ }
+ if (rt5670_quirk & RT5670_GPIO1_IS_EXT_SPK_EN) {
+ rt5670->gpio1_is_ext_spk_en = true;
+ dev_info(&i2c->dev, "quirk GPIO1 is external speaker enable\n");
}
if (rt5670_quirk & RT5670_IN2_DIFF) {
- rt5670->pdata.in2_diff = true;
+ rt5670->in2_diff = true;
dev_info(&i2c->dev, "quirk IN2_DIFF\n");
}
if (rt5670_quirk & RT5670_DMIC_EN) {
- rt5670->pdata.dmic_en = true;
+ rt5670->dmic_en = true;
dev_info(&i2c->dev, "quirk DMIC enabled\n");
}
if (rt5670_quirk & RT5670_DMIC1_IN2P) {
- rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
dev_info(&i2c->dev, "quirk DMIC1 on IN2P pin\n");
}
if (rt5670_quirk & RT5670_DMIC1_GPIO6) {
- rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO6;
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO6;
dev_info(&i2c->dev, "quirk DMIC1 on GPIO6 pin\n");
}
if (rt5670_quirk & RT5670_DMIC1_GPIO7) {
- rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO7;
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO7;
dev_info(&i2c->dev, "quirk DMIC1 on GPIO7 pin\n");
}
if (rt5670_quirk & RT5670_DMIC2_INR) {
- rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_IN3N;
+ rt5670->dmic2_data_pin = RT5670_DMIC_DATA_IN3N;
dev_info(&i2c->dev, "quirk DMIC2 on INR pin\n");
}
if (rt5670_quirk & RT5670_DMIC2_GPIO8) {
- rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_GPIO8;
+ rt5670->dmic2_data_pin = RT5670_DMIC_DATA_GPIO8;
dev_info(&i2c->dev, "quirk DMIC2 on GPIO8 pin\n");
}
if (rt5670_quirk & RT5670_DMIC3_GPIO5) {
- rt5670->pdata.dmic3_data_pin = RT5670_DMIC_DATA_GPIO5;
+ rt5670->dmic3_data_pin = RT5670_DMIC_DATA_GPIO5;
dev_info(&i2c->dev, "quirk DMIC3 on GPIO5 pin\n");
}
if (rt5670_quirk & RT5670_JD_MODE1) {
- rt5670->pdata.jd_mode = 1;
+ rt5670->jd_mode = 1;
dev_info(&i2c->dev, "quirk JD mode 1\n");
}
if (rt5670_quirk & RT5670_JD_MODE2) {
- rt5670->pdata.jd_mode = 2;
+ rt5670->jd_mode = 2;
dev_info(&i2c->dev, "quirk JD mode 2\n");
}
if (rt5670_quirk & RT5670_JD_MODE3) {
- rt5670->pdata.jd_mode = 3;
+ rt5670->jd_mode = 3;
dev_info(&i2c->dev, "quirk JD mode 3\n");
}
+ /*
+ * Enable the emulated "DAC1 Playback Switch" by default to avoid
+ * muting the output with older UCM profiles.
+ */
+ rt5670->dac1_playback_switch_l = true;
+ rt5670->dac1_playback_switch_r = true;
+ /* The Power-On-Reset values for the DAC1 mixer have the DAC1 input enabled. */
+ rt5670->dac1_mixl_dac1_switch = true;
+ rt5670->dac1_mixr_dac1_switch = true;
+
rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap);
if (IS_ERR(rt5670->regmap)) {
ret = PTR_ERR(rt5670->regmap);
@@ -3040,11 +3167,11 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC,
RT5670_MCLK_DET, RT5670_MCLK_DET);
- if (rt5670->pdata.in2_diff)
+ if (rt5670->in2_diff)
regmap_update_bits(rt5670->regmap, RT5670_IN2,
RT5670_IN_DF2, RT5670_IN_DF2);
- if (rt5670->pdata.dev_gpio) {
+ if (rt5670->gpio1_is_irq) {
/* for push button */
regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000);
regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010);
@@ -3056,7 +3183,14 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
}
- if (rt5670->pdata.jd_mode) {
+ if (rt5670->gpio1_is_ext_spk_en) {
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_GPIO1);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
+ }
+
+ if (rt5670->jd_mode) {
regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK);
rt5670->sysclk = 0;
@@ -3071,7 +3205,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
RT5670_JD_TRI_CBJ_SEL_MASK |
RT5670_JD_TRI_HPO_SEL_MASK,
RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1);
- switch (rt5670->pdata.jd_mode) {
+ switch (rt5670->jd_mode) {
case 1:
regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
RT5670_JD1_MODE_MASK,
@@ -3092,12 +3226,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
}
}
- if (rt5670->pdata.dmic_en) {
+ if (rt5670->dmic_en) {
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
RT5670_GP2_PIN_MASK,
RT5670_GP2_PIN_DMIC1_SCL);
- switch (rt5670->pdata.dmic1_data_pin) {
+ switch (rt5670->dmic1_data_pin) {
case RT5670_DMIC_DATA_IN2P:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_1_DP_MASK,
@@ -3126,7 +3260,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic2_data_pin) {
+ switch (rt5670->dmic2_data_pin) {
case RT5670_DMIC_DATA_IN3N:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_2_DP_MASK,
@@ -3146,7 +3280,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic3_data_pin) {
+ switch (rt5670->dmic3_data_pin) {
case RT5670_DMIC_DATA_GPIO5:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2,
RT5670_DMIC_3_DP_MASK,
@@ -3171,13 +3305,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5670,
rt5670_dai, ARRAY_SIZE(rt5670_dai));
if (ret < 0)
goto err;
- pm_runtime_put(&i2c->dev);
-
return 0;
err:
pm_runtime_disable(&i2c->dev);
@@ -3185,12 +3318,9 @@ err:
return ret;
}
-static int rt5670_i2c_remove(struct i2c_client *i2c)
+static void rt5670_i2c_remove(struct i2c_client *i2c)
{
pm_runtime_disable(&i2c->dev);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
static struct i2c_driver rt5670_i2c_driver = {
@@ -3198,7 +3328,7 @@ static struct i2c_driver rt5670_i2c_driver = {
.name = "rt5670",
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
- .probe = rt5670_i2c_probe,
+ .probe = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,
.id_table = rt5670_i2c_id,
};
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index 265df80d504e..5b230897f630 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670.h -- RT5670 ALSA SoC audio driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@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 __RT5670_H__
#define __RT5670_H__
-#include <sound/rt5670.h>
-
/* Info */
#define RT5670_RESET 0x00
#define RT5670_VENDOR_ID 0xfd
@@ -217,12 +212,8 @@
/* global definition */
#define RT5670_L_MUTE (0x1 << 15)
#define RT5670_L_MUTE_SFT 15
-#define RT5670_VOL_L_MUTE (0x1 << 14)
-#define RT5670_VOL_L_SFT 14
#define RT5670_R_MUTE (0x1 << 7)
#define RT5670_R_MUTE_SFT 7
-#define RT5670_VOL_R_MUTE (0x1 << 6)
-#define RT5670_VOL_R_SFT 6
#define RT5670_L_VOL_MASK (0x3f << 8)
#define RT5670_L_VOL_SFT 8
#define RT5670_R_VOL_MASK (0x3f)
@@ -760,7 +751,7 @@
#define RT5670_PWR_VREF2_BIT 4
#define RT5670_PWR_FV2 (0x1 << 3)
#define RT5670_PWR_FV2_BIT 3
-#define RT5670_LDO_SEL_MASK (0x3)
+#define RT5670_LDO_SEL_MASK (0x7)
#define RT5670_LDO_SEL_SFT 0
/* Power Management for Analog 2 (0x64) */
@@ -1986,16 +1977,28 @@ enum {
RT5670_DOWN_RATE_FILTER = (0x1 << 7),
};
-int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5670_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
struct rt5670_priv {
- struct snd_soc_codec *codec;
- struct rt5670_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
struct snd_soc_jack *jack;
struct snd_soc_jack_gpio hp_gpio;
+ int jd_mode;
+ bool in2_diff;
+ bool gpio1_is_irq;
+ bool gpio1_is_ext_spk_en;
+
+ bool dmic_en;
+ unsigned int dmic1_data_pin;
+ /* 0 = GPIO6; 1 = IN2P; 3 = GPIO7*/
+ unsigned int dmic2_data_pin;
+ /* 0 = GPIO8; 1 = IN3N; */
+ unsigned int dmic3_data_pin;
+ /* 0 = GPIO9; 1 = GPIO10; 2 = GPIO5*/
+
int sysclk;
int sysclk_src;
int lrck[RT5670_AIFS];
@@ -2010,10 +2013,17 @@ struct rt5670_priv {
int dsp_rate;
int jack_type;
int jack_type_saved;
+
+ bool dac1_mixl_dac1_switch;
+ bool dac1_mixr_dac1_switch;
+ bool dac1_playback_switch_l;
+ bool dac1_playback_switch_r;
};
-void rt5670_jack_suspend(struct snd_soc_codec *codec);
-void rt5670_jack_resume(struct snd_soc_codec *codec);
-int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+void rt5670_jack_suspend(struct snd_soc_component *component);
+void rt5670_jack_resume(struct snd_soc_component *component);
+int rt5670_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
+const char *rt5670_components(void);
+
#endif /* __RT5670_H__ */
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index bd51f3655ee3..885edcf0a3a5 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677-spi.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 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.
*/
#include <linux/module.h>
@@ -18,7 +15,6 @@
#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>
@@ -26,9 +22,15 @@
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
+#include <linux/acpi.h>
+
+#include <sound/soc.h>
+#include "rt5677.h"
#include "rt5677-spi.h"
+#define DRV_NAME "rt5677spi"
+
#define RT5677_SPI_BURST_LEN 240
#define RT5677_SPI_HEADER 5
#define RT5677_SPI_FREQ 6000000
@@ -46,9 +48,366 @@
#define RT5677_SPI_WRITE_16 0x1
#define RT5677_SPI_READ_16 0x0
+#define RT5677_BUF_BYTES_TOTAL 0x20000
+#define RT5677_MIC_BUF_ADDR 0x60030000
+#define RT5677_MODEL_ADDR 0x5FFC9800
+#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
+ sizeof(u32)))
+#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
+
static struct spi_device *g_spi;
static DEFINE_MUTEX(spi_mutex);
+struct rt5677_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ size_t dma_offset; /* zero-based offset into runtime->dma_area */
+ size_t avail_bytes; /* number of new bytes since last period */
+ u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
+ bool new_hotword; /* a new hotword is fired */
+};
+
+static const struct snd_pcm_hardware rt5677_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 = RT5677_BUF_BYTES_TOTAL / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
+};
+
+static struct snd_soc_dai_driver rt5677_spi_dai = {
+ /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
+ * registered with ASoC is the name of the device "spi-RT5677AA:00",
+ * because we only have one DAI. See snd_soc_register_dais().
+ */
+ .name = "rt5677-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,
+ },
+};
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5677_spi_pcm_open(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
+ return 0;
+}
+
+static int rt5677_spi_pcm_close(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *codec_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(codec_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5677_dsp->copy_work);
+ rt5677->set_dsp_vad(codec_component, false);
+ return 0;
+}
+
+static int rt5677_spi_hw_params(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ rt5677_dsp->substream = substream;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5677_spi_hw_free(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ rt5677_dsp->substream = NULL;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5677_spi_prepare(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *rt5677_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(rt5677_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ rt5677->set_dsp_vad(rt5677_component, true);
+ rt5677_dsp->dma_offset = 0;
+ rt5677_dsp->avail_bytes = 0;
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
+}
+
+static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
+{
+ int ret;
+ /* Grab the first 4 bytes that hold the write pointer on the
+ * dsp, and check to make sure that it points somewhere inside the
+ * buffer.
+ */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
+ sizeof(u32));
+ if (ret)
+ return ret;
+ /* Adjust the offset so that it's zero-based */
+ *mic_write_offset = *mic_write_offset - sizeof(u32);
+ return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
+}
+
+/*
+ * Copy one contiguous block of audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime. The receiving buffer may wrap around.
+ * @begin: start offset of the block to copy, in bytes.
+ * @end: offset of the first byte after the block to copy, must be greater
+ * than or equal to begin.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
+ u32 begin, u32 end)
+{
+ struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
+ size_t bytes_per_frame = frames_to_bytes(runtime, 1);
+ size_t first_chunk_len, second_chunk_len;
+ int ret;
+
+ if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
+ dev_err(rt5677_dsp->dev,
+ "Invalid copy from (%u, %u), dma_area size %zu\n",
+ begin, end, runtime->dma_bytes);
+ return -EINVAL;
+ }
+
+ /* The block to copy is empty */
+ if (begin == end)
+ return 0;
+
+ /* If the incoming chunk is too big for the receiving buffer, only the
+ * last "receiving buffer size - one frame" bytes are copied.
+ */
+ if (end - begin > runtime->dma_bytes - bytes_per_frame)
+ begin = end - (runtime->dma_bytes - bytes_per_frame);
+
+ /* May need to split to two chunks, calculate the size of each */
+ first_chunk_len = end - begin;
+ second_chunk_len = 0;
+ if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
+ /* Receiving buffer wrapped around */
+ second_chunk_len = first_chunk_len;
+ first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
+ second_chunk_len -= first_chunk_len;
+ }
+
+ /* Copy first chunk */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
+ runtime->dma_area + rt5677_dsp->dma_offset,
+ first_chunk_len);
+ if (ret)
+ return ret;
+ rt5677_dsp->dma_offset += first_chunk_len;
+ if (rt5677_dsp->dma_offset == runtime->dma_bytes)
+ rt5677_dsp->dma_offset = 0;
+
+ /* Copy second chunk */
+ if (second_chunk_len) {
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
+ begin + first_chunk_len, runtime->dma_area,
+ second_chunk_len);
+ if (!ret)
+ rt5677_dsp->dma_offset = second_chunk_len;
+ }
+ return ret;
+}
+
+/*
+ * Copy a given amount of audio samples from the DSP mic buffer starting at
+ * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
+ * wrap around. mic_read_offset is updated after successful copy.
+ * @amount: amount of samples to copy, in bytes.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
+{
+ int ret = 0;
+ u32 target;
+
+ if (amount == 0)
+ return ret;
+
+ target = rt5677_dsp->mic_read_offset + amount;
+ /* Copy the first chunk in DSP's mic buffer */
+ ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
+ min(target, RT5677_MIC_BUF_BYTES));
+
+ if (target >= RT5677_MIC_BUF_BYTES) {
+ /* Wrap around, copy the second chunk */
+ target -= RT5677_MIC_BUF_BYTES;
+ ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
+ }
+
+ if (!ret)
+ rt5677_dsp->mic_read_offset = target;
+ return ret;
+}
+
+/*
+ * A delayed work that streams audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime via SPI.
+ */
+static void rt5677_spi_copy_work(struct work_struct *work)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ container_of(work, struct rt5677_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ u32 mic_write_offset;
+ size_t new_bytes, copy_bytes, period_bytes;
+ unsigned int delay;
+ int ret = 0;
+
+ /* Ensure runtime->dma_area buffer does not go away while copying. */
+ mutex_lock(&rt5677_dsp->dma_lock);
+ if (!rt5677_dsp->substream) {
+ dev_err(rt5677_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5677_dsp->substream->runtime;
+
+ if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
+ dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
+ goto done;
+ }
+
+ /* If this is the first time that we've asked for streaming data after
+ * a hotword is fired, we should start reading from the previous 2
+ * seconds of audio from wherever the mic_write_offset is currently.
+ */
+ if (rt5677_dsp->new_hotword) {
+ rt5677_dsp->new_hotword = false;
+ /* See if buffer wraparound happens */
+ if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
+ rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
+ (RT5677_MIC_BUF_FIRST_READ_SIZE -
+ mic_write_offset);
+ else
+ rt5677_dsp->mic_read_offset = mic_write_offset -
+ RT5677_MIC_BUF_FIRST_READ_SIZE;
+ }
+
+ /* Calculate the amount of new samples in bytes */
+ if (rt5677_dsp->mic_read_offset <= mic_write_offset)
+ new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
+ else
+ new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
+ - rt5677_dsp->mic_read_offset;
+
+ /* Copy all new samples from DSP mic buffer, one period at a time */
+ period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
+ while (new_bytes) {
+ copy_bytes = min(new_bytes, period_bytes
+ - rt5677_dsp->avail_bytes);
+ ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
+ if (ret) {
+ dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
+ goto done;
+ }
+ rt5677_dsp->avail_bytes += copy_bytes;
+ if (rt5677_dsp->avail_bytes >= period_bytes) {
+ snd_pcm_period_elapsed(rt5677_dsp->substream);
+ rt5677_dsp->avail_bytes = 0;
+ }
+ new_bytes -= copy_bytes;
+ }
+
+ delay = bytes_to_frames(runtime, period_bytes) / runtime->rate;
+ schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay));
+done:
+ mutex_unlock(&rt5677_dsp->dma_lock);
+}
+
+static int rt5677_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 int rt5677_spi_pcm_probe(struct snd_soc_component *component)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
+ GFP_KERNEL);
+ if (!rt5677_dsp)
+ return -ENOMEM;
+ rt5677_dsp->dev = &g_spi->dev;
+ mutex_init(&rt5677_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
+
+ snd_soc_component_set_drvdata(component, rt5677_dsp);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt5677_spi_dai_component = {
+ .name = DRV_NAME,
+ .probe = rt5677_spi_pcm_probe,
+ .open = rt5677_spi_pcm_open,
+ .close = rt5677_spi_pcm_close,
+ .hw_params = rt5677_spi_hw_params,
+ .hw_free = rt5677_spi_hw_free,
+ .prepare = rt5677_spi_prepare,
+ .pointer = rt5677_spi_pcm_pointer,
+ .pcm_construct = rt5677_spi_pcm_new,
+ .legacy_dai_naming = 1,
+};
+
/* Select a suitable transfer command for the next transfer to ensure
* the transfer address is always naturally aligned while minimizing
* the total number of transfers required.
@@ -58,13 +417,15 @@ static DEFINE_MUTEX(spi_mutex);
* RT5677_SPI_READ/WRITE_32: Transfer 4 bytes
* RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes
*
- * For example, reading 260 bytes at 0x60030002 uses the following commands:
- * 0x60030002 RT5677_SPI_READ_16 2 bytes
+ * Note:
+ * 16 Bit writes and reads are restricted to the address range
+ * 0x18020000 ~ 0x18021000
+ *
+ * For example, reading 256 bytes at 0x60030004 uses the following commands:
* 0x60030004 RT5677_SPI_READ_32 4 bytes
* 0x60030008 RT5677_SPI_READ_BURST 240 bytes
* 0x600300F8 RT5677_SPI_READ_BURST 8 bytes
* 0x60030100 RT5677_SPI_READ_32 4 bytes
- * 0x60030104 RT5677_SPI_READ_16 2 bytes
*
* Input:
* @read: true for read commands; false for write commands
@@ -79,15 +440,13 @@ static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
{
u8 cmd;
- if (align == 2 || align == 6 || remain == 2) {
- cmd = RT5677_SPI_READ_16;
- *len = 2;
- } else if (align == 4 || remain <= 6) {
+ if (align == 4 || remain <= 4) {
cmd = RT5677_SPI_READ_32;
*len = 4;
} else {
cmd = RT5677_SPI_READ_BURST;
- *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN);
+ *len = (((remain - 1) >> 3) + 1) << 3;
+ *len = min_t(u32, *len, RT5677_SPI_BURST_LEN);
}
return read ? cmd : cmd + 1;
}
@@ -101,14 +460,14 @@ static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen)
u32 word_size = min_t(u32, dstlen, 8);
for (w = 0; w < dstlen; w += word_size) {
- for (i = 0; i < word_size; i++) {
+ for (i = 0; i < word_size && i + w < dstlen; i++) {
si = w + word_size - i - 1;
dst[w + i] = si < srclen ? src[si] : 0;
}
}
}
-/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */
+/* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
{
u32 offset;
@@ -124,7 +483,7 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
if (!g_spi)
return -ENODEV;
- if ((addr & 1) || (len & 1)) {
+ if ((addr & 3) || (len & 3)) {
dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
@@ -152,20 +511,21 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
status |= spi_sync(g_spi, &m);
mutex_unlock(&spi_mutex);
+
/* Copy data back to caller buffer */
- rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len);
+ rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len);
}
return status;
}
EXPORT_SYMBOL_GPL(rt5677_spi_read);
-/* Write DSP address space using SPI. addr has to be 2-byte aligned.
- * If len is not 2-byte aligned, an extra byte of zero is written at the end
+/* Write DSP address space using SPI. addr has to be 4-byte aligned.
+ * If len is not 4-byte aligned, then extra zeros are written at the end
* as padding.
*/
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
{
- u32 offset, len_with_pad = len;
+ u32 offset;
int status = 0;
struct spi_transfer t;
struct spi_message m;
@@ -178,22 +538,19 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
if (!g_spi)
return -ENODEV;
- if (addr & 1) {
+ if (addr & 3) {
dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
- if (len & 1)
- len_with_pad = len + 1;
-
memset(&t, 0, sizeof(t));
t.tx_buf = buf;
t.speed_hz = RT5677_SPI_FREQ;
spi_message_init_with_transfers(&m, &t, 1);
- for (offset = 0; offset < len_with_pad;) {
+ for (offset = 0; offset < len;) {
spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7,
- len_with_pad - offset, &t.len);
+ len - offset, &t.len);
/* Construct SPI message header */
buf[0] = spi_cmd;
@@ -221,15 +578,56 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
}
EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
+void rt5677_spi_hotword_detected(void)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ if (!g_spi)
+ return;
+
+ rt5677_dsp = dev_get_drvdata(&g_spi->dev);
+ if (!rt5677_dsp) {
+ dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
+ return;
+ }
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ dev_info(rt5677_dsp->dev, "Hotword detected\n");
+ rt5677_dsp->new_hotword = true;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ schedule_delayed_work(&rt5677_dsp->copy_work, 0);
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
+
static int rt5677_spi_probe(struct spi_device *spi)
{
+ int ret;
+
g_spi = spi;
- return 0;
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5677_spi_dai_component,
+ &rt5677_spi_dai, 1);
+ if (ret < 0)
+ dev_err(&spi->dev, "Failed to register component.\n");
+
+ return ret;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5677_spi_acpi_id[] = {
+ { "10EC5677" },
+ { "RT5677AA" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
+#endif
+
static struct spi_driver rt5677_spi_driver = {
.driver = {
- .name = "rt5677",
+ .name = DRV_NAME,
+ .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
};
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
index 662db16cfb6a..088b77931727 100644
--- a/sound/soc/codecs/rt5677-spi.h
+++ b/sound/soc/codecs/rt5677-spi.h
@@ -1,19 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5677-spi.h -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 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 __RT5677_SPI_H__
#define __RT5677_SPI_H__
+#if IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI)
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len);
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len);
int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw);
+void rt5677_spi_hotword_detected(void);
+#else
+static inline int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
+{
+ return -EINVAL;
+}
+static inline int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
+{
+ return -EINVAL;
+}
+static inline int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
+{
+ return -EINVAL;
+}
+static inline void rt5677_spi_hotword_detected(void){}
+#endif
#endif /* __RT5677_SPI_H__ */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 0791fec398fb..6b6c690a9e45 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1,28 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 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.
*/
-#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/firmware.h>
-#include <linux/of_device.h>
-#include <linux/property.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -37,6 +36,10 @@
#define RT5677_DEVICE_ID 0x6327
+/* Register controlling boot vector */
+#define RT5677_DSP_BOOT_VECTOR 0x1801f090
+#define RT5677_MODEL_ADDR 0x5FFC9800
+
#define RT5677_PR_RANGE_BASE (0xff + 1)
#define RT5677_PR_SPACING 0x100
@@ -297,6 +300,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
@@ -307,6 +311,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_IRQ_CTRL1:
case RT5677_IRQ_CTRL2:
case RT5677_GPIO_ST:
+ case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */
+ case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */
case RT5677_DSP_INB1_SRC_CTRL4:
case RT5677_DSP_INB2_SRC_CTRL4:
case RT5677_DSP_INB3_SRC_CTRL4:
@@ -547,14 +553,14 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
* @rt5677: Private Data.
* @addr: Address index.
* @value: Address data.
- *
+ * @opcode: opcode value
*
* Returns 0 for success or negative error code.
*/
static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
unsigned int addr, unsigned int value, unsigned int opcode)
{
- struct snd_soc_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
mutex_lock(&rt5677->dsp_cmd_lock);
@@ -562,35 +568,35 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
addr & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB,
value >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set data msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set data msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB,
value & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set data lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
opcode);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -602,7 +608,7 @@ err:
/**
* rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @addr: Address index.
* @value: Address data.
*
@@ -612,7 +618,7 @@ err:
static int rt5677_dsp_mode_i2c_read_addr(
struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
{
- struct snd_soc_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
unsigned int msb, lsb;
@@ -621,21 +627,21 @@ static int rt5677_dsp_mode_i2c_read_addr(
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
addr & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
0x0002);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -651,7 +657,7 @@ err:
/**
* rt5677_dsp_mode_i2c_write - Write register on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @reg: Register index.
* @value: Register data.
*
@@ -667,7 +673,7 @@ static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
/**
* rt5677_dsp_mode_i2c_read - Read register on DSP mode.
- * @codec: SoC audio codec device.
+ * @rt5677: Private Data
* @reg: Register index.
* @value: Register data.
*
@@ -685,99 +691,272 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
-static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
-
if (on) {
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP, RT5677_PWR_DSP);
rt5677->is_dsp_mode = true;
} else {
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP, 0x0);
rt5677->is_dsp_mode = false;
}
}
-static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- static bool activity;
- int ret;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt5677->component);
+ /* Force dapm to sync before we enable the
+ * DSP to prevent write corruption
+ */
+ snd_soc_dapm_sync(dapm);
+
+ /* DMIC1 power = enabled
+ * DMIC CLK = 256 * fs / 12
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+ RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT);
+
+ /* I2S pre divide 2 = /6 (clk_sys2) */
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6);
+
+ /* DSP Clock = MCLK1 (bypassed PLL2) */
+ regmap_write(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_BYPASS);
+
+ /* SAD Threshold1 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f);
+ /* SAD Threshold2 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5);
+ /* SAD Sample Rate Converter = Up 6 (8K to 48K)
+ * SAD Output Sample Rate = Same as I2S
+ * SAD Threshold3
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4,
+ RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK |
+ RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT);
+ /* Minimum frame level within a pre-determined duration = 32 frames
+ * Bypass ADPCM Encoder/Decoder = Bypass ADPCM
+ * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable
+ * SAD Buffer Over-Writing = enable
+ * SAD Buffer Pop Mode Control = disable
+ * SAD Buffer Push Mode Control = enable
+ * SAD Detector Control = enable
+ * SAD Function Control = enable
+ * SAD Function Reset = normal
+ */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1,
+ RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE |
+ RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH |
+ RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC |
+ RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT);
+
+ /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it
+ * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save
+ * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack
+ * Detection" for more info.
+ */
+
+ /* Private register, no doc */
+ regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4,
+ 0x0f00, 0x0100);
+
+ /* LDO2 output = 1.2V
+ * LDO1 output = 1.2V (LDO_IN = 1.8V)
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+ 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT);
+
+ /* Codec core power = power on
+ * LDO1 power = power on
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1);
+
+ /* Isolation for DCVDD4 = normal (set during probe)
+ * Isolation for DCVDD2 = normal (set during probe)
+ * Isolation for DSP = normal
+ * Isolation for Band 0~7 = disable
+ * Isolation for InBound 4~10 and OutBound 4~10 = disable
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO |
+ RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO |
+ RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO |
+ RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO |
+ RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO |
+ RT5677_PWR_MLT_ISO);
+
+ /* System Band 0~7 = power on
+ * InBound 4~10 and OutBound 4~10 = power on
+ * DSP = power on
+ * DSP CPU = stop (will be set to "run" after firmware loaded)
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_SR7 | RT5677_PWR_SR6 |
+ RT5677_PWR_SR5 | RT5677_PWR_SR4 |
+ RT5677_PWR_SR3 | RT5677_PWR_SR2 |
+ RT5677_PWR_SR1 | RT5677_PWR_SR0 |
+ RT5677_PWR_MLT | RT5677_PWR_DSP |
+ RT5677_PWR_DSP_CPU);
+
+ return 0;
+}
+
+static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf,
+ unsigned int len)
+{
+ struct snd_soc_component *component = rt5677->component;
+ Elf32_Ehdr *elf_hdr;
+ Elf32_Phdr *pr_hdr;
+ Elf32_Half i;
+ int ret = 0;
+
+ if (!buf || (len < sizeof(Elf32_Ehdr)))
+ return -ENOMEM;
+
+ elf_hdr = (Elf32_Ehdr *)buf;
+#ifndef EM_XTENSA
+#define EM_XTENSA 94
+#endif
+ if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1))
+ dev_err(component->dev, "Wrong ELF header prefix\n");
+ if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr))
+ dev_err(component->dev, "Wrong ELF header size\n");
+ if (elf_hdr->e_machine != EM_XTENSA)
+ dev_err(component->dev, "Wrong DSP code file\n");
+
+ if (len < elf_hdr->e_phoff)
+ return -ENOMEM;
+ pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff);
+ for (i = 0; i < elf_hdr->e_phnum; i++) {
+ /* TODO: handle p_memsz != p_filesz */
+ if (pr_hdr->p_paddr && pr_hdr->p_filesz) {
+ dev_info(component->dev, "Load 0x%x bytes to 0x%x\n",
+ pr_hdr->p_filesz, pr_hdr->p_paddr);
+
+ ret = rt5677_spi_write(pr_hdr->p_paddr,
+ buf + pr_hdr->p_offset,
+ pr_hdr->p_filesz);
+ if (ret)
+ dev_err(component->dev, "Load firmware failed %d\n",
+ ret);
+ }
+ pr_hdr++;
+ }
+ return ret;
+}
+
+static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677)
+{
+ const struct firmware *fwp;
+ struct device *dev = rt5677->component->dev;
+ int ret = 0;
+
+ /* Load dsp firmware from rt5677_elf_vad file */
+ ret = request_firmware(&fwp, "rt5677_elf_vad", dev);
+ if (ret) {
+ dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret);
+ return ret;
+ }
+ dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size);
+
+ ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size);
+ release_firmware(fwp);
+ return ret;
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on)
+{
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ rt5677->dsp_vad_en_request = on;
+ rt5677->dsp_vad_en = on;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
- if (on && !activity) {
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ return 0;
+}
+
+static void rt5677_dsp_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, dsp_work.work);
+ static bool activity;
+ bool enable = rt5677->dsp_vad_en;
+ int i, val;
+
+
+ dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n",
+ enable, activity);
+
+ if (enable && !activity) {
activity = true;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Before a hotword is detected, GPIO1 pin is configured as IRQ
+ * output so that jack detect works. When a hotword is detected,
+ * the DSP firmware configures the GPIO1 pin as GPIO1 and
+ * drives a 1. rt5677_irq() is called after a rising edge on
+ * the GPIO1 pin, due to either jack detect event or hotword
+ * event, or both. All possible events are checked and handled
+ * in rt5677_irq() where GPIO1 pin is configured back to IRQ
+ * output if a hotword is detected.
+ */
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
- regmap_update_bits(rt5677->regmap,
- RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
- RT5677_LDO1_SEL_MASK, 0x0);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
- RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- switch (rt5677->type) {
- case RT5677:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
- RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_PLL2_PR_SRC_MASK |
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_PLL2_PR_SRC_MCLK2 |
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- case RT5676:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- default:
- break;
+ rt5677_set_vad_source(rt5677);
+ rt5677_set_dsp_mode(rt5677, true);
+
+#define RT5677_BOOT_RETRY 20
+ for (i = 0; i < RT5677_BOOT_RETRY; i++) {
+ regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val);
+ if (val == 0x3ff)
+ break;
+ udelay(500);
}
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
- rt5677_set_dsp_mode(codec, true);
-
- ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
- codec->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x50000000, rt5677->fw1);
- release_firmware(rt5677->fw1);
+ if (i == RT5677_BOOT_RETRY && val != 0x3ff) {
+ dev_err(rt5677->component->dev, "DSP Boot Timed Out!");
+ return;
}
- ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
- codec->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
- release_firmware(rt5677->fw2);
- }
+ /* Boot the firmware from IRAM instead of SRAM0. */
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0019, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+ rt5677_load_dsp_from_file(rt5677);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_cache_only(rt5677->regmap, true);
- } else if (!on && activity) {
+ /* Set DSP CPU to Run */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, 0x0);
+ } else if (!enable && activity) {
activity = false;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Don't turn off the DSP while handling irqs */
+ mutex_lock(&rt5677->irq_lock);
+ /* Set DSP CPU to Stop */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
- rt5677_set_dsp_mode(codec, false);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+ rt5677_set_dsp_mode(rt5677, false);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ /* Disable and clear VAD interrupt */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_mark_dirty(rt5677->regmap);
- regcache_sync(rt5677->regmap);
- }
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
- return 0;
+ mutex_unlock(&rt5677->irq_lock);
+ }
}
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
@@ -802,7 +981,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@@ -811,13 +990,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
-
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+ rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@@ -833,13 +1007,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
@@ -911,15 +1085,15 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5677->sysclk / rl6231_get_pre_div(rt5677->regmap,
RT5677_CLK_TREE_CTRL1, RT5677_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
RT5677_DMIC_CLK_MASK, idx << RT5677_DMIC_CLK_SFT);
@@ -929,8 +1103,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val;
regmap_read(rt5677->regmap, RT5677_GLB_CLK1, &val);
@@ -944,8 +1118,8 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg, shift, val;
if (source->reg == RT5677_ASRC_1) {
@@ -1027,8 +1201,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (rt5677->sysclk > rt5677->lrck[RT5677_AIF1] * 384)
return 1;
@@ -1038,7 +1212,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
/**
* rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -1050,10 +1224,10 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
* 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 rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int asrc3_mask = 0, asrc3_value = 0;
unsigned int asrc4_mask = 0, asrc4_value = 0;
unsigned int asrc5_mask = 0, asrc5_value = 0;
@@ -1232,8 +1406,8 @@ EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src);
static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int asrc_setting;
switch (source->shift) {
@@ -2393,8 +2567,8 @@ static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2417,8 +2591,8 @@ static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2441,8 +2615,8 @@ static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -2463,8 +2637,8 @@ static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -2485,8 +2659,8 @@ static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2512,8 +2686,8 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int value;
switch (event) {
@@ -2535,8 +2709,8 @@ static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int value;
switch (event) {
@@ -2558,12 +2732,12 @@ static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON &&
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON &&
!rt5677->is_vref_slow) {
mdelay(20);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -2608,7 +2782,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5677_ASRC_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5677_ASRC_1, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("I2S4 ASRC", 1, RT5677_ASRC_1, 3, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0,
+ rt5677_filter_power_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("DAC MONO2 L ASRC", 1, RT5677_ASRC_2, 13, 0, NULL,
0),
SND_SOC_DAPM_SUPPLY_S("DAC MONO2 R ASRC", 1, RT5677_ASRC_2, 12, 0, NULL,
@@ -3007,6 +3182,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0),
/* Sidetone Mux */
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
@@ -3541,11 +3717,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "SLBTX", NULL, "SLB ADC3 Mux" },
{ "SLBTX", NULL, "SLB ADC4 Mux" },
+ { "DSPTX", NULL, "IB01 Bypass Mux" },
+
{ "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
{ "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
{ "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
{ "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
- { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+ /* The IB01 Mux controls the source for InBound0 and InBound1.
+ * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to
+ * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for
+ * hotwording. "DAC1 FS" is not used currently.
+ *
+ * Creating a common widget node for "VAD ADC" + "DAC1 FS" and
+ * connecting the common widget to IB01 Mux causes the issue where
+ * there is an active path going from system playback -> "DAC1 FS" ->
+ * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses
+ * DAPM. Therefore "DAC1 FS" is ignored for now.
+ */
+ { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" },
{ "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
{ "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
@@ -4098,21 +4287,21 @@ static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
static int rt5677_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 rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
rt5677->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+ dev_err(component->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
rt5677->sysclk, rt5677->lrck[dai->id]);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -4183,15 +4372,15 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5677->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5677_I2S_MS_S;
rt5677->master[dai->id] = 0;
break;
@@ -4257,8 +4446,8 @@ static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5677->sysclk && clk_id == rt5677->sysclk_src)
@@ -4275,7 +4464,7 @@ static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5677_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
@@ -4310,8 +4499,8 @@ static int rt5677_pll_calc(const unsigned int freq_in,
static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -4320,7 +4509,7 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5677->pll_in = 0;
rt5677->pll_out = 0;
@@ -4360,25 +4549,25 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rt5677_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "m_bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "m_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(rt5677->regmap, RT5677_PLL1_CTRL1,
pll_code.n_code << RT5677_PLL_N_SFT | pll_code.k_code);
regmap_write(rt5677->regmap, RT5677_PLL1_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT |
- pll_code.m_bp << RT5677_PLL_M_BP_SFT);
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT) |
+ (pll_code.m_bp << RT5677_PLL_M_BP_SFT));
rt5677->pll_in = freq_in;
rt5677->pll_out = freq_out;
@@ -4390,8 +4579,8 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val = 0, slot_width_25 = 0;
if (rx_mask || tx_mask)
@@ -4418,6 +4607,7 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 25:
slot_width_25 = 0x8080;
+ fallthrough;
case 24:
val |= (2 << 8);
break;
@@ -4449,22 +4639,24 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int rt5677_set_bias_level(struct snd_soc_codec *codec,
+static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ enum snd_soc_bias_level prev_bias =
+ snd_soc_component_get_bias_level(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
- rt5677_set_dsp_vad(codec, false);
+ if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
- 0x0055);
+ 5 << RT5677_LDO1_SEL_SFT |
+ 5 << RT5677_LDO2_SEL_SFT);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4,
0x0f00, 0x0f00);
@@ -4483,19 +4675,37 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
+ if (prev_bias == SND_SOC_BIAS_OFF &&
+ rt5677->dsp_vad_en_request) {
+ /* Re-enable the DSP if it was turned off at suspend */
+ rt5677->dsp_vad_en = true;
+ /* The delay is to wait for MCLK */
+ schedule_delayed_work(&rt5677->dsp_work,
+ msecs_to_jiffies(1000));
+ }
break;
case SND_SOC_BIAS_OFF:
+ flush_delayed_work(&rt5677->dsp_work);
+ if (rt5677->is_dsp_mode) {
+ /* Turn off the DSP before suspend */
+ rt5677->dsp_vad_en = false;
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ flush_delayed_work(&rt5677->dsp_work);
+ }
+
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
- regmap_write(rt5677->regmap, RT5677_PWR_DIG2, 0x0000);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0022);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
+ regmap_write(rt5677->regmap, RT5677_PWR_ANLG1,
+ 2 << RT5677_LDO1_SEL_SFT |
+ 2 << RT5677_LDO2_SEL_SFT);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE, 0);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
if (rt5677->dsp_vad_en)
- rt5677_set_dsp_vad(codec, true);
+ rt5677_set_dsp_vad(component, true);
break;
default:
@@ -4505,50 +4715,35 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#ifdef CONFIG_GPIOLIB
-static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v)
{
- struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ unsigned int bank = offset / 5;
+ unsigned int shift = (offset % 5) * 3;
+ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
- break;
+ return regmap_update_bits(rt5677->regmap, reg, m << shift, v << shift);
+}
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
- break;
+#ifdef CONFIG_GPIOLIB
+static int rt5677_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_OUT_MASK;
- default:
- break;
- }
+ return rt5677_update_gpio_bits(rt5677, offset, m, level);
}
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_DIR_MASK | RT5677_GPIOx_OUT_MASK;
+ int v = RT5677_GPIOx_DIR_OUT | level;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x3 << (offset * 3 + 1),
- (0x2 | !!value) << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
- RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
- break;
-
- default:
- break;
- }
-
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -4566,26 +4761,14 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int m = RT5677_GPIOx_DIR_MASK;
+ int v = RT5677_GPIOx_DIR_IN;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 2), 0x0);
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN);
- break;
-
- default:
- break;
- }
-
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
-/** Configures the gpio as
+/*
+ * Configures the GPIO as
* 0 - floating
* 1 - pull down
* 2 - pull up
@@ -4620,7 +4803,6 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
int irq;
if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
@@ -4646,11 +4828,11 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
return -ENXIO;
}
- return regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
static const struct gpio_chip rt5677_template_chip = {
- .label = "rt5677",
+ .label = RT5677_DRV_NAME,
.owner = THIS_MODULE,
.direction_output = rt5677_gpio_direction_out,
.set = rt5677_gpio_set,
@@ -4696,13 +4878,13 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
}
#endif
-static int rt5677_probe(struct snd_soc_codec *codec)
+static int rt5677_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
int i;
- rt5677->codec = codec;
+ rt5677->component = component;
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
snd_soc_dapm_add_routes(dapm,
@@ -4714,60 +4896,42 @@ static int rt5677_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5677_dmic2_clk_1));
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020);
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_SLIM_ISO | RT5677_PWR_CORE_ISO);
for (i = 0; i < RT5677_GPIO_NUM; i++)
rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
return 0;
}
-static int rt5677_remove(struct snd_soc_codec *codec)
+static void rt5677_remove(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5677->dsp_work);
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5677_suspend(struct snd_soc_codec *codec)
+static int rt5677_suspend(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+
+ if (rt5677->irq) {
+ cancel_delayed_work_sync(&rt5677->resume_irq_check);
+ disable_irq(rt5677->irq);
+ }
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
@@ -4780,9 +4944,9 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5677_resume(struct snd_soc_codec *codec)
+static int rt5677_resume(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (!rt5677->dsp_vad_en) {
rt5677->pll_src = 0;
@@ -4797,6 +4961,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
regcache_sync(rt5677->regmap);
}
+ if (rt5677->irq) {
+ enable_irq(rt5677->irq);
+ schedule_delayed_work(&rt5677->resume_irq_check, 0);
+ }
+
return 0;
}
#else
@@ -4861,6 +5030,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.set_tdm_slot = rt5677_set_tdm_slot,
};
+static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
+ .set_sysclk = rt5677_set_dai_sysclk,
+ .set_pll = rt5677_set_dai_pll,
+};
+
static struct snd_soc_dai_driver rt5677_dai[] = {
{
.name = "rt5677-aif1",
@@ -4957,23 +5131,35 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.ops = &rt5677_aif_dai_ops,
},
+ {
+ .name = "rt5677-dspbuffer",
+ .id = RT5677_DSPBUFF,
+ .capture = {
+ .stream_name = "DSP Buffer",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &rt5677_dsp_dai_ops,
+ },
};
-static const struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
- .probe = rt5677_probe,
- .remove = rt5677_remove,
- .suspend = rt5677_suspend,
- .resume = rt5677_resume,
- .set_bias_level = rt5677_set_bias_level,
- .idle_bias_off = true,
- .component_driver = {
- .controls = rt5677_snd_controls,
- .num_controls = ARRAY_SIZE(rt5677_snd_controls),
- .dapm_widgets = rt5677_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
- .dapm_routes = rt5677_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_rt5677 = {
+ .name = RT5677_DRV_NAME,
+ .probe = rt5677_probe,
+ .remove = rt5677_remove,
+ .suspend = rt5677_suspend,
+ .resume = rt5677_resume,
+ .set_bias_level = rt5677_set_bias_level,
+ .controls = rt5677_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5677_snd_controls),
+ .dapm_widgets = rt5677_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
+ .dapm_routes = rt5677_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5677_regmap_physical = {
@@ -5009,99 +5195,277 @@ static const struct regmap_config rt5677_regmap = {
.num_ranges = ARRAY_SIZE(rt5677_ranges),
};
-static const struct i2c_device_id rt5677_i2c_id[] = {
- { "rt5677", RT5677 },
- { "rt5676", RT5676 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
-
static const struct of_device_id rt5677_of_match[] = {
- { .compatible = "realtek,rt5677", RT5677 },
+ { .compatible = "realtek,rt5677", .data = (const void *)RT5677 },
{ }
};
MODULE_DEVICE_TABLE(of, rt5677_of_match);
static const struct acpi_device_id rt5677_acpi_match[] = {
+ { "10EC5677", RT5677 },
{ "RT5677CE", RT5677 },
{ }
};
MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
-static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677,
+static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
u32 val;
- if (!device_property_read_u32(dev, "DCLK", &val))
- rt5677->pdata.dmic2_clk_pin = val;
+ rt5677->pdata.in1_diff =
+ device_property_read_bool(dev, "IN1") ||
+ device_property_read_bool(dev, "realtek,in1-differential");
- rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1");
- rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3");
+ rt5677->pdata.in2_diff =
+ device_property_read_bool(dev, "IN2") ||
+ device_property_read_bool(dev, "realtek,in2-differential");
- device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio);
-}
+ rt5677->pdata.lout1_diff =
+ device_property_read_bool(dev, "OUT1") ||
+ device_property_read_bool(dev, "realtek,lout1-differential");
-static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
- struct device *dev)
-{
- rt5677->pdata.in1_diff = device_property_read_bool(dev,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev,
- "realtek,lout3-differential");
+ rt5677->pdata.lout2_diff =
+ device_property_read_bool(dev, "OUT2") ||
+ device_property_read_bool(dev, "realtek,lout2-differential");
+
+ rt5677->pdata.lout3_diff =
+ device_property_read_bool(dev, "OUT3") ||
+ device_property_read_bool(dev, "realtek,lout3-differential");
device_property_read_u8_array(dev, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
-
- device_property_read_u32(dev, "realtek,jd1-gpio",
- &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "realtek,jd2-gpio",
- &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "realtek,jd3-gpio",
- &rt5677->pdata.jd3_gpio);
+ rt5677->pdata.gpio_config,
+ RT5677_GPIO_NUM);
+
+ if (!device_property_read_u32(dev, "DCLK", &val) ||
+ !device_property_read_u32(dev, "realtek,dmic2_clk_pin", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
+
+ if (!device_property_read_u32(dev, "JD1", &val) ||
+ !device_property_read_u32(dev, "realtek,jd1-gpio", &val))
+ rt5677->pdata.jd1_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD2", &val) ||
+ !device_property_read_u32(dev, "realtek,jd2-gpio", &val))
+ rt5677->pdata.jd2_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD3", &val) ||
+ !device_property_read_u32(dev, "realtek,jd3-gpio", &val))
+ rt5677->pdata.jd3_gpio = val;
}
-static struct regmap_irq rt5677_irqs[] = {
+struct rt5677_irq_desc {
+ unsigned int enable_mask;
+ unsigned int status_mask;
+ unsigned int polarity_mask;
+};
+
+static const struct rt5677_irq_desc rt5677_irq_descs[] = {
[RT5677_IRQ_JD1] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
+static bool rt5677_check_hotword(struct rt5677_priv *rt5677)
+{
+ int reg_gpio;
+
+ if (!rt5677->is_dsp_mode)
+ return false;
+
+ if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, &reg_gpio))
+ return false;
+
+ /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */
+ if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ)
+ return false;
+
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ rt5677_spi_hotword_detected();
+ return true;
+}
+
+static irqreturn_t rt5677_irq(int unused, void *data)
+{
+ struct rt5677_priv *rt5677 = data;
+ int ret, loop, i, reg_irq, virq;
+ bool irq_fired = false;
+
+ mutex_lock(&rt5677->irq_lock);
+
+ /*
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
+ */
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed reading IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ irq_fired = true;
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+
+ /* Clear the interrupt by flipping the polarity
+ * of the interrupt source line that fired
+ */
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ }
+ }
+
+ /* Exit the loop only when we know for sure that GPIO1 pin
+ * was low at some point since irq_lock was acquired. Any event
+ * after that point creates a rising edge that triggers another
+ * call to rt5677_irq().
+ */
+ if (!irq_fired && !rt5677_check_hotword(rt5677))
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed updating IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ }
+exit:
+ WARN_ON_ONCE(loop == 20);
+ mutex_unlock(&rt5677->irq_lock);
+ if (irq_fired)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void rt5677_resume_irq_check(struct work_struct *work)
+{
+ int i, virq;
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, resume_irq_check.work);
+
+ /* This is needed to check and clear the interrupt status register
+ * at resume. If the headset is plugged/unplugged when the device is
+ * fully suspended, there won't be a rising edge at resume to trigger
+ * the interrupt. Without this, we miss the next unplug/plug event.
+ */
+ rt5677_irq(0, rt5677);
+
+ /* Call all enabled jack detect irq handlers again. This is needed in
+ * addition to the above check for a corner case caused by jack gpio
+ * debounce. After codec irq is disabled at suspend, the delayed work
+ * scheduled by soc-jack may run and read wrong jack gpio values, since
+ * the regmap is in cache only mode. At resume, there is no irq because
+ * rt5677_irq has already ran and cleared the irq status at suspend.
+ * Without this explicit check, unplug the headset right after suspend
+ * starts, then after resume the headset is still shown as plugged in.
+ */
+ mutex_lock(&rt5677->irq_lock);
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
+ }
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_lock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ // Set the enable/disable bits for the jack detect IRQs.
+ regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1,
+ RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 |
+ RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en);
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_enable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static void rt5677_irq_disable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static struct irq_chip rt5677_irq_chip = {
+ .name = "rt5677_irq_chip",
+ .irq_bus_lock = rt5677_irq_bus_lock,
+ .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock,
+ .irq_disable = rt5677_irq_disable,
+ .irq_enable = rt5677_irq_enable,
+};
+
+static int rt5677_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct rt5677_priv *rt5677 = h->host_data;
+
+ irq_set_chip_data(virq, rt5677);
+ irq_set_chip(virq, &rt5677_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+ return 0;
+}
+
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
+static const struct irq_domain_ops rt5677_domain_ops = {
+ .map = rt5677_irq_map,
+ .xlate = irq_domain_xlate_twocell,
};
static int rt5677_init_irq(struct i2c_client *i2c)
{
int ret;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ unsigned int jd_mask = 0, jd_val = 0;
if (!rt5677->pdata.jd1_gpio &&
!rt5677->pdata.jd2_gpio &&
@@ -5113,29 +5477,61 @@ static int rt5677_init_irq(struct i2c_client *i2c)
return -EINVAL;
}
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
+ /*
+ * Select RC as the debounce clock so that GPIO works even when
+ * MCLK is gated which happens when there is no audio stream
+ * (SND_SOC_BIAS_OFF).
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ RT5677_IRQ_DEBOUNCE_SEL_MASK,
+ RT5677_IRQ_DEBOUNCE_SEL_RC);
+ /* Enable auto power on RC when GPIO states are changed */
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff);
+
+ /* Select and enable jack detection sources per platform data */
+ if (rt5677->pdata.jd1_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD1_MASK;
+ jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT;
+ }
+ if (rt5677->pdata.jd2_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD2_MASK;
+ jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT;
}
+ if (rt5677->pdata.jd3_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD3_MASK;
+ jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val);
- return 0;
-}
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
-static void rt5677_free_irq(struct i2c_client *i2c)
-{
- struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ /* Ready to listen for interrupts */
+ rt5677->domain = irq_domain_create_linear(dev_fwnode(&i2c->dev),
+ RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
+ if (!rt5677->domain) {
+ dev_err(&i2c->dev, "Failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5677", rt5677);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ rt5677->irq = i2c->irq;
+
+ return ret;
}
-static int rt5677_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5677_i2c_probe(struct i2c_client *i2c)
{
+ struct device *dev = &i2c->dev;
struct rt5677_priv *rt5677;
int ret;
unsigned int val;
@@ -5145,27 +5541,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
if (rt5677 == NULL)
return -ENOMEM;
+ rt5677->dev = &i2c->dev;
+ rt5677->set_dsp_vad = rt5677_set_dsp_vad;
+ INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
- if (i2c->dev.of_node) {
- const struct of_device_id *match_id;
-
- match_id = of_match_device(rt5677_of_match, &i2c->dev);
- if (match_id)
- rt5677->type = (enum rt5677_type)match_id->data;
-
- rt5677_read_device_properties(rt5677, &i2c->dev);
- } else if (ACPI_HANDLE(&i2c->dev)) {
- const struct acpi_device_id *acpi_id;
-
- acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev);
- if (acpi_id)
- rt5677->type = (enum rt5677_type)acpi_id->driver_data;
-
- rt5677_read_acpi_properties(rt5677, &i2c->dev);
- } else {
+ rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev);
+ if (rt5677->type == 0)
return -EINVAL;
- }
+
+ rt5677_read_device_properties(rt5677, &i2c->dev);
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
@@ -5249,9 +5634,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
RT5677_GPIO5_FUNC_MASK,
RT5677_GPIO5_FUNC_DMIC);
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- RT5677_GPIO5_DIR_MASK,
- RT5677_GPIO5_DIR_OUT);
+ rt5677_update_gpio_bits(rt5677, RT5677_GPIO5,
+ RT5677_GPIOx_DIR_MASK,
+ RT5677_GPIOx_DIR_OUT);
}
if (rt5677->pdata.micbias1_vdd_3v3)
@@ -5260,33 +5645,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
RT5677_MICBIAS1_CTRL_VDD_3_3V);
rt5677_init_gpio(i2c);
- rt5677_init_irq(i2c);
+ ret = rt5677_init_irq(i2c);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to initialize irq: %d\n", ret);
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5677,
rt5677_dai, ARRAY_SIZE(rt5677_dai));
}
-static int rt5677_i2c_remove(struct i2c_client *i2c)
+static void rt5677_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
- rt5677_free_irq(i2c);
rt5677_free_gpio(i2c);
-
- return 0;
}
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
- .name = "rt5677",
+ .name = RT5677_DRV_NAME,
.of_match_table = rt5677_of_match,
- .acpi_match_table = ACPI_PTR(rt5677_acpi_match),
+ .acpi_match_table = rt5677_acpi_match,
},
- .probe = rt5677_i2c_probe,
+ .probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
- .id_table = rt5677_i2c_id,
};
module_i2c_driver(rt5677_i2c_driver);
MODULE_DESCRIPTION("ASoC RT5677 driver");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");
+
+MODULE_FIRMWARE("rt5677_elf_vad");
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 97239973edc4..d67ebae067d9 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5677.h -- RT5677 ALSA SoC audio driver
*
* Copyright 2013 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 __RT5677_H__
@@ -1339,6 +1336,8 @@
#define RT5677_PLL_M_SFT 12
#define RT5677_PLL_M_BP (0x1 << 11)
#define RT5677_PLL_M_BP_SFT 11
+#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1)
+#define RT5677_PLL_UPDATE_PLL1_SFT 1
/* Global Clock Control 1 (0x80) */
#define RT5677_SCLK_SRC_MASK (0x3 << 14)
@@ -1456,9 +1455,37 @@
#define RT5677_I2S4_CLK_SEL_MASK (0xf)
#define RT5677_I2S4_CLK_SEL_SFT 0
+/* VAD Function Control 1 (0x9c) */
+#define RT5677_VAD_MIN_DUR_MASK (0x3 << 13)
+#define RT5677_VAD_MIN_DUR_SFT 13
+#define RT5677_VAD_ADPCM_BYPASS (1 << 10)
+#define RT5677_VAD_ADPCM_BYPASS_BIT 10
+#define RT5677_VAD_FG2ENC (1 << 9)
+#define RT5677_VAD_FG2ENC_BIT 9
+#define RT5677_VAD_BUF_OW (1 << 8)
+#define RT5677_VAD_BUF_OW_BIT 8
+#define RT5677_VAD_CLR_FLAG (1 << 7)
+#define RT5677_VAD_CLR_FLAG_BIT 7
+#define RT5677_VAD_BUF_POP (1 << 6)
+#define RT5677_VAD_BUF_POP_BIT 6
+#define RT5677_VAD_BUF_PUSH (1 << 5)
+#define RT5677_VAD_BUF_PUSH_BIT 5
+#define RT5677_VAD_DET_ENABLE (1 << 4)
+#define RT5677_VAD_DET_ENABLE_BIT 4
+#define RT5677_VAD_FUNC_ENABLE (1 << 3)
+#define RT5677_VAD_FUNC_ENABLE_BIT 3
+#define RT5677_VAD_FUNC_RESET (1 << 2)
+#define RT5677_VAD_FUNC_RESET_BIT 2
+
/* VAD Function Control 4 (0x9f) */
-#define RT5677_VAD_SRC_MASK (0x7 << 8)
+#define RT5677_VAD_OUT_SRC_RATE_MASK (0x1 << 11)
+#define RT5677_VAD_OUT_SRC_RATE_SFT 11
+#define RT5677_VAD_OUT_SRC_MASK (0x1 << 10)
+#define RT5677_VAD_OUT_SRC_SFT 10
+#define RT5677_VAD_SRC_MASK (0x3 << 8)
#define RT5677_VAD_SRC_SFT 8
+#define RT5677_VAD_LV_DIFF_MASK (0xff << 0)
+#define RT5677_VAD_LV_DIFF_SFT 0
/* DSP InBound Control (0xa3) */
#define RT5677_IB01_SRC_MASK (0x7 << 12)
@@ -1560,81 +1587,25 @@
#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
-/* GPIO Control 2 (0xc1) */
-#define RT5677_GPIO5_DIR_MASK (0x1 << 14)
-#define RT5677_GPIO5_DIR_SFT 14
-#define RT5677_GPIO5_DIR_IN (0x0 << 14)
-#define RT5677_GPIO5_DIR_OUT (0x1 << 14)
-#define RT5677_GPIO5_OUT_MASK (0x1 << 13)
-#define RT5677_GPIO5_OUT_SFT 13
-#define RT5677_GPIO5_OUT_LO (0x0 << 13)
-#define RT5677_GPIO5_OUT_HI (0x1 << 13)
-#define RT5677_GPIO5_P_MASK (0x1 << 12)
-#define RT5677_GPIO5_P_SFT 12
-#define RT5677_GPIO5_P_NOR (0x0 << 12)
-#define RT5677_GPIO5_P_INV (0x1 << 12)
-#define RT5677_GPIO4_DIR_MASK (0x1 << 11)
-#define RT5677_GPIO4_DIR_SFT 11
-#define RT5677_GPIO4_DIR_IN (0x0 << 11)
-#define RT5677_GPIO4_DIR_OUT (0x1 << 11)
-#define RT5677_GPIO4_OUT_MASK (0x1 << 10)
-#define RT5677_GPIO4_OUT_SFT 10
-#define RT5677_GPIO4_OUT_LO (0x0 << 10)
-#define RT5677_GPIO4_OUT_HI (0x1 << 10)
-#define RT5677_GPIO4_P_MASK (0x1 << 9)
-#define RT5677_GPIO4_P_SFT 9
-#define RT5677_GPIO4_P_NOR (0x0 << 9)
-#define RT5677_GPIO4_P_INV (0x1 << 9)
-#define RT5677_GPIO3_DIR_MASK (0x1 << 8)
-#define RT5677_GPIO3_DIR_SFT 8
-#define RT5677_GPIO3_DIR_IN (0x0 << 8)
-#define RT5677_GPIO3_DIR_OUT (0x1 << 8)
-#define RT5677_GPIO3_OUT_MASK (0x1 << 7)
-#define RT5677_GPIO3_OUT_SFT 7
-#define RT5677_GPIO3_OUT_LO (0x0 << 7)
-#define RT5677_GPIO3_OUT_HI (0x1 << 7)
-#define RT5677_GPIO3_P_MASK (0x1 << 6)
-#define RT5677_GPIO3_P_SFT 6
-#define RT5677_GPIO3_P_NOR (0x0 << 6)
-#define RT5677_GPIO3_P_INV (0x1 << 6)
-#define RT5677_GPIO2_DIR_MASK (0x1 << 5)
-#define RT5677_GPIO2_DIR_SFT 5
-#define RT5677_GPIO2_DIR_IN (0x0 << 5)
-#define RT5677_GPIO2_DIR_OUT (0x1 << 5)
-#define RT5677_GPIO2_OUT_MASK (0x1 << 4)
-#define RT5677_GPIO2_OUT_SFT 4
-#define RT5677_GPIO2_OUT_LO (0x0 << 4)
-#define RT5677_GPIO2_OUT_HI (0x1 << 4)
-#define RT5677_GPIO2_P_MASK (0x1 << 3)
-#define RT5677_GPIO2_P_SFT 3
-#define RT5677_GPIO2_P_NOR (0x0 << 3)
-#define RT5677_GPIO2_P_INV (0x1 << 3)
-#define RT5677_GPIO1_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO1_DIR_SFT 2
-#define RT5677_GPIO1_DIR_IN (0x0 << 2)
-#define RT5677_GPIO1_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO1_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO1_OUT_SFT 1
-#define RT5677_GPIO1_OUT_LO (0x0 << 1)
-#define RT5677_GPIO1_OUT_HI (0x1 << 1)
-#define RT5677_GPIO1_P_MASK (0x1 << 0)
-#define RT5677_GPIO1_P_SFT 0
-#define RT5677_GPIO1_P_NOR (0x0 << 0)
-#define RT5677_GPIO1_P_INV (0x1 << 0)
-
-/* GPIO Control 3 (0xc2) */
-#define RT5677_GPIO6_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO6_DIR_SFT 2
-#define RT5677_GPIO6_DIR_IN (0x0 << 2)
-#define RT5677_GPIO6_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO6_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO6_OUT_SFT 1
-#define RT5677_GPIO6_OUT_LO (0x0 << 1)
-#define RT5677_GPIO6_OUT_HI (0x1 << 1)
-#define RT5677_GPIO6_P_MASK (0x1 << 0)
-#define RT5677_GPIO6_P_SFT 0
-#define RT5677_GPIO6_P_NOR (0x0 << 0)
-#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* GPIO Control 2 (0xc1) & 3 (0xc2) common bits */
+#define RT5677_GPIOx_DIR_MASK (0x1 << 2)
+#define RT5677_GPIOx_DIR_SFT 2
+#define RT5677_GPIOx_DIR_IN (0x0 << 2)
+#define RT5677_GPIOx_DIR_OUT (0x1 << 2)
+#define RT5677_GPIOx_OUT_MASK (0x1 << 1)
+#define RT5677_GPIOx_OUT_SFT 1
+#define RT5677_GPIOx_OUT_LO (0x0 << 1)
+#define RT5677_GPIOx_OUT_HI (0x1 << 1)
+#define RT5677_GPIOx_P_MASK (0x1 << 0)
+#define RT5677_GPIOx_P_SFT 0
+#define RT5677_GPIOx_P_NOR (0x0 << 0)
+#define RT5677_GPIOx_P_INV (0x1 << 0)
+
+/* General Control (0xfa) */
+#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3)
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
#define RT5677_DSP_IB_01_H (0x1 << 15)
@@ -1674,6 +1645,8 @@
#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin"
#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin"
+#define RT5677_DRV_NAME "rt5677"
+
/* System Clock Source */
enum {
RT5677_SCLK_S_MCLK,
@@ -1697,6 +1670,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
+ RT5677_DSPBUFF,
};
enum {
@@ -1713,11 +1687,12 @@ enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
+ RT5677_IRQ_NUM,
};
enum rt5677_type {
- RT5677,
- RT5676,
+ RT5677 = 1,
+ RT5676 = 2,
};
/* ASRC clock source selection */
@@ -1790,7 +1765,8 @@ struct rt5677_platform_data {
};
struct rt5677_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+ struct device *dev;
struct rt5677_platform_data pdata;
struct regmap *regmap, *regmap_physical;
const struct firmware *fw1, *fw2;
@@ -1810,13 +1786,23 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
- bool dsp_vad_en;
- struct regmap_irq_chip_data *irq_data;
+ bool dsp_vad_en_request; /* DSP VAD enable/disable request */
+ bool dsp_vad_en; /* dsp_work parameter */
bool is_dsp_mode;
bool is_vref_slow;
+ struct delayed_work dsp_work;
+
+ /* Interrupt handling */
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+ unsigned int irq_en;
+ struct delayed_work resume_irq_check;
+ int irq;
+
+ int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
};
-int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
#endif /* __RT5677_H__ */
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
new file mode 100644
index 000000000000..bba987308e15
--- /dev/null
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 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/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.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 <sound/rt5682.h>
+
+#include "rl6231.h"
+#include "rt5682.h"
+
+static const struct rt5682_platform_data i2s_default_platform_data = {
+ .dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2,
+ .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3,
+ .jd_src = RT5682_JD1,
+ .btndet_delay = 16,
+ .dai_clk_names[RT5682_DAI_WCLK_IDX] = "rt5682-dai-wclk",
+ .dai_clk_names[RT5682_DAI_BCLK_IDX] = "rt5682-dai-bclk",
+};
+
+static const struct regmap_config rt5682_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682_I2C_MODE,
+ .volatile_reg = rt5682_volatile_register,
+ .readable_reg = rt5682_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682_reg,
+ .num_reg_defaults = RT5682_REG_NUM,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static void rt5682_jd_check_handler(struct work_struct *work)
+{
+ struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv,
+ jd_check_work.work);
+
+ if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) & RT5682_JDH_RS_MASK)
+ /* jack out */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, 0);
+ else
+ schedule_delayed_work(&rt5682->jd_check_work, 500);
+}
+
+static irqreturn_t rt5682_irq(int irq, void *data)
+{
+ struct rt5682_priv *rt5682 = data;
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time));
+
+ return IRQ_HANDLED;
+}
+
+static struct snd_soc_dai_driver rt5682_dai[] = {
+ {
+ .name = "rt5682-aif1",
+ .id = RT5682_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682-aif2",
+ .id = RT5682_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif2_dai_ops,
+ },
+};
+
+static void rt5682_i2c_disable_regulators(void *data)
+{
+ struct rt5682_priv *rt5682 = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies);
+}
+
+static int rt5682_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5682_priv *rt5682;
+ int i, ret;
+ unsigned int val;
+
+ rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv),
+ GFP_KERNEL);
+ if (!rt5682)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5682);
+
+ rt5682->i2c_dev = &i2c->dev;
+
+ rt5682->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt5682->pdata = *pdata;
+ else
+ rt5682_parse_dt(rt5682, &i2c->dev);
+
+ rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap);
+ if (IS_ERR(rt5682->regmap)) {
+ ret = PTR_ERR(rt5682->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++)
+ rt5682->supplies[i].supply = rt5682_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies),
+ rt5682->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies),
+ rt5682->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators,
+ rt5682);
+ if (ret)
+ return ret;
+
+ ret = rt5682_get_ldo1(rt5682, &i2c->dev);
+ if (ret)
+ return ret;
+
+ /* Sleep for 300 ms miniumum */
+ usleep_range(300000, 350000);
+
+ regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1);
+ usleep_range(10000, 15000);
+
+ regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5682\n", val);
+ return -ENODEV;
+ }
+
+ regmap_read(rt5682->regmap, RT5682_INT_DEVICE_ID, &val);
+ if (val == 0x6956) {
+ dev_dbg(&i2c->dev, "ALC5682I-VE device\n");
+ rt5682->ve_ic = true;
+ }
+
+ mutex_init(&rt5682->calibrate_mutex);
+ rt5682_calibrate(rt5682);
+
+ rt5682_apply_patch_list(rt5682, &i2c->dev);
+
+ regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000);
+
+ /* DMIC pin*/
+ if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) {
+ switch (rt5682->pdata.dmic1_data_pin) {
+ case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA);
+ break;
+
+ case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA);
+ break;
+
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ switch (rt5682->pdata.dmic1_clk_pin) {
+ case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK);
+ break;
+
+ case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK);
+ if (rt5682->pdata.dmic_clk_driving_high)
+ regmap_update_bits(rt5682->regmap,
+ RT5682_PAD_DRIVING_CTRL,
+ RT5682_PAD_DRV_GP3_MASK,
+ 2 << RT5682_PAD_DRV_GP3_SFT);
+ break;
+
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+ }
+
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK,
+ RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK,
+ RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1);
+ regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
+ regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8,
+ RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
+ regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
+ regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_FIFO_CLK_DIV_MASK, RT5682_FIFO_CLK_DIV_2);
+
+ INIT_DELAYED_WORK(&rt5682->jack_detect_work,
+ rt5682_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5682->jd_check_work,
+ rt5682_jd_check_handler);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5682", rt5682);
+ if (!ret)
+ rt5682->irq = i2c->irq;
+ else
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ }
+
+#ifdef CONFIG_COMMON_CLK
+ /* Check if MCLK provided */
+ rt5682->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5682->mclk))
+ return PTR_ERR(rt5682->mclk);
+
+ /* Register CCF DAI clock control */
+ ret = rt5682_register_dai_clks(rt5682);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682->lrck[RT5682_AIF1] = 48000;
+#endif
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &rt5682_soc_component_dev,
+ rt5682_dai, ARRAY_SIZE(rt5682_dai));
+}
+
+static void rt5682_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5682_priv *rt5682 = i2c_get_clientdata(client);
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+
+ rt5682_reset(rt5682);
+}
+
+static void rt5682_i2c_remove(struct i2c_client *client)
+{
+ rt5682_i2c_shutdown(client);
+}
+
+static const struct of_device_id rt5682_of_match[] = {
+ {.compatible = "realtek,rt5682i"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5682_of_match);
+
+static const struct acpi_device_id rt5682_acpi_match[] = {
+ { "10EC5682" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match);
+
+static const struct i2c_device_id rt5682_i2c_id[] = {
+ {"rt5682"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id);
+
+static struct i2c_driver rt5682_i2c_driver = {
+ .driver = {
+ .name = "rt5682",
+ .of_match_table = rt5682_of_match,
+ .acpi_match_table = rt5682_acpi_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = rt5682_i2c_probe,
+ .remove = rt5682_i2c_remove,
+ .shutdown = rt5682_i2c_shutdown,
+ .id_table = rt5682_i2c_id,
+};
+module_i2c_driver(rt5682_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
new file mode 100644
index 000000000000..055bea0a4a3b
--- /dev/null
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682-sdw.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Oder Chiou <oder_chiou@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/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5682.h"
+
+#define RT5682_SDW_ADDR_L 0x3000
+#define RT5682_SDW_ADDR_H 0x3001
+#define RT5682_SDW_DATA_L 0x3004
+#define RT5682_SDW_DATA_H 0x3005
+#define RT5682_SDW_CMD 0x3008
+
+static int rt5682_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned int data_l, data_h;
+
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 0);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff));
+ regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_H, &data_h);
+ regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_L, &data_l);
+
+ *val = (data_h << 8) | data_l;
+
+ dev_vdbg(dev, "[%s] %04x => %04x\n", __func__, reg, *val);
+
+ return 0;
+}
+
+static int rt5682_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 1);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff));
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_H, (val >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_L, (val & 0xff));
+
+ dev_vdbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt5682_sdw_indirect_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682_I2C_MODE,
+ .volatile_reg = rt5682_volatile_register,
+ .readable_reg = rt5682_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682_reg,
+ .num_reg_defaults = RT5682_REG_NUM,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt5682_sdw_read,
+ .reg_write = rt5682_sdw_write,
+};
+
+static int rt5682_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 rt5682_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt5682_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 rt5682_priv *rt5682 = 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;
+ unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream)
+ return -ENOMEM;
+
+ if (!rt5682->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt5682->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ case 48000:
+ val_p = RT5682_SDW_REF_1_48K;
+ val_c = RT5682_SDW_REF_2_48K;
+ break;
+ case 96000:
+ val_p = RT5682_SDW_REF_1_96K;
+ val_c = RT5682_SDW_REF_2_96K;
+ break;
+ case 192000:
+ val_p = RT5682_SDW_REF_1_192K;
+ val_c = RT5682_SDW_REF_2_192K;
+ break;
+ case 32000:
+ val_p = RT5682_SDW_REF_1_32K;
+ val_c = RT5682_SDW_REF_2_32K;
+ break;
+ case 24000:
+ val_p = RT5682_SDW_REF_1_24K;
+ val_c = RT5682_SDW_REF_2_24K;
+ break;
+ case 16000:
+ val_p = RT5682_SDW_REF_1_16K;
+ val_c = RT5682_SDW_REF_2_16K;
+ break;
+ case 12000:
+ val_p = RT5682_SDW_REF_1_12K;
+ val_c = RT5682_SDW_REF_2_12K;
+ break;
+ case 8000:
+ val_p = RT5682_SDW_REF_1_8K;
+ val_c = RT5682_SDW_REF_2_8K;
+ break;
+ case 44100:
+ val_p = RT5682_SDW_REF_1_44K;
+ val_c = RT5682_SDW_REF_2_44K;
+ break;
+ case 88200:
+ val_p = RT5682_SDW_REF_1_88K;
+ val_c = RT5682_SDW_REF_2_88K;
+ break;
+ case 176400:
+ val_p = RT5682_SDW_REF_1_176K;
+ val_c = RT5682_SDW_REF_2_176K;
+ break;
+ case 22050:
+ val_p = RT5682_SDW_REF_1_22K;
+ val_c = RT5682_SDW_REF_2_22K;
+ break;
+ case 11025:
+ val_p = RT5682_SDW_REF_1_11K;
+ val_c = RT5682_SDW_REF_2_11K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (params_rate(params) <= 48000) {
+ osr_p = RT5682_DAC_OSR_D_8;
+ osr_c = RT5682_ADC_OSR_D_8;
+ } else if (params_rate(params) <= 96000) {
+ osr_p = RT5682_DAC_OSR_D_4;
+ osr_c = RT5682_ADC_OSR_D_4;
+ } else {
+ osr_p = RT5682_DAC_OSR_D_2;
+ osr_c = RT5682_ADC_OSR_D_2;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK,
+ RT5682_SDW_REF_1_MASK, val_p);
+ regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1,
+ RT5682_DAC_OSR_MASK, osr_p);
+ } else {
+ regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK,
+ RT5682_SDW_REF_2_MASK, val_c);
+ regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1,
+ RT5682_ADC_OSR_MASK, osr_c);
+ }
+
+ return retval;
+}
+
+static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt5682->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt5682->slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt5682_sdw_ops = {
+ .hw_params = rt5682_sdw_hw_params,
+ .hw_free = rt5682_sdw_hw_free,
+ .set_stream = rt5682_set_sdw_stream,
+ .shutdown = rt5682_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver rt5682_dai[] = {
+ {
+ .name = "rt5682-aif1",
+ .id = RT5682_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682-aif2",
+ .id = RT5682_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif2_dai_ops,
+ },
+ {
+ .name = "rt5682-sdw",
+ .id = RT5682_SDW,
+ .playback = {
+ .stream_name = "SDW Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDW Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_sdw_ops,
+ },
+};
+
+static int rt5682_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682;
+ int ret;
+
+ rt5682 = devm_kzalloc(dev, sizeof(*rt5682), GFP_KERNEL);
+ if (!rt5682)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt5682);
+ rt5682->slave = slave;
+ rt5682->sdw_regmap = regmap;
+ rt5682->is_sdw = true;
+
+ mutex_init(&rt5682->disable_irq_lock);
+
+ rt5682->regmap = devm_regmap_init(dev, NULL, dev,
+ &rt5682_sdw_indirect_regmap);
+ if (IS_ERR(rt5682->regmap)) {
+ ret = PTR_ERR(rt5682->regmap);
+ dev_err(dev, "%s: Failed to allocate register map: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+
+ ret = rt5682_get_ldo1(rt5682, dev);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(rt5682->sdw_regmap, true);
+ regcache_cache_only(rt5682->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt5682->hw_init = false;
+ rt5682->first_hw_init = false;
+
+ mutex_init(&rt5682->calibrate_mutex);
+ INIT_DELAYED_WORK(&rt5682->jack_detect_work,
+ rt5682_jack_detect_handler);
+
+ ret = devm_snd_soc_register_component(dev,
+ &rt5682_soc_component_dev,
+ rt5682_dai, ARRAY_SIZE(rt5682_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt5682_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ int ret = 0, loop = 10;
+ unsigned int val;
+
+ rt5682->disable_irq = false;
+
+ if (rt5682->hw_init)
+ return 0;
+
+ regcache_cache_only(rt5682->sdw_regmap, false);
+ regcache_cache_only(rt5682->regmap, false);
+ if (rt5682->first_hw_init)
+ regcache_cache_bypass(rt5682->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt5682->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ while (loop > 0) {
+ regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val);
+ if (val == DEVICE_ID)
+ break;
+ dev_warn(dev, "Device with ID register %x is not rt5682\n", val);
+ usleep_range(30000, 30005);
+ loop--;
+ }
+
+ if (val != DEVICE_ID) {
+ dev_err(dev, "%s: Device with ID register %x is not rt5682\n", __func__, val);
+ ret = -ENODEV;
+ goto err_nodev;
+ }
+
+ rt5682_calibrate(rt5682);
+
+ if (rt5682->first_hw_init) {
+ regcache_cache_bypass(rt5682->regmap, false);
+ regcache_mark_dirty(rt5682->regmap);
+ regcache_sync(rt5682->regmap);
+
+ /* volatile registers */
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2,
+ RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL);
+
+ goto reinit;
+ }
+
+ rt5682_apply_patch_list(rt5682, dev);
+
+ regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000);
+
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK,
+ RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
+ regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8,
+ RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
+ regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
+ regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
+
+ /* Soundwire */
+ regmap_write(rt5682->regmap, RT5682_PLL2_INTERNAL, 0xa266);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_1, 0x1700);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_2, 0x0006);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_3, 0x2600);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_4, 0x0c8f);
+ regmap_write(rt5682->regmap, RT5682_PLL_TRACK_2, 0x3000);
+ regmap_write(rt5682->regmap, RT5682_PLL_TRACK_3, 0x4000);
+ regmap_update_bits(rt5682->regmap, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK | RT5682_PLL2_SRC_MASK,
+ RT5682_SCLK_SRC_PLL2 | RT5682_PLL2_SRC_SDW);
+
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2,
+ RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL);
+ regmap_write(rt5682->regmap, RT5682_CBJ_CTRL_1, 0xd142);
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_5, 0x0700, 0x0600);
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_3,
+ RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_IRQ | RT5682_POW_JDH |
+ RT5682_POW_ANA, RT5682_POW_IRQ |
+ RT5682_POW_JDH | RT5682_POW_ANA);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2,
+ RT5682_PWR_JDH, RT5682_PWR_JDH);
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK | RT5682_JD1_IRQ_MASK,
+ RT5682_JD1_EN | RT5682_JD1_IRQ_PUL);
+
+reinit:
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(250));
+
+ /* Mark Slave initialization complete */
+ rt5682->hw_init = true;
+ rt5682->first_hw_init = true;
+
+err_nodev:
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool rt5682_sdw_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x3000:
+ case 0x3001:
+ case 0x3004:
+ case 0x3005:
+ case 0x3008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt5682_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = RT5682_I2C_MODE,
+ .readable_reg = rt5682_sdw_readable_register,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt5682_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt5682->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt5682_io_init(&slave->dev, slave);
+}
+
+static int rt5682_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_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x4; /* 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;
+
+ 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;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+/* Bus clock frequency */
+#define RT5682_CLK_FREQ_9600000HZ 9600000
+#define RT5682_CLK_FREQ_12000000HZ 12000000
+#define RT5682_CLK_FREQ_6000000HZ 6000000
+#define RT5682_CLK_FREQ_4800000HZ 4800000
+#define RT5682_CLK_FREQ_2400000HZ 2400000
+#define RT5682_CLK_FREQ_12288000HZ 12288000
+
+static int rt5682_clock_config(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt5682->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT5682_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT5682_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT5682_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT5682_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT5682_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT5682_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt5682->sdw_regmap, 0xe0, value);
+ regmap_write(rt5682->sdw_regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static int rt5682_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt5682->params, params, sizeof(*params));
+
+ ret = rt5682_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return ret;
+}
+
+static int rt5682_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt5682->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt5682->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time));
+ }
+ mutex_unlock(&rt5682->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt5682_slave_ops = {
+ .read_prop = rt5682_read_prop,
+ .interrupt_callback = rt5682_interrupt_callback,
+ .update_status = rt5682_update_status,
+ .bus_config = rt5682_bus_config,
+};
+
+static int rt5682_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt5682_sdw_regmap);
+ if (IS_ERR(regmap))
+ return -EINVAL;
+
+ return rt5682_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt5682_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ if (rt5682->hw_init)
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt5682_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x5682, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt5682_id);
+
+static int rt5682_dev_suspend(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+
+ if (!rt5682->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ regcache_cache_only(rt5682->sdw_regmap, true);
+ regcache_cache_only(rt5682->regmap, true);
+ regcache_mark_dirty(rt5682->regmap);
+
+ return 0;
+}
+
+static int rt5682_dev_system_suspend(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!rt5682->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt5682->disable_irq_lock);
+ rt5682->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt5682->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt5682_dev_suspend(dev);
+}
+
+static int rt5682_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt5682->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt5682->disable_irq_lock);
+ if (rt5682->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF);
+ rt5682->disable_irq = false;
+ }
+ mutex_unlock(&rt5682->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt5682->sdw_regmap, false);
+ regcache_cache_only(rt5682->regmap, false);
+ regcache_sync(rt5682->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt5682_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume)
+ RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL)
+};
+
+static struct sdw_driver rt5682_sdw_driver = {
+ .driver = {
+ .name = "rt5682",
+ .pm = pm_ptr(&rt5682_pm),
+ },
+ .probe = rt5682_sdw_probe,
+ .remove = rt5682_sdw_remove,
+ .ops = &rt5682_slave_ops,
+ .id_table = rt5682_id,
+};
+module_sdw_driver(rt5682_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver SDW");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
new file mode 100644
index 000000000000..a0abd2ce0c1e
--- /dev/null
+++ b/sound/soc/codecs/rt5682.c
@@ -0,0 +1,3191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 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/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.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 <sound/rt5682.h>
+
+#include "rl6231.h"
+#include "rt5682.h"
+
+const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = {
+ "AVDD",
+ "MICVDD",
+ "VBAT",
+ "DBVDD",
+ "LDO1-IN",
+};
+EXPORT_SYMBOL_GPL(rt5682_supply_names);
+
+static const struct reg_sequence patch_list[] = {
+ {RT5682_HP_IMP_SENS_CTRL_19, 0x1000},
+ {RT5682_DAC_ADC_DIG_VOL1, 0xa020},
+ {RT5682_I2C_CTRL, 0x000f},
+ {RT5682_PLL2_INTERNAL, 0x8266},
+ {RT5682_SAR_IL_CMD_1, 0x22b7},
+ {RT5682_SAR_IL_CMD_3, 0x0365},
+ {RT5682_SAR_IL_CMD_6, 0x0110},
+ {RT5682_CHARGE_PUMP_1, 0x0210},
+ {RT5682_HP_LOGIC_CTRL_2, 0x0007},
+ {RT5682_SAR_IL_CMD_2, 0xac00},
+ {RT5682_CBJ_CTRL_7, 0x0104},
+};
+
+void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(rt5682->regmap, patch_list,
+ ARRAY_SIZE(patch_list));
+ if (ret)
+ dev_warn(dev, "Failed to apply regmap patch: %d\n", ret);
+}
+EXPORT_SYMBOL_GPL(rt5682_apply_patch_list);
+
+const struct reg_default rt5682_reg[RT5682_REG_NUM] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x8000},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x800f},
+ {0x000b, 0x0000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x1404},
+ {0x0013, 0x1000},
+ {0x0014, 0xa00a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0019, 0xafaf},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x0080},
+ {0x0044, 0x0c0c},
+ {0x0049, 0x0000},
+ {0x0061, 0x0000},
+ {0x0062, 0x0000},
+ {0x0063, 0x003f},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0a10},
+ {0x0070, 0x8000},
+ {0x0071, 0x8000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x0080, 0x0000},
+ {0x0081, 0x0000},
+ {0x0082, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008d, 0x0000},
+ {0x008e, 0x0060},
+ {0x008f, 0x1000},
+ {0x0091, 0x0c26},
+ {0x0092, 0x0073},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0098, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x100c},
+ {0x009f, 0x0000},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00ae, 0x2040},
+ {0x00af, 0x0000},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d1, 0x2244},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0009},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f1, 0x00d0},
+ {0x00f2, 0x00d0},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6530},
+ {0x0100, 0xa0a0},
+ {0x010b, 0x0000},
+ {0x010c, 0xae00},
+ {0x010d, 0xaaa0},
+ {0x010e, 0x8aa2},
+ {0x010f, 0x02a2},
+ {0x0110, 0xc000},
+ {0x0111, 0x04a2},
+ {0x0112, 0x2800},
+ {0x0113, 0x0000},
+ {0x0117, 0x0100},
+ {0x0125, 0x0410},
+ {0x0132, 0x6026},
+ {0x0136, 0x5555},
+ {0x0138, 0x3700},
+ {0x013a, 0x2000},
+ {0x013b, 0x2000},
+ {0x013c, 0x2005},
+ {0x013f, 0x0000},
+ {0x0142, 0x0000},
+ {0x0145, 0x0002},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0149, 0x0000},
+ {0x0150, 0x79a1},
+ {0x0156, 0xaaaa},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0021},
+ {0x0190, 0x413d},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01af, 0x0000},
+ {0x01b0, 0x0400},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0000},
+ {0x01b4, 0x0000},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x01c3},
+ {0x01b7, 0x02a0},
+ {0x01b8, 0x03e9},
+ {0x01b9, 0x1389},
+ {0x01ba, 0xc351},
+ {0x01bb, 0x0009},
+ {0x01bc, 0x0018},
+ {0x01bd, 0x002a},
+ {0x01be, 0x004c},
+ {0x01bf, 0x0097},
+ {0x01c0, 0x433d},
+ {0x01c2, 0x0000},
+ {0x01c3, 0x0000},
+ {0x01c4, 0x0000},
+ {0x01c5, 0x0000},
+ {0x01c6, 0x0000},
+ {0x01c7, 0x0000},
+ {0x01c8, 0x40af},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0008},
+ {0x01d8, 0x0029},
+ {0x01d9, 0x3333},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0320},
+ {0x01e0, 0x06a1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01ea, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa005},
+ {0x0212, 0x824c},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x0250, 0x4500},
+ {0x0251, 0x40b3},
+ {0x0252, 0x0000},
+ {0x0253, 0x0000},
+ {0x0254, 0x0000},
+ {0x0255, 0x0000},
+ {0x0256, 0x0000},
+ {0x0257, 0x0000},
+ {0x0258, 0x0000},
+ {0x0259, 0x0000},
+ {0x025a, 0x0005},
+ {0x0270, 0x0000},
+ {0x02ff, 0x0110},
+ {0x0300, 0x001f},
+ {0x0301, 0x032c},
+ {0x0302, 0x5f21},
+ {0x0303, 0x4000},
+ {0x0304, 0x4000},
+ {0x0305, 0x06d5},
+ {0x0306, 0x8000},
+ {0x0307, 0x0700},
+ {0x0310, 0x4560},
+ {0x0311, 0xa4a8},
+ {0x0312, 0x7418},
+ {0x0313, 0x0000},
+ {0x0314, 0x0006},
+ {0x0315, 0xffff},
+ {0x0316, 0xc400},
+ {0x0317, 0x0000},
+ {0x03c0, 0x7e00},
+ {0x03c1, 0x8000},
+ {0x03c2, 0x8000},
+ {0x03c3, 0x8000},
+ {0x03c4, 0x8000},
+ {0x03c5, 0x8000},
+ {0x03c6, 0x8000},
+ {0x03c7, 0x8000},
+ {0x03c8, 0x8000},
+ {0x03c9, 0x8000},
+ {0x03ca, 0x8000},
+ {0x03cb, 0x8000},
+ {0x03cc, 0x8000},
+ {0x03d0, 0x0000},
+ {0x03d1, 0x0000},
+ {0x03d2, 0x0000},
+ {0x03d3, 0x0000},
+ {0x03d4, 0x2000},
+ {0x03d5, 0x2000},
+ {0x03d6, 0x0000},
+ {0x03d7, 0x0000},
+ {0x03d8, 0x2000},
+ {0x03d9, 0x2000},
+ {0x03da, 0x2000},
+ {0x03db, 0x2000},
+ {0x03dc, 0x0000},
+ {0x03dd, 0x0000},
+ {0x03de, 0x0000},
+ {0x03df, 0x2000},
+ {0x03e0, 0x0000},
+ {0x03e1, 0x0000},
+ {0x03e2, 0x0000},
+ {0x03e3, 0x0000},
+ {0x03e4, 0x0000},
+ {0x03e5, 0x0000},
+ {0x03e6, 0x0000},
+ {0x03e7, 0x0000},
+ {0x03e8, 0x0000},
+ {0x03e9, 0x0000},
+ {0x03ea, 0x0000},
+ {0x03eb, 0x0000},
+ {0x03ec, 0x0000},
+ {0x03ed, 0x0000},
+ {0x03ee, 0x0000},
+ {0x03ef, 0x0000},
+ {0x03f0, 0x0800},
+ {0x03f1, 0x0800},
+ {0x03f2, 0x0800},
+ {0x03f3, 0x0800},
+};
+EXPORT_SYMBOL_GPL(rt5682_reg);
+
+bool rt5682_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682_RESET:
+ case RT5682_CBJ_CTRL_2:
+ case RT5682_INT_ST_1:
+ case RT5682_4BTN_IL_CMD_1:
+ case RT5682_AJD1_CTRL:
+ case RT5682_HP_CALIB_CTRL_1:
+ case RT5682_INT_DEVICE_ID:
+ case RT5682_DEVICE_ID:
+ case RT5682_I2C_MODE:
+ case RT5682_HP_CALIB_CTRL_10:
+ case RT5682_EFUSE_CTRL_2:
+ case RT5682_JD_TOP_VC_VTRL:
+ case RT5682_HP_IMP_SENS_CTRL_19:
+ case RT5682_IL_CMD_1:
+ case RT5682_SAR_IL_CMD_2:
+ case RT5682_SAR_IL_CMD_4:
+ case RT5682_SAR_IL_CMD_10:
+ case RT5682_SAR_IL_CMD_11:
+ case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11:
+ case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_volatile_register);
+
+bool rt5682_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682_RESET:
+ case RT5682_INT_DEVICE_ID:
+ case RT5682_VERSION_ID:
+ case RT5682_VENDOR_ID:
+ case RT5682_DEVICE_ID:
+ case RT5682_HP_CTRL_1:
+ case RT5682_HP_CTRL_2:
+ case RT5682_HPL_GAIN:
+ case RT5682_HPR_GAIN:
+ case RT5682_I2C_CTRL:
+ case RT5682_CBJ_BST_CTRL:
+ case RT5682_CBJ_CTRL_1:
+ case RT5682_CBJ_CTRL_2:
+ case RT5682_CBJ_CTRL_3:
+ case RT5682_CBJ_CTRL_4:
+ case RT5682_CBJ_CTRL_5:
+ case RT5682_CBJ_CTRL_6:
+ case RT5682_CBJ_CTRL_7:
+ case RT5682_DAC1_DIG_VOL:
+ case RT5682_STO1_ADC_DIG_VOL:
+ case RT5682_STO1_ADC_BOOST:
+ case RT5682_HP_IMP_GAIN_1:
+ case RT5682_HP_IMP_GAIN_2:
+ case RT5682_SIDETONE_CTRL:
+ case RT5682_STO1_ADC_MIXER:
+ case RT5682_AD_DA_MIXER:
+ case RT5682_STO1_DAC_MIXER:
+ case RT5682_A_DAC1_MUX:
+ case RT5682_DIG_INF2_DATA:
+ case RT5682_REC_MIXER:
+ case RT5682_CAL_REC:
+ case RT5682_ALC_BACK_GAIN:
+ case RT5682_PWR_DIG_1:
+ case RT5682_PWR_DIG_2:
+ case RT5682_PWR_ANLG_1:
+ case RT5682_PWR_ANLG_2:
+ case RT5682_PWR_ANLG_3:
+ case RT5682_PWR_MIXER:
+ case RT5682_PWR_VOL:
+ case RT5682_CLK_DET:
+ case RT5682_RESET_LPF_CTRL:
+ case RT5682_RESET_HPF_CTRL:
+ case RT5682_DMIC_CTRL_1:
+ case RT5682_I2S1_SDP:
+ case RT5682_I2S2_SDP:
+ case RT5682_ADDA_CLK_1:
+ case RT5682_ADDA_CLK_2:
+ case RT5682_I2S1_F_DIV_CTRL_1:
+ case RT5682_I2S1_F_DIV_CTRL_2:
+ case RT5682_TDM_CTRL:
+ case RT5682_TDM_ADDA_CTRL_1:
+ case RT5682_TDM_ADDA_CTRL_2:
+ case RT5682_DATA_SEL_CTRL_1:
+ case RT5682_TDM_TCON_CTRL:
+ case RT5682_GLB_CLK:
+ case RT5682_PLL_CTRL_1:
+ case RT5682_PLL_CTRL_2:
+ case RT5682_PLL_TRACK_1:
+ case RT5682_PLL_TRACK_2:
+ case RT5682_PLL_TRACK_3:
+ case RT5682_PLL_TRACK_4:
+ case RT5682_PLL_TRACK_5:
+ case RT5682_PLL_TRACK_6:
+ case RT5682_PLL_TRACK_11:
+ case RT5682_SDW_REF_CLK:
+ case RT5682_DEPOP_1:
+ case RT5682_DEPOP_2:
+ case RT5682_HP_CHARGE_PUMP_1:
+ case RT5682_HP_CHARGE_PUMP_2:
+ case RT5682_MICBIAS_1:
+ case RT5682_MICBIAS_2:
+ case RT5682_PLL_TRACK_12:
+ case RT5682_PLL_TRACK_14:
+ case RT5682_PLL2_CTRL_1:
+ case RT5682_PLL2_CTRL_2:
+ case RT5682_PLL2_CTRL_3:
+ case RT5682_PLL2_CTRL_4:
+ case RT5682_RC_CLK_CTRL:
+ case RT5682_I2S_M_CLK_CTRL_1:
+ case RT5682_I2S2_F_DIV_CTRL_1:
+ case RT5682_I2S2_F_DIV_CTRL_2:
+ case RT5682_EQ_CTRL_1:
+ case RT5682_EQ_CTRL_2:
+ case RT5682_IRQ_CTRL_1:
+ case RT5682_IRQ_CTRL_2:
+ case RT5682_IRQ_CTRL_3:
+ case RT5682_IRQ_CTRL_4:
+ case RT5682_INT_ST_1:
+ case RT5682_GPIO_CTRL_1:
+ case RT5682_GPIO_CTRL_2:
+ case RT5682_GPIO_CTRL_3:
+ case RT5682_HP_AMP_DET_CTRL_1:
+ case RT5682_HP_AMP_DET_CTRL_2:
+ case RT5682_MID_HP_AMP_DET:
+ case RT5682_LOW_HP_AMP_DET:
+ case RT5682_DELAY_BUF_CTRL:
+ case RT5682_SV_ZCD_1:
+ case RT5682_SV_ZCD_2:
+ case RT5682_IL_CMD_1:
+ case RT5682_IL_CMD_2:
+ case RT5682_IL_CMD_3:
+ case RT5682_IL_CMD_4:
+ case RT5682_IL_CMD_5:
+ case RT5682_IL_CMD_6:
+ case RT5682_4BTN_IL_CMD_1:
+ case RT5682_4BTN_IL_CMD_2:
+ case RT5682_4BTN_IL_CMD_3:
+ case RT5682_4BTN_IL_CMD_4:
+ case RT5682_4BTN_IL_CMD_5:
+ case RT5682_4BTN_IL_CMD_6:
+ case RT5682_4BTN_IL_CMD_7:
+ case RT5682_ADC_STO1_HP_CTRL_1:
+ case RT5682_ADC_STO1_HP_CTRL_2:
+ case RT5682_AJD1_CTRL:
+ case RT5682_JD1_THD:
+ case RT5682_JD2_THD:
+ case RT5682_JD_CTRL_1:
+ case RT5682_DUMMY_1:
+ case RT5682_DUMMY_2:
+ case RT5682_DUMMY_3:
+ case RT5682_DAC_ADC_DIG_VOL1:
+ case RT5682_BIAS_CUR_CTRL_2:
+ case RT5682_BIAS_CUR_CTRL_3:
+ case RT5682_BIAS_CUR_CTRL_4:
+ case RT5682_BIAS_CUR_CTRL_5:
+ case RT5682_BIAS_CUR_CTRL_6:
+ case RT5682_BIAS_CUR_CTRL_7:
+ case RT5682_BIAS_CUR_CTRL_8:
+ case RT5682_BIAS_CUR_CTRL_9:
+ case RT5682_BIAS_CUR_CTRL_10:
+ case RT5682_VREF_REC_OP_FB_CAP_CTRL:
+ case RT5682_CHARGE_PUMP_1:
+ case RT5682_DIG_IN_CTRL_1:
+ case RT5682_PAD_DRIVING_CTRL:
+ case RT5682_SOFT_RAMP_DEPOP:
+ case RT5682_CHOP_DAC:
+ case RT5682_CHOP_ADC:
+ case RT5682_CALIB_ADC_CTRL:
+ case RT5682_VOL_TEST:
+ case RT5682_SPKVDD_DET_STA:
+ case RT5682_TEST_MODE_CTRL_1:
+ case RT5682_TEST_MODE_CTRL_2:
+ case RT5682_TEST_MODE_CTRL_3:
+ case RT5682_TEST_MODE_CTRL_4:
+ case RT5682_TEST_MODE_CTRL_5:
+ case RT5682_PLL1_INTERNAL:
+ case RT5682_PLL2_INTERNAL:
+ case RT5682_STO_NG2_CTRL_1:
+ case RT5682_STO_NG2_CTRL_2:
+ case RT5682_STO_NG2_CTRL_3:
+ case RT5682_STO_NG2_CTRL_4:
+ case RT5682_STO_NG2_CTRL_5:
+ case RT5682_STO_NG2_CTRL_6:
+ case RT5682_STO_NG2_CTRL_7:
+ case RT5682_STO_NG2_CTRL_8:
+ case RT5682_STO_NG2_CTRL_9:
+ case RT5682_STO_NG2_CTRL_10:
+ case RT5682_STO1_DAC_SIL_DET:
+ case RT5682_SIL_PSV_CTRL1:
+ case RT5682_SIL_PSV_CTRL2:
+ case RT5682_SIL_PSV_CTRL3:
+ case RT5682_SIL_PSV_CTRL4:
+ case RT5682_SIL_PSV_CTRL5:
+ case RT5682_HP_IMP_SENS_CTRL_01:
+ case RT5682_HP_IMP_SENS_CTRL_02:
+ case RT5682_HP_IMP_SENS_CTRL_03:
+ case RT5682_HP_IMP_SENS_CTRL_04:
+ case RT5682_HP_IMP_SENS_CTRL_05:
+ case RT5682_HP_IMP_SENS_CTRL_06:
+ case RT5682_HP_IMP_SENS_CTRL_07:
+ case RT5682_HP_IMP_SENS_CTRL_08:
+ case RT5682_HP_IMP_SENS_CTRL_09:
+ case RT5682_HP_IMP_SENS_CTRL_10:
+ case RT5682_HP_IMP_SENS_CTRL_11:
+ case RT5682_HP_IMP_SENS_CTRL_12:
+ case RT5682_HP_IMP_SENS_CTRL_13:
+ case RT5682_HP_IMP_SENS_CTRL_14:
+ case RT5682_HP_IMP_SENS_CTRL_15:
+ case RT5682_HP_IMP_SENS_CTRL_16:
+ case RT5682_HP_IMP_SENS_CTRL_17:
+ case RT5682_HP_IMP_SENS_CTRL_18:
+ case RT5682_HP_IMP_SENS_CTRL_19:
+ case RT5682_HP_IMP_SENS_CTRL_20:
+ case RT5682_HP_IMP_SENS_CTRL_21:
+ case RT5682_HP_IMP_SENS_CTRL_22:
+ case RT5682_HP_IMP_SENS_CTRL_23:
+ case RT5682_HP_IMP_SENS_CTRL_24:
+ case RT5682_HP_IMP_SENS_CTRL_25:
+ case RT5682_HP_IMP_SENS_CTRL_26:
+ case RT5682_HP_IMP_SENS_CTRL_27:
+ case RT5682_HP_IMP_SENS_CTRL_28:
+ case RT5682_HP_IMP_SENS_CTRL_29:
+ case RT5682_HP_IMP_SENS_CTRL_30:
+ case RT5682_HP_IMP_SENS_CTRL_31:
+ case RT5682_HP_IMP_SENS_CTRL_32:
+ case RT5682_HP_IMP_SENS_CTRL_33:
+ case RT5682_HP_IMP_SENS_CTRL_34:
+ case RT5682_HP_IMP_SENS_CTRL_35:
+ case RT5682_HP_IMP_SENS_CTRL_36:
+ case RT5682_HP_IMP_SENS_CTRL_37:
+ case RT5682_HP_IMP_SENS_CTRL_38:
+ case RT5682_HP_IMP_SENS_CTRL_39:
+ case RT5682_HP_IMP_SENS_CTRL_40:
+ case RT5682_HP_IMP_SENS_CTRL_41:
+ case RT5682_HP_IMP_SENS_CTRL_42:
+ case RT5682_HP_IMP_SENS_CTRL_43:
+ case RT5682_HP_LOGIC_CTRL_1:
+ case RT5682_HP_LOGIC_CTRL_2:
+ case RT5682_HP_LOGIC_CTRL_3:
+ case RT5682_HP_CALIB_CTRL_1:
+ case RT5682_HP_CALIB_CTRL_2:
+ case RT5682_HP_CALIB_CTRL_3:
+ case RT5682_HP_CALIB_CTRL_4:
+ case RT5682_HP_CALIB_CTRL_5:
+ case RT5682_HP_CALIB_CTRL_6:
+ case RT5682_HP_CALIB_CTRL_7:
+ case RT5682_HP_CALIB_CTRL_9:
+ case RT5682_HP_CALIB_CTRL_10:
+ case RT5682_HP_CALIB_CTRL_11:
+ case RT5682_HP_CALIB_STA_1:
+ case RT5682_HP_CALIB_STA_2:
+ case RT5682_HP_CALIB_STA_3:
+ case RT5682_HP_CALIB_STA_4:
+ case RT5682_HP_CALIB_STA_5:
+ case RT5682_HP_CALIB_STA_6:
+ case RT5682_HP_CALIB_STA_7:
+ case RT5682_HP_CALIB_STA_8:
+ case RT5682_HP_CALIB_STA_9:
+ case RT5682_HP_CALIB_STA_10:
+ case RT5682_HP_CALIB_STA_11:
+ case RT5682_SAR_IL_CMD_1:
+ case RT5682_SAR_IL_CMD_2:
+ case RT5682_SAR_IL_CMD_3:
+ case RT5682_SAR_IL_CMD_4:
+ case RT5682_SAR_IL_CMD_5:
+ case RT5682_SAR_IL_CMD_6:
+ case RT5682_SAR_IL_CMD_7:
+ case RT5682_SAR_IL_CMD_8:
+ case RT5682_SAR_IL_CMD_9:
+ case RT5682_SAR_IL_CMD_10:
+ case RT5682_SAR_IL_CMD_11:
+ case RT5682_SAR_IL_CMD_12:
+ case RT5682_SAR_IL_CMD_13:
+ case RT5682_EFUSE_CTRL_1:
+ case RT5682_EFUSE_CTRL_2:
+ case RT5682_EFUSE_CTRL_3:
+ case RT5682_EFUSE_CTRL_4:
+ case RT5682_EFUSE_CTRL_5:
+ case RT5682_EFUSE_CTRL_6:
+ case RT5682_EFUSE_CTRL_7:
+ case RT5682_EFUSE_CTRL_8:
+ case RT5682_EFUSE_CTRL_9:
+ case RT5682_EFUSE_CTRL_10:
+ case RT5682_EFUSE_CTRL_11:
+ case RT5682_JD_TOP_VC_VTRL:
+ case RT5682_DRC1_CTRL_0:
+ case RT5682_DRC1_CTRL_1:
+ case RT5682_DRC1_CTRL_2:
+ case RT5682_DRC1_CTRL_3:
+ case RT5682_DRC1_CTRL_4:
+ case RT5682_DRC1_CTRL_5:
+ case RT5682_DRC1_CTRL_6:
+ case RT5682_DRC1_HARD_LMT_CTRL_1:
+ case RT5682_DRC1_HARD_LMT_CTRL_2:
+ case RT5682_DRC1_PRIV_1:
+ case RT5682_DRC1_PRIV_2:
+ case RT5682_DRC1_PRIV_3:
+ case RT5682_DRC1_PRIV_4:
+ case RT5682_DRC1_PRIV_5:
+ case RT5682_DRC1_PRIV_6:
+ case RT5682_DRC1_PRIV_7:
+ case RT5682_DRC1_PRIV_8:
+ case RT5682_EQ_AUTO_RCV_CTRL1:
+ case RT5682_EQ_AUTO_RCV_CTRL2:
+ case RT5682_EQ_AUTO_RCV_CTRL3:
+ case RT5682_EQ_AUTO_RCV_CTRL4:
+ case RT5682_EQ_AUTO_RCV_CTRL5:
+ case RT5682_EQ_AUTO_RCV_CTRL6:
+ case RT5682_EQ_AUTO_RCV_CTRL7:
+ case RT5682_EQ_AUTO_RCV_CTRL8:
+ case RT5682_EQ_AUTO_RCV_CTRL9:
+ case RT5682_EQ_AUTO_RCV_CTRL10:
+ case RT5682_EQ_AUTO_RCV_CTRL11:
+ case RT5682_EQ_AUTO_RCV_CTRL12:
+ case RT5682_EQ_AUTO_RCV_CTRL13:
+ case RT5682_ADC_L_EQ_LPF1_A1:
+ case RT5682_R_EQ_LPF1_A1:
+ case RT5682_L_EQ_LPF1_H0:
+ case RT5682_R_EQ_LPF1_H0:
+ case RT5682_L_EQ_BPF1_A1:
+ case RT5682_R_EQ_BPF1_A1:
+ case RT5682_L_EQ_BPF1_A2:
+ case RT5682_R_EQ_BPF1_A2:
+ case RT5682_L_EQ_BPF1_H0:
+ case RT5682_R_EQ_BPF1_H0:
+ case RT5682_L_EQ_BPF2_A1:
+ case RT5682_R_EQ_BPF2_A1:
+ case RT5682_L_EQ_BPF2_A2:
+ case RT5682_R_EQ_BPF2_A2:
+ case RT5682_L_EQ_BPF2_H0:
+ case RT5682_R_EQ_BPF2_H0:
+ case RT5682_L_EQ_BPF3_A1:
+ case RT5682_R_EQ_BPF3_A1:
+ case RT5682_L_EQ_BPF3_A2:
+ case RT5682_R_EQ_BPF3_A2:
+ case RT5682_L_EQ_BPF3_H0:
+ case RT5682_R_EQ_BPF3_H0:
+ case RT5682_L_EQ_BPF4_A1:
+ case RT5682_R_EQ_BPF4_A1:
+ case RT5682_L_EQ_BPF4_A2:
+ case RT5682_R_EQ_BPF4_A2:
+ case RT5682_L_EQ_BPF4_H0:
+ case RT5682_R_EQ_BPF4_H0:
+ case RT5682_L_EQ_HPF1_A1:
+ case RT5682_R_EQ_HPF1_A1:
+ case RT5682_L_EQ_HPF1_H0:
+ case RT5682_R_EQ_HPF1_H0:
+ case RT5682_L_EQ_PRE_VOL:
+ case RT5682_R_EQ_PRE_VOL:
+ case RT5682_L_EQ_POST_VOL:
+ case RT5682_R_EQ_POST_VOL:
+ case RT5682_I2C_MODE:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_readable_register);
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 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)
+);
+
+/* Interface data select */
+static const char * const rt5682_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum,
+ RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select);
+
+static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum);
+
+static const char * const rt5682_dac_select[] = {
+ "IF1", "SOUND"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682_dacl_enum,
+ RT5682_AD_DA_MIXER, RT5682_DAC1_L_SEL_SFT, rt5682_dac_select);
+
+static const struct snd_kcontrol_new rt5682_dac_l_mux =
+ SOC_DAPM_ENUM("DAC L Mux", rt5682_dacl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_dacr_enum,
+ RT5682_AD_DA_MIXER, RT5682_DAC1_R_SEL_SFT, rt5682_dac_select);
+
+static const struct snd_kcontrol_new rt5682_dac_r_mux =
+ SOC_DAPM_ENUM("DAC R Mux", rt5682_dacr_enum);
+
+void rt5682_reset(struct rt5682_priv *rt5682)
+{
+ regmap_write(rt5682->regmap, RT5682_RESET, 0);
+ if (!rt5682->is_sdw)
+ regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1);
+}
+EXPORT_SYMBOL_GPL(rt5682_reset);
+
+/**
+ * rt5682_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 RT5682 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 component driver will turn on
+ * ASRC for these filters if ASRC is selected as their clock source.
+ */
+int rt5682_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ switch (clk_src) {
+ case RT5682_CLK_SEL_SYS:
+ case RT5682_CLK_SEL_I2S1_ASRC:
+ case RT5682_CLK_SEL_I2S2_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5682_DA_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2,
+ RT5682_FILTER_CLK_SEL_MASK,
+ clk_src << RT5682_FILTER_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5682_AD_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3,
+ RT5682_FILTER_CLK_SEL_MASK,
+ clk_src << RT5682_FILTER_CLK_SEL_SFT);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src);
+
+static int rt5682_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5682_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val);
+ dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type);
+ snd_soc_component_update_bits(component,
+ RT5682_SAR_IL_CMD_2, 0x10, 0x10);
+
+ return btn_type;
+}
+
+static void rt5682_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13,
+ RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN);
+ snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK,
+ RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR);
+ if (rt5682->is_sdw)
+ snd_soc_component_update_bits(component,
+ RT5682_IRQ_CTRL_3,
+ RT5682_IL_IRQ_MASK | RT5682_IL_IRQ_TYPE_MASK,
+ RT5682_IL_IRQ_EN | RT5682_IL_IRQ_PUL);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK,
+ RT5682_IL_IRQ_EN);
+ } else {
+ snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3,
+ RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13,
+ RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE);
+ }
+}
+
+/**
+ * rt5682_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = &component->dapm;
+ unsigned int val, count;
+
+ if (jack_insert) {
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, RT5682_PWR_CBJ);
+ snd_soc_component_update_bits(component,
+ RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
+ rt5682_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
+ usleep_range(55000, 60000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH);
+
+ count = 0;
+ val = snd_soc_component_read(component, RT5682_CBJ_CTRL_2)
+ & RT5682_JACK_TYPE_MASK;
+ while (val == 0 && count < 50) {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component,
+ RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK;
+ count++;
+ }
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ rt5682->jack_type = SND_JACK_HEADSET;
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_EN);
+ rt5682_enable_push_button_irq(component, true);
+ break;
+ default:
+ rt5682->jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK,
+ RT5682_OSW_L_EN | RT5682_OSW_R_EN);
+ snd_soc_component_update_bits(component, RT5682_MICBIAS_2,
+ RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK,
+ RT5682_PWR_CLK25M_PU | RT5682_PWR_CLK1M_PU);
+ } else {
+ rt5682_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
+ if (!snd_soc_dapm_get_pin_status(dapm, "MICBIAS") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL1") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL2B"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(dapm, "Vref2") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL1") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL2B"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, 0);
+ snd_soc_component_update_bits(component, RT5682_MICBIAS_2,
+ RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK,
+ RT5682_PWR_CLK25M_PD | RT5682_PWR_CLK1M_PD);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_DIS);
+
+ rt5682->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type);
+ return rt5682->jack_type;
+}
+
+static int rt5682_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->hs_jack = hs_jack;
+
+ if (rt5682->is_sdw && !rt5682->first_hw_init)
+ return 0;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ return 0;
+ }
+
+ if (!rt5682->is_sdw) {
+ switch (rt5682->pdata.jd_src) {
+ case RT5682_JD1:
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_5, 0x0700, 0x0600);
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_2, RT5682_EXT_JD_SRC,
+ RT5682_EXT_JD_SRC_MANUAL);
+ snd_soc_component_write(component, RT5682_CBJ_CTRL_1,
+ 0xd142);
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_3, RT5682_CBJ_IN_BUF_EN,
+ RT5682_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component,
+ RT5682_SAR_IL_CMD_1, RT5682_SAR_POW_MASK,
+ RT5682_SAR_POW_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_IRQ | RT5682_POW_JDH |
+ RT5682_POW_ANA, RT5682_POW_IRQ |
+ RT5682_POW_JDH | RT5682_POW_ANA);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2,
+ RT5682_PWR_JDH, RT5682_PWR_JDH);
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK,
+ RT5682_JD1_EN | RT5682_JD1_POL_NOR);
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work,
+ msecs_to_jiffies(250));
+ break;
+
+ case RT5682_JD_NULL:
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void rt5682_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(work, struct rt5682_priv, jack_detect_work.work);
+ struct snd_soc_dapm_context *dapm;
+ int val, btn_type;
+
+ if (!rt5682->component ||
+ !snd_soc_card_is_instantiated(rt5682->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ if (rt5682->is_sdw) {
+ if (pm_runtime_status_suspended(rt5682->slave->dev.parent)) {
+ dev_dbg(&rt5682->slave->dev,
+ "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n",
+ __func__);
+ return;
+ }
+ }
+
+ dapm = snd_soc_component_get_dapm(rt5682->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&rt5682->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL)
+ & RT5682_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5682->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5682->jack_type =
+ rt5682_headset_detect(rt5682->component, 1);
+ rt5682->irq_work_delay_time = 0;
+ } else if ((rt5682->jack_type & SND_JACK_HEADSET) ==
+ SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ rt5682->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5682_button_detect(rt5682->component);
+ /**
+ * rt5682 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5682->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5682->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5682->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5682->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5682->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0);
+ rt5682->irq_work_delay_time = 50;
+ }
+
+ mutex_unlock(&rt5682->calibrate_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (!rt5682->is_sdw) {
+ if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5682->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler);
+
+static const struct snd_kcontrol_new rt5682_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL,
+ RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
+
+ /* IN Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL,
+ RT5682_BST_CBJ_SFT, 8, 0, bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST,
+ RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+};
+
+static int rt5682_div_sel(struct rt5682_priv *rt5682,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5682->sysclk < target) {
+ dev_err(rt5682->component->dev,
+ "sysclk rate %d is too low\n", rt5682->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ dev_dbg(rt5682->component->dev, "div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5682->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5682->sysclk) {
+ dev_dbg(rt5682->component->dev,
+ "can't find div for sysclk %d\n",
+ rt5682->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5682->sysclk)
+ dev_err(rt5682->component->dev,
+ "sysclk rate %d is too high\n", rt5682->sysclk);
+
+ return size - 1;
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ int idx, dmic_clk_rate = 3072000;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ if (rt5682->pdata.dmic_clk_rate)
+ dmic_clk_rate = rt5682->pdata.dmic_clk_rate;
+
+ idx = rt5682_div_sel(rt5682, dmic_clk_rate, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+static int set_filter_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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ int ref, val, reg, idx;
+ static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+ static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ val = snd_soc_component_read(component, RT5682_GPIO_CTRL_1) &
+ RT5682_GP4_PIN_MASK;
+ if (w->shift == RT5682_PWR_ADC_S1F_BIT &&
+ val == RT5682_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5682->lrck[RT5682_AIF2];
+ else
+ ref = 256 * rt5682->lrck[RT5682_AIF1];
+
+ idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f));
+
+ if (w->shift == RT5682_PWR_ADC_S1F_BIT)
+ reg = RT5682_PLL_TRACK_3;
+ else
+ reg = RT5682_PLL_TRACK_2;
+
+ snd_soc_component_update_bits(component, reg,
+ RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT);
+
+ /* select over sample rate */
+ for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) {
+ if (rt5682->sysclk <= 12288000 * div_o[idx])
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1,
+ RT5682_ADC_OSR_MASK | RT5682_DAC_OSR_MASK,
+ (idx << RT5682_ADC_OSR_SFT) | (idx << RT5682_DAC_OSR_SFT));
+
+ return 0;
+}
+
+static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_sys_clk_from_pll2(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL2)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5682_ADC_STO1_ASRC_SFT:
+ reg = RT5682_PLL_TRACK_3;
+ shift = RT5682_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5682_DAC_STO1_ASRC_SFT:
+ reg = RT5682_PLL_TRACK_2;
+ shift = RT5682_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case RT5682_CLK_SEL_I2S1_ASRC:
+ case RT5682_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER,
+ RT5682_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5682_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5682_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5682_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5682_if1_adc_slot_values[] = {
+ 0,
+ 2,
+ 4,
+ 6,
+};
+
+static const char * const rt5682_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum,
+ RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK,
+ rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5682_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX,
+ RT5682_A_DACL1_SFT, rt5682_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX,
+ RT5682_A_DACR1_SFT, rt5682_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum);
+
+/* Out Switch */
+static const struct snd_kcontrol_new hpol_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
+ RT5682_L_MUTE_SFT, 1, 1);
+static const struct snd_kcontrol_new hpor_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
+ RT5682_R_MUTE_SFT, 1, 1);
+
+static int rt5682_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_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_AMP_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5682_HP_LOGIC_CTRL_2,
+ RT5682_HP_LC2_SIG_SOUR2_MASK, RT5682_HP_LC2_SIG_SOUR2_REG);
+ snd_soc_component_update_bits(component,
+ RT5682_DEPOP_1, 0x60, 0x60);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_M);
+ snd_soc_component_update_bits(component,
+ RT5682_DEPOP_1, 0x60, 0x0);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000);
+ break;
+ }
+
+ return 0;
+}
+
+static int set_dmic_power(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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 50, val;
+
+ if (rt5682->pdata.dmic_delay)
+ delay = rt5682->pdata.dmic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL1 || val == RT5682_SCLK_SRC_PLL2)
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB);
+
+ /*Add delay to avoid pop noise*/
+ msleep(delay);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (!rt5682->jack_type) {
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_verf(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:
+ switch (w->shift) {
+ case RT5682_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0);
+ break;
+
+ case RT5682_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0);
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(15000, 20000);
+ switch (w->shift) {
+ case RT5682_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV1,
+ RT5682_PWR_FV1);
+ break;
+
+ case RT5682_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2,
+ RT5682_PWR_FV2);
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const unsigned int rt5682_adcdat_pin_values[] = {
+ 1,
+ 3,
+};
+
+static const char * const rt5682_adcdat_pin_select[] = {
+ "ADCDAT1",
+ "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum,
+ RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK,
+ rt5682_adcdat_pin_select, rt5682_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum);
+
+static const unsigned int rt5682_hpo_sig_out_values[] = {
+ 2,
+ 7,
+};
+
+static const char * const rt5682_hpo_sig_out_mode[] = {
+ "Legacy",
+ "OneBit",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_hpo_sig_out_enum,
+ RT5682_HP_LOGIC_CTRL_2, 0, RT5682_HP_LC2_SIG_SOUR1_MASK,
+ rt5682_hpo_sig_out_mode, rt5682_hpo_sig_out_values);
+
+static const struct snd_kcontrol_new rt5682_hpo_sig_demux =
+ SOC_DAPM_ENUM("HPO Signal Demux", rt5682_hpo_sig_out_enum);
+
+static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT,
+ 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0,
+ rt5682_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT,
+ 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ 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", RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_EN_SFT, 0, set_dmic_power,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix,
+ ARRAY_SIZE(rt5682_rec1_l_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2,
+ RT5682_PWR_RM1_L_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1,
+ RT5682_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1,
+ RT5682_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC,
+ RT5682_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2,
+ RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5682_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5682_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", 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("SOUND DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SOUND DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_adcdat_pin_ctrl),
+
+ SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_dac_l_mux),
+ SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_dac_r_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDWRX", "SDW Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDWTX", "SDW 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,
+ rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5682_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5682_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2,
+ RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1,
+ RT5682_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1,
+ RT5682_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC,
+ RT5682_CKGEN_DAC1_SFT, 0, NULL, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1,
+ RT5682_PWR_HA_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1,
+ RT5682_PWR_HA_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1,
+ RT5682_PUMP_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1,
+ RT5682_CAPLESS_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0,
+ &hpol_switch),
+ SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0,
+ &hpor_switch),
+
+ SND_SOC_DAPM_OUT_DRV("HPO Legacy", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HPO OneBit", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DEMUX("HPO Signal Demux", SND_SOC_NOPM, 0, 0, &rt5682_hpo_sig_demux),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET,
+ RT5682_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET,
+ RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET,
+ RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET,
+ RT5682_POW_CLK_DET_SFT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"ADC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2},
+ {"ADC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2},
+ {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"DAC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2},
+ {"DAC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"ADC STO1 ASRC", NULL, "DA ASRC"},
+ {"ADC STO1 ASRC", NULL, "CLKDET"},
+ {"DAC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+ {"DAC STO1 ASRC", NULL, "CLKDET"},
+
+ /*Vref*/
+ {"MICBIAS1", NULL, "Vref1"},
+ {"MICBIAS2", NULL, "Vref1"},
+
+ {"CLKDET SYS", NULL, "CLKDET"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "I2S1"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"SDWTX", NULL, "PLL2B"},
+ {"SDWTX", NULL, "PLL2F"},
+ {"SDWTX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"SOUND DAC L", NULL, "SDWRX"},
+ {"SOUND DAC L", NULL, "DAC Stereo1 Filter"},
+ {"SOUND DAC L", NULL, "PLL2B"},
+ {"SOUND DAC L", NULL, "PLL2F"},
+ {"SOUND DAC R", NULL, "SDWRX"},
+ {"SOUND DAC R", NULL, "DAC Stereo1 Filter"},
+ {"SOUND DAC R", NULL, "PLL2B"},
+ {"SOUND DAC R", NULL, "PLL2F"},
+
+ {"DAC L Mux", "IF1", "IF1 DAC1 L"},
+ {"DAC L Mux", "SOUND", "SOUND DAC L"},
+ {"DAC R Mux", "IF1", "IF1 DAC1 R"},
+ {"DAC R Mux", "SOUND", "SOUND DAC R"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "DAC L Mux"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "DAC R Mux"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"DAC L1", NULL, "DAC 1 Clock"},
+ {"DAC R1", NULL, "DAC 1 Clock"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "HP Amp L"},
+ {"HP Amp", NULL, "HP Amp R"},
+ {"HP Amp", NULL, "Capless"},
+ {"HP Amp", NULL, "Charge Pump"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "Vref1"},
+
+ {"HPO Signal Demux", NULL, "HP Amp"},
+
+ {"HPO Legacy", "Legacy", "HPO Signal Demux"},
+ {"HPO OneBit", "OneBit", "HPO Signal Demux"},
+
+ {"HPOL Playback", "Switch", "HPO Legacy"},
+ {"HPOR Playback", "Switch", "HPO Legacy"},
+
+ {"HPOL", NULL, "HPOL Playback"},
+ {"HPOR", NULL, "HPOR Playback"},
+ {"HPOL", NULL, "HPO OneBit"},
+ {"HPOR", NULL, "HPO OneBit"},
+};
+
+static int rt5682_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 cl, val = 0;
+
+ if (tx_mask || rx_mask)
+ snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2,
+ RT5682_TDM_EN, RT5682_TDM_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2,
+ RT5682_TDM_EN, 0);
+
+ switch (slots) {
+ case 4:
+ val |= RT5682_TDM_TX_CH_4;
+ val |= RT5682_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5682_TDM_TX_CH_6;
+ val |= RT5682_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5682_TDM_TX_CH_8;
+ val |= RT5682_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_TDM_CTRL,
+ RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val);
+
+ switch (slot_width) {
+ case 8:
+ if (tx_mask || rx_mask)
+ return -EINVAL;
+ cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8;
+ break;
+ case 16:
+ val = RT5682_TDM_CL_16;
+ cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16;
+ break;
+ case 20:
+ val = RT5682_TDM_CL_20;
+ cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20;
+ break;
+ case 24:
+ val = RT5682_TDM_CL_24;
+ cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24;
+ break;
+ case 32:
+ val = RT5682_TDM_CL_32;
+ cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_CL_MASK, val);
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl);
+
+ return 0;
+}
+
+static int rt5682_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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int pre_div, frame_size;
+
+ rt5682->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]);
+
+ 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(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt5682->lrck[dai->id], pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5682_I2S1_DL_20;
+ len_2 |= RT5682_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5682_I2S1_DL_24;
+ len_2 |= RT5682_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5682_I2S1_DL_32;
+ len_2 |= RT5682_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5682_I2S2_DL_8;
+ len_2 |= RT5682_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682_AIF1:
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S1_DL_MASK, len_1);
+ if (rt5682->master[RT5682_AIF1]) {
+ snd_soc_component_update_bits(component,
+ RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK |
+ RT5682_I2S_CLK_SRC_MASK,
+ pre_div << RT5682_I2S_M_DIV_SFT |
+ (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK,
+ RT5682_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK,
+ RT5682_I2S1_MONO_DIS);
+ break;
+ case RT5682_AIF2:
+ snd_soc_component_update_bits(component, RT5682_I2S2_SDP,
+ RT5682_I2S2_DL_MASK, len_2);
+ if (rt5682->master[RT5682_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK,
+ pre_div << RT5682_I2S2_M_PD_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK,
+ RT5682_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK,
+ RT5682_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5682->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5682->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 |= RT5682_I2S_BP_INV;
+ tdm_ctrl |= RT5682_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5682_AIF1)
+ tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5682_AIF1)
+ tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV |
+ RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ 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 |= RT5682_I2S_DF_LEFT;
+ tdm_ctrl |= RT5682_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5682_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5682_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5682_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5682_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682_AIF1:
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK |
+ RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK |
+ RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK,
+ tdm_ctrl | rt5682->master[dai->id]);
+ break;
+ case RT5682_AIF2:
+ if (rt5682->master[dai->id] == 0)
+ reg_val |= RT5682_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5682_I2S2_SDP,
+ RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK |
+ RT5682_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5682_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, src = 0;
+
+ if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5682_SCLK_S_MCLK:
+ reg_val |= RT5682_SCLK_SRC_MCLK;
+ src = RT5682_CLK_SRC_MCLK;
+ break;
+ case RT5682_SCLK_S_PLL1:
+ reg_val |= RT5682_SCLK_SRC_PLL1;
+ src = RT5682_CLK_SRC_PLL1;
+ break;
+ case RT5682_SCLK_S_PLL2:
+ reg_val |= RT5682_SCLK_SRC_PLL2;
+ src = RT5682_CLK_SRC_PLL2;
+ break;
+ case RT5682_SCLK_S_RCCLK:
+ reg_val |= RT5682_SCLK_SRC_RCCLK;
+ src = RT5682_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK, reg_val);
+
+ if (rt5682->master[RT5682_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK,
+ src << RT5682_I2S2_SRC_SFT);
+ }
+
+ rt5682->sysclk = freq;
+ rt5682->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt5682_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code, pll2f_code, pll2b_code;
+ unsigned int pll2_fout1, pll2_ps_val;
+ int ret;
+
+ if (source == rt5682->pll_src[pll_id] &&
+ freq_in == rt5682->pll_in[pll_id] &&
+ freq_out == rt5682->pll_out[pll_id])
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5682->pll_in[pll_id] = 0;
+ rt5682->pll_out[pll_id] = 0;
+ snd_soc_component_update_bits(component, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ if (pll_id == RT5682_PLL2) {
+ switch (source) {
+ case RT5682_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL2_SRC_MASK,
+ RT5682_PLL2_SRC_MCLK);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL2 Source %d\n",
+ source);
+ return -EINVAL;
+ }
+
+ /**
+ * PLL2 concatenates 2 PLL units.
+ * We suggest the Fout of the front PLL is 3.84MHz.
+ */
+ pll2_fout1 = 3840000;
+ ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+ dev_dbg(component->dev, "PLL2F: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n",
+ freq_in, pll2_fout1,
+ pll2f_code.m_bp,
+ (pll2f_code.m_bp ? 0 : pll2f_code.m_code),
+ pll2f_code.n_code, pll2f_code.k_code);
+
+ ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ pll2_fout1);
+ return ret;
+ }
+ dev_dbg(component->dev, "PLL2B: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n",
+ pll2_fout1, freq_out,
+ pll2b_code.m_bp,
+ (pll2b_code.m_bp ? 0 : pll2b_code.m_code),
+ pll2b_code.n_code, pll2b_code.k_code);
+
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_1,
+ pll2f_code.k_code << RT5682_PLL2F_K_SFT |
+ pll2b_code.k_code << RT5682_PLL2B_K_SFT |
+ pll2b_code.m_code);
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_2,
+ pll2f_code.m_code << RT5682_PLL2F_M_SFT |
+ pll2b_code.n_code);
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_3,
+ pll2f_code.n_code << RT5682_PLL2F_N_SFT);
+
+ if (freq_out == 22579200)
+ pll2_ps_val = 1 << RT5682_PLL2B_SEL_PS_SFT;
+ else
+ pll2_ps_val = 1 << RT5682_PLL2B_PS_BYP_SFT;
+ snd_soc_component_update_bits(component, RT5682_PLL2_CTRL_4,
+ RT5682_PLL2B_SEL_PS_MASK | RT5682_PLL2B_PS_BYP_MASK |
+ RT5682_PLL2B_M_BP_MASK | RT5682_PLL2F_M_BP_MASK | 0xf,
+ pll2_ps_val |
+ (pll2b_code.m_bp ? 1 : 0) << RT5682_PLL2B_M_BP_SFT |
+ (pll2f_code.m_bp ? 1 : 0) << RT5682_PLL2F_M_BP_SFT |
+ 0xf);
+ } else {
+ switch (source) {
+ case RT5682_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK,
+ RT5682_PLL1_SRC_MCLK);
+ break;
+ case RT5682_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK,
+ RT5682_PLL1_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL1 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, RT5682_PLL_CTRL_1,
+ (pll_code.n_code << RT5682_PLL_N_SFT) | pll_code.k_code);
+ snd_soc_component_write(component, RT5682_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT) |
+ ((pll_code.m_bp << RT5682_PLL_M_BP_SFT) | RT5682_PLL_RST));
+ }
+
+ rt5682->pll_in[pll_id] = freq_in;
+ rt5682->pll_out[pll_id] = freq_out;
+ rt5682->pll_src[pll_id] = source;
+
+ return 0;
+}
+
+static int rt5682_set_bclk1_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 256:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_256);
+ break;
+ case 128:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_128);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2,
+ RT5682_I2S2_BCLK_MS2_MASK,
+ RT5682_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2,
+ RT5682_I2S2_BCLK_MS2_MASK,
+ RT5682_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_PWR_BG, RT5682_PWR_BG);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_PWR_BG, 0);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+#define CLK_PLL2_FIN 48000000
+#define CLK_48 48000
+#define CLK_44 44100
+
+static bool rt5682_clk_check(struct rt5682_priv *rt5682)
+{
+ if (!rt5682->master[RT5682_AIF1]) {
+ dev_dbg(rt5682->i2c_dev, "sysclk/dai not set correctly\n");
+ return false;
+ }
+ return true;
+}
+
+static int rt5682_wclk_prepare(struct clk_hw *hw)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+ dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_MB, RT5682_PWR_MB);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Vref2");
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_FV2,
+ RT5682_PWR_VREF2);
+ usleep_range(55000, 60000);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_FV2, RT5682_PWR_FV2);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2F");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2B");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void rt5682_wclk_unprepare(struct clk_hw *hw)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!rt5682_clk_check(rt5682))
+ return;
+
+ component = rt5682->component;
+ dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Vref2");
+ if (!rt5682->jack_type)
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_FV2 |
+ RT5682_PWR_MB, 0);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2F");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2B");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682_clk_check(rt5682))
+ return 0;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ */
+ if (rt5682->lrck[RT5682_AIF1] != CLK_48 &&
+ rt5682->lrck[RT5682_AIF1] != CLK_44) {
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682->lrck[RT5682_AIF1];
+}
+
+static int rt5682_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
+ */
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct clk_hw *parent_hw;
+ const char * const clk_name = clk_hw_get_name(hw);
+ int pre_div;
+ unsigned int clk_pll2_out;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+
+ /*
+ * Whether the wclk's parent clk (mclk) exists or not, please ensure
+ * it is fixed or set to 48MHz before setting wclk rate. It's a
+ * temporary limitation. Only accept 48MHz clk as the clk provider.
+ *
+ * It will set the codec anyway by assuming mclk is 48MHz.
+ */
+ parent_hw = clk_hw_get_parent(hw);
+ if (!parent_hw)
+ dev_warn(rt5682->i2c_dev,
+ "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
+ CLK_PLL2_FIN);
+
+ if (parent_rate != CLK_PLL2_FIN)
+ dev_warn(rt5682->i2c_dev, "clk %s only support %d Hz input\n",
+ clk_name, CLK_PLL2_FIN);
+
+ /*
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
+ */
+ clk_pll2_out = rate * 512;
+ rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ CLK_PLL2_FIN, clk_pll2_out);
+
+ rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0,
+ clk_pll2_out, SND_SOC_CLOCK_IN);
+
+ rt5682->lrck[RT5682_AIF1] = rate;
+
+ pre_div = rl6231_get_clk_info(rt5682->sysclk, rate);
+
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1,
+ RT5682_I2S_M_DIV_MASK | RT5682_I2S_CLK_SRC_MASK,
+ pre_div << RT5682_I2S_M_DIV_SFT |
+ (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT);
+
+ return 0;
+}
+
+static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ unsigned int bclks_per_wclk;
+
+ regmap_read(rt5682->regmap, RT5682_TDM_TCON_CTRL, &bclks_per_wclk);
+
+ switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) {
+ case RT5682_TDM_BCLK_MS1_256:
+ return parent_rate * 256;
+ case RT5682_TDM_BCLK_MS1_128:
+ return parent_rate * 128;
+ case RT5682_TDM_BCLK_MS1_64:
+ return parent_rate * 64;
+ case RT5682_TDM_BCLK_MS1_32:
+ return parent_rate * 32;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long rt5682_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 int rt5682_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_parent_rate || !rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ /*
+ * BCLK rates are set as a multiplier of WCLK in HW.
+ * We don't allow changing the parent WCLK. We just do
+ * some rounding down based on the parent WCLK rate
+ * and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = rt5682_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dai *dai;
+ unsigned long factor;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+
+ factor = rt5682_bclk_get_factor(rate, parent_rate);
+
+ for_each_component_dais(component, dai)
+ if (dai->id == RT5682_AIF1)
+ return rt5682_set_bclk1_ratio(dai, factor);
+
+ dev_err(rt5682->i2c_dev, "dai %d not found in component\n",
+ RT5682_AIF1);
+ return -ENODEV;
+}
+
+static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = {
+ [RT5682_DAI_WCLK_IDX] = {
+ .prepare = rt5682_wclk_prepare,
+ .unprepare = rt5682_wclk_unprepare,
+ .recalc_rate = rt5682_wclk_recalc_rate,
+ .determine_rate = rt5682_wclk_determine_rate,
+ .set_rate = rt5682_wclk_set_rate,
+ },
+ [RT5682_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682_bclk_recalc_rate,
+ .determine_rate = rt5682_bclk_determine_rate,
+ .set_rate = rt5682_bclk_set_rate,
+ },
+};
+
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682)
+{
+ struct device *dev = rt5682->i2c_dev;
+ struct rt5682_platform_data *pdata = &rt5682->pdata;
+ struct clk_hw *dai_clk_hw;
+ int i, ret;
+
+ for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = { };
+ const struct clk_hw *parent;
+
+ dai_clk_hw = &rt5682->dai_clks_hw[i];
+
+ switch (i) {
+ case RT5682_DAI_WCLK_IDX:
+ /* Make MCLK the parent of WCLK */
+ if (rt5682->mclk) {
+ parent = __clk_get_hw(rt5682->mclk);
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ }
+ break;
+ case RT5682_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent = &rt5682->dai_clks_hw[RT5682_DAI_WCLK_IDX];
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ return -EINVAL;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &rt5682_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n",
+ init.name, ret);
+ return ret;
+ }
+
+ if (dev->of_node) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ dai_clk_hw);
+ if (ret)
+ return ret;
+ } else {
+ ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
+ init.name,
+ dev_name(dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_register_dai_clks);
+#endif /* CONFIG_COMMON_CLK */
+
+static int rt5682_probe(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *slave;
+ unsigned long time;
+ struct snd_soc_dapm_context *dapm = &component->dapm;
+
+ rt5682->component = component;
+
+ if (rt5682->is_sdw) {
+ slave = rt5682->slave;
+ time = wait_for_completion_timeout(
+ &slave->initialization_complete,
+ msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(dapm, "Vref2");
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static void rt5682_remove(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682_reset(rt5682);
+}
+
+#ifdef CONFIG_PM
+static int rt5682_suspend(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ if (rt5682->irq)
+ disable_irq(rt5682->irq);
+
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+ if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ val = snd_soc_component_read(component,
+ RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK;
+
+ switch (val) {
+ case 0x1:
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK,
+ RT5682_SAR_SEL_MB1_NOSEL | RT5682_SAR_SEL_MB2_SEL);
+ break;
+ case 0x2:
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK,
+ RT5682_SAR_SEL_MB1_SEL | RT5682_SAR_SEL_MB2_NOSEL);
+ break;
+ default:
+ break;
+ }
+
+ /* enter SAR ADC power saving mode */
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK |
+ RT5682_SAR_SEL_MB1_MB2_MASK, 0);
+ usleep_range(5000, 6000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
+ RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG);
+ usleep_range(10000, 12000);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK,
+ RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV);
+ snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
+ }
+
+ regcache_cache_only(rt5682->regmap, true);
+ regcache_mark_dirty(rt5682->regmap);
+ return 0;
+}
+
+static int rt5682_resume(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ regcache_cache_only(rt5682->regmap, false);
+ regcache_sync(rt5682->regmap);
+
+ if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK,
+ RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO);
+ usleep_range(5000, 6000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
+ RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, RT5682_PWR_CBJ);
+ }
+
+ rt5682->jack_type = 0;
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(0));
+
+ if (rt5682->irq)
+ enable_irq(rt5682->irq);
+
+ return 0;
+}
+#else
+#define rt5682_suspend NULL
+#define rt5682_resume NULL
+#endif
+
+const struct snd_soc_dai_ops rt5682_aif1_dai_ops = {
+ .hw_params = rt5682_hw_params,
+ .set_fmt = rt5682_set_dai_fmt,
+ .set_tdm_slot = rt5682_set_tdm_slot,
+ .set_bclk_ratio = rt5682_set_bclk1_ratio,
+};
+EXPORT_SYMBOL_GPL(rt5682_aif1_dai_ops);
+
+const struct snd_soc_dai_ops rt5682_aif2_dai_ops = {
+ .hw_params = rt5682_hw_params,
+ .set_fmt = rt5682_set_dai_fmt,
+ .set_bclk_ratio = rt5682_set_bclk2_ratio,
+};
+EXPORT_SYMBOL_GPL(rt5682_aif2_dai_ops);
+
+const struct snd_soc_component_driver rt5682_soc_component_dev = {
+ .probe = rt5682_probe,
+ .remove = rt5682_remove,
+ .suspend = rt5682_suspend,
+ .resume = rt5682_resume,
+ .set_bias_level = rt5682_set_bias_level,
+ .controls = rt5682_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5682_snd_controls),
+ .dapm_widgets = rt5682_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets),
+ .dapm_routes = rt5682_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes),
+ .set_sysclk = rt5682_set_component_sysclk,
+ .set_pll = rt5682_set_component_pll,
+ .set_jack = rt5682_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(rt5682_soc_component_dev);
+
+int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
+{
+
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5682->pdata.dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic1-clk-pin",
+ &rt5682->pdata.dmic1_clk_pin);
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt5682->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,btndet-delay",
+ &rt5682->pdata.btndet_delay);
+ device_property_read_u32(dev, "realtek,dmic-clk-rate-hz",
+ &rt5682->pdata.dmic_clk_rate);
+ device_property_read_u32(dev, "realtek,dmic-delay-ms",
+ &rt5682->pdata.dmic_delay);
+
+ if (device_property_read_string_array(dev, "clock-output-names",
+ rt5682->pdata.dai_clk_names,
+ RT5682_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX],
+ rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]);
+
+ rt5682->pdata.dmic_clk_driving_high = device_property_read_bool(dev,
+ "realtek,dmic-clk-driving-high");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_parse_dt);
+
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev)
+{
+ rt5682->ldo1_en = devm_gpiod_get_optional(dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682->ldo1_en)) {
+ dev_err(dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682->ldo1_en);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_get_ldo1);
+
+void rt5682_calibrate(struct rt5682_priv *rt5682)
+{
+ int value, count;
+
+ mutex_lock(&rt5682->calibrate_mutex);
+
+ rt5682_reset(rt5682);
+ regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f);
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af);
+ usleep_range(15000, 20000);
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300);
+ regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000);
+ regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100);
+ regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800);
+ regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x7005);
+ else
+ regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005);
+ regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1);
+ regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
+
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ dev_err(rt5682->component->dev, "HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x002f);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000);
+ regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
+ regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x2005);
+ else
+ regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
+ regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c);
+
+ mutex_unlock(&rt5682->calibrate_mutex);
+}
+EXPORT_SYMBOL_GPL(rt5682_calibrate);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
new file mode 100644
index 000000000000..de43a5d99403
--- /dev/null
+++ b/sound/soc/codecs/rt5682.h
@@ -0,0 +1,1499 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5682_H__
+#define __RT5682_H__
+
+#include <sound/rt5682.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define DEVICE_ID 0x6530
+
+/* Info */
+#define RT5682_RESET 0x0000
+#define RT5682_INT_DEVICE_ID 0x00f9
+#define RT5682_VERSION_ID 0x00fd
+#define RT5682_VENDOR_ID 0x00fe
+#define RT5682_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5682_HP_CTRL_1 0x0002
+#define RT5682_HP_CTRL_2 0x0003
+#define RT5682_HPL_GAIN 0x0005
+#define RT5682_HPR_GAIN 0x0006
+
+#define RT5682_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5682_CBJ_BST_CTRL 0x000b
+#define RT5682_CBJ_CTRL_1 0x0010
+#define RT5682_CBJ_CTRL_2 0x0011
+#define RT5682_CBJ_CTRL_3 0x0012
+#define RT5682_CBJ_CTRL_4 0x0013
+#define RT5682_CBJ_CTRL_5 0x0014
+#define RT5682_CBJ_CTRL_6 0x0015
+#define RT5682_CBJ_CTRL_7 0x0016
+/* I/O - ADC/DAC/DMIC */
+#define RT5682_DAC1_DIG_VOL 0x0019
+#define RT5682_STO1_ADC_DIG_VOL 0x001c
+#define RT5682_STO1_ADC_BOOST 0x001f
+#define RT5682_HP_IMP_GAIN_1 0x0022
+#define RT5682_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5682_SIDETONE_CTRL 0x0024
+#define RT5682_STO1_ADC_MIXER 0x0026
+#define RT5682_AD_DA_MIXER 0x0029
+#define RT5682_STO1_DAC_MIXER 0x002a
+#define RT5682_A_DAC1_MUX 0x002b
+#define RT5682_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5682_REC_MIXER 0x003c
+#define RT5682_CAL_REC 0x0044
+#define RT5682_ALC_BACK_GAIN 0x0049
+/* Power */
+#define RT5682_PWR_DIG_1 0x0061
+#define RT5682_PWR_DIG_2 0x0062
+#define RT5682_PWR_ANLG_1 0x0063
+#define RT5682_PWR_ANLG_2 0x0064
+#define RT5682_PWR_ANLG_3 0x0065
+#define RT5682_PWR_MIXER 0x0066
+#define RT5682_PWR_VOL 0x0067
+/* Clock Detect */
+#define RT5682_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5682_RESET_LPF_CTRL 0x006c
+#define RT5682_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5682_DMIC_CTRL_1 0x006e
+/* Format - ADC/DAC */
+#define RT5682_I2S1_SDP 0x0070
+#define RT5682_I2S2_SDP 0x0071
+#define RT5682_ADDA_CLK_1 0x0073
+#define RT5682_ADDA_CLK_2 0x0074
+#define RT5682_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5682_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5682_TDM_CTRL 0x0079
+#define RT5682_TDM_ADDA_CTRL_1 0x007a
+#define RT5682_TDM_ADDA_CTRL_2 0x007b
+#define RT5682_DATA_SEL_CTRL_1 0x007c
+#define RT5682_TDM_TCON_CTRL 0x007e
+/* Function - Analog */
+#define RT5682_GLB_CLK 0x0080
+#define RT5682_PLL_CTRL_1 0x0081
+#define RT5682_PLL_CTRL_2 0x0082
+#define RT5682_PLL_TRACK_1 0x0083
+#define RT5682_PLL_TRACK_2 0x0084
+#define RT5682_PLL_TRACK_3 0x0085
+#define RT5682_PLL_TRACK_4 0x0086
+#define RT5682_PLL_TRACK_5 0x0087
+#define RT5682_PLL_TRACK_6 0x0088
+#define RT5682_PLL_TRACK_11 0x008c
+#define RT5682_SDW_REF_CLK 0x008d
+#define RT5682_DEPOP_1 0x008e
+#define RT5682_DEPOP_2 0x008f
+#define RT5682_HP_CHARGE_PUMP_1 0x0091
+#define RT5682_HP_CHARGE_PUMP_2 0x0092
+#define RT5682_MICBIAS_1 0x0093
+#define RT5682_MICBIAS_2 0x0094
+#define RT5682_PLL_TRACK_12 0x0098
+#define RT5682_PLL_TRACK_14 0x009a
+#define RT5682_PLL2_CTRL_1 0x009b
+#define RT5682_PLL2_CTRL_2 0x009c
+#define RT5682_PLL2_CTRL_3 0x009d
+#define RT5682_PLL2_CTRL_4 0x009e
+#define RT5682_RC_CLK_CTRL 0x009f
+#define RT5682_I2S_M_CLK_CTRL_1 0x00a0
+#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4
+/* Function - Digital */
+#define RT5682_EQ_CTRL_1 0x00ae
+#define RT5682_EQ_CTRL_2 0x00af
+#define RT5682_IRQ_CTRL_1 0x00b6
+#define RT5682_IRQ_CTRL_2 0x00b7
+#define RT5682_IRQ_CTRL_3 0x00b8
+#define RT5682_IRQ_CTRL_4 0x00b9
+#define RT5682_INT_ST_1 0x00be
+#define RT5682_GPIO_CTRL_1 0x00c0
+#define RT5682_GPIO_CTRL_2 0x00c1
+#define RT5682_GPIO_CTRL_3 0x00c2
+#define RT5682_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5682_HP_AMP_DET_CTRL_2 0x00d1
+#define RT5682_MID_HP_AMP_DET 0x00d2
+#define RT5682_LOW_HP_AMP_DET 0x00d3
+#define RT5682_DELAY_BUF_CTRL 0x00d4
+#define RT5682_SV_ZCD_1 0x00d9
+#define RT5682_SV_ZCD_2 0x00da
+#define RT5682_IL_CMD_1 0x00db
+#define RT5682_IL_CMD_2 0x00dc
+#define RT5682_IL_CMD_3 0x00dd
+#define RT5682_IL_CMD_4 0x00de
+#define RT5682_IL_CMD_5 0x00df
+#define RT5682_IL_CMD_6 0x00e0
+#define RT5682_4BTN_IL_CMD_1 0x00e2
+#define RT5682_4BTN_IL_CMD_2 0x00e3
+#define RT5682_4BTN_IL_CMD_3 0x00e4
+#define RT5682_4BTN_IL_CMD_4 0x00e5
+#define RT5682_4BTN_IL_CMD_5 0x00e6
+#define RT5682_4BTN_IL_CMD_6 0x00e7
+#define RT5682_4BTN_IL_CMD_7 0x00e8
+
+#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5682_AJD1_CTRL 0x00f0
+#define RT5682_JD1_THD 0x00f1
+#define RT5682_JD2_THD 0x00f2
+#define RT5682_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5682_DUMMY_1 0x00fa
+#define RT5682_DUMMY_2 0x00fb
+#define RT5682_DUMMY_3 0x00fc
+
+#define RT5682_DAC_ADC_DIG_VOL1 0x0100
+#define RT5682_BIAS_CUR_CTRL_2 0x010b
+#define RT5682_BIAS_CUR_CTRL_3 0x010c
+#define RT5682_BIAS_CUR_CTRL_4 0x010d
+#define RT5682_BIAS_CUR_CTRL_5 0x010e
+#define RT5682_BIAS_CUR_CTRL_6 0x010f
+#define RT5682_BIAS_CUR_CTRL_7 0x0110
+#define RT5682_BIAS_CUR_CTRL_8 0x0111
+#define RT5682_BIAS_CUR_CTRL_9 0x0112
+#define RT5682_BIAS_CUR_CTRL_10 0x0113
+#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117
+#define RT5682_CHARGE_PUMP_1 0x0125
+#define RT5682_DIG_IN_CTRL_1 0x0132
+#define RT5682_PAD_DRIVING_CTRL 0x0136
+#define RT5682_SOFT_RAMP_DEPOP 0x0138
+#define RT5682_CHOP_DAC 0x013a
+#define RT5682_CHOP_ADC 0x013b
+#define RT5682_CALIB_ADC_CTRL 0x013c
+#define RT5682_VOL_TEST 0x013f
+#define RT5682_SPKVDD_DET_STA 0x0142
+#define RT5682_TEST_MODE_CTRL_1 0x0145
+#define RT5682_TEST_MODE_CTRL_2 0x0146
+#define RT5682_TEST_MODE_CTRL_3 0x0147
+#define RT5682_TEST_MODE_CTRL_4 0x0148
+#define RT5682_TEST_MODE_CTRL_5 0x0149
+#define RT5682_PLL1_INTERNAL 0x0150
+#define RT5682_PLL2_INTERNAL 0x0156
+#define RT5682_STO_NG2_CTRL_1 0x0160
+#define RT5682_STO_NG2_CTRL_2 0x0161
+#define RT5682_STO_NG2_CTRL_3 0x0162
+#define RT5682_STO_NG2_CTRL_4 0x0163
+#define RT5682_STO_NG2_CTRL_5 0x0164
+#define RT5682_STO_NG2_CTRL_6 0x0165
+#define RT5682_STO_NG2_CTRL_7 0x0166
+#define RT5682_STO_NG2_CTRL_8 0x0167
+#define RT5682_STO_NG2_CTRL_9 0x0168
+#define RT5682_STO_NG2_CTRL_10 0x0169
+#define RT5682_STO1_DAC_SIL_DET 0x0190
+#define RT5682_SIL_PSV_CTRL1 0x0194
+#define RT5682_SIL_PSV_CTRL2 0x0195
+#define RT5682_SIL_PSV_CTRL3 0x0197
+#define RT5682_SIL_PSV_CTRL4 0x0198
+#define RT5682_SIL_PSV_CTRL5 0x0199
+#define RT5682_HP_IMP_SENS_CTRL_01 0x01af
+#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0
+#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1
+#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2
+#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3
+#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4
+#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5
+#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6
+#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7
+#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8
+#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9
+#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba
+#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb
+#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc
+#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd
+#define RT5682_HP_IMP_SENS_CTRL_16 0x01be
+#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf
+#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0
+#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1
+#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2
+#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3
+#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4
+#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5
+#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6
+#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7
+#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8
+#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9
+#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca
+#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb
+#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc
+#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd
+#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce
+#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf
+#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0
+#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1
+#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2
+#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3
+#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4
+#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5
+#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6
+#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7
+#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8
+#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9
+#define RT5682_HP_LOGIC_CTRL_1 0x01da
+#define RT5682_HP_LOGIC_CTRL_2 0x01db
+#define RT5682_HP_LOGIC_CTRL_3 0x01dc
+#define RT5682_HP_CALIB_CTRL_1 0x01de
+#define RT5682_HP_CALIB_CTRL_2 0x01df
+#define RT5682_HP_CALIB_CTRL_3 0x01e0
+#define RT5682_HP_CALIB_CTRL_4 0x01e1
+#define RT5682_HP_CALIB_CTRL_5 0x01e2
+#define RT5682_HP_CALIB_CTRL_6 0x01e3
+#define RT5682_HP_CALIB_CTRL_7 0x01e4
+#define RT5682_HP_CALIB_CTRL_9 0x01e6
+#define RT5682_HP_CALIB_CTRL_10 0x01e7
+#define RT5682_HP_CALIB_CTRL_11 0x01e8
+#define RT5682_HP_CALIB_STA_1 0x01ea
+#define RT5682_HP_CALIB_STA_2 0x01eb
+#define RT5682_HP_CALIB_STA_3 0x01ec
+#define RT5682_HP_CALIB_STA_4 0x01ed
+#define RT5682_HP_CALIB_STA_5 0x01ee
+#define RT5682_HP_CALIB_STA_6 0x01ef
+#define RT5682_HP_CALIB_STA_7 0x01f0
+#define RT5682_HP_CALIB_STA_8 0x01f1
+#define RT5682_HP_CALIB_STA_9 0x01f2
+#define RT5682_HP_CALIB_STA_10 0x01f3
+#define RT5682_HP_CALIB_STA_11 0x01f4
+#define RT5682_SAR_IL_CMD_1 0x0210
+#define RT5682_SAR_IL_CMD_2 0x0211
+#define RT5682_SAR_IL_CMD_3 0x0212
+#define RT5682_SAR_IL_CMD_4 0x0213
+#define RT5682_SAR_IL_CMD_5 0x0214
+#define RT5682_SAR_IL_CMD_6 0x0215
+#define RT5682_SAR_IL_CMD_7 0x0216
+#define RT5682_SAR_IL_CMD_8 0x0217
+#define RT5682_SAR_IL_CMD_9 0x0218
+#define RT5682_SAR_IL_CMD_10 0x0219
+#define RT5682_SAR_IL_CMD_11 0x021a
+#define RT5682_SAR_IL_CMD_12 0x021b
+#define RT5682_SAR_IL_CMD_13 0x021c
+#define RT5682_EFUSE_CTRL_1 0x0250
+#define RT5682_EFUSE_CTRL_2 0x0251
+#define RT5682_EFUSE_CTRL_3 0x0252
+#define RT5682_EFUSE_CTRL_4 0x0253
+#define RT5682_EFUSE_CTRL_5 0x0254
+#define RT5682_EFUSE_CTRL_6 0x0255
+#define RT5682_EFUSE_CTRL_7 0x0256
+#define RT5682_EFUSE_CTRL_8 0x0257
+#define RT5682_EFUSE_CTRL_9 0x0258
+#define RT5682_EFUSE_CTRL_10 0x0259
+#define RT5682_EFUSE_CTRL_11 0x025a
+#define RT5682_JD_TOP_VC_VTRL 0x0270
+#define RT5682_DRC1_CTRL_0 0x02ff
+#define RT5682_DRC1_CTRL_1 0x0300
+#define RT5682_DRC1_CTRL_2 0x0301
+#define RT5682_DRC1_CTRL_3 0x0302
+#define RT5682_DRC1_CTRL_4 0x0303
+#define RT5682_DRC1_CTRL_5 0x0304
+#define RT5682_DRC1_CTRL_6 0x0305
+#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306
+#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307
+#define RT5682_DRC1_PRIV_1 0x0310
+#define RT5682_DRC1_PRIV_2 0x0311
+#define RT5682_DRC1_PRIV_3 0x0312
+#define RT5682_DRC1_PRIV_4 0x0313
+#define RT5682_DRC1_PRIV_5 0x0314
+#define RT5682_DRC1_PRIV_6 0x0315
+#define RT5682_DRC1_PRIV_7 0x0316
+#define RT5682_DRC1_PRIV_8 0x0317
+#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0
+#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1
+#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2
+#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3
+#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4
+#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5
+#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6
+#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7
+#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8
+#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9
+#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca
+#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb
+#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc
+#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0
+#define RT5682_R_EQ_LPF1_A1 0x03d1
+#define RT5682_L_EQ_LPF1_H0 0x03d2
+#define RT5682_R_EQ_LPF1_H0 0x03d3
+#define RT5682_L_EQ_BPF1_A1 0x03d4
+#define RT5682_R_EQ_BPF1_A1 0x03d5
+#define RT5682_L_EQ_BPF1_A2 0x03d6
+#define RT5682_R_EQ_BPF1_A2 0x03d7
+#define RT5682_L_EQ_BPF1_H0 0x03d8
+#define RT5682_R_EQ_BPF1_H0 0x03d9
+#define RT5682_L_EQ_BPF2_A1 0x03da
+#define RT5682_R_EQ_BPF2_A1 0x03db
+#define RT5682_L_EQ_BPF2_A2 0x03dc
+#define RT5682_R_EQ_BPF2_A2 0x03dd
+#define RT5682_L_EQ_BPF2_H0 0x03de
+#define RT5682_R_EQ_BPF2_H0 0x03df
+#define RT5682_L_EQ_BPF3_A1 0x03e0
+#define RT5682_R_EQ_BPF3_A1 0x03e1
+#define RT5682_L_EQ_BPF3_A2 0x03e2
+#define RT5682_R_EQ_BPF3_A2 0x03e3
+#define RT5682_L_EQ_BPF3_H0 0x03e4
+#define RT5682_R_EQ_BPF3_H0 0x03e5
+#define RT5682_L_EQ_BPF4_A1 0x03e6
+#define RT5682_R_EQ_BPF4_A1 0x03e7
+#define RT5682_L_EQ_BPF4_A2 0x03e8
+#define RT5682_R_EQ_BPF4_A2 0x03e9
+#define RT5682_L_EQ_BPF4_H0 0x03ea
+#define RT5682_R_EQ_BPF4_H0 0x03eb
+#define RT5682_L_EQ_HPF1_A1 0x03ec
+#define RT5682_R_EQ_HPF1_A1 0x03ed
+#define RT5682_L_EQ_HPF1_H0 0x03ee
+#define RT5682_R_EQ_HPF1_H0 0x03ef
+#define RT5682_L_EQ_PRE_VOL 0x03f0
+#define RT5682_R_EQ_PRE_VOL 0x03f1
+#define RT5682_L_EQ_POST_VOL 0x03f2
+#define RT5682_R_EQ_POST_VOL 0x03f3
+#define RT5682_I2C_MODE 0xffff
+
+
+/* global definition */
+#define RT5682_L_MUTE (0x1 << 15)
+#define RT5682_L_MUTE_SFT 15
+#define RT5682_VOL_L_MUTE (0x1 << 14)
+#define RT5682_VOL_L_SFT 14
+#define RT5682_R_MUTE (0x1 << 7)
+#define RT5682_R_MUTE_SFT 7
+#define RT5682_VOL_R_MUTE (0x1 << 6)
+#define RT5682_VOL_R_SFT 6
+#define RT5682_L_VOL_MASK (0x3f << 8)
+#define RT5682_L_VOL_SFT 8
+#define RT5682_R_VOL_MASK (0x3f)
+#define RT5682_R_VOL_SFT 0
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682_HP_C2_DAC_AMP_MUTE_SFT 15
+#define RT5682_HP_C2_DAC_AMP_MUTE (0x1 << 15)
+#define RT5682_HP_C2_DAC_L_EN_SFT 14
+#define RT5682_HP_C2_DAC_L_EN (0x1 << 14)
+#define RT5682_HP_C2_DAC_R_EN_SFT 13
+#define RT5682_HP_C2_DAC_R_EN (0x1 << 13)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5682_G_HP (0xf << 8)
+#define RT5682_G_HP_SFT 8
+#define RT5682_G_STO_DA_DMIX (0xf)
+#define RT5682_G_STO_DA_SFT 0
+
+/* CBJ Control (0x000b) */
+#define RT5682_BST_CBJ_MASK (0xf << 8)
+#define RT5682_BST_CBJ_SFT 8
+
+/* Embeeded Jack and Type Detection Control 1 (0x0010) */
+#define RT5682_EMB_JD_EN (0x1 << 15)
+#define RT5682_EMB_JD_EN_SFT 15
+#define RT5682_EMB_JD_RST (0x1 << 14)
+#define RT5682_JD_MODE (0x1 << 13)
+#define RT5682_JD_MODE_SFT 13
+#define RT5682_DET_TYPE (0x1 << 12)
+#define RT5682_DET_TYPE_SFT 12
+#define RT5682_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5682_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5682_EXT_JD_DIG (0x1 << 9)
+#define RT5682_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5682_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5682_FAST_OFF_MASK (0x1 << 7)
+#define RT5682_FAST_OFF_EN (0x1 << 7)
+#define RT5682_FAST_OFF_DIS (0x0 << 7)
+#define RT5682_VREF_POW_MASK (0x1 << 6)
+#define RT5682_VREF_POW_FSM (0x0 << 6)
+#define RT5682_VREF_POW_REG (0x1 << 6)
+#define RT5682_MB1_PATH_MASK (0x1 << 5)
+#define RT5682_CTRL_MB1_REG (0x1 << 5)
+#define RT5682_CTRL_MB1_FSM (0x0 << 5)
+#define RT5682_MB2_PATH_MASK (0x1 << 4)
+#define RT5682_CTRL_MB2_REG (0x1 << 4)
+#define RT5682_CTRL_MB2_FSM (0x0 << 4)
+#define RT5682_TRIG_JD_MASK (0x1 << 3)
+#define RT5682_TRIG_JD_HIGH (0x1 << 3)
+#define RT5682_TRIG_JD_LOW (0x0 << 3)
+#define RT5682_MIC_CAP_MASK (0x1 << 1)
+#define RT5682_MIC_CAP_HS (0x1 << 1)
+#define RT5682_MIC_CAP_HP (0x0 << 1)
+#define RT5682_MIC_CAP_SRC_MASK (0x1)
+#define RT5682_MIC_CAP_SRC_REG (0x1)
+#define RT5682_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 2 (0x0011) */
+#define RT5682_EXT_JD_SRC (0x7 << 4)
+#define RT5682_EXT_JD_SRC_SFT 4
+#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5682_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5682_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5682_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 3 (0x0012) */
+#define RT5682_CBJ_IN_BUF_EN (0x1 << 7)
+
+/* Combo Jack and Type Detection Control 4 (0x0013) */
+#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5682_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5682_DAC_L1_VOL_SFT 8
+#define RT5682_DAC_R1_VOL_MASK (0xff)
+#define RT5682_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5682_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5682_ADC_L_VOL_SFT 8
+#define RT5682_ADC_R_VOL_MASK (0x7f)
+#define RT5682_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5682_STO1_ADC_L_BST_SFT 14
+#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5682_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5682_ST_SRC_SEL (0x1 << 8)
+#define RT5682_ST_SRC_SFT 8
+#define RT5682_ST_EN_MASK (0x1 << 6)
+#define RT5682_ST_DIS (0x0 << 6)
+#define RT5682_ST_EN (0x1 << 6)
+#define RT5682_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5682_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5682_M_STO1_ADC_L1_SFT 15
+#define RT5682_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5682_M_STO1_ADC_L2_SFT 14
+#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5682_STO1_ADC1L_SRC_SFT 13
+#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5682_STO1_ADC2L_SRC_SFT 12
+#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5682_STO1_ADCL_SRC_SFT 10
+#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9)
+#define RT5682_STO1_DD_L_SRC_SFT 9
+#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8)
+#define RT5682_STO1_DMIC_SRC_SFT 8
+#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5682_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5682_M_STO1_ADC_R1_SFT 7
+#define RT5682_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5682_M_STO1_ADC_R2_SFT 6
+#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5682_STO1_ADC1R_SRC_SFT 5
+#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5682_STO1_ADC2R_SRC_SFT 4
+#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5682_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5682_M_ADCMIX_L (0x1 << 15)
+#define RT5682_M_ADCMIX_L_SFT 15
+#define RT5682_M_DAC1_L (0x1 << 14)
+#define RT5682_M_DAC1_L_SFT 14
+#define RT5682_DAC1_R_SEL_MASK (0x1 << 10)
+#define RT5682_DAC1_R_SEL_SFT 10
+#define RT5682_DAC1_L_SEL_MASK (0x1 << 8)
+#define RT5682_DAC1_L_SEL_SFT 8
+#define RT5682_M_ADCMIX_R (0x1 << 7)
+#define RT5682_M_ADCMIX_R_SFT 7
+#define RT5682_M_DAC1_R (0x1 << 6)
+#define RT5682_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5682_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5682_M_DAC_L1_STO_L_SFT 15
+#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5682_G_DAC_L1_STO_L_SFT 14
+#define RT5682_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5682_M_DAC_R1_STO_L_SFT 13
+#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5682_G_DAC_R1_STO_L_SFT 12
+#define RT5682_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5682_M_DAC_L1_STO_R_SFT 7
+#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5682_G_DAC_L1_STO_R_SFT 6
+#define RT5682_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5682_M_DAC_R1_STO_R_SFT 5
+#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5682_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5682_M_ST_STO_L (0x1 << 9)
+#define RT5682_M_ST_STO_L_SFT 9
+#define RT5682_M_ST_STO_R (0x1 << 8)
+#define RT5682_M_ST_STO_R_SFT 8
+#define RT5682_DAC_L1_SRC_MASK (0x3 << 4)
+#define RT5682_A_DACL1_SFT 4
+#define RT5682_DAC_R1_SRC_MASK (0x3)
+#define RT5682_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5682_IF2_ADC_SEL_SFT 0
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5682_G_CBJ_RM1_L (0x7 << 10)
+#define RT5682_G_CBJ_RM1_L_SFT 10
+#define RT5682_M_CBJ_RM1_L (0x1 << 7)
+#define RT5682_M_CBJ_RM1_L_SFT 7
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5682_PWR_I2S1 (0x1 << 15)
+#define RT5682_PWR_I2S1_BIT 15
+#define RT5682_PWR_I2S2 (0x1 << 14)
+#define RT5682_PWR_I2S2_BIT 14
+#define RT5682_PWR_DAC_L1 (0x1 << 11)
+#define RT5682_PWR_DAC_L1_BIT 11
+#define RT5682_PWR_DAC_R1 (0x1 << 10)
+#define RT5682_PWR_DAC_R1_BIT 10
+#define RT5682_PWR_LDO (0x1 << 8)
+#define RT5682_PWR_LDO_BIT 8
+#define RT5682_PWR_ADC_L1 (0x1 << 4)
+#define RT5682_PWR_ADC_L1_BIT 4
+#define RT5682_PWR_ADC_R1 (0x1 << 3)
+#define RT5682_PWR_ADC_R1_BIT 3
+#define RT5682_DIG_GATE_CTRL (0x1 << 0)
+#define RT5682_DIG_GATE_CTRL_SFT 0
+
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5682_PWR_ADC_S1F (0x1 << 15)
+#define RT5682_PWR_ADC_S1F_BIT 15
+#define RT5682_PWR_DAC_S1F (0x1 << 10)
+#define RT5682_PWR_DAC_S1F_BIT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5682_PWR_VREF1 (0x1 << 15)
+#define RT5682_PWR_VREF1_BIT 15
+#define RT5682_PWR_FV1 (0x1 << 14)
+#define RT5682_PWR_FV1_BIT 14
+#define RT5682_PWR_VREF2 (0x1 << 13)
+#define RT5682_PWR_VREF2_BIT 13
+#define RT5682_PWR_FV2 (0x1 << 12)
+#define RT5682_PWR_FV2_BIT 12
+#define RT5682_LDO1_DBG_MASK (0x3 << 10)
+#define RT5682_PWR_MB (0x1 << 9)
+#define RT5682_PWR_MB_BIT 9
+#define RT5682_PWR_BG (0x1 << 7)
+#define RT5682_PWR_BG_BIT 7
+#define RT5682_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5682_LDO1_BYPASS (0x1 << 6)
+#define RT5682_LDO1_NOT_BYPASS (0x0 << 6)
+#define RT5682_PWR_MA_BIT 6
+#define RT5682_LDO1_DVO_MASK (0x3 << 4)
+#define RT5682_LDO1_DVO_09 (0x0 << 4)
+#define RT5682_LDO1_DVO_10 (0x1 << 4)
+#define RT5682_LDO1_DVO_12 (0x2 << 4)
+#define RT5682_LDO1_DVO_14 (0x3 << 4)
+#define RT5682_HP_DRIVER_MASK (0x3 << 2)
+#define RT5682_HP_DRIVER_1X (0x0 << 2)
+#define RT5682_HP_DRIVER_3X (0x1 << 2)
+#define RT5682_HP_DRIVER_5X (0x3 << 2)
+#define RT5682_PWR_HA_L (0x1 << 1)
+#define RT5682_PWR_HA_L_BIT 1
+#define RT5682_PWR_HA_R (0x1 << 0)
+#define RT5682_PWR_HA_R_BIT 0
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5682_PWR_MB1 (0x1 << 11)
+#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5682_PWR_MB1_BIT 11
+#define RT5682_PWR_MB2 (0x1 << 10)
+#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5682_PWR_MB2_BIT 10
+#define RT5682_PWR_JDH (0x1 << 3)
+#define RT5682_PWR_JDH_BIT 3
+#define RT5682_PWR_JDL (0x1 << 2)
+#define RT5682_PWR_JDL_BIT 2
+#define RT5682_PWR_RM1_L (0x1 << 1)
+#define RT5682_PWR_RM1_L_BIT 1
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5682_PWR_CBJ (0x1 << 9)
+#define RT5682_PWR_CBJ_BIT 9
+#define RT5682_PWR_PLL (0x1 << 6)
+#define RT5682_PWR_PLL_BIT 6
+#define RT5682_PWR_PLL2B (0x1 << 5)
+#define RT5682_PWR_PLL2B_BIT 5
+#define RT5682_PWR_PLL2F (0x1 << 4)
+#define RT5682_PWR_PLL2F_BIT 4
+#define RT5682_PWR_LDO2 (0x1 << 2)
+#define RT5682_PWR_LDO2_BIT 2
+#define RT5682_PWR_DET_SPKVDD (0x1 << 1)
+#define RT5682_PWR_DET_SPKVDD_BIT 1
+
+/* Power Management for Mixer (0x0066) */
+#define RT5682_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5682_PWR_STO1_DAC_L_BIT 5
+#define RT5682_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5682_PWR_STO1_DAC_R_BIT 4
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5682_SYS_CLK_DET (0x1 << 15)
+#define RT5682_SYS_CLK_DET_SFT 15
+#define RT5682_PLL1_CLK_DET (0x1 << 14)
+#define RT5682_PLL1_CLK_DET_SFT 14
+#define RT5682_PLL2_CLK_DET (0x1 << 13)
+#define RT5682_PLL2_CLK_DET_SFT 13
+#define RT5682_POW_CLK_DET2_SFT 8
+#define RT5682_POW_CLK_DET_SFT 0
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5682_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5682_DMIC_1_EN_SFT 15
+#define RT5682_DMIC_1_DIS (0x0 << 15)
+#define RT5682_DMIC_1_EN (0x1 << 15)
+#define RT5682_FIFO_CLK_DIV_MASK (0x7 << 12)
+#define RT5682_FIFO_CLK_DIV_2 (0x1 << 12)
+#define RT5682_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5682_DMIC_1_DP_SFT 4
+#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5682_DMIC_CLK_MASK (0xf << 0)
+#define RT5682_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5682_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5682_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5682_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5682_SEL_ADCDAT_SFT 15
+#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5682_I2S1_TX_CHL_SFT 12
+#define RT5682_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5682_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5682_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5682_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5682_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5682_I2S1_RX_CHL_SFT 8
+#define RT5682_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5682_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5682_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5682_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5682_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5682_I2S1_MONO_MASK (0x1 << 7)
+#define RT5682_I2S1_MONO_EN (0x1 << 7)
+#define RT5682_I2S1_MONO_DIS (0x0 << 7)
+#define RT5682_I2S2_MONO_MASK (0x1 << 6)
+#define RT5682_I2S2_MONO_EN (0x1 << 6)
+#define RT5682_I2S2_MONO_DIS (0x0 << 6)
+#define RT5682_I2S1_DL_MASK (0x7 << 4)
+#define RT5682_I2S1_DL_SFT 4
+#define RT5682_I2S1_DL_16 (0x0 << 4)
+#define RT5682_I2S1_DL_20 (0x1 << 4)
+#define RT5682_I2S1_DL_24 (0x2 << 4)
+#define RT5682_I2S1_DL_32 (0x3 << 4)
+#define RT5682_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */
+#define RT5682_I2S2_MS_MASK (0x1 << 15)
+#define RT5682_I2S2_MS_SFT 15
+#define RT5682_I2S2_MS_M (0x0 << 15)
+#define RT5682_I2S2_MS_S (0x1 << 15)
+#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5682_I2S2_PIN_CFG_SFT 14
+#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11)
+#define RT5682_I2S2_CLK_SEL_SFT 11
+#define RT5682_I2S2_OUT_MASK (0x1 << 9)
+#define RT5682_I2S2_OUT_SFT 9
+#define RT5682_I2S2_OUT_UM (0x0 << 9)
+#define RT5682_I2S2_OUT_M (0x1 << 9)
+#define RT5682_I2S_BP_MASK (0x1 << 8)
+#define RT5682_I2S_BP_SFT 8
+#define RT5682_I2S_BP_NOR (0x0 << 8)
+#define RT5682_I2S_BP_INV (0x1 << 8)
+#define RT5682_I2S2_MONO_EN (0x1 << 6)
+#define RT5682_I2S2_MONO_DIS (0x0 << 6)
+#define RT5682_I2S2_DL_MASK (0x3 << 4)
+#define RT5682_I2S2_DL_SFT 4
+#define RT5682_I2S2_DL_16 (0x0 << 4)
+#define RT5682_I2S2_DL_20 (0x1 << 4)
+#define RT5682_I2S2_DL_24 (0x2 << 4)
+#define RT5682_I2S2_DL_8 (0x3 << 4)
+#define RT5682_I2S_DF_MASK (0x7)
+#define RT5682_I2S_DF_SFT 0
+#define RT5682_I2S_DF_I2S (0x0)
+#define RT5682_I2S_DF_LEFT (0x1)
+#define RT5682_I2S_DF_PCM_A (0x2)
+#define RT5682_I2S_DF_PCM_B (0x3)
+#define RT5682_I2S_DF_PCM_A_N (0x6)
+#define RT5682_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5682_ADC_OSR_MASK (0xf << 12)
+#define RT5682_ADC_OSR_SFT 12
+#define RT5682_ADC_OSR_D_1 (0x0 << 12)
+#define RT5682_ADC_OSR_D_2 (0x1 << 12)
+#define RT5682_ADC_OSR_D_4 (0x2 << 12)
+#define RT5682_ADC_OSR_D_6 (0x3 << 12)
+#define RT5682_ADC_OSR_D_8 (0x4 << 12)
+#define RT5682_ADC_OSR_D_12 (0x5 << 12)
+#define RT5682_ADC_OSR_D_16 (0x6 << 12)
+#define RT5682_ADC_OSR_D_24 (0x7 << 12)
+#define RT5682_ADC_OSR_D_32 (0x8 << 12)
+#define RT5682_ADC_OSR_D_48 (0x9 << 12)
+#define RT5682_I2S_M_DIV_MASK (0xf << 8)
+#define RT5682_I2S_M_DIV_SFT 8
+#define RT5682_I2S_M_D_1 (0x0 << 8)
+#define RT5682_I2S_M_D_2 (0x1 << 8)
+#define RT5682_I2S_M_D_3 (0x2 << 8)
+#define RT5682_I2S_M_D_4 (0x3 << 8)
+#define RT5682_I2S_M_D_6 (0x4 << 8)
+#define RT5682_I2S_M_D_8 (0x5 << 8)
+#define RT5682_I2S_M_D_12 (0x6 << 8)
+#define RT5682_I2S_M_D_16 (0x7 << 8)
+#define RT5682_I2S_M_D_24 (0x8 << 8)
+#define RT5682_I2S_M_D_32 (0x9 << 8)
+#define RT5682_I2S_M_D_48 (0x10 << 8)
+#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4)
+#define RT5682_I2S_CLK_SRC_SFT 4
+#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4)
+#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4)
+#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4)
+#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4)
+#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */
+#define RT5682_DAC_OSR_MASK (0xf << 0)
+#define RT5682_DAC_OSR_SFT 0
+#define RT5682_DAC_OSR_D_1 (0x0 << 0)
+#define RT5682_DAC_OSR_D_2 (0x1 << 0)
+#define RT5682_DAC_OSR_D_4 (0x2 << 0)
+#define RT5682_DAC_OSR_D_6 (0x3 << 0)
+#define RT5682_DAC_OSR_D_8 (0x4 << 0)
+#define RT5682_DAC_OSR_D_12 (0x5 << 0)
+#define RT5682_DAC_OSR_D_16 (0x6 << 0)
+#define RT5682_DAC_OSR_D_24 (0x7 << 0)
+#define RT5682_DAC_OSR_D_32 (0x8 << 0)
+#define RT5682_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5682_I2S2_BCLK_MS2_SFT 11
+#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5682_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5682_TDM_TX_CH_2 (0x0 << 12)
+#define RT5682_TDM_TX_CH_4 (0x1 << 12)
+#define RT5682_TDM_TX_CH_6 (0x2 << 12)
+#define RT5682_TDM_TX_CH_8 (0x3 << 12)
+#define RT5682_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5682_TDM_RX_CH_2 (0x0 << 8)
+#define RT5682_TDM_RX_CH_4 (0x1 << 8)
+#define RT5682_TDM_RX_CH_6 (0x2 << 8)
+#define RT5682_TDM_RX_CH_8 (0x3 << 8)
+#define RT5682_TDM_ADC_LCA_MASK (0xf << 4)
+#define RT5682_TDM_ADC_LCA_SFT 4
+#define RT5682_TDM_ADC_DL_SFT 0
+
+/* TDM control 2 (0x007a) */
+#define RT5682_IF1_ADC1_SEL_SFT 14
+#define RT5682_IF1_ADC2_SEL_SFT 12
+#define RT5682_IF1_ADC3_SEL_SFT 10
+#define RT5682_IF1_ADC4_SEL_SFT 8
+#define RT5682_TDM_ADC_SEL_SFT 4
+
+/* TDM control 3 (0x007b) */
+#define RT5682_TDM_EN (0x1 << 7)
+
+/* TDM/I2S control (0x007e) */
+#define RT5682_TDM_S_BP_MASK (0x1 << 15)
+#define RT5682_TDM_S_BP_SFT 15
+#define RT5682_TDM_S_BP_NOR (0x0 << 15)
+#define RT5682_TDM_S_BP_INV (0x1 << 15)
+#define RT5682_TDM_S_LP_MASK (0x1 << 14)
+#define RT5682_TDM_S_LP_SFT 14
+#define RT5682_TDM_S_LP_NOR (0x0 << 14)
+#define RT5682_TDM_S_LP_INV (0x1 << 14)
+#define RT5682_TDM_DF_MASK (0x7 << 11)
+#define RT5682_TDM_DF_SFT 11
+#define RT5682_TDM_DF_I2S (0x0 << 11)
+#define RT5682_TDM_DF_LEFT (0x1 << 11)
+#define RT5682_TDM_DF_PCM_A (0x2 << 11)
+#define RT5682_TDM_DF_PCM_B (0x3 << 11)
+#define RT5682_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5682_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5682_TDM_BCLK_MS1_MASK (0x3 << 9)
+#define RT5682_TDM_BCLK_MS1_SFT 9
+#define RT5682_TDM_BCLK_MS1_32 (0x0 << 9)
+#define RT5682_TDM_BCLK_MS1_64 (0x1 << 9)
+#define RT5682_TDM_BCLK_MS1_128 (0x2 << 9)
+#define RT5682_TDM_BCLK_MS1_256 (0x3 << 9)
+#define RT5682_TDM_CL_MASK (0x3 << 4)
+#define RT5682_TDM_CL_16 (0x0 << 4)
+#define RT5682_TDM_CL_20 (0x1 << 4)
+#define RT5682_TDM_CL_24 (0x2 << 4)
+#define RT5682_TDM_CL_32 (0x3 << 4)
+#define RT5682_TDM_M_BP_MASK (0x1 << 2)
+#define RT5682_TDM_M_BP_SFT 2
+#define RT5682_TDM_M_BP_NOR (0x0 << 2)
+#define RT5682_TDM_M_BP_INV (0x1 << 2)
+#define RT5682_TDM_M_LP_MASK (0x1 << 1)
+#define RT5682_TDM_M_LP_SFT 1
+#define RT5682_TDM_M_LP_NOR (0x0 << 1)
+#define RT5682_TDM_M_LP_INV (0x1 << 1)
+#define RT5682_TDM_MS_MASK (0x1 << 0)
+#define RT5682_TDM_MS_SFT 0
+#define RT5682_TDM_MS_S (0x0 << 0)
+#define RT5682_TDM_MS_M (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5682_SCLK_SRC_MASK (0x7 << 13)
+#define RT5682_SCLK_SRC_SFT 13
+#define RT5682_SCLK_SRC_MCLK (0x0 << 13)
+#define RT5682_SCLK_SRC_PLL1 (0x1 << 13)
+#define RT5682_SCLK_SRC_PLL2 (0x2 << 13)
+#define RT5682_SCLK_SRC_SDW (0x3 << 13)
+#define RT5682_SCLK_SRC_RCCLK (0x4 << 13)
+#define RT5682_PLL2_SRC_MASK (0x3 << 10)
+#define RT5682_PLL2_SRC_SFT 10
+#define RT5682_PLL2_SRC_MCLK (0x0 << 10)
+#define RT5682_PLL2_SRC_BCLK1 (0x1 << 10)
+#define RT5682_PLL2_SRC_SDW (0x2 << 10)
+#define RT5682_PLL2_SRC_RC (0x3 << 10)
+#define RT5682_PLL1_SRC_MASK (0x3 << 8)
+#define RT5682_PLL1_SRC_SFT 8
+#define RT5682_PLL1_SRC_MCLK (0x0 << 8)
+#define RT5682_PLL1_SRC_BCLK1 (0x1 << 8)
+#define RT5682_PLL1_SRC_SDW (0x2 << 8)
+#define RT5682_PLL1_SRC_RC (0x3 << 8)
+
+
+
+#define RT5682_PLL_INP_MAX 40000000
+#define RT5682_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5682_PLL_N_MAX 0x001ff
+#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7)
+#define RT5682_PLL_N_SFT 7
+#define RT5682_PLL_K_MAX 0x001f
+#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX)
+#define RT5682_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5682_PLL_M_MAX 0x00f
+#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12)
+#define RT5682_PLL_M_SFT 12
+#define RT5682_PLL_M_BP (0x1 << 11)
+#define RT5682_PLL_M_BP_SFT 11
+#define RT5682_PLL_K_BP (0x1 << 10)
+#define RT5682_PLL_K_BP_SFT 10
+#define RT5682_PLL_RST (0x1 << 1)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5682_DA_ASRC_MASK (0x1 << 13)
+#define RT5682_DA_ASRC_SFT 13
+#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5682_DAC_STO1_ASRC_SFT 12
+#define RT5682_AD_ASRC_MASK (0x1 << 8)
+#define RT5682_AD_ASRC_SFT 8
+#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5682_AD_ASRC_SEL_SFT 4
+#define RT5682_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5682_DMIC_ASRC_SFT 3
+#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5682_ADC_STO1_ASRC_SFT 2
+#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5682_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5682_FILTER_CLK_SEL_SFT 12
+#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8)
+#define RT5682_FILTER_CLK_DIV_SFT 8
+
+/* ASRC Control 4 (0x0086) */
+#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5682_ASRCIN_FTK_N1_SFT 14
+#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5682_ASRCIN_FTK_N2_SFT 12
+#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5682_ASRCIN_FTK_M1_SFT 8
+#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5682_ASRCIN_FTK_M2_SFT 4
+
+/* SoundWire reference clk (0x008d) */
+#define RT5682_PLL2_OUT_MASK (0x1 << 8)
+#define RT5682_PLL2_OUT_98M (0x0 << 8)
+#define RT5682_PLL2_OUT_49M (0x1 << 8)
+#define RT5682_SDW_REF_2_MASK (0xf << 4)
+#define RT5682_SDW_REF_2_SFT 4
+#define RT5682_SDW_REF_2_48K (0x0 << 4)
+#define RT5682_SDW_REF_2_96K (0x1 << 4)
+#define RT5682_SDW_REF_2_192K (0x2 << 4)
+#define RT5682_SDW_REF_2_32K (0x3 << 4)
+#define RT5682_SDW_REF_2_24K (0x4 << 4)
+#define RT5682_SDW_REF_2_16K (0x5 << 4)
+#define RT5682_SDW_REF_2_12K (0x6 << 4)
+#define RT5682_SDW_REF_2_8K (0x7 << 4)
+#define RT5682_SDW_REF_2_44K (0x8 << 4)
+#define RT5682_SDW_REF_2_88K (0x9 << 4)
+#define RT5682_SDW_REF_2_176K (0xa << 4)
+#define RT5682_SDW_REF_2_353K (0xb << 4)
+#define RT5682_SDW_REF_2_22K (0xc << 4)
+#define RT5682_SDW_REF_2_384K (0xd << 4)
+#define RT5682_SDW_REF_2_11K (0xe << 4)
+#define RT5682_SDW_REF_1_MASK (0xf << 0)
+#define RT5682_SDW_REF_1_SFT 0
+#define RT5682_SDW_REF_1_48K (0x0 << 0)
+#define RT5682_SDW_REF_1_96K (0x1 << 0)
+#define RT5682_SDW_REF_1_192K (0x2 << 0)
+#define RT5682_SDW_REF_1_32K (0x3 << 0)
+#define RT5682_SDW_REF_1_24K (0x4 << 0)
+#define RT5682_SDW_REF_1_16K (0x5 << 0)
+#define RT5682_SDW_REF_1_12K (0x6 << 0)
+#define RT5682_SDW_REF_1_8K (0x7 << 0)
+#define RT5682_SDW_REF_1_44K (0x8 << 0)
+#define RT5682_SDW_REF_1_88K (0x9 << 0)
+#define RT5682_SDW_REF_1_176K (0xa << 0)
+#define RT5682_SDW_REF_1_353K (0xb << 0)
+#define RT5682_SDW_REF_1_22K (0xc << 0)
+#define RT5682_SDW_REF_1_384K (0xd << 0)
+#define RT5682_SDW_REF_1_11K (0xe << 0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5682_PUMP_EN (0x1 << 3)
+#define RT5682_PUMP_EN_SFT 3
+#define RT5682_CAPLESS_EN (0x1 << 0)
+#define RT5682_CAPLESS_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5682_RAMP_MASK (0x1 << 12)
+#define RT5682_RAMP_SFT 12
+#define RT5682_RAMP_DIS (0x0 << 12)
+#define RT5682_RAMP_EN (0x1 << 12)
+#define RT5682_BPS_MASK (0x1 << 11)
+#define RT5682_BPS_SFT 11
+#define RT5682_BPS_DIS (0x0 << 11)
+#define RT5682_BPS_EN (0x1 << 11)
+#define RT5682_FAST_UPDN_MASK (0x1 << 10)
+#define RT5682_FAST_UPDN_SFT 10
+#define RT5682_FAST_UPDN_DIS (0x0 << 10)
+#define RT5682_FAST_UPDN_EN (0x1 << 10)
+#define RT5682_VLO_MASK (0x1 << 7)
+#define RT5682_VLO_SFT 7
+#define RT5682_VLO_3V (0x0 << 7)
+#define RT5682_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5682_OSW_L_MASK (0x1 << 11)
+#define RT5682_OSW_L_SFT 11
+#define RT5682_OSW_L_DIS (0x0 << 11)
+#define RT5682_OSW_L_EN (0x1 << 11)
+#define RT5682_OSW_R_MASK (0x1 << 10)
+#define RT5682_OSW_R_SFT 10
+#define RT5682_OSW_R_DIS (0x0 << 10)
+#define RT5682_OSW_R_EN (0x1 << 10)
+#define RT5682_PM_HP_MASK (0x3 << 8)
+#define RT5682_PM_HP_SFT 8
+#define RT5682_PM_HP_LV (0x0 << 8)
+#define RT5682_PM_HP_MV (0x1 << 8)
+#define RT5682_PM_HP_HV (0x2 << 8)
+#define RT5682_IB_HP_MASK (0x3 << 6)
+#define RT5682_IB_HP_SFT 6
+#define RT5682_IB_HP_125IL (0x0 << 6)
+#define RT5682_IB_HP_25IL (0x1 << 6)
+#define RT5682_IB_HP_5IL (0x2 << 6)
+#define RT5682_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control1 (0x93) */
+#define RT5682_MIC1_OV_MASK (0x3 << 14)
+#define RT5682_MIC1_OV_SFT 14
+#define RT5682_MIC1_OV_2V7 (0x0 << 14)
+#define RT5682_MIC1_OV_2V4 (0x1 << 14)
+#define RT5682_MIC1_OV_2V25 (0x3 << 14)
+#define RT5682_MIC1_OV_1V8 (0x4 << 14)
+#define RT5682_MIC1_CLK_MASK (0x1 << 13)
+#define RT5682_MIC1_CLK_SFT 13
+#define RT5682_MIC1_CLK_DIS (0x0 << 13)
+#define RT5682_MIC1_CLK_EN (0x1 << 13)
+#define RT5682_MIC1_OVCD_MASK (0x1 << 12)
+#define RT5682_MIC1_OVCD_SFT 12
+#define RT5682_MIC1_OVCD_DIS (0x0 << 12)
+#define RT5682_MIC1_OVCD_EN (0x1 << 12)
+#define RT5682_MIC1_OVTH_MASK (0x3 << 10)
+#define RT5682_MIC1_OVTH_SFT 10
+#define RT5682_MIC1_OVTH_768UA (0x0 << 10)
+#define RT5682_MIC1_OVTH_960UA (0x1 << 10)
+#define RT5682_MIC1_OVTH_1152UA (0x2 << 10)
+#define RT5682_MIC1_OVTH_1960UA (0x3 << 10)
+#define RT5682_MIC2_OV_MASK (0x3 << 8)
+#define RT5682_MIC2_OV_SFT 8
+#define RT5682_MIC2_OV_2V7 (0x0 << 8)
+#define RT5682_MIC2_OV_2V4 (0x1 << 8)
+#define RT5682_MIC2_OV_2V25 (0x3 << 8)
+#define RT5682_MIC2_OV_1V8 (0x4 << 8)
+#define RT5682_MIC2_CLK_MASK (0x1 << 7)
+#define RT5682_MIC2_CLK_SFT 7
+#define RT5682_MIC2_CLK_DIS (0x0 << 7)
+#define RT5682_MIC2_CLK_EN (0x1 << 7)
+#define RT5682_MIC2_OVTH_MASK (0x3 << 4)
+#define RT5682_MIC2_OVTH_SFT 4
+#define RT5682_MIC2_OVTH_768UA (0x0 << 4)
+#define RT5682_MIC2_OVTH_960UA (0x1 << 4)
+#define RT5682_MIC2_OVTH_1152UA (0x2 << 4)
+#define RT5682_MIC2_OVTH_1960UA (0x3 << 4)
+#define RT5682_PWR_MB_MASK (0x1 << 3)
+#define RT5682_PWR_MB_SFT 3
+#define RT5682_PWR_MB_PD (0x0 << 3)
+#define RT5682_PWR_MB_PU (0x1 << 3)
+
+/* Micbias Control2 (0x0094) */
+#define RT5682_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5682_PWR_CLK25M_SFT 9
+#define RT5682_PWR_CLK25M_PD (0x0 << 9)
+#define RT5682_PWR_CLK25M_PU (0x1 << 9)
+#define RT5682_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5682_PWR_CLK1M_SFT 8
+#define RT5682_PWR_CLK1M_PD (0x0 << 8)
+#define RT5682_PWR_CLK1M_PU (0x1 << 8)
+
+/* PLL2 M/N/K Code Control 1 (0x009b) */
+#define RT5682_PLL2F_K_MASK (0x1f << 8)
+#define RT5682_PLL2F_K_SFT 8
+#define RT5682_PLL2B_K_MASK (0xf << 4)
+#define RT5682_PLL2B_K_SFT 4
+#define RT5682_PLL2B_M_MASK (0xf << 0)
+
+/* PLL2 M/N/K Code Control 2 (0x009c) */
+#define RT5682_PLL2F_M_MASK (0x3f << 8)
+#define RT5682_PLL2F_M_SFT 8
+#define RT5682_PLL2B_N_MASK (0x3f << 0)
+
+/* PLL2 M/N/K Code Control 2 (0x009d) */
+#define RT5682_PLL2F_N_MASK (0x7f << 8)
+#define RT5682_PLL2F_N_SFT 8
+
+/* PLL2 M/N/K Code Control 2 (0x009e) */
+#define RT5682_PLL2B_SEL_PS_MASK (0x1 << 13)
+#define RT5682_PLL2B_SEL_PS_SFT 13
+#define RT5682_PLL2B_PS_BYP_MASK (0x1 << 12)
+#define RT5682_PLL2B_PS_BYP_SFT 12
+#define RT5682_PLL2B_M_BP_MASK (0x1 << 11)
+#define RT5682_PLL2B_M_BP_SFT 11
+#define RT5682_PLL2F_M_BP_MASK (0x1 << 7)
+#define RT5682_PLL2F_M_BP_SFT 7
+
+/* RC Clock Control (0x009f) */
+#define RT5682_POW_IRQ (0x1 << 15)
+#define RT5682_POW_JDH (0x1 << 14)
+#define RT5682_POW_JDL (0x1 << 13)
+#define RT5682_POW_ANA (0x1 << 12)
+
+/* I2S Master Mode Clock Control 1 (0x00a0) */
+#define RT5682_CLK_SRC_MCLK (0x0)
+#define RT5682_CLK_SRC_PLL1 (0x1)
+#define RT5682_CLK_SRC_PLL2 (0x2)
+#define RT5682_CLK_SRC_SDW (0x3)
+#define RT5682_CLK_SRC_RCCLK (0x4)
+#define RT5682_I2S_PD_1 (0x0)
+#define RT5682_I2S_PD_2 (0x1)
+#define RT5682_I2S_PD_3 (0x2)
+#define RT5682_I2S_PD_4 (0x3)
+#define RT5682_I2S_PD_6 (0x4)
+#define RT5682_I2S_PD_8 (0x5)
+#define RT5682_I2S_PD_12 (0x6)
+#define RT5682_I2S_PD_16 (0x7)
+#define RT5682_I2S_PD_24 (0x8)
+#define RT5682_I2S_PD_32 (0x9)
+#define RT5682_I2S_PD_48 (0xa)
+#define RT5682_I2S2_SRC_MASK (0x3 << 4)
+#define RT5682_I2S2_SRC_SFT 4
+#define RT5682_I2S2_M_PD_MASK (0xf << 0)
+#define RT5682_I2S2_M_PD_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5682_JD1_PULSE_EN_SFT 10
+#define RT5682_JD1_PULSE_DIS (0x0 << 10)
+#define RT5682_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5682_JD1_EN_MASK (0x1 << 15)
+#define RT5682_JD1_EN_SFT 15
+#define RT5682_JD1_DIS (0x0 << 15)
+#define RT5682_JD1_EN (0x1 << 15)
+#define RT5682_JD1_POL_MASK (0x1 << 13)
+#define RT5682_JD1_POL_NOR (0x0 << 13)
+#define RT5682_JD1_POL_INV (0x1 << 13)
+#define RT5682_JD1_IRQ_MASK (0x1 << 10)
+#define RT5682_JD1_IRQ_LEV (0x0 << 10)
+#define RT5682_JD1_IRQ_PUL (0x1 << 10)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5682_IL_IRQ_MASK (0x1 << 7)
+#define RT5682_IL_IRQ_DIS (0x0 << 7)
+#define RT5682_IL_IRQ_EN (0x1 << 7)
+#define RT5682_IL_IRQ_TYPE_MASK (0x1 << 4)
+#define RT5682_IL_IRQ_LEV (0x0 << 4)
+#define RT5682_IL_IRQ_PUL (0x1 << 4)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5682_GP1_PIN_MASK (0x3 << 14)
+#define RT5682_GP1_PIN_SFT 14
+#define RT5682_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5682_GP1_PIN_IRQ (0x1 << 14)
+#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5682_GP2_PIN_MASK (0x3 << 12)
+#define RT5682_GP2_PIN_SFT 12
+#define RT5682_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5682_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5682_GP3_PIN_MASK (0x3 << 10)
+#define RT5682_GP3_PIN_SFT 10
+#define RT5682_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5682_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5682_GP4_PIN_MASK (0x3 << 8)
+#define RT5682_GP4_PIN_SFT 8
+#define RT5682_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5682_GP5_PIN_MASK (0x3 << 6)
+#define RT5682_GP5_PIN_SFT 6
+#define RT5682_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5682_GP6_PIN_MASK (0x1 << 5)
+#define RT5682_GP6_PIN_SFT 5
+#define RT5682_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5682_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5682_GP1_PF_MASK (0x1 << 15)
+#define RT5682_GP1_PF_IN (0x0 << 15)
+#define RT5682_GP1_PF_OUT (0x1 << 15)
+#define RT5682_GP1_OUT_MASK (0x1 << 14)
+#define RT5682_GP1_OUT_L (0x0 << 14)
+#define RT5682_GP1_OUT_H (0x1 << 14)
+#define RT5682_GP2_PF_MASK (0x1 << 13)
+#define RT5682_GP2_PF_IN (0x0 << 13)
+#define RT5682_GP2_PF_OUT (0x1 << 13)
+#define RT5682_GP2_OUT_MASK (0x1 << 12)
+#define RT5682_GP2_OUT_L (0x0 << 12)
+#define RT5682_GP2_OUT_H (0x1 << 12)
+#define RT5682_GP3_PF_MASK (0x1 << 11)
+#define RT5682_GP3_PF_IN (0x0 << 11)
+#define RT5682_GP3_PF_OUT (0x1 << 11)
+#define RT5682_GP3_OUT_MASK (0x1 << 10)
+#define RT5682_GP3_OUT_L (0x0 << 10)
+#define RT5682_GP3_OUT_H (0x1 << 10)
+#define RT5682_GP4_PF_MASK (0x1 << 9)
+#define RT5682_GP4_PF_IN (0x0 << 9)
+#define RT5682_GP4_PF_OUT (0x1 << 9)
+#define RT5682_GP4_OUT_MASK (0x1 << 8)
+#define RT5682_GP4_OUT_L (0x0 << 8)
+#define RT5682_GP4_OUT_H (0x1 << 8)
+#define RT5682_GP5_PF_MASK (0x1 << 7)
+#define RT5682_GP5_PF_IN (0x0 << 7)
+#define RT5682_GP5_PF_OUT (0x1 << 7)
+#define RT5682_GP5_OUT_MASK (0x1 << 6)
+#define RT5682_GP5_OUT_L (0x0 << 6)
+#define RT5682_GP5_OUT_H (0x1 << 6)
+#define RT5682_GP6_PF_MASK (0x1 << 5)
+#define RT5682_GP6_PF_IN (0x0 << 5)
+#define RT5682_GP6_PF_OUT (0x1 << 5)
+#define RT5682_GP6_OUT_MASK (0x1 << 4)
+#define RT5682_GP6_OUT_L (0x0 << 4)
+#define RT5682_GP6_OUT_H (0x1 << 4)
+
+
+/* GPIO Status (0x00c2) */
+#define RT5682_GP6_STA (0x1 << 6)
+#define RT5682_GP5_STA (0x1 << 5)
+#define RT5682_GP4_STA (0x1 << 4)
+#define RT5682_GP3_STA (0x1 << 3)
+#define RT5682_GP2_STA (0x1 << 2)
+#define RT5682_GP1_STA (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5682_SV_MASK (0x1 << 15)
+#define RT5682_SV_SFT 15
+#define RT5682_SV_DIS (0x0 << 15)
+#define RT5682_SV_EN (0x1 << 15)
+#define RT5682_ZCD_MASK (0x1 << 10)
+#define RT5682_ZCD_SFT 10
+#define RT5682_ZCD_PD (0x0 << 10)
+#define RT5682_ZCD_PU (0x1 << 10)
+#define RT5682_SV_DLY_MASK (0xf)
+#define RT5682_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0x00da) */
+#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7)
+#define RT5682_ZCD_BST1_CBJ_SFT 7
+#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7)
+#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7)
+#define RT5682_ZCD_RECMIX_MASK (0x1)
+#define RT5682_ZCD_RECMIX_SFT 0
+#define RT5682_ZCD_RECMIX_DIS (0x0)
+#define RT5682_ZCD_RECMIX_EN (0x1)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5682_4BTN_IL_MASK (0x1 << 15)
+#define RT5682_4BTN_IL_EN (0x1 << 15)
+#define RT5682_4BTN_IL_DIS (0x0 << 15)
+#define RT5682_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5682_4BTN_IL_NOR (0x1 << 14)
+#define RT5682_4BTN_IL_RST (0x0 << 14)
+
+/* Analog JD Control (0x00f0) */
+#define RT5682_JDH_RS_MASK (0x1 << 4)
+#define RT5682_JDH_NO_PLUG (0x1 << 4)
+#define RT5682_JDH_PLUG (0x0 << 4)
+
+/* Bias current control 8 (0x0111) */
+#define RT5682_HPA_CP_BIAS_CTRL_MASK (0x3 << 2)
+#define RT5682_HPA_CP_BIAS_2UA (0x0 << 2)
+#define RT5682_HPA_CP_BIAS_3UA (0x1 << 2)
+#define RT5682_HPA_CP_BIAS_4UA (0x2 << 2)
+#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682_CP_SW_SIZE_MASK (0x7 << 8)
+#define RT5682_CP_SW_SIZE_L (0x4 << 8)
+#define RT5682_CP_SW_SIZE_M (0x2 << 8)
+#define RT5682_CP_SW_SIZE_S (0x1 << 8)
+#define RT5682_CP_CLK_HP_MASK (0x3 << 4)
+#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4)
+#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4)
+#define RT5682_CP_CLK_HP_300KHZ (0x2 << 4)
+#define RT5682_CP_CLK_HP_600KHZ (0x3 << 4)
+
+/* Pad Driving Control (0x0136) */
+#define RT5682_PAD_DRV_GP1_MASK (0x3 << 14)
+#define RT5682_PAD_DRV_GP1_SFT 14
+#define RT5682_PAD_DRV_GP2_MASK (0x3 << 12)
+#define RT5682_PAD_DRV_GP2_SFT 12
+#define RT5682_PAD_DRV_GP3_MASK (0x3 << 10)
+#define RT5682_PAD_DRV_GP3_SFT 10
+#define RT5682_PAD_DRV_GP4_MASK (0x3 << 8)
+#define RT5682_PAD_DRV_GP4_SFT 8
+#define RT5682_PAD_DRV_GP5_MASK (0x3 << 6)
+#define RT5682_PAD_DRV_GP5_SFT 6
+#define RT5682_PAD_DRV_GP6_MASK (0x3 << 4)
+#define RT5682_PAD_DRV_GP6_SFT 4
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5682_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5682_CKXEN_DAC1_SFT 13
+#define RT5682_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5682_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5682_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5682_CKXEN_ADC1_SFT 13
+#define RT5682_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5682_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5682_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5682_AD2DA_LB_MASK (0x1 << 10)
+#define RT5682_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5682_NG2_EN_MASK (0x1 << 15)
+#define RT5682_NG2_EN (0x1 << 15)
+#define RT5682_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5682_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5682_DEB_80_MS (0x0 << 4)
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682_HP_LC2_SIG_SOUR2_MASK (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_REG (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_DC_CAL (0x0 << 4)
+#define RT5682_HP_LC2_SIG_SOUR1_MASK (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_1BIT (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_LEGA (0x2)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15)
+#define RT5682_SAR_BUTT_DET_EN (0x1 << 15)
+#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15)
+#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14)
+#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13)
+#define RT5682_SAR_BUTDET_RST (0x0 << 13)
+#define RT5682_SAR_POW_MASK (0x1 << 12)
+#define RT5682_SAR_POW_EN (0x1 << 12)
+#define RT5682_SAR_POW_DIS (0x0 << 12)
+#define RT5682_SAR_RST_MASK (0x1 << 11)
+#define RT5682_SAR_RST_NORMAL (0x1 << 11)
+#define RT5682_SAR_RST (0x0 << 11)
+#define RT5682_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5682_SAR_BYPASS_EN (0x1 << 10)
+#define RT5682_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9)
+#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9)
+#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9)
+#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8)
+#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8)
+#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8)
+#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5)
+#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5)
+#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5)
+#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5682_SAR_SOUR_MASK (0x3f)
+#define RT5682_SAR_SOUR_BTN (0x3f)
+#define RT5682_SAR_SOUR_TYPE (0x0)
+
+/* soundwire timeout */
+#define RT5682_PROBE_TIMEOUT 5000
+
+
+#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+/* System Clock Source */
+enum {
+ RT5682_SCLK_S_MCLK,
+ RT5682_SCLK_S_PLL1,
+ RT5682_SCLK_S_PLL2,
+ RT5682_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5682_PLL1_S_MCLK,
+ RT5682_PLL1_S_BCLK1,
+ RT5682_PLL1_S_RCCLK,
+ RT5682_PLL2_S_MCLK,
+};
+
+enum {
+ RT5682_PLL1,
+ RT5682_PLL2,
+ RT5682_PLLS,
+};
+
+enum {
+ RT5682_AIF1,
+ RT5682_AIF2,
+ RT5682_SDW,
+ RT5682_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5682_DA_STEREO1_FILTER = 0x1,
+ RT5682_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5682_CLK_SEL_SYS,
+ RT5682_CLK_SEL_I2S1_ASRC,
+ RT5682_CLK_SEL_I2S2_ASRC,
+};
+
+#define RT5682_NUM_SUPPLIES 5
+
+struct rt5682_priv {
+ struct snd_soc_component *component;
+ struct device *i2c_dev;
+ struct rt5682_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+ struct mutex calibrate_mutex;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ bool is_sdw;
+ bool ve_ic;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS];
+ struct clk *mclk;
+#endif
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5682_AIFS];
+ int bclk[RT5682_AIFS];
+ int master[RT5682_AIFS];
+
+ int pll_src[RT5682_PLLS];
+ int pll_in[RT5682_PLLS];
+ int pll_out[RT5682_PLLS];
+
+ int jack_type;
+ int irq;
+ int irq_work_delay_time;
+};
+
+extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES];
+
+int rt5682_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev);
+
+void rt5682_jack_detect_handler(struct work_struct *work);
+
+bool rt5682_volatile_register(struct device *dev, unsigned int reg);
+bool rt5682_readable_register(struct device *dev, unsigned int reg);
+
+int rt5682_register_component(struct device *dev);
+void rt5682_calibrate(struct rt5682_priv *rt5682);
+void rt5682_reset(struct rt5682_priv *rt5682);
+int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev);
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev);
+
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682);
+
+#define RT5682_REG_NUM 318
+extern const struct reg_default rt5682_reg[RT5682_REG_NUM];
+
+extern const struct snd_soc_dai_ops rt5682_aif1_dai_ops;
+extern const struct snd_soc_dai_ops rt5682_aif2_dai_ops;
+extern const struct snd_soc_component_driver rt5682_soc_component_dev;
+
+#endif /* __RT5682_H__ */
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
new file mode 100644
index 000000000000..1d80a4b862e2
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.c
@@ -0,0 +1,3348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682s.c -- RT5682I-VS ALSA SoC audio component driver
+//
+// Copyright 2021 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/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.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 <sound/rt5682s.h>
+
+#include "rt5682s.h"
+
+#define DEVICE_ID 0x6749
+
+static const struct rt5682s_platform_data i2s_default_platform_data = {
+ .dmic1_data_pin = RT5682S_DMIC1_DATA_GPIO2,
+ .dmic1_clk_pin = RT5682S_DMIC1_CLK_GPIO3,
+ .jd_src = RT5682S_JD1,
+ .dai_clk_names[RT5682S_DAI_WCLK_IDX] = "rt5682-dai-wclk",
+ .dai_clk_names[RT5682S_DAI_BCLK_IDX] = "rt5682-dai-bclk",
+};
+
+static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = {
+ [RT5682S_SUPPLY_AVDD] = "AVDD",
+ [RT5682S_SUPPLY_MICVDD] = "MICVDD",
+ [RT5682S_SUPPLY_DBVDD] = "DBVDD",
+ [RT5682S_SUPPLY_LDO1_IN] = "LDO1-IN",
+};
+
+static const struct reg_sequence patch_list[] = {
+ {RT5682S_I2C_CTRL, 0x0007},
+ {RT5682S_DIG_IN_CTRL_1, 0x0000},
+ {RT5682S_CHOP_DAC_2, 0x2020},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_2, 0x0101},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_1, 0x80c0},
+ {RT5682S_HP_CALIB_CTRL_9, 0x0002},
+ {RT5682S_DEPOP_1, 0x0000},
+ {RT5682S_HP_CHARGE_PUMP_2, 0x3c15},
+ {RT5682S_DAC1_DIG_VOL, 0xfefe},
+ {RT5682S_SAR_IL_CMD_2, 0xac00},
+ {RT5682S_SAR_IL_CMD_3, 0x024c},
+ {RT5682S_CBJ_CTRL_6, 0x0804},
+};
+
+static void rt5682s_apply_patch_list(struct rt5682s_priv *rt5682s,
+ struct device *dev)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(rt5682s->regmap, patch_list, ARRAY_SIZE(patch_list));
+ if (ret)
+ dev_warn(dev, "Failed to apply regmap patch: %d\n", ret);
+}
+
+static const struct reg_default rt5682s_reg[] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x0001},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x8007},
+ {0x000b, 0x0000},
+ {0x000f, 0x4000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x0000},
+ {0x0013, 0x1200},
+ {0x0014, 0x200a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0017, 0x05a4},
+ {0x0019, 0xffff},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x08c0},
+ {0x0044, 0x1818},
+ {0x004b, 0x00c0},
+ {0x004c, 0x0000},
+ {0x004d, 0x0000},
+ {0x0061, 0x00c0},
+ {0x0062, 0x008a},
+ {0x0063, 0x0800},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x000c},
+ {0x0068, 0x0000},
+ {0x0069, 0x0000},
+ {0x006a, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0810},
+ {0x006f, 0xe4de},
+ {0x0070, 0x3320},
+ {0x0071, 0x0000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x007f, 0x0000},
+ {0x0080, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008e, 0x0060},
+ {0x008f, 0x4da1},
+ {0x0091, 0x1c15},
+ {0x0092, 0x0425},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0095, 0x008f},
+ {0x0096, 0x0000},
+ {0x0097, 0x0000},
+ {0x0098, 0x0000},
+ {0x0099, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x0000},
+ {0x009f, 0x0009},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0000},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6749},
+ {0x0100, 0xa000},
+ {0x010b, 0x0066},
+ {0x010c, 0x6666},
+ {0x010d, 0x2202},
+ {0x010e, 0x6666},
+ {0x010f, 0xa800},
+ {0x0110, 0x0006},
+ {0x0111, 0x0460},
+ {0x0112, 0x2000},
+ {0x0113, 0x0200},
+ {0x0117, 0x8000},
+ {0x0118, 0x0303},
+ {0x0125, 0x0020},
+ {0x0132, 0x5026},
+ {0x0136, 0x8000},
+ {0x0139, 0x0005},
+ {0x013a, 0x3030},
+ {0x013b, 0xa000},
+ {0x013c, 0x4110},
+ {0x013f, 0x0000},
+ {0x0145, 0x0022},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0156, 0x0022},
+ {0x0157, 0x0303},
+ {0x0158, 0x2222},
+ {0x0159, 0x0000},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0001},
+ {0x0190, 0x4131},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01ac, 0x0000},
+ {0x01ad, 0x0000},
+ {0x01ae, 0x0000},
+ {0x01af, 0x2000},
+ {0x01b0, 0x0000},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0017},
+ {0x01b4, 0x004b},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x03e8},
+ {0x01b7, 0x0000},
+ {0x01b8, 0x0000},
+ {0x01b9, 0x0400},
+ {0x01ba, 0xb5b6},
+ {0x01bb, 0x9124},
+ {0x01bc, 0x4924},
+ {0x01bd, 0x0009},
+ {0x01be, 0x0018},
+ {0x01bf, 0x002a},
+ {0x01c0, 0x004c},
+ {0x01c1, 0x0097},
+ {0x01c2, 0x01c3},
+ {0x01c3, 0x03e9},
+ {0x01c4, 0x1389},
+ {0x01c5, 0xc351},
+ {0x01c6, 0x02a0},
+ {0x01c7, 0x0b0f},
+ {0x01c8, 0x402f},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0000},
+ {0x01d8, 0x0162},
+ {0x01d9, 0x0007},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0020},
+ {0x01e0, 0x04c1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e5, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa004},
+ {0x0212, 0x0365},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x021d, 0x024c},
+ {0x02fa, 0x0000},
+ {0x02fb, 0x0000},
+ {0x02fc, 0x0000},
+ {0x03fe, 0x0000},
+ {0x03ff, 0x0000},
+ {0x0500, 0x0000},
+ {0x0600, 0x0000},
+ {0x0610, 0x6666},
+ {0x0611, 0xa9aa},
+ {0x0620, 0x6666},
+ {0x0621, 0xa9aa},
+ {0x0630, 0x6666},
+ {0x0631, 0xa9aa},
+ {0x0640, 0x6666},
+ {0x0641, 0xa9aa},
+ {0x07fa, 0x0000},
+ {0x08fa, 0x0000},
+ {0x08fb, 0x0000},
+ {0x0d00, 0x0000},
+ {0x1100, 0x0000},
+ {0x1101, 0x0000},
+ {0x1102, 0x0000},
+ {0x1103, 0x0000},
+ {0x1104, 0x0000},
+ {0x1105, 0x0000},
+ {0x1106, 0x0000},
+ {0x1107, 0x0000},
+ {0x1108, 0x0000},
+ {0x1109, 0x0000},
+ {0x110a, 0x0000},
+ {0x110b, 0x0000},
+ {0x110c, 0x0000},
+ {0x1111, 0x0000},
+ {0x1112, 0x0000},
+ {0x1113, 0x0000},
+ {0x1114, 0x0000},
+ {0x1115, 0x0000},
+ {0x1116, 0x0000},
+ {0x1117, 0x0000},
+ {0x1118, 0x0000},
+ {0x1119, 0x0000},
+ {0x111a, 0x0000},
+ {0x111b, 0x0000},
+ {0x111c, 0x0000},
+ {0x1401, 0x0404},
+ {0x1402, 0x0007},
+ {0x1403, 0x0365},
+ {0x1404, 0x0210},
+ {0x1405, 0x0365},
+ {0x1406, 0x0210},
+ {0x1407, 0x0000},
+ {0x1408, 0x0000},
+ {0x1409, 0x0000},
+ {0x140a, 0x0000},
+ {0x140b, 0x0000},
+ {0x140c, 0x0000},
+ {0x140d, 0x0000},
+ {0x140e, 0x0000},
+ {0x140f, 0x0000},
+ {0x1410, 0x0000},
+ {0x1411, 0x0000},
+ {0x1801, 0x0004},
+ {0x1802, 0x0000},
+ {0x1803, 0x0000},
+ {0x1804, 0x0000},
+ {0x1805, 0x00ff},
+ {0x2c00, 0x0000},
+ {0x3400, 0x0200},
+ {0x3404, 0x0000},
+ {0x3405, 0x0000},
+ {0x3406, 0x0000},
+ {0x3407, 0x0000},
+ {0x3408, 0x0000},
+ {0x3409, 0x0000},
+ {0x340a, 0x0000},
+ {0x340b, 0x0000},
+ {0x340c, 0x0000},
+ {0x340d, 0x0000},
+ {0x340e, 0x0000},
+ {0x340f, 0x0000},
+ {0x3410, 0x0000},
+ {0x3411, 0x0000},
+ {0x3412, 0x0000},
+ {0x3413, 0x0000},
+ {0x3414, 0x0000},
+ {0x3415, 0x0000},
+ {0x3424, 0x0000},
+ {0x3425, 0x0000},
+ {0x3426, 0x0000},
+ {0x3427, 0x0000},
+ {0x3428, 0x0000},
+ {0x3429, 0x0000},
+ {0x342a, 0x0000},
+ {0x342b, 0x0000},
+ {0x342c, 0x0000},
+ {0x342d, 0x0000},
+ {0x342e, 0x0000},
+ {0x342f, 0x0000},
+ {0x3430, 0x0000},
+ {0x3431, 0x0000},
+ {0x3432, 0x0000},
+ {0x3433, 0x0000},
+ {0x3434, 0x0000},
+ {0x3435, 0x0000},
+ {0x3440, 0x6319},
+ {0x3441, 0x3771},
+ {0x3500, 0x0002},
+ {0x3501, 0x5728},
+ {0x3b00, 0x3010},
+ {0x3b01, 0x3300},
+ {0x3b02, 0x2200},
+ {0x3b03, 0x0100},
+};
+
+static bool rt5682s_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_CBJ_CTRL_2:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_ST:
+ case RT5682S_IL_CMD_1:
+ case RT5682S_4BTN_IL_CMD_1:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_VERSION_ID...RT5682S_DEVICE_ID:
+ case RT5682S_STO_NG2_CTRL_1:
+ case RT5682S_STO_NG2_CTRL_5...RT5682S_STO_NG2_CTRL_7:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_4:
+ case RT5682S_HP_IMP_SENS_CTRL_13:
+ case RT5682S_HP_IMP_SENS_CTRL_14:
+ case RT5682S_HP_IMP_SENS_CTRL_43...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_CALIB_CTRL_1:
+ case RT5682S_HP_CALIB_CTRL_10:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_2...RT5682S_SAR_IL_CMD_5:
+ case RT5682S_SAR_IL_CMD_10:
+ case RT5682S_SAR_IL_CMD_11:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1:
+ case RT5682S_NEW_CBJ_DET_CTL_10...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_PILOT_DIG_CTL_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5682s_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_VERSION_ID:
+ case RT5682S_VENDOR_ID:
+ case RT5682S_DEVICE_ID:
+ case RT5682S_HP_CTRL_1:
+ case RT5682S_HP_CTRL_2:
+ case RT5682S_HPL_GAIN:
+ case RT5682S_HPR_GAIN:
+ case RT5682S_I2C_CTRL:
+ case RT5682S_CBJ_BST_CTRL:
+ case RT5682S_CBJ_DET_CTRL:
+ case RT5682S_CBJ_CTRL_1...RT5682S_CBJ_CTRL_8:
+ case RT5682S_DAC1_DIG_VOL:
+ case RT5682S_STO1_ADC_DIG_VOL:
+ case RT5682S_STO1_ADC_BOOST:
+ case RT5682S_HP_IMP_GAIN_1:
+ case RT5682S_HP_IMP_GAIN_2:
+ case RT5682S_SIDETONE_CTRL:
+ case RT5682S_STO1_ADC_MIXER:
+ case RT5682S_AD_DA_MIXER:
+ case RT5682S_STO1_DAC_MIXER:
+ case RT5682S_A_DAC1_MUX:
+ case RT5682S_DIG_INF2_DATA:
+ case RT5682S_REC_MIXER:
+ case RT5682S_CAL_REC:
+ case RT5682S_HP_ANA_OST_CTRL_1...RT5682S_HP_ANA_OST_CTRL_3:
+ case RT5682S_PWR_DIG_1...RT5682S_PWR_MIXER:
+ case RT5682S_MB_CTRL:
+ case RT5682S_CLK_GATE_TCON_1...RT5682S_CLK_GATE_TCON_3:
+ case RT5682S_CLK_DET...RT5682S_LPF_AD_DMIC:
+ case RT5682S_I2S1_SDP:
+ case RT5682S_I2S2_SDP:
+ case RT5682S_ADDA_CLK_1:
+ case RT5682S_ADDA_CLK_2:
+ case RT5682S_I2S1_F_DIV_CTRL_1:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_TDM_CTRL:
+ case RT5682S_TDM_ADDA_CTRL_1:
+ case RT5682S_TDM_ADDA_CTRL_2:
+ case RT5682S_DATA_SEL_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_2:
+ case RT5682S_GLB_CLK:
+ case RT5682S_PLL_TRACK_1...RT5682S_PLL_TRACK_6:
+ case RT5682S_PLL_TRACK_11:
+ case RT5682S_DEPOP_1:
+ case RT5682S_HP_CHARGE_PUMP_1:
+ case RT5682S_HP_CHARGE_PUMP_2:
+ case RT5682S_HP_CHARGE_PUMP_3:
+ case RT5682S_MICBIAS_1...RT5682S_MICBIAS_3:
+ case RT5682S_PLL_TRACK_12...RT5682S_PLL_CTRL_7:
+ case RT5682S_RC_CLK_CTRL:
+ case RT5682S_I2S2_M_CLK_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_IRQ_CTRL_1...RT5682S_IRQ_CTRL_4:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_CTRL_1:
+ case RT5682S_GPIO_CTRL_2:
+ case RT5682S_GPIO_ST:
+ case RT5682S_HP_AMP_DET_CTRL_1:
+ case RT5682S_MID_HP_AMP_DET:
+ case RT5682S_LOW_HP_AMP_DET:
+ case RT5682S_DELAY_BUF_CTRL:
+ case RT5682S_SV_ZCD_1:
+ case RT5682S_SV_ZCD_2:
+ case RT5682S_IL_CMD_1...RT5682S_IL_CMD_6:
+ case RT5682S_4BTN_IL_CMD_1...RT5682S_4BTN_IL_CMD_7:
+ case RT5682S_ADC_STO1_HP_CTRL_1:
+ case RT5682S_ADC_STO1_HP_CTRL_2:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_JD_CTRL_1:
+ case RT5682S_DUMMY_1...RT5682S_DUMMY_3:
+ case RT5682S_DAC_ADC_DIG_VOL1:
+ case RT5682S_BIAS_CUR_CTRL_2...RT5682S_BIAS_CUR_CTRL_10:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_1:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_2:
+ case RT5682S_CHARGE_PUMP_1:
+ case RT5682S_DIG_IN_CTRL_1:
+ case RT5682S_PAD_DRIVING_CTRL:
+ case RT5682S_CHOP_DAC_1:
+ case RT5682S_CHOP_DAC_2:
+ case RT5682S_CHOP_ADC:
+ case RT5682S_CALIB_ADC_CTRL:
+ case RT5682S_VOL_TEST:
+ case RT5682S_SPKVDD_DET_ST:
+ case RT5682S_TEST_MODE_CTRL_1...RT5682S_TEST_MODE_CTRL_4:
+ case RT5682S_PLL_INTERNAL_1...RT5682S_PLL_INTERNAL_4:
+ case RT5682S_STO_NG2_CTRL_1...RT5682S_STO_NG2_CTRL_10:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_SIL_PSV_CTRL1:
+ case RT5682S_SIL_PSV_CTRL2:
+ case RT5682S_SIL_PSV_CTRL3:
+ case RT5682S_SIL_PSV_CTRL4:
+ case RT5682S_SIL_PSV_CTRL5:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_LOGIC_CTRL_1...RT5682S_HP_LOGIC_CTRL_3:
+ case RT5682S_HP_CALIB_CTRL_1...RT5682S_HP_CALIB_CTRL_11:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_1...RT5682S_SAR_IL_CMD_14:
+ case RT5682S_DUMMY_4...RT5682S_DUMMY_6:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_SCAN_CTL:
+ case RT5682S_HP_AMP_DET:
+ case RT5682S_BIAS_CUR_CTRL_11:
+ case RT5682S_BIAS_CUR_CTRL_12:
+ case RT5682S_BIAS_CUR_CTRL_13:
+ case RT5682S_BIAS_CUR_CTRL_14:
+ case RT5682S_BIAS_CUR_CTRL_15:
+ case RT5682S_BIAS_CUR_CTRL_16:
+ case RT5682S_BIAS_CUR_CTRL_17:
+ case RT5682S_BIAS_CUR_CTRL_18:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DUMMY_7:
+ case RT5682S_DUMMY_8:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1...RT5682S_HA_CMP_OP_13:
+ case RT5682S_HA_CMP_OP_14...RT5682S_HA_CMP_OP_25:
+ case RT5682S_NEW_CBJ_DET_CTL_1...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_DA_FILTER_1...RT5682S_DA_FILTER_5:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_CLK_SW_TEST_3...RT5682S_CLK_SW_TEST_14:
+ case RT5682S_EFUSE_MANU_WRITE_1...RT5682S_EFUSE_MANU_WRITE_6:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_EFUSE_TIMING_CTL_1:
+ case RT5682S_EFUSE_TIMING_CTL_2:
+ case RT5682S_PILOT_DIG_CTL_1:
+ case RT5682S_PILOT_DIG_CTL_2:
+ case RT5682S_HP_AMP_DET_CTL_1...RT5682S_HP_AMP_DET_CTL_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void rt5682s_reset(struct rt5682s_priv *rt5682s)
+{
+ regmap_write(rt5682s->regmap, RT5682S_RESET, 0);
+}
+
+static int rt5682s_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5682S_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5682S_4BTN_IL_CMD_1, val);
+ dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+
+ return btn_type;
+}
+
+enum {
+ SAR_PWR_OFF,
+ SAR_PWR_NORMAL,
+ SAR_PWR_SAVING,
+};
+
+static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5682s->sar_mutex);
+
+ switch (mode) {
+ case SAR_PWR_SAVING:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK |
+ RT5682S_VREF_POW_MASK, RT5682S_CTRL_MB1_FSM |
+ RT5682S_CTRL_MB2_FSM | RT5682S_VREF_POW_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK, RT5682S_SAR_BUTDET_EN);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+ break;
+ case SAR_PWR_NORMAL:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK,
+ RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM);
+ break;
+ case SAR_PWR_OFF:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+ break;
+ default:
+ dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&rt5682s->sar_mutex);
+}
+
+static void rt5682s_enable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_BTN);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_EN |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_AUTO);
+ snd_soc_component_write(component, RT5682S_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK | RT5682S_4BTN_IL_RST_MASK,
+ RT5682S_4BTN_IL_EN | RT5682S_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_EN);
+}
+
+static void rt5682s_disable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+}
+
+/**
+ * rt5682s_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int val, count;
+ int jack_type = 0;
+
+ if (jack_insert) {
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, RT5682S_PWR_CBJ);
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x0365);
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_DIS | RT5682S_OSW_R_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+ usleep_range(45000, 50000);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_HIGH);
+
+ count = 0;
+ do {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component, RT5682S_CBJ_CTRL_2)
+ & RT5682S_JACK_TYPE_MASK;
+ count++;
+ } while (val == 0 && count < 50);
+
+ dev_dbg(component->dev, "%s, val=%d, count=%d\n", __func__, val, count);
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ jack_type = SND_JACK_HEADSET;
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x024c);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_MASK, val << RT5682S_SAR_SEL_MB1_2_SFT);
+ rt5682s_enable_push_button_irq(component);
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING);
+ break;
+ default:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_EN | RT5682S_OSW_R_EN);
+ usleep_range(35000, 40000);
+ } else {
+ rt5682s_sar_power_mode(component, SAR_PWR_OFF);
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+
+ if (!rt5682s->wclk_enabled) {
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0);
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, 0);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS);
+ jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", jack_type);
+
+ return jack_type;
+}
+
+static void rt5682s_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jack_detect_work.work);
+ struct snd_soc_dapm_context *dapm;
+ int val, btn_type;
+
+ if (!rt5682s->component ||
+ !snd_soc_card_is_instantiated(rt5682s->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ dapm = snd_soc_component_get_dapm(rt5682s->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&rt5682s->calibrate_mutex);
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
+ & RT5682S_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5682s->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 1);
+ rt5682s->irq_work_delay_time = 0;
+ } else if ((rt5682s->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ rt5682s->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5682s_button_detect(rt5682s->component);
+ /**
+ * rt5682s can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5682s->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5682s->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5682s->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5682s->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5682s->component->dev,
+ "Unexpected button code 0x%04x\n", btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0);
+ rt5682s->irq_work_delay_time = 50;
+ }
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+ mutex_unlock(&rt5682s->calibrate_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5682s->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5682s->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+}
+
+static void rt5682s_jd_check_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jd_check_work.work);
+
+ if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) & RT5682S_JDH_RS_MASK) {
+ /* jack out */
+ schedule_delayed_work(&rt5682s->jack_detect_work, 0);
+ } else {
+ schedule_delayed_work(&rt5682s->jd_check_work, 500);
+ }
+}
+
+static irqreturn_t rt5682s_irq(int irq, void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+
+ mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work,
+ msecs_to_jiffies(rt5682s->irq_work_delay_time));
+
+ return IRQ_HANDLED;
+}
+
+static int rt5682s_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int btndet_delay = 16;
+
+ rt5682s->hs_jack = hs_jack;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+
+ return 0;
+ }
+
+ switch (rt5682s->pdata.jd_src) {
+ case RT5682S_JD1:
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_5,
+ RT5682S_JD_FAST_OFF_SRC_MASK, RT5682S_JD_FAST_OFF_SRC_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_2,
+ RT5682S_EXT_JD_SRC, RT5682S_EXT_JD_SRC_MANUAL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_1,
+ RT5682S_EMB_JD_MASK | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_MASK | RT5682S_MIC_CAP_MASK,
+ RT5682S_EMB_JD_EN | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_LOW | RT5682S_MIC_CAP_HS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_POW_MASK, RT5682S_SAR_POW_EN);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_IRQ);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BGLDO, RT5682S_PWR_BGLDO);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_JD_MASK, RT5682S_PWR_JD_ENABLE);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_IRQ | RT5682S_POW_JDH, RT5682S_POW_IRQ | RT5682S_POW_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK | RT5682S_JD1_POL_MASK,
+ RT5682S_JD1_EN | RT5682S_JD1_POL_NOR);
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_4,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_5,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_6,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_7,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(250));
+ break;
+
+ case RT5682S_JD_NULL:
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9562, 75, 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);
+static const DECLARE_TLV_DB_SCALE(cbj_bst_tlv, -1200, 150, 0);
+
+static const struct snd_kcontrol_new rt5682s_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682S_DAC1_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 127, 0, dac_vol_tlv),
+
+ /* CBJ Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682S_REC_MIXER,
+ RT5682S_BST_CBJ_SFT, 35, 0, cbj_bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE_SFT, RT5682S_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682S_STO1_ADC_BOOST,
+ RT5682S_STO1_ADC_L_BST_SFT, RT5682S_STO1_ADC_R_BST_SFT, 3, 0, adc_bst_tlv),
+};
+
+/**
+ * rt5682s_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 RT5682S 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 component driver will turn on
+ * ASRC for these filters if ASRC is selected as their clock source.
+ */
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ switch (clk_src) {
+ case RT5682S_CLK_SEL_SYS:
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5682S_DA_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_2,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5682S_AD_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_3,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_11,
+ RT5682S_ASRCIN_AUTO_CLKOUT_MASK, RT5682S_ASRCIN_AUTO_CLKOUT_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682s_sel_asrc_clk_src);
+
+static int rt5682s_div_sel(struct rt5682s_priv *rt5682s,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5682s->sysclk < target) {
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too low\n", rt5682s->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ dev_dbg(rt5682s->component->dev, "div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5682s->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5682s->sysclk) {
+ dev_dbg(rt5682s->component->dev,
+ "can't find div for sysclk %d\n", rt5682s->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5682s->sysclk)
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too high\n", rt5682s->sysclk);
+
+ return size - 1;
+}
+
+static int get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ 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;
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int idx, dmic_clk_rate = 3072000;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ if (rt5682s->pdata.dmic_clk_rate)
+ dmic_clk_rate = rt5682s->pdata.dmic_clk_rate;
+
+ idx = rt5682s_div_sel(rt5682s, dmic_clk_rate, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_CLK_MASK, idx << RT5682S_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+
+static int rt5682s_set_pllb_power(struct rt5682s_priv *rt5682s, int on)
+{
+ struct snd_soc_component *component = rt5682s->component;
+
+ if (on) {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLB, RT5682S_RSTB_PLLB);
+ } else {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB |
+ RT5682S_RSTB_PLLB | RT5682S_PWR_PLLB, 0);
+ }
+
+ return 0;
+}
+
+static int set_pllb_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int on = 0;
+
+ if (rt5682s->wclk_enabled)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ on = 1;
+
+ rt5682s_set_pllb_power(rt5682s, on);
+
+ return 0;
+}
+
+static void rt5682s_set_filter_clk(struct rt5682s_priv *rt5682s, int reg, int ref)
+{
+ struct snd_soc_component *component = rt5682s->component;
+ int idx;
+ static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+ static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ idx = rt5682s_div_sel(rt5682s, ref, div_f, ARRAY_SIZE(div_f));
+
+ snd_soc_component_update_bits(component, reg,
+ RT5682S_FILTER_CLK_DIV_MASK, idx << RT5682S_FILTER_CLK_DIV_SFT);
+
+ /* select over sample rate */
+ for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) {
+ if (rt5682s->sysclk <= 12288000 * div_o[idx])
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_ADC_OSR_MASK | RT5682S_DAC_OSR_MASK,
+ (idx << RT5682S_ADC_OSR_SFT) | (idx << RT5682S_DAC_OSR_SFT));
+}
+
+static int set_filter_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ref, reg, val;
+
+ val = snd_soc_component_read(component, RT5682S_GPIO_CTRL_1)
+ & RT5682S_GP4_PIN_MASK;
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT && val == RT5682S_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5682s->lrck[RT5682S_AIF2];
+ else
+ ref = 256 * rt5682s->lrck[RT5682S_AIF1];
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT)
+ reg = RT5682S_PLL_TRACK_3;
+ else
+ reg = RT5682S_PLL_TRACK_2;
+
+ rt5682s_set_filter_clk(rt5682s, reg, ref);
+
+ return 0;
+}
+
+static int set_dmic_power(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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 50, val;
+
+ if (rt5682s->pdata.dmic_delay)
+ delay = rt5682s->pdata.dmic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = (snd_soc_component_read(component, RT5682S_GLB_CLK)
+ & RT5682S_SCLK_SRC_MASK) >> RT5682S_SCLK_SRC_SFT;
+ if (val == RT5682S_CLK_SRC_PLL1 || val == RT5682S_CLK_SRC_PLL2)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+
+ /*Add delay to avoid pop noise*/
+ msleep(delay);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (!rt5682s->jack_type && !rt5682s->wclk_enabled) {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void rt5682s_set_i2s(struct rt5682s_priv *rt5682s, int id, int on)
+{
+ struct snd_soc_component *component = rt5682s->component;
+ int pre_div;
+ unsigned int p_reg, p_mask, p_sft;
+ unsigned int c_reg, c_mask, c_sft;
+
+ if (id == RT5682S_AIF1) {
+ c_reg = RT5682S_ADDA_CLK_1;
+ c_mask = RT5682S_I2S_M_D_MASK;
+ c_sft = RT5682S_I2S_M_D_SFT;
+ p_reg = RT5682S_PWR_DIG_1;
+ p_mask = RT5682S_PWR_I2S1;
+ p_sft = RT5682S_PWR_I2S1_BIT;
+ } else {
+ c_reg = RT5682S_I2S2_M_CLK_CTRL_1;
+ c_mask = RT5682S_I2S2_M_D_MASK;
+ c_sft = RT5682S_I2S2_M_D_SFT;
+ p_reg = RT5682S_PWR_DIG_1;
+ p_mask = RT5682S_PWR_I2S2;
+ p_sft = RT5682S_PWR_I2S2_BIT;
+ }
+
+ if (on && rt5682s->master[id]) {
+ pre_div = get_clk_info(rt5682s->sysclk, rt5682s->lrck[id]);
+ if (pre_div < 0) {
+ dev_err(component->dev, "get pre_div failed\n");
+ return;
+ }
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d master\n",
+ rt5682s->lrck[id], pre_div, id);
+ snd_soc_component_update_bits(component, c_reg, c_mask, pre_div << c_sft);
+ }
+
+ snd_soc_component_update_bits(component, p_reg, p_mask, on << p_sft);
+}
+
+static int set_i2s_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int on = 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ on = 1;
+
+ if (!snd_soc_dapm_widget_name_cmp(w, "I2S1") && !rt5682s->wclk_enabled)
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, on);
+ else if (!snd_soc_dapm_widget_name_cmp(w, "I2S2"))
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF2, on);
+
+ return 0;
+}
+
+static int is_sys_clk_from_plla(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL1) ||
+ (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2 && rt5682s->pll_comb == USE_PLLAB))
+ return 1;
+
+ return 0;
+}
+
+static int is_sys_clk_from_pllb(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2)
+ return 1;
+
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, sft, val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5682S_ADC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_3;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5682S_DAC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_2;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> sft) & 0xf;
+ switch (val) {
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int rt5682s_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a);
+
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN |
+ RT5682S_HPO_R_PATH_EN | RT5682S_HPO_IP_EN_GATING);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, 0);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5682s_stereo1_adc_mixl_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 0;
+
+ if (rt5682s->pdata.amic_delay)
+ delay = rt5682s->pdata.amic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(delay);
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, RT5682S_L_MUTE);
+ break;
+ }
+
+ return 0;
+}
+
+static int sar_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->jack_type & SND_JACK_HEADSET) != SND_JACK_HEADSET)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rt5682s_sar_power_mode(component, SAR_PWR_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING);
+ break;
+ }
+
+ return 0;
+}
+
+/* Interface data select */
+static const char * const rt5682s_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if2_adc_enum, RT5682S_DIG_INF2_DATA,
+ RT5682S_IF2_ADC_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_01_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC1_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_23_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC2_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_45_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC3_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_67_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC4_SEL_SFT, rt5682s_data_select);
+
+static const struct snd_kcontrol_new rt5682s_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682s_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682s_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682s_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682s_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682s_if1_67_adc_enum);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5682s_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5682s_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_rec1_r_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_R_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5682s_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1L_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1R_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5682s_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcl_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCL_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682s_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcr_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCR_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682s_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5682s_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2L_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682s_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2R_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682s_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5682s_if1_adc_slot_values[] = {
+ 0, 2, 4, 6,
+};
+
+static const char * const rt5682s_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_if1_adc_slot_enum,
+ RT5682S_TDM_CTRL, RT5682S_TDM_ADC_LCA_SFT, RT5682S_TDM_ADC_LCA_MASK,
+ rt5682s_if1_adc_slot_src, rt5682s_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5682s_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682s_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5682s_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_l1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACL1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682s_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_r1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACR1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682s_alg_dac_r1_enum);
+
+static const unsigned int rt5682s_adcdat_pin_values[] = {
+ 1, 3,
+};
+
+static const char * const rt5682s_adcdat_pin_select[] = {
+ "ADCDAT1", "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_adcdat_pin_enum,
+ RT5682S_GPIO_CTRL_1, RT5682S_GP4_PIN_SFT, RT5682S_GP4_PIN_MASK,
+ rt5682s_adcdat_pin_select, rt5682s_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5682s_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5682s_adcdat_pin_enum);
+
+static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO MB1", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO MB2", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_BIT, 0, NULL, 0),
+
+ /* PLL Powers */
+ SND_SOC_DAPM_SUPPLY_S("PLLA_LDO", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_BIAS", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BIAS_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_RST", 1, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLB", SND_SOC_NOPM, 0, 0,
+ set_pllb_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB2_BIT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ 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", RT5682S_DMIC_CTRL_1, RT5682S_DMIC_1_EN_SFT, 0,
+ set_dmic_power, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682s_rec1_l_mix,
+ ARRAY_SIZE(rt5682s_rec1_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5682s_rec1_r_mix,
+ ARRAY_SIZE(rt5682s_rec1_r_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_R_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682S_CHOP_ADC,
+ RT5682S_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix),
+ rt5682s_stereo1_adc_mixl_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5682s_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", SND_SOC_NOPM, 0, 0,
+ set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("I2S2", SND_SOC_NOPM, 0, 0,
+ set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ 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),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682s_adcdat_pin_ctrl),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682S_I2S1_SDP,
+ RT5682S_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_l_mix, ARRAY_SIZE(rt5682s_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_r_mix, ARRAY_SIZE(rt5682s_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_DAC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_l_mix, ARRAY_SIZE(rt5682s_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_r_mix, ARRAY_SIZE(rt5682s_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_R1_BIT, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682s_hp_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682S_CLK_DET,
+ RT5682S_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682S_CLK_DET,
+ RT5682S_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MCLK0 DET PWR", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MCLK0_WD_BIT, 0, NULL, 0),
+
+ /* SAR */
+ SND_SOC_DAPM_SUPPLY("SAR", SND_SOC_NOPM, 0, 0, sar_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5682s_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"ADC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"DAC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"DAC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"PLLA", NULL, "PLLA_LDO"},
+ {"PLLA", NULL, "PLLA_BIAS"},
+ {"PLLA", NULL, "PLLA_RST"},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"ADC STO1 ASRC", NULL, "DA ASRC"},
+ {"DAC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+
+ {"CLKDET SYS", NULL, "MCLK0 DET PWR"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "SAR"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+ {"RECMIX1R", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1R", NULL, "RECMIX1R Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+ {"ADC1 R", NULL, "RECMIX1R"},
+ {"ADC1 R", NULL, "ADC1 R Power"},
+ {"ADC1 R", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "I2S1"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "SAR"},
+
+ {"HPOL", NULL, "HP Amp"},
+ {"HPOR", NULL, "HP Amp"},
+};
+
+static int rt5682s_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 cl, val = 0, tx_slotnum;
+
+ if (tx_mask || rx_mask)
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, RT5682S_TDM_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, 0);
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum) {
+ if (tx_slotnum > slots) {
+ dev_err(component->dev, "Invalid or oversized Tx slots.\n");
+ return -EINVAL;
+ }
+ val |= (tx_slotnum - 1) << RT5682S_TDM_ADC_DL_SFT;
+ }
+
+ switch (slots) {
+ case 4:
+ val |= RT5682S_TDM_TX_CH_4;
+ val |= RT5682S_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5682S_TDM_TX_CH_6;
+ val |= RT5682S_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5682S_TDM_TX_CH_8;
+ val |= RT5682S_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_CTRL,
+ RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK |
+ RT5682S_TDM_ADC_DL_MASK, val);
+
+ switch (slot_width) {
+ case 8:
+ if (tx_mask || rx_mask)
+ return -EINVAL;
+ cl = RT5682S_I2S1_TX_CHL_8 | RT5682S_I2S1_RX_CHL_8;
+ break;
+ case 16:
+ val = RT5682S_TDM_CL_16;
+ cl = RT5682S_I2S1_TX_CHL_16 | RT5682S_I2S1_RX_CHL_16;
+ break;
+ case 20:
+ val = RT5682S_TDM_CL_20;
+ cl = RT5682S_I2S1_TX_CHL_20 | RT5682S_I2S1_RX_CHL_20;
+ break;
+ case 24:
+ val = RT5682S_TDM_CL_24;
+ cl = RT5682S_I2S1_TX_CHL_24 | RT5682S_I2S1_RX_CHL_24;
+ break;
+ case 32:
+ val = RT5682S_TDM_CL_32;
+ cl = RT5682S_I2S1_TX_CHL_32 | RT5682S_I2S1_RX_CHL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_CL_MASK, val);
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_TX_CHL_MASK | RT5682S_I2S1_RX_CHL_MASK, cl);
+
+ return 0;
+}
+
+static int rt5682s_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int frame_size;
+
+ rt5682s->lrck[dai->id] = params_rate(params);
+
+ 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;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5682S_I2S1_DL_20;
+ len_2 |= RT5682S_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5682S_I2S1_DL_24;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5682S_I2S1_DL_32;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5682S_I2S2_DL_8;
+ len_2 |= RT5682S_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_DL_MASK, len_1);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_DIS);
+ break;
+ case RT5682S_AIF2:
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_DL_MASK, len_2);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5682s->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5682s->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 |= RT5682S_I2S_BP_INV;
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_LP_INV | RT5682S_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV | RT5682S_TDM_S_LP_INV |
+ RT5682S_TDM_M_BP_INV | RT5682S_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ 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 |= RT5682S_I2S_DF_LEFT;
+ tdm_ctrl |= RT5682S_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5682S_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5682S_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_MS_MASK | RT5682S_TDM_S_BP_MASK |
+ RT5682S_TDM_DF_MASK | RT5682S_TDM_M_BP_MASK |
+ RT5682S_TDM_M_LP_MASK | RT5682S_TDM_S_LP_MASK,
+ tdm_ctrl | rt5682s->master[dai->id]);
+ break;
+ case RT5682S_AIF2:
+ if (rt5682s->master[dai->id] == 0)
+ reg_val |= RT5682S_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MS_MASK | RT5682S_I2S_BP_MASK |
+ RT5682S_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5682s_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int src = 0;
+
+ if (freq == rt5682s->sysclk && clk_id == rt5682s->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5682S_SCLK_S_MCLK:
+ src = RT5682S_CLK_SRC_MCLK;
+ break;
+ case RT5682S_SCLK_S_PLL1:
+ src = RT5682S_CLK_SRC_PLL1;
+ break;
+ case RT5682S_SCLK_S_PLL2:
+ src = RT5682S_CLK_SRC_PLL2;
+ break;
+ case RT5682S_SCLK_S_RCCLK:
+ src = RT5682S_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, src << RT5682S_SCLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_I2S_M_CLK_SRC_MASK, src << RT5682S_I2S_M_CLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_I2S2_M_CLK_CTRL_1,
+ RT5682S_I2S2_M_CLK_SRC_MASK, src << RT5682S_I2S2_M_CLK_SRC_SFT);
+
+ rt5682s->sysclk = freq;
+ rt5682s->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static const struct pll_calc_map plla_table[] = {
+ {2048000, 24576000, 0, 46, 2, true, false, false, false},
+ {256000, 24576000, 0, 382, 2, true, false, false, false},
+ {512000, 24576000, 0, 190, 2, true, false, false, false},
+ {4096000, 24576000, 0, 22, 2, true, false, false, false},
+ {1024000, 24576000, 0, 94, 2, true, false, false, false},
+ {11289600, 22579200, 1, 22, 2, false, false, false, false},
+ {1411200, 22579200, 0, 62, 2, true, false, false, false},
+ {2822400, 22579200, 0, 30, 2, true, false, false, false},
+ {12288000, 24576000, 1, 22, 2, false, false, false, false},
+ {1536000, 24576000, 0, 62, 2, true, false, false, false},
+ {3072000, 24576000, 0, 30, 2, true, false, false, false},
+ {24576000, 49152000, 4, 22, 0, false, false, false, false},
+ {3072000, 49152000, 0, 30, 0, true, false, false, false},
+ {6144000, 49152000, 0, 30, 0, false, false, false, false},
+ {49152000, 98304000, 10, 22, 0, false, true, false, false},
+ {6144000, 98304000, 0, 30, 0, false, true, false, false},
+ {12288000, 98304000, 1, 22, 0, false, true, false, false},
+ {48000000, 3840000, 10, 22, 23, false, false, false, false},
+ {24000000, 3840000, 4, 22, 23, false, false, false, false},
+ {19200000, 3840000, 3, 23, 23, false, false, false, false},
+ {38400000, 3840000, 8, 23, 23, false, false, false, false},
+};
+
+static const struct pll_calc_map pllb_table[] = {
+ {48000000, 24576000, 8, 6, 3, false, false, false, false},
+ {48000000, 22579200, 23, 12, 3, false, false, false, true},
+ {24000000, 24576000, 3, 6, 3, false, false, false, false},
+ {24000000, 22579200, 23, 26, 3, false, false, false, true},
+ {19200000, 24576000, 2, 6, 3, false, false, false, false},
+ {19200000, 22579200, 3, 5, 3, false, false, false, true},
+ {38400000, 24576000, 6, 6, 3, false, false, false, false},
+ {38400000, 22579200, 8, 5, 3, false, false, false, true},
+ {3840000, 49152000, 0, 6, 0, true, false, false, false},
+};
+
+static int find_pll_inter_combination(unsigned int f_in, unsigned int f_out,
+ struct pll_calc_map *a, struct pll_calc_map *b)
+{
+ int i, j;
+
+ /* Look at PLLA table */
+ for (i = 0; i < ARRAY_SIZE(plla_table); i++) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ return USE_PLLA;
+ }
+ }
+
+ /* Look at PLLB table */
+ for (i = 0; i < ARRAY_SIZE(pllb_table); i++) {
+ if (pllb_table[i].freq_in == f_in && pllb_table[i].freq_out == f_out) {
+ memcpy(b, pllb_table + i, sizeof(*b));
+ return USE_PLLB;
+ }
+ }
+
+ /* Find a combination of PLLA & PLLB */
+ for (i = ARRAY_SIZE(plla_table) - 1; i >= 0; i--) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == 3840000) {
+ for (j = ARRAY_SIZE(pllb_table) - 1; j >= 0; j--) {
+ if (pllb_table[j].freq_in == 3840000 &&
+ pllb_table[j].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ memcpy(b, pllb_table + j, sizeof(*b));
+ return USE_PLLAB;
+ }
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int rt5682s_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct pll_calc_map a_map, b_map;
+
+ if (source == rt5682s->pll_src[pll_id] && freq_in == rt5682s->pll_in[pll_id] &&
+ freq_out == rt5682s->pll_out[pll_id])
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt5682s->pll_in[pll_id] = 0;
+ rt5682s->pll_out[pll_id] = 0;
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, RT5682S_CLK_SRC_MCLK << RT5682S_SCLK_SRC_SFT);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5682S_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_MCLK);
+ break;
+ case RT5682S_PLL_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ rt5682s->pll_comb = find_pll_inter_combination(freq_in, freq_out,
+ &a_map, &b_map);
+
+ if ((pll_id == RT5682S_PLL1 && rt5682s->pll_comb == USE_PLLA) ||
+ (pll_id == RT5682S_PLL2 && (rt5682s->pll_comb == USE_PLLB ||
+ rt5682s->pll_comb == USE_PLLAB))) {
+ dev_dbg(component->dev,
+ "Supported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ } else {
+ dev_err(component->dev,
+ "Unsupported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ return -EINVAL;
+ }
+
+ if (rt5682s->pll_comb == USE_PLLA || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLA: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d\n",
+ a_map.freq_in, a_map.freq_out, a_map.m_bp, a_map.k_bp,
+ (a_map.m_bp ? 0 : a_map.m), a_map.n, (a_map.k_bp ? 0 : a_map.k));
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_1,
+ RT5682S_PLLA_N_MASK, a_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_2,
+ RT5682S_PLLA_M_MASK | RT5682S_PLLA_K_MASK,
+ a_map.m << RT5682S_PLLA_M_SFT | a_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLA_M_BP_MASK | RT5682S_PLLA_K_BP_MASK,
+ a_map.m_bp << RT5682S_PLLA_M_BP_SFT |
+ a_map.k_bp << RT5682S_PLLA_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLB: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d byp_ps=%d sel_ps=%d\n",
+ b_map.freq_in, b_map.freq_out, b_map.m_bp, b_map.k_bp,
+ (b_map.m_bp ? 0 : b_map.m), b_map.n, (b_map.k_bp ? 0 : b_map.k),
+ b_map.byp_ps, b_map.sel_ps);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_3,
+ RT5682S_PLLB_N_MASK, b_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_4,
+ RT5682S_PLLB_M_MASK | RT5682S_PLLB_K_MASK,
+ b_map.m << RT5682S_PLLB_M_SFT | b_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLB_SEL_PS_MASK | RT5682S_PLLB_BYP_PS_MASK |
+ RT5682S_PLLB_M_BP_MASK | RT5682S_PLLB_K_BP_MASK,
+ b_map.sel_ps << RT5682S_PLLB_SEL_PS_SFT |
+ b_map.byp_ps << RT5682S_PLLB_BYP_PS_SFT |
+ b_map.m_bp << RT5682S_PLLB_M_BP_SFT |
+ b_map.k_bp << RT5682S_PLLB_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB)
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_7,
+ RT5682S_PLLB_SRC_MASK, RT5682S_PLLB_SRC_DFIN);
+
+ rt5682s->pll_in[pll_id] = freq_in;
+ rt5682s->pll_out[pll_id] = freq_out;
+ rt5682s->pll_src[pll_id] = source;
+
+ return 0;
+}
+
+static int rt5682s_set_bclk1_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 256:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_256);
+ break;
+ case 128:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_128);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_PWR_LDO, RT5682S_PWR_LDO);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, RT5682S_PWR_LDO, 0);
+ if (!rt5682s->wclk_enabled)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, 0);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+#define CLK_PLL2_FIN 48000000
+#define CLK_48 48000
+#define CLK_44 44100
+
+static bool rt5682s_clk_check(struct rt5682s_priv *rt5682s)
+{
+ if (!rt5682s->master[RT5682S_AIF1]) {
+ dev_dbg(rt5682s->component->dev, "dai clk fmt not set correctly\n");
+ return false;
+ }
+ return true;
+}
+
+static int rt5682s_wclk_prepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ int ref, reg;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV2, RT5682S_PWR_FV2);
+
+ /* Set and power on I2S1 */
+ snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL);
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 1);
+
+ /* Only need to power on PLLB due to the rate set restriction */
+ reg = RT5682S_PLL_TRACK_2;
+ ref = 256 * rt5682s->lrck[RT5682S_AIF1];
+ rt5682s_set_filter_clk(rt5682s, reg, ref);
+ rt5682s_set_pllb_power(rt5682s, 1);
+
+ rt5682s->wclk_enabled = 1;
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+
+ return 0;
+}
+
+static void rt5682s_wclk_unprepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return;
+
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ if (!rt5682s->jack_type)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, 0);
+
+ /* Power down I2S1 */
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 0);
+ snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, 0);
+
+ /* Power down PLLB */
+ rt5682s_set_pllb_power(rt5682s, 0);
+
+ rt5682s->wclk_enabled = 0;
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+}
+
+static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return 0;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ */
+ if (rt5682s->lrck[RT5682S_AIF1] != CLK_48 &&
+ rt5682s->lrck[RT5682S_AIF1] != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682s->lrck[RT5682S_AIF1];
+}
+
+static int rt5682s_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
+ */
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct clk *parent_clk;
+ const char * const clk_name = clk_hw_get_name(hw);
+ unsigned int clk_pll2_fout;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * Whether the wclk's parent clk (mclk) exists or not, please ensure
+ * it is fixed or set to 48MHz before setting wclk rate. It's a
+ * temporary limitation. Only accept 48MHz clk as the clk provider.
+ *
+ * It will set the codec anyway by assuming mclk is 48MHz.
+ */
+ parent_clk = clk_get_parent(hw->clk);
+ if (!parent_clk)
+ dev_warn(component->dev,
+ "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
+ CLK_PLL2_FIN);
+
+ if (parent_rate != CLK_PLL2_FIN)
+ dev_warn(component->dev, "clk %s only support %d Hz input\n",
+ clk_name, CLK_PLL2_FIN);
+
+ /*
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
+ */
+ clk_pll2_fout = rate * 512;
+ rt5682s_set_component_pll(component, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ CLK_PLL2_FIN, clk_pll2_fout);
+
+ rt5682s_set_component_sysclk(component, RT5682S_SCLK_S_PLL2, 0,
+ clk_pll2_fout, SND_SOC_CLOCK_IN);
+
+ rt5682s->lrck[RT5682S_AIF1] = rate;
+
+ return 0;
+}
+
+static unsigned long rt5682s_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ unsigned int bclks_per_wclk;
+
+ bclks_per_wclk = snd_soc_component_read(component, RT5682S_TDM_TCON_CTRL_1);
+
+ switch (bclks_per_wclk & RT5682S_TDM_BCLK_MS1_MASK) {
+ case RT5682S_TDM_BCLK_MS1_256:
+ return parent_rate * 256;
+ case RT5682S_TDM_BCLK_MS1_128:
+ return parent_rate * 128;
+ case RT5682S_TDM_BCLK_MS1_64:
+ return parent_rate * 64;
+ case RT5682S_TDM_BCLK_MS1_32:
+ return parent_rate * 32;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long rt5682s_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 int rt5682s_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_parent_rate || !rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * BCLK rates are set as a multiplier of WCLK in HW.
+ * We don't allow changing the parent WCLK. We just do
+ * some rounding down based on the parent WCLK rate
+ * and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = rt5682s_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct snd_soc_dai *dai;
+ unsigned long factor;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ factor = rt5682s_bclk_get_factor(rate, parent_rate);
+
+ for_each_component_dais(component, dai)
+ if (dai->id == RT5682S_AIF1)
+ return rt5682s_set_bclk1_ratio(dai, factor);
+
+ dev_err(component->dev, "dai %d not found in component\n",
+ RT5682S_AIF1);
+ return -ENODEV;
+}
+
+static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = {
+ [RT5682S_DAI_WCLK_IDX] = {
+ .prepare = rt5682s_wclk_prepare,
+ .unprepare = rt5682s_wclk_unprepare,
+ .recalc_rate = rt5682s_wclk_recalc_rate,
+ .determine_rate = rt5682s_wclk_determine_rate,
+ .set_rate = rt5682s_wclk_set_rate,
+ },
+ [RT5682S_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682s_bclk_recalc_rate,
+ .determine_rate = rt5682s_bclk_determine_rate,
+ .set_rate = rt5682s_bclk_set_rate,
+ },
+};
+
+static int rt5682s_register_dai_clks(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct rt5682s_platform_data *pdata = &rt5682s->pdata;
+ struct clk_hw *dai_clk_hw;
+ int i, ret;
+
+ for (i = 0; i < RT5682S_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = { };
+ struct clk_parent_data parent_data;
+ const struct clk_hw *parent;
+
+ dai_clk_hw = &rt5682s->dai_clks_hw[i];
+
+ switch (i) {
+ case RT5682S_DAI_WCLK_IDX:
+ /* Make MCLK the parent of WCLK */
+ if (rt5682s->mclk) {
+ parent_data = (struct clk_parent_data){
+ .fw_name = "mclk",
+ };
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ }
+ break;
+ case RT5682S_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent = &rt5682s->dai_clks_hw[RT5682S_DAI_WCLK_IDX];
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ return -EINVAL;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &rt5682s_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n", init.name, ret);
+ return ret;
+ }
+
+ if (dev->of_node) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw);
+ if (ret)
+ return ret;
+ } else {
+ ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
+ init.name, dev_name(dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Check if MCLK provided */
+ rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5682s->mclk))
+ return PTR_ERR(rt5682s->mclk);
+
+ /* Register CCF DAI clock control */
+ ret = rt5682s_register_dai_clks(component);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682s->lrck[RT5682S_AIF1] = CLK_48;
+
+ return 0;
+}
+#else
+static inline int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+static int rt5682s_probe(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->component = component;
+
+ return rt5682s_dai_probe_clks(component);
+}
+
+static void rt5682s_remove(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s_reset(rt5682s);
+}
+
+#ifdef CONFIG_PM
+static int rt5682s_suspend(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if (rt5682s->irq)
+ disable_irq(rt5682s->irq);
+
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ if (rt5682s->hs_jack)
+ rt5682s->jack_type = rt5682s_headset_detect(component, 0);
+
+ regcache_cache_only(rt5682s->regmap, true);
+ regcache_mark_dirty(rt5682s->regmap);
+
+ return 0;
+}
+
+static int rt5682s_resume(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5682s->regmap, false);
+ regcache_sync(rt5682s->regmap);
+
+ if (rt5682s->hs_jack) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(0));
+ }
+
+ if (rt5682s->irq)
+ enable_irq(rt5682s->irq);
+
+ return 0;
+}
+#else
+#define rt5682s_suspend NULL
+#define rt5682s_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops rt5682s_aif1_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_tdm_slot = rt5682s_set_tdm_slot,
+ .set_bclk_ratio = rt5682s_set_bclk1_ratio,
+};
+
+static const struct snd_soc_dai_ops rt5682s_aif2_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_bclk_ratio = rt5682s_set_bclk2_ratio,
+};
+
+static const struct snd_soc_component_driver rt5682s_soc_component_dev = {
+ .probe = rt5682s_probe,
+ .remove = rt5682s_remove,
+ .suspend = rt5682s_suspend,
+ .resume = rt5682s_resume,
+ .set_bias_level = rt5682s_set_bias_level,
+ .controls = rt5682s_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5682s_snd_controls),
+ .dapm_widgets = rt5682s_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5682s_dapm_widgets),
+ .dapm_routes = rt5682s_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5682s_dapm_routes),
+ .set_sysclk = rt5682s_set_component_sysclk,
+ .set_pll = rt5682s_set_component_pll,
+ .set_jack = rt5682s_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5682s->pdata.dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic1-clk-pin",
+ &rt5682s->pdata.dmic1_clk_pin);
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt5682s->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,dmic-clk-rate-hz",
+ &rt5682s->pdata.dmic_clk_rate);
+ device_property_read_u32(dev, "realtek,dmic-delay-ms",
+ &rt5682s->pdata.dmic_delay);
+ device_property_read_u32(dev, "realtek,amic-delay-ms",
+ &rt5682s->pdata.amic_delay);
+ device_property_read_u32(dev, "realtek,ldo-sel",
+ &rt5682s->pdata.ldo_dacref);
+
+ if (device_property_read_string_array(dev, "clock-output-names",
+ rt5682s->pdata.dai_clk_names,
+ RT5682S_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_WCLK_IDX],
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_BCLK_IDX]);
+
+ rt5682s->pdata.dmic_clk_driving_high = device_property_read_bool(dev,
+ "realtek,dmic-clk-driving-high");
+
+ return 0;
+}
+
+static void rt5682s_calibrate(struct rt5682s_priv *rt5682s)
+{
+ unsigned int count, value;
+
+ mutex_lock(&rt5682s->calibrate_mutex);
+
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80);
+ usleep_range(15000, 20000);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xfa80);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x01c0);
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0380);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x8000);
+ regmap_write(rt5682s->regmap, RT5682S_ADDA_CLK_1, 0x1001);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_DAC_2, 0x3030);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_ADC, 0xb000);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5151);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5682s->regmap, RT5682S_HP_CALIB_ST_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ dev_err(rt5682s->component->dev, "HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0180);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5858);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0320);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000);
+
+ mutex_unlock(&rt5682s->calibrate_mutex);
+}
+
+static const struct regmap_config rt5682s_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682S_MAX_REG,
+ .volatile_reg = rt5682s_volatile_register,
+ .readable_reg = rt5682s_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682s_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5682s_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static struct snd_soc_dai_driver rt5682s_dai[] = {
+ {
+ .name = "rt5682s-aif1",
+ .id = RT5682S_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682s-aif2",
+ .id = RT5682S_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif2_dai_ops,
+ },
+};
+
+static void rt5682s_i2c_disable_regulators(void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+ struct device *dev = regmap_get_device(rt5682s->regmap);
+ int ret;
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply AVDD: %d\n", ret);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply DBVDD: %d\n", ret);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply LDO1-IN: %d\n", ret);
+
+ usleep_range(1000, 1500);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret);
+}
+
+static int rt5682s_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5682s_priv *rt5682s;
+ int i, ret;
+ unsigned int val;
+
+ rt5682s = devm_kzalloc(&i2c->dev, sizeof(struct rt5682s_priv), GFP_KERNEL);
+ if (!rt5682s)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5682s);
+
+ rt5682s->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt5682s->pdata = *pdata;
+ else
+ rt5682s_parse_dt(rt5682s, &i2c->dev);
+
+ rt5682s->regmap = devm_regmap_init_i2c(i2c, &rt5682s_regmap);
+ if (IS_ERR(rt5682s->regmap)) {
+ ret = PTR_ERR(rt5682s->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5682s->supplies); i++)
+ rt5682s->supplies[i].supply = rt5682s_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, rt5682s_i2c_disable_regulators, rt5682s);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret);
+ return ret;
+ }
+ usleep_range(1000, 1500);
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply DBVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply LDO1-IN: %d\n", ret);
+ return ret;
+ }
+
+ rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682s->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682s->ldo1_en);
+ }
+
+ /* Sleep for 50 ms minimum */
+ usleep_range(50000, 55000);
+
+ regmap_read(rt5682s->regmap, RT5682S_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev, "Device with ID register %x is not rt5682s\n", val);
+ return -ENODEV;
+ }
+
+ rt5682s_reset(rt5682s);
+ rt5682s_apply_patch_list(rt5682s, &i2c->dev);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_2,
+ RT5682S_DLDO_I_LIMIT_MASK, RT5682S_DLDO_I_LIMIT_DIS);
+ usleep_range(20000, 25000);
+
+ mutex_init(&rt5682s->calibrate_mutex);
+ mutex_init(&rt5682s->sar_mutex);
+ mutex_init(&rt5682s->wclk_mutex);
+ rt5682s_calibrate(rt5682s);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2,
+ RT5682S_PWR_CLK25M_MASK | RT5682S_PWR_CLK1M_MASK,
+ RT5682S_PWR_CLK25M_PD | RT5682S_PWR_CLK1M_PU);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_BG, RT5682S_PWR_BG);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2,
+ RT5682S_HP_SIG_SRC_MASK, RT5682S_HP_SIG_SRC_1BIT_CTL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_PM_HP_MASK, RT5682S_PM_HP_HV);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+
+ /* DMIC data pin */
+ switch (rt5682s->pdata.dmic1_data_pin) {
+ case RT5682S_DMIC1_DATA_NULL:
+ break;
+ case RT5682S_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP2_PIN_MASK, RT5682S_GP2_PIN_DMIC_SDA);
+ break;
+ case RT5682S_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP5_PIN_MASK, RT5682S_GP5_PIN_DMIC_SDA);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ /* DMIC clk pin */
+ switch (rt5682s->pdata.dmic1_clk_pin) {
+ case RT5682S_DMIC1_CLK_NULL:
+ break;
+ case RT5682S_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_DMIC_CLK);
+ break;
+ case RT5682S_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP3_PIN_MASK, RT5682S_GP3_PIN_DMIC_CLK);
+ if (rt5682s->pdata.dmic_clk_driving_high)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PAD_DRIVING_CTRL,
+ RT5682S_PAD_DRV_GP3_MASK, RT5682S_PAD_DRV_GP3_HIGH);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+
+ /* LDO output voltage control */
+ switch (rt5682s->pdata.ldo_dacref) {
+ case RT5682S_LDO_1_607V:
+ break;
+ case RT5682S_LDO_1_5V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_5V);
+ break;
+ case RT5682S_LDO_1_406V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_406V);
+ break;
+ case RT5682S_LDO_1_731V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_731V);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid LDO output setting.\n");
+ break;
+ }
+
+ INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5682s", rt5682s);
+ if (!ret)
+ rt5682s->irq = i2c->irq;
+ else
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev,
+ rt5682s_dai, ARRAY_SIZE(rt5682s_dai));
+}
+
+static void rt5682s_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5682s_priv *rt5682s = i2c_get_clientdata(client);
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ rt5682s_reset(rt5682s);
+}
+
+static void rt5682s_i2c_remove(struct i2c_client *client)
+{
+ rt5682s_i2c_shutdown(client);
+}
+
+static const struct of_device_id rt5682s_of_match[] = {
+ {.compatible = "realtek,rt5682s"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5682s_of_match);
+
+static const struct acpi_device_id rt5682s_acpi_match[] = {
+ {"RTL5682", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match);
+
+static const struct i2c_device_id rt5682s_i2c_id[] = {
+ {"rt5682s"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id);
+
+static struct i2c_driver rt5682s_i2c_driver = {
+ .driver = {
+ .name = "rt5682s",
+ .of_match_table = rt5682s_of_match,
+ .acpi_match_table = rt5682s_acpi_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = rt5682s_i2c_probe,
+ .remove = rt5682s_i2c_remove,
+ .shutdown = rt5682s_i2c_shutdown,
+ .id_table = rt5682s_i2c_id,
+};
+module_i2c_driver(rt5682s_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682I-VS driver");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
new file mode 100644
index 000000000000..67f42898de96
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.h
@@ -0,0 +1,1492 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682s.h -- RT5682I-VS ALSA SoC audio driver
+ *
+ * Copyright 2021 Realtek Microelectronics
+ * Author: Derek Fang <derek.fang@realtek.com>
+ */
+
+#ifndef __RT5682S_H__
+#define __RT5682S_H__
+
+#include <sound/rt5682s.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+
+/* Info */
+#define RT5682S_RESET 0x0000
+#define RT5682S_VERSION_ID 0x00fd
+#define RT5682S_VENDOR_ID 0x00fe
+#define RT5682S_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5682S_HP_CTRL_1 0x0002
+#define RT5682S_HP_CTRL_2 0x0003
+#define RT5682S_HPL_GAIN 0x0005
+#define RT5682S_HPR_GAIN 0x0006
+
+#define RT5682S_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5682S_CBJ_BST_CTRL 0x000b
+#define RT5682S_CBJ_DET_CTRL 0x000f
+#define RT5682S_CBJ_CTRL_1 0x0010
+#define RT5682S_CBJ_CTRL_2 0x0011
+#define RT5682S_CBJ_CTRL_3 0x0012
+#define RT5682S_CBJ_CTRL_4 0x0013
+#define RT5682S_CBJ_CTRL_5 0x0014
+#define RT5682S_CBJ_CTRL_6 0x0015
+#define RT5682S_CBJ_CTRL_7 0x0016
+#define RT5682S_CBJ_CTRL_8 0x0017
+/* I/O - ADC/DAC/DMIC */
+#define RT5682S_DAC1_DIG_VOL 0x0019
+#define RT5682S_STO1_ADC_DIG_VOL 0x001c
+#define RT5682S_STO1_ADC_BOOST 0x001f
+#define RT5682S_HP_IMP_GAIN_1 0x0022
+#define RT5682S_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5682S_SIDETONE_CTRL 0x0024
+#define RT5682S_STO1_ADC_MIXER 0x0026
+#define RT5682S_AD_DA_MIXER 0x0029
+#define RT5682S_STO1_DAC_MIXER 0x002a
+#define RT5682S_A_DAC1_MUX 0x002b
+#define RT5682S_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5682S_REC_MIXER 0x003c
+#define RT5682S_CAL_REC 0x0044
+/* HP Analog Offset Control */
+#define RT5682S_HP_ANA_OST_CTRL_1 0x004b
+#define RT5682S_HP_ANA_OST_CTRL_2 0x004c
+#define RT5682S_HP_ANA_OST_CTRL_3 0x004d
+/* Power */
+#define RT5682S_PWR_DIG_1 0x0061
+#define RT5682S_PWR_DIG_2 0x0062
+#define RT5682S_PWR_ANLG_1 0x0063
+#define RT5682S_PWR_ANLG_2 0x0064
+#define RT5682S_PWR_ANLG_3 0x0065
+#define RT5682S_PWR_MIXER 0x0066
+
+#define RT5682S_MB_CTRL 0x0067
+#define RT5682S_CLK_GATE_TCON_1 0x0068
+#define RT5682S_CLK_GATE_TCON_2 0x0069
+#define RT5682S_CLK_GATE_TCON_3 0x006a
+/* Clock Detect */
+#define RT5682S_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5682S_RESET_LPF_CTRL 0x006c
+#define RT5682S_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5682S_DMIC_CTRL_1 0x006e
+#define RT5682S_LPF_AD_DMIC 0x006f
+/* Format - ADC/DAC */
+#define RT5682S_I2S1_SDP 0x0070
+#define RT5682S_I2S2_SDP 0x0071
+#define RT5682S_ADDA_CLK_1 0x0073
+#define RT5682S_ADDA_CLK_2 0x0074
+#define RT5682S_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5682S_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5682S_TDM_CTRL 0x0079
+#define RT5682S_TDM_ADDA_CTRL_1 0x007a
+#define RT5682S_TDM_ADDA_CTRL_2 0x007b
+#define RT5682S_DATA_SEL_CTRL_1 0x007c
+#define RT5682S_TDM_TCON_CTRL_1 0x007e
+#define RT5682S_TDM_TCON_CTRL_2 0x007f
+/* Function - Analog */
+#define RT5682S_GLB_CLK 0x0080
+#define RT5682S_PLL_TRACK_1 0x0083
+#define RT5682S_PLL_TRACK_2 0x0084
+#define RT5682S_PLL_TRACK_3 0x0085
+#define RT5682S_PLL_TRACK_4 0x0086
+#define RT5682S_PLL_TRACK_5 0x0087
+#define RT5682S_PLL_TRACK_6 0x0088
+#define RT5682S_PLL_TRACK_11 0x008c
+#define RT5682S_DEPOP_1 0x008e
+#define RT5682S_HP_CHARGE_PUMP_1 0x008f
+#define RT5682S_HP_CHARGE_PUMP_2 0x0091
+#define RT5682S_HP_CHARGE_PUMP_3 0x0092
+#define RT5682S_MICBIAS_1 0x0093
+#define RT5682S_MICBIAS_2 0x0094
+#define RT5682S_MICBIAS_3 0x0095
+
+#define RT5682S_PLL_TRACK_12 0x0096
+#define RT5682S_PLL_TRACK_14 0x0097
+#define RT5682S_PLL_CTRL_1 0x0098
+#define RT5682S_PLL_CTRL_2 0x0099
+#define RT5682S_PLL_CTRL_3 0x009a
+#define RT5682S_PLL_CTRL_4 0x009b
+#define RT5682S_PLL_CTRL_5 0x009c
+#define RT5682S_PLL_CTRL_6 0x009d
+#define RT5682S_PLL_CTRL_7 0x009e
+
+#define RT5682S_RC_CLK_CTRL 0x009f
+#define RT5682S_I2S2_M_CLK_CTRL_1 0x00a0
+#define RT5682S_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5682S_I2S2_F_DIV_CTRL_2 0x00a4
+
+#define RT5682S_IRQ_CTRL_1 0x00b6
+#define RT5682S_IRQ_CTRL_2 0x00b7
+#define RT5682S_IRQ_CTRL_3 0x00b8
+#define RT5682S_IRQ_CTRL_4 0x00b9
+#define RT5682S_INT_ST_1 0x00be
+#define RT5682S_GPIO_CTRL_1 0x00c0
+#define RT5682S_GPIO_CTRL_2 0x00c1
+#define RT5682S_GPIO_ST 0x00c2
+#define RT5682S_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5682S_MID_HP_AMP_DET 0x00d2
+#define RT5682S_LOW_HP_AMP_DET 0x00d3
+#define RT5682S_DELAY_BUF_CTRL 0x00d4
+#define RT5682S_SV_ZCD_1 0x00d9
+#define RT5682S_SV_ZCD_2 0x00da
+#define RT5682S_IL_CMD_1 0x00db
+#define RT5682S_IL_CMD_2 0x00dc
+#define RT5682S_IL_CMD_3 0x00dd
+#define RT5682S_IL_CMD_4 0x00de
+#define RT5682S_IL_CMD_5 0x00df
+#define RT5682S_IL_CMD_6 0x00e0
+#define RT5682S_4BTN_IL_CMD_1 0x00e2
+#define RT5682S_4BTN_IL_CMD_2 0x00e3
+#define RT5682S_4BTN_IL_CMD_3 0x00e4
+#define RT5682S_4BTN_IL_CMD_4 0x00e5
+#define RT5682S_4BTN_IL_CMD_5 0x00e6
+#define RT5682S_4BTN_IL_CMD_6 0x00e7
+#define RT5682S_4BTN_IL_CMD_7 0x00e8
+
+#define RT5682S_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5682S_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5682S_AJD1_CTRL 0x00f0
+#define RT5682S_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5682S_DUMMY_1 0x00fa
+#define RT5682S_DUMMY_2 0x00fb
+#define RT5682S_DUMMY_3 0x00fc
+
+#define RT5682S_DAC_ADC_DIG_VOL1 0x0100
+#define RT5682S_BIAS_CUR_CTRL_2 0x010b
+#define RT5682S_BIAS_CUR_CTRL_3 0x010c
+#define RT5682S_BIAS_CUR_CTRL_4 0x010d
+#define RT5682S_BIAS_CUR_CTRL_5 0x010e
+#define RT5682S_BIAS_CUR_CTRL_6 0x010f
+#define RT5682S_BIAS_CUR_CTRL_7 0x0110
+#define RT5682S_BIAS_CUR_CTRL_8 0x0111
+#define RT5682S_BIAS_CUR_CTRL_9 0x0112
+#define RT5682S_BIAS_CUR_CTRL_10 0x0113
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_1 0x0117
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_2 0x0118
+#define RT5682S_CHARGE_PUMP_1 0x0125
+#define RT5682S_DIG_IN_CTRL_1 0x0132
+#define RT5682S_PAD_DRIVING_CTRL 0x0136
+#define RT5682S_CHOP_DAC_1 0x0139
+#define RT5682S_CHOP_DAC_2 0x013a
+#define RT5682S_CHOP_ADC 0x013b
+#define RT5682S_CALIB_ADC_CTRL 0x013c
+#define RT5682S_VOL_TEST 0x013f
+#define RT5682S_SPKVDD_DET_ST 0x0142
+#define RT5682S_TEST_MODE_CTRL_1 0x0145
+#define RT5682S_TEST_MODE_CTRL_2 0x0146
+#define RT5682S_TEST_MODE_CTRL_3 0x0147
+#define RT5682S_TEST_MODE_CTRL_4 0x0148
+#define RT5682S_PLL_INTERNAL_1 0x0156
+#define RT5682S_PLL_INTERNAL_2 0x0157
+#define RT5682S_PLL_INTERNAL_3 0x0158
+#define RT5682S_PLL_INTERNAL_4 0x0159
+#define RT5682S_STO_NG2_CTRL_1 0x0160
+#define RT5682S_STO_NG2_CTRL_2 0x0161
+#define RT5682S_STO_NG2_CTRL_3 0x0162
+#define RT5682S_STO_NG2_CTRL_4 0x0163
+#define RT5682S_STO_NG2_CTRL_5 0x0164
+#define RT5682S_STO_NG2_CTRL_6 0x0165
+#define RT5682S_STO_NG2_CTRL_7 0x0166
+#define RT5682S_STO_NG2_CTRL_8 0x0167
+#define RT5682S_STO_NG2_CTRL_9 0x0168
+#define RT5682S_STO_NG2_CTRL_10 0x0169
+#define RT5682S_STO1_DAC_SIL_DET 0x0190
+#define RT5682S_SIL_PSV_CTRL1 0x0194
+#define RT5682S_SIL_PSV_CTRL2 0x0195
+#define RT5682S_SIL_PSV_CTRL3 0x0197
+#define RT5682S_SIL_PSV_CTRL4 0x0198
+#define RT5682S_SIL_PSV_CTRL5 0x0199
+#define RT5682S_HP_IMP_SENS_CTRL_1 0x01ac
+#define RT5682S_HP_IMP_SENS_CTRL_2 0x01ad
+#define RT5682S_HP_IMP_SENS_CTRL_3 0x01ae
+#define RT5682S_HP_IMP_SENS_CTRL_4 0x01af
+#define RT5682S_HP_IMP_SENS_CTRL_5 0x01b0
+#define RT5682S_HP_IMP_SENS_CTRL_6 0x01b1
+#define RT5682S_HP_IMP_SENS_CTRL_7 0x01b2
+#define RT5682S_HP_IMP_SENS_CTRL_8 0x01b3
+#define RT5682S_HP_IMP_SENS_CTRL_9 0x01b4
+#define RT5682S_HP_IMP_SENS_CTRL_10 0x01b5
+#define RT5682S_HP_IMP_SENS_CTRL_11 0x01b6
+#define RT5682S_HP_IMP_SENS_CTRL_12 0x01b7
+#define RT5682S_HP_IMP_SENS_CTRL_13 0x01b8
+#define RT5682S_HP_IMP_SENS_CTRL_14 0x01b9
+#define RT5682S_HP_IMP_SENS_CTRL_15 0x01ba
+#define RT5682S_HP_IMP_SENS_CTRL_16 0x01bb
+#define RT5682S_HP_IMP_SENS_CTRL_17 0x01bc
+#define RT5682S_HP_IMP_SENS_CTRL_18 0x01bd
+#define RT5682S_HP_IMP_SENS_CTRL_19 0x01be
+#define RT5682S_HP_IMP_SENS_CTRL_20 0x01bf
+#define RT5682S_HP_IMP_SENS_CTRL_21 0x01c0
+#define RT5682S_HP_IMP_SENS_CTRL_22 0x01c1
+#define RT5682S_HP_IMP_SENS_CTRL_23 0x01c2
+#define RT5682S_HP_IMP_SENS_CTRL_24 0x01c3
+#define RT5682S_HP_IMP_SENS_CTRL_25 0x01c4
+#define RT5682S_HP_IMP_SENS_CTRL_26 0x01c5
+#define RT5682S_HP_IMP_SENS_CTRL_27 0x01c6
+#define RT5682S_HP_IMP_SENS_CTRL_28 0x01c7
+#define RT5682S_HP_IMP_SENS_CTRL_29 0x01c8
+#define RT5682S_HP_IMP_SENS_CTRL_30 0x01c9
+#define RT5682S_HP_IMP_SENS_CTRL_31 0x01ca
+#define RT5682S_HP_IMP_SENS_CTRL_32 0x01cb
+#define RT5682S_HP_IMP_SENS_CTRL_33 0x01cc
+#define RT5682S_HP_IMP_SENS_CTRL_34 0x01cd
+#define RT5682S_HP_IMP_SENS_CTRL_35 0x01ce
+#define RT5682S_HP_IMP_SENS_CTRL_36 0x01cf
+#define RT5682S_HP_IMP_SENS_CTRL_37 0x01d0
+#define RT5682S_HP_IMP_SENS_CTRL_38 0x01d1
+#define RT5682S_HP_IMP_SENS_CTRL_39 0x01d2
+#define RT5682S_HP_IMP_SENS_CTRL_40 0x01d3
+#define RT5682S_HP_IMP_SENS_CTRL_41 0x01d4
+#define RT5682S_HP_IMP_SENS_CTRL_42 0x01d5
+#define RT5682S_HP_IMP_SENS_CTRL_43 0x01d6
+#define RT5682S_HP_IMP_SENS_CTRL_44 0x01d7
+#define RT5682S_HP_IMP_SENS_CTRL_45 0x01d8
+#define RT5682S_HP_IMP_SENS_CTRL_46 0x01d9
+#define RT5682S_HP_LOGIC_CTRL_1 0x01da
+#define RT5682S_HP_LOGIC_CTRL_2 0x01db
+#define RT5682S_HP_LOGIC_CTRL_3 0x01dc
+#define RT5682S_HP_CALIB_CTRL_1 0x01de
+#define RT5682S_HP_CALIB_CTRL_2 0x01df
+#define RT5682S_HP_CALIB_CTRL_3 0x01e0
+#define RT5682S_HP_CALIB_CTRL_4 0x01e1
+#define RT5682S_HP_CALIB_CTRL_5 0x01e2
+#define RT5682S_HP_CALIB_CTRL_6 0x01e3
+#define RT5682S_HP_CALIB_CTRL_7 0x01e4
+#define RT5682S_HP_CALIB_CTRL_8 0x01e5
+#define RT5682S_HP_CALIB_CTRL_9 0x01e6
+#define RT5682S_HP_CALIB_CTRL_10 0x01e7
+#define RT5682S_HP_CALIB_CTRL_11 0x01e8
+#define RT5682S_HP_CALIB_ST_1 0x01ea
+#define RT5682S_HP_CALIB_ST_2 0x01eb
+#define RT5682S_HP_CALIB_ST_3 0x01ec
+#define RT5682S_HP_CALIB_ST_4 0x01ed
+#define RT5682S_HP_CALIB_ST_5 0x01ee
+#define RT5682S_HP_CALIB_ST_6 0x01ef
+#define RT5682S_HP_CALIB_ST_7 0x01f0
+#define RT5682S_HP_CALIB_ST_8 0x01f1
+#define RT5682S_HP_CALIB_ST_9 0x01f2
+#define RT5682S_HP_CALIB_ST_10 0x01f3
+#define RT5682S_HP_CALIB_ST_11 0x01f4
+#define RT5682S_SAR_IL_CMD_1 0x0210
+#define RT5682S_SAR_IL_CMD_2 0x0211
+#define RT5682S_SAR_IL_CMD_3 0x0212
+#define RT5682S_SAR_IL_CMD_4 0x0213
+#define RT5682S_SAR_IL_CMD_5 0x0214
+#define RT5682S_SAR_IL_CMD_6 0x0215
+#define RT5682S_SAR_IL_CMD_7 0x0216
+#define RT5682S_SAR_IL_CMD_8 0x0217
+#define RT5682S_SAR_IL_CMD_9 0x0218
+#define RT5682S_SAR_IL_CMD_10 0x0219
+#define RT5682S_SAR_IL_CMD_11 0x021a
+#define RT5682S_SAR_IL_CMD_12 0x021b
+#define RT5682S_SAR_IL_CMD_13 0x021c
+#define RT5682S_SAR_IL_CMD_14 0x021d
+#define RT5682S_DUMMY_4 0x02fa
+#define RT5682S_DUMMY_5 0x02fb
+#define RT5682S_DUMMY_6 0x02fc
+#define RT5682S_VERSION_ID_HIDE 0x03fe
+#define RT5682S_VERSION_ID_CUS 0x03ff
+#define RT5682S_SCAN_CTL 0x0500
+#define RT5682S_HP_AMP_DET 0x0600
+#define RT5682S_BIAS_CUR_CTRL_11 0x0610
+#define RT5682S_BIAS_CUR_CTRL_12 0x0611
+#define RT5682S_BIAS_CUR_CTRL_13 0x0620
+#define RT5682S_BIAS_CUR_CTRL_14 0x0621
+#define RT5682S_BIAS_CUR_CTRL_15 0x0630
+#define RT5682S_BIAS_CUR_CTRL_16 0x0631
+#define RT5682S_BIAS_CUR_CTRL_17 0x0640
+#define RT5682S_BIAS_CUR_CTRL_18 0x0641
+#define RT5682S_I2C_TRANS_CTRL 0x07fa
+#define RT5682S_DUMMY_7 0x08fa
+#define RT5682S_DUMMY_8 0x08fb
+#define RT5682S_DMIC_FLOAT_DET 0x0d00
+#define RT5682S_HA_CMP_OP_1 0x1100
+#define RT5682S_HA_CMP_OP_2 0x1101
+#define RT5682S_HA_CMP_OP_3 0x1102
+#define RT5682S_HA_CMP_OP_4 0x1103
+#define RT5682S_HA_CMP_OP_5 0x1104
+#define RT5682S_HA_CMP_OP_6 0x1105
+#define RT5682S_HA_CMP_OP_7 0x1106
+#define RT5682S_HA_CMP_OP_8 0x1107
+#define RT5682S_HA_CMP_OP_9 0x1108
+#define RT5682S_HA_CMP_OP_10 0x1109
+#define RT5682S_HA_CMP_OP_11 0x110a
+#define RT5682S_HA_CMP_OP_12 0x110b
+#define RT5682S_HA_CMP_OP_13 0x110c
+#define RT5682S_HA_CMP_OP_14 0x1111
+#define RT5682S_HA_CMP_OP_15 0x1112
+#define RT5682S_HA_CMP_OP_16 0x1113
+#define RT5682S_HA_CMP_OP_17 0x1114
+#define RT5682S_HA_CMP_OP_18 0x1115
+#define RT5682S_HA_CMP_OP_19 0x1116
+#define RT5682S_HA_CMP_OP_20 0x1117
+#define RT5682S_HA_CMP_OP_21 0x1118
+#define RT5682S_HA_CMP_OP_22 0x1119
+#define RT5682S_HA_CMP_OP_23 0x111a
+#define RT5682S_HA_CMP_OP_24 0x111b
+#define RT5682S_HA_CMP_OP_25 0x111c
+#define RT5682S_NEW_CBJ_DET_CTL_1 0x1401
+#define RT5682S_NEW_CBJ_DET_CTL_2 0x1402
+#define RT5682S_NEW_CBJ_DET_CTL_3 0x1403
+#define RT5682S_NEW_CBJ_DET_CTL_4 0x1404
+#define RT5682S_NEW_CBJ_DET_CTL_5 0x1406
+#define RT5682S_NEW_CBJ_DET_CTL_6 0x1407
+#define RT5682S_NEW_CBJ_DET_CTL_7 0x1408
+#define RT5682S_NEW_CBJ_DET_CTL_8 0x1409
+#define RT5682S_NEW_CBJ_DET_CTL_9 0x140a
+#define RT5682S_NEW_CBJ_DET_CTL_10 0x140b
+#define RT5682S_NEW_CBJ_DET_CTL_11 0x140c
+#define RT5682S_NEW_CBJ_DET_CTL_12 0x140d
+#define RT5682S_NEW_CBJ_DET_CTL_13 0x140e
+#define RT5682S_NEW_CBJ_DET_CTL_14 0x140f
+#define RT5682S_NEW_CBJ_DET_CTL_15 0x1410
+#define RT5682S_NEW_CBJ_DET_CTL_16 0x1411
+#define RT5682S_DA_FILTER_1 0x1801
+#define RT5682S_DA_FILTER_2 0x1802
+#define RT5682S_DA_FILTER_3 0x1803
+#define RT5682S_DA_FILTER_4 0x1804
+#define RT5682S_DA_FILTER_5 0x1805
+#define RT5682S_CLK_SW_TEST_1 0x2c00
+#define RT5682S_CLK_SW_TEST_2 0x3400
+#define RT5682S_CLK_SW_TEST_3 0x3404
+#define RT5682S_CLK_SW_TEST_4 0x3405
+#define RT5682S_CLK_SW_TEST_5 0x3406
+#define RT5682S_CLK_SW_TEST_6 0x3407
+#define RT5682S_CLK_SW_TEST_7 0x3408
+#define RT5682S_CLK_SW_TEST_8 0x3409
+#define RT5682S_CLK_SW_TEST_9 0x340a
+#define RT5682S_CLK_SW_TEST_10 0x340b
+#define RT5682S_CLK_SW_TEST_11 0x340c
+#define RT5682S_CLK_SW_TEST_12 0x340d
+#define RT5682S_CLK_SW_TEST_13 0x340e
+#define RT5682S_CLK_SW_TEST_14 0x340f
+#define RT5682S_EFUSE_MANU_WRITE_1 0x3410
+#define RT5682S_EFUSE_MANU_WRITE_2 0x3411
+#define RT5682S_EFUSE_MANU_WRITE_3 0x3412
+#define RT5682S_EFUSE_MANU_WRITE_4 0x3413
+#define RT5682S_EFUSE_MANU_WRITE_5 0x3414
+#define RT5682S_EFUSE_MANU_WRITE_6 0x3415
+#define RT5682S_EFUSE_READ_1 0x3424
+#define RT5682S_EFUSE_READ_2 0x3425
+#define RT5682S_EFUSE_READ_3 0x3426
+#define RT5682S_EFUSE_READ_4 0x3427
+#define RT5682S_EFUSE_READ_5 0x3428
+#define RT5682S_EFUSE_READ_6 0x3429
+#define RT5682S_EFUSE_READ_7 0x342a
+#define RT5682S_EFUSE_READ_8 0x342b
+#define RT5682S_EFUSE_READ_9 0x342c
+#define RT5682S_EFUSE_READ_10 0x342d
+#define RT5682S_EFUSE_READ_11 0x342e
+#define RT5682S_EFUSE_READ_12 0x342f
+#define RT5682S_EFUSE_READ_13 0x3430
+#define RT5682S_EFUSE_READ_14 0x3431
+#define RT5682S_EFUSE_READ_15 0x3432
+#define RT5682S_EFUSE_READ_16 0x3433
+#define RT5682S_EFUSE_READ_17 0x3434
+#define RT5682S_EFUSE_READ_18 0x3435
+#define RT5682S_EFUSE_TIMING_CTL_1 0x3440
+#define RT5682S_EFUSE_TIMING_CTL_2 0x3441
+#define RT5682S_PILOT_DIG_CTL_1 0x3500
+#define RT5682S_PILOT_DIG_CTL_2 0x3501
+#define RT5682S_HP_AMP_DET_CTL_1 0x3b00
+#define RT5682S_HP_AMP_DET_CTL_2 0x3b01
+#define RT5682S_HP_AMP_DET_CTL_3 0x3b02
+#define RT5682S_HP_AMP_DET_CTL_4 0x3b03
+
+#define RT5682S_MAX_REG (RT5682S_HP_AMP_DET_CTL_4)
+
+/* global definition */
+#define RT5682S_L_MUTE (0x1 << 15)
+#define RT5682S_L_MUTE_SFT 15
+#define RT5682S_R_MUTE (0x1 << 7)
+#define RT5682S_R_MUTE_SFT 7
+#define RT5682S_L_VOL_SFT 8
+#define RT5682S_R_VOL_SFT 0
+#define RT5682S_CLK_SRC_MCLK (0x0)
+#define RT5682S_CLK_SRC_PLL1 (0x1)
+#define RT5682S_CLK_SRC_PLL2 (0x2)
+#define RT5682S_CLK_SRC_RCCLK (0x4) /* 25M */
+
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682S_HPO_L_PATH_MASK (0x1 << 14)
+#define RT5682S_HPO_L_PATH_EN (0x1 << 14)
+#define RT5682S_HPO_L_PATH_DIS (0x0 << 14)
+#define RT5682S_HPO_R_PATH_MASK (0x1 << 13)
+#define RT5682S_HPO_R_PATH_EN (0x1 << 13)
+#define RT5682S_HPO_R_PATH_DIS (0x0 << 13)
+#define RT5682S_HPO_SEL_IP_EN_SW (0x1)
+#define RT5682S_HPO_IP_EN_GATING (0x1)
+#define RT5682S_HPO_IP_NO_GATING (0x0)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5682S_G_HP (0xf << 8)
+#define RT5682S_G_HP_SFT 8
+#define RT5682S_G_STO_DA_DMIX (0xf)
+#define RT5682S_G_STO_DA_SFT 0
+
+/* Embeeded Jack and Type Detection Control 2 (0x0010) */
+#define RT5682S_EMB_JD_MASK (0x1 << 15)
+#define RT5682S_EMB_JD_EN (0x1 << 15)
+#define RT5682S_EMB_JD_EN_SFT 15
+#define RT5682S_EMB_JD_RST (0x1 << 14)
+#define RT5682S_JD_MODE (0x1 << 13)
+#define RT5682S_JD_MODE_SFT 13
+#define RT5682S_DET_TYPE (0x1 << 12)
+#define RT5682S_DET_TYPE_SFT 12
+#define RT5682S_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5682S_SEL_FAST_OFF_MASK (0x3 << 9)
+#define RT5682S_SEL_FAST_OFF_SFT 9
+#define RT5682S_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5682S_FAST_OFF_MASK (0x1 << 7)
+#define RT5682S_FAST_OFF_EN (0x1 << 7)
+#define RT5682S_FAST_OFF_DIS (0x0 << 7)
+#define RT5682S_VREF_POW_MASK (0x1 << 6)
+#define RT5682S_VREF_POW_FSM (0x0 << 6)
+#define RT5682S_VREF_POW_REG (0x1 << 6)
+#define RT5682S_MB1_PATH_BIT 5
+#define RT5682S_MB1_PATH_MASK (0x1 << 5)
+#define RT5682S_CTRL_MB1_REG (0x1 << 5)
+#define RT5682S_CTRL_MB1_FSM (0x0 << 5)
+#define RT5682S_MB2_PATH_BIT 4
+#define RT5682S_MB2_PATH_MASK (0x1 << 4)
+#define RT5682S_CTRL_MB2_REG (0x1 << 4)
+#define RT5682S_CTRL_MB2_FSM (0x0 << 4)
+#define RT5682S_TRIG_JD_MASK (0x1 << 3)
+#define RT5682S_TRIG_JD_HIGH (0x1 << 3)
+#define RT5682S_TRIG_JD_LOW (0x0 << 3)
+#define RT5682S_MIC_CAP_MASK (0x1 << 1)
+#define RT5682S_MIC_CAP_HS (0x1 << 1)
+#define RT5682S_MIC_CAP_HP (0x0 << 1)
+#define RT5682S_MIC_CAP_SRC_MASK (0x1)
+#define RT5682S_MIC_CAP_SRC_REG (0x1)
+#define RT5682S_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 3 (0x0011) */
+#define RT5682S_SEL_CBJ_TYPE_SLOW (0x1 << 15)
+#define RT5682S_SEL_CBJ_TYPE_NORM (0x0 << 15)
+#define RT5682S_SEL_CBJ_TYPE_MASK (0x1 << 15)
+#define RT5682S_POW_BG_MB1_MASK (0x1 << 13)
+#define RT5682S_POW_BG_MB1_REG (0x1 << 13)
+#define RT5682S_POW_BG_MB1_FSM (0x0 << 13)
+#define RT5682S_POW_BG_MB2_MASK (0x1 << 12)
+#define RT5682S_POW_BG_MB2_REG (0x1 << 12)
+#define RT5682S_POW_BG_MB2_FSM (0x0 << 12)
+#define RT5682S_EXT_JD_SRC (0x7 << 4)
+#define RT5682S_EXT_JD_SRC_SFT 4
+#define RT5682S_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5682S_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5682S_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5682S_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5682S_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5682S_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 4 (0x0012) */
+#define RT5682S_CBJ_IN_BUF_MASK (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_EN (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_DIS (0x0 << 7)
+#define RT5682S_CBJ_IN_BUF_BIT 7
+
+/* Combo Jack and Type Detection Control 5 (0x0013) */
+#define RT5682S_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5682S_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5682S_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5682S_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5682S_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5682S_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* Combo Jack and Type Detection Control 6 (0x0014) */
+#define RT5682S_JD_FAST_OFF_SRC_MASK (0x7 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_JDH (0x6 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO6 (0x5 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO5 (0x4 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO4 (0x3 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO3 (0x2 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO2 (0x1 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO1 (0x0 << 8)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5682S_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5682S_DAC_L1_VOL_SFT 8
+#define RT5682S_DAC_R1_VOL_MASK (0xff)
+#define RT5682S_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5682S_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5682S_ADC_L_VOL_SFT 8
+#define RT5682S_ADC_R_VOL_MASK (0x7f)
+#define RT5682S_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5682S_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5682S_STO1_ADC_L_BST_SFT 14
+#define RT5682S_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5682S_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5682S_ST_SRC_SEL (0x1 << 8)
+#define RT5682S_ST_SRC_SFT 8
+#define RT5682S_ST_EN_MASK (0x1 << 6)
+#define RT5682S_ST_DIS (0x0 << 6)
+#define RT5682S_ST_EN (0x1 << 6)
+#define RT5682S_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5682S_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5682S_M_STO1_ADC_L1_SFT 15
+#define RT5682S_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5682S_M_STO1_ADC_L2_SFT 14
+#define RT5682S_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5682S_STO1_ADC1L_SRC_SFT 13
+#define RT5682S_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5682S_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5682S_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5682S_STO1_ADC2L_SRC_SFT 12
+#define RT5682S_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5682S_STO1_ADCL_SRC_SFT 10
+#define RT5682S_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5682S_M_STO1_ADC_R1_SFT 7
+#define RT5682S_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5682S_M_STO1_ADC_R2_SFT 6
+#define RT5682S_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5682S_STO1_ADC1R_SRC_SFT 5
+#define RT5682S_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5682S_STO1_ADC2R_SRC_SFT 4
+#define RT5682S_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5682S_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5682S_M_ADCMIX_L (0x1 << 15)
+#define RT5682S_M_ADCMIX_L_SFT 15
+#define RT5682S_M_DAC1_L (0x1 << 14)
+#define RT5682S_M_DAC1_L_SFT 14
+#define RT5682S_M_ADCMIX_R (0x1 << 7)
+#define RT5682S_M_ADCMIX_R_SFT 7
+#define RT5682S_M_DAC1_R (0x1 << 6)
+#define RT5682S_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5682S_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5682S_M_DAC_L1_STO_L_SFT 15
+#define RT5682S_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5682S_G_DAC_L1_STO_L_SFT 14
+#define RT5682S_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5682S_M_DAC_R1_STO_L_SFT 13
+#define RT5682S_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5682S_G_DAC_R1_STO_L_SFT 12
+#define RT5682S_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5682S_M_DAC_L1_STO_R_SFT 7
+#define RT5682S_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5682S_G_DAC_L1_STO_R_SFT 6
+#define RT5682S_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5682S_M_DAC_R1_STO_R_SFT 5
+#define RT5682S_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5682S_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5682S_M_ST_STO_L (0x1 << 9)
+#define RT5682S_M_ST_STO_L_SFT 9
+#define RT5682S_M_ST_STO_R (0x1 << 8)
+#define RT5682S_M_ST_STO_R_SFT 8
+#define RT5682S_DAC_L1_SRC_MASK (0x1 << 4)
+#define RT5682S_A_DACL1_SFT 4
+#define RT5682S_DAC_R1_SRC_MASK (0x1)
+#define RT5682S_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5682S_IF2_DAC_SEL_MASK (0x3 << 2)
+#define RT5682S_IF2_DAC_SEL_SFT 2
+#define RT5682S_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5682S_IF2_ADC_SEL_SFT 0
+
+/* REC Left/Right Mixer Control 2 (0x003c) */
+#define RT5682S_BST_CBJ_MASK (0x3f << 8)
+#define RT5682S_BST_CBJ_SFT 8
+#define RT5682S_M_CBJ_RM1_L (0x1 << 7)
+#define RT5682S_M_CBJ_RM1_L_SFT 7
+#define RT5682S_M_CBJ_RM1_R (0x1 << 6)
+#define RT5682S_M_CBJ_RM1_R_SFT 6
+
+/* REC Left/Right Mixer Calibration Control(0x0044) */
+#define RT5682S_PWR_RM1_R_BIT 8
+#define RT5682S_PWR_RM1_L_BIT 0
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5682S_PWR_I2S1 (0x1 << 15)
+#define RT5682S_PWR_I2S1_BIT 15
+#define RT5682S_PWR_I2S2 (0x1 << 14)
+#define RT5682S_PWR_I2S2_BIT 14
+#define RT5682S_PRE_CHR_DAC_L1 (0x1 << 13)
+#define RT5682S_PRE_CHR_DAC_L1_BIT 13
+#define RT5682S_PRE_CHR_DAC_R1 (0x1 << 12)
+#define RT5682S_PRE_CHR_DAC_R1_BIT 12
+#define RT5682S_PWR_DAC_L1 (0x1 << 11)
+#define RT5682S_PWR_DAC_L1_BIT 11
+#define RT5682S_PWR_DAC_R1 (0x1 << 10)
+#define RT5682S_PWR_DAC_R1_BIT 10
+#define RT5682S_PWR_LDO (0x1 << 8)
+#define RT5682S_PWR_LDO_BIT 8
+#define RT5682S_PWR_D2S_L (0x1 << 7)
+#define RT5682S_PWR_D2S_L_BIT 7
+#define RT5682S_PWR_D2S_R (0x1 << 6)
+#define RT5682S_PWR_D2S_R_BIT 6
+#define RT5682S_PWR_ADC_L1 (0x1 << 4)
+#define RT5682S_PWR_ADC_L1_BIT 4
+#define RT5682S_PWR_ADC_R1 (0x1 << 3)
+#define RT5682S_PWR_ADC_R1_BIT 3
+#define RT5682S_EFUSE_SW_EN (0x1 << 2)
+#define RT5682S_EFUSE_SW_DIS (0x0 << 2)
+#define RT5682S_PWR_EFUSE (0x1 << 1)
+#define RT5682S_PWR_EFUSE_BIT 1
+#define RT5682S_DIG_GATE_CTRL (0x1 << 0)
+#define RT5682S_DIG_GATE_CTRL_SFT 0
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5682S_PWR_ADC_S1F (0x1 << 15)
+#define RT5682S_PWR_ADC_S1F_BIT 15
+#define RT5682S_PWR_DAC_S1F (0x1 << 10)
+#define RT5682S_PWR_DAC_S1F_BIT 10
+#define RT5682S_DLDO_I_LIMIT_MASK (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_EN (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_DIS (0x0 << 7)
+#define RT5682S_DLDO_I_BIAS_SEL_4 (0x1 << 6)
+#define RT5682S_DLDO_I_BIAS_SEL_0 (0x0 << 6)
+#define RT5682S_DLDO_REG_TEST_1 (0x1 << 5)
+#define RT5682S_DLDO_REG_TEST_0 (0x0 << 5)
+#define RT5682S_DLDO_SRC_REG (0x1 << 4)
+#define RT5682S_DLDO_SRC_EFUSE (0x0 << 4)
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5682S_PWR_VREF1 (0x1 << 15)
+#define RT5682S_PWR_VREF1_BIT 15
+#define RT5682S_PWR_FV1 (0x1 << 14)
+#define RT5682S_PWR_FV1_BIT 14
+#define RT5682S_PWR_VREF2 (0x1 << 13)
+#define RT5682S_PWR_VREF2_BIT 13
+#define RT5682S_PWR_FV2 (0x1 << 12)
+#define RT5682S_PWR_FV2_BIT 12
+#define RT5682S_LDO1_DBG_MASK (0x3 << 10)
+#define RT5682S_PWR_MB (0x1 << 9)
+#define RT5682S_PWR_MB_BIT 9
+#define RT5682S_PWR_BG (0x1 << 7)
+#define RT5682S_PWR_BG_BIT 7
+#define RT5682S_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5682S_LDO1_BYPASS (0x1 << 6)
+#define RT5682S_LDO1_NOT_BYPASS (0x0 << 6)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5682S_PWR_MCLK0_WD (0x1 << 15)
+#define RT5682S_PWR_MCLK0_WD_BIT 15
+#define RT5682S_PWR_MCLK1_WD (0x1 << 14)
+#define RT5682S_PWR_MCLK1_WD_BIT 14
+#define RT5682S_RST_MCLK0 (0x1 << 13)
+#define RT5682S_RST_MCLK0_BIT 13
+#define RT5682S_RST_MCLK1 (0x1 << 12)
+#define RT5682S_RST_MCLK1_BIT 12
+#define RT5682S_PWR_MB1 (0x1 << 11)
+#define RT5682S_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5682S_PWR_MB1_BIT 11
+#define RT5682S_PWR_MB2 (0x1 << 10)
+#define RT5682S_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5682S_PWR_MB2_BIT 10
+#define RT5682S_PWR_JD_MASK (0x1 << 0)
+#define RT5682S_PWR_JD_ENABLE (0x1 << 0)
+#define RT5682S_PWR_JD_DISABLE (0x0 << 0)
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5682S_PWR_LDO_PLLA (0x1 << 15)
+#define RT5682S_PWR_LDO_PLLA_BIT 15
+#define RT5682S_PWR_LDO_PLLB (0x1 << 14)
+#define RT5682S_PWR_LDO_PLLB_BIT 14
+#define RT5682S_PWR_BIAS_PLLA (0x1 << 13)
+#define RT5682S_PWR_BIAS_PLLA_BIT 13
+#define RT5682S_PWR_BIAS_PLLB (0x1 << 12)
+#define RT5682S_PWR_BIAS_PLLB_BIT 12
+#define RT5682S_PWR_CBJ (0x1 << 9)
+#define RT5682S_PWR_CBJ_BIT 9
+#define RT5682S_RSTB_PLLB (0x1 << 7)
+#define RT5682S_RSTB_PLLB_BIT 7
+#define RT5682S_RSTB_PLLA (0x1 << 6)
+#define RT5682S_RSTB_PLLA_BIT 6
+#define RT5682S_PWR_PLLB (0x1 << 5)
+#define RT5682S_PWR_PLLB_BIT 5
+#define RT5682S_PWR_PLLA (0x1 << 4)
+#define RT5682S_PWR_PLLA_BIT 4
+#define RT5682S_PWR_LDO_MB2 (0x1 << 2)
+#define RT5682S_PWR_LDO_MB2_BIT 2
+#define RT5682S_PWR_LDO_MB1 (0x1 << 1)
+#define RT5682S_PWR_LDO_MB1_BIT 1
+#define RT5682S_PWR_BGLDO (0x1 << 0)
+#define RT5682S_PWR_BGLDO_BIT 0
+
+/* Power Management for Mixer (0x0066) */
+#define RT5682S_PWR_CLK_COMP_8FS (0x1 << 15)
+#define RT5682S_PWR_CLK_COMP_8FS_BIT 15
+#define RT5682S_DBG_BGLDO_MASK (0x3 << 12)
+#define RT5682S_DBG_BGLDO_SFT 12
+#define RT5682S_DBG_BGLDO_MB1_MASK (0x3 << 10)
+#define RT5682S_DBG_BGLDO_MB1_SFT 10
+#define RT5682S_DBG_BGLDO_MB2_MASK (0x3 << 8)
+#define RT5682S_DBG_BGLDO_MB2_SFT 8
+#define RT5682S_DLDO_BGLDO_MASK (0x3 << 6)
+#define RT5682S_DLDO_BGLDO_MB2_SFT 6
+#define RT5682S_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5682S_PWR_STO1_DAC_L_BIT 5
+#define RT5682S_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5682S_PWR_STO1_DAC_R_BIT 4
+#define RT5682S_DVO_BGLDO_MB1_MASK (0x3 << 2)
+#define RT5682S_DVO_BGLDO_MB1_SFT 2
+#define RT5682S_DVO_BGLDO_MB2_MASK (0x3 << 0)
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5682S_SYS_CLK_DET (0x1 << 15)
+#define RT5682S_SYS_CLK_DET_SFT 15
+#define RT5682S_PLL1_CLK_DET (0x1 << 14)
+#define RT5682S_PLL1_CLK_DET_SFT 14
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5682S_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5682S_DMIC_1_EN_SFT 15
+#define RT5682S_DMIC_1_DIS (0x0 << 15)
+#define RT5682S_DMIC_1_EN (0x1 << 15)
+#define RT5682S_FIFO_CLK_DIV_MASK (0x7 << 12)
+#define RT5682S_FIFO_CLK_DIV_2 (0x1 << 12)
+#define RT5682S_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5682S_DMIC_1_DP_SFT 4
+#define RT5682S_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5682S_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5682S_DMIC_CLK_MASK (0xf << 0)
+#define RT5682S_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5682S_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5682S_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_SFT 15
+#define RT5682S_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5682S_I2S1_TX_CHL_SFT 12
+#define RT5682S_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5682S_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5682S_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5682S_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5682S_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5682S_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5682S_I2S1_RX_CHL_SFT 8
+#define RT5682S_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5682S_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5682S_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5682S_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5682S_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5682S_I2S1_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S1_MONO_EN (0x1 << 7)
+#define RT5682S_I2S1_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S1_DL_MASK (0x7 << 4)
+#define RT5682S_I2S1_DL_SFT 4
+#define RT5682S_I2S1_DL_16 (0x0 << 4)
+#define RT5682S_I2S1_DL_20 (0x1 << 4)
+#define RT5682S_I2S1_DL_24 (0x2 << 4)
+#define RT5682S_I2S1_DL_32 (0x3 << 4)
+#define RT5682S_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0071) */
+#define RT5682S_I2S2_MS_MASK (0x1 << 15)
+#define RT5682S_I2S2_MS_SFT 15
+#define RT5682S_I2S2_MS_M (0x0 << 15)
+#define RT5682S_I2S2_MS_S (0x1 << 15)
+#define RT5682S_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5682S_I2S2_PIN_CFG_SFT 14
+#define RT5682S_I2S2_OUT_MASK (0x1 << 9)
+#define RT5682S_I2S2_OUT_SFT 9
+#define RT5682S_I2S2_OUT_UM (0x0 << 9)
+#define RT5682S_I2S2_OUT_M (0x1 << 9)
+#define RT5682S_I2S_BP_MASK (0x1 << 8)
+#define RT5682S_I2S_BP_SFT 8
+#define RT5682S_I2S_BP_NOR (0x0 << 8)
+#define RT5682S_I2S_BP_INV (0x1 << 8)
+#define RT5682S_I2S2_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S2_MONO_EN (0x1 << 7)
+#define RT5682S_I2S2_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S2_DL_MASK (0x7 << 4)
+#define RT5682S_I2S2_DL_SFT 4
+#define RT5682S_I2S2_DL_8 (0x0 << 4)
+#define RT5682S_I2S2_DL_16 (0x1 << 4)
+#define RT5682S_I2S2_DL_20 (0x2 << 4)
+#define RT5682S_I2S2_DL_24 (0x3 << 4)
+#define RT5682S_I2S2_DL_32 (0x4 << 4)
+#define RT5682S_I2S_DF_MASK (0x7)
+#define RT5682S_I2S_DF_SFT 0
+#define RT5682S_I2S_DF_I2S (0x0)
+#define RT5682S_I2S_DF_LEFT (0x1)
+#define RT5682S_I2S_DF_PCM_A (0x2)
+#define RT5682S_I2S_DF_PCM_B (0x3)
+#define RT5682S_I2S_DF_PCM_A_N (0x6)
+#define RT5682S_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5682S_ADC_OSR_MASK (0xf << 12)
+#define RT5682S_ADC_OSR_SFT 12
+#define RT5682S_ADC_OSR_D_1 (0x0 << 12)
+#define RT5682S_ADC_OSR_D_2 (0x1 << 12)
+#define RT5682S_ADC_OSR_D_4 (0x2 << 12)
+#define RT5682S_ADC_OSR_D_6 (0x3 << 12)
+#define RT5682S_ADC_OSR_D_8 (0x4 << 12)
+#define RT5682S_ADC_OSR_D_12 (0x5 << 12)
+#define RT5682S_ADC_OSR_D_16 (0x6 << 12)
+#define RT5682S_ADC_OSR_D_24 (0x7 << 12)
+#define RT5682S_ADC_OSR_D_32 (0x8 << 12)
+#define RT5682S_ADC_OSR_D_48 (0x9 << 12)
+#define RT5682S_I2S_M_D_MASK (0xf << 8)
+#define RT5682S_I2S_M_D_SFT 8
+#define RT5682S_I2S_M_D_1 (0x0 << 8)
+#define RT5682S_I2S_M_D_2 (0x1 << 8)
+#define RT5682S_I2S_M_D_3 (0x2 << 8)
+#define RT5682S_I2S_M_D_4 (0x3 << 8)
+#define RT5682S_I2S_M_D_6 (0x4 << 8)
+#define RT5682S_I2S_M_D_8 (0x5 << 8)
+#define RT5682S_I2S_M_D_12 (0x6 << 8)
+#define RT5682S_I2S_M_D_16 (0x7 << 8)
+#define RT5682S_I2S_M_D_24 (0x8 << 8)
+#define RT5682S_I2S_M_D_32 (0x9 << 8)
+#define RT5682S_I2S_M_D_48 (0x10 << 8)
+#define RT5682S_I2S_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S_M_CLK_SRC_SFT 4
+#define RT5682S_DAC_OSR_MASK (0xf << 0)
+#define RT5682S_DAC_OSR_SFT 0
+#define RT5682S_DAC_OSR_D_1 (0x0 << 0)
+#define RT5682S_DAC_OSR_D_2 (0x1 << 0)
+#define RT5682S_DAC_OSR_D_4 (0x2 << 0)
+#define RT5682S_DAC_OSR_D_6 (0x3 << 0)
+#define RT5682S_DAC_OSR_D_8 (0x4 << 0)
+#define RT5682S_DAC_OSR_D_12 (0x5 << 0)
+#define RT5682S_DAC_OSR_D_16 (0x6 << 0)
+#define RT5682S_DAC_OSR_D_24 (0x7 << 0)
+#define RT5682S_DAC_OSR_D_32 (0x8 << 0)
+#define RT5682S_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5682S_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5682S_I2S2_BCLK_MS2_SFT 11
+#define RT5682S_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5682S_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5682S_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5682S_TDM_TX_CH_2 (0x0 << 12)
+#define RT5682S_TDM_TX_CH_4 (0x1 << 12)
+#define RT5682S_TDM_TX_CH_6 (0x2 << 12)
+#define RT5682S_TDM_TX_CH_8 (0x3 << 12)
+#define RT5682S_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5682S_TDM_RX_CH_2 (0x0 << 8)
+#define RT5682S_TDM_RX_CH_4 (0x1 << 8)
+#define RT5682S_TDM_RX_CH_6 (0x2 << 8)
+#define RT5682S_TDM_RX_CH_8 (0x3 << 8)
+#define RT5682S_TDM_ADC_LCA_MASK (0x7 << 4)
+#define RT5682S_TDM_ADC_LCA_SFT 4
+#define RT5682S_TDM_ADC_DL_MASK (0x3 << 0)
+#define RT5682S_TDM_ADC_DL_SFT 0
+
+/* TDM control 2 (0x007a) */
+#define RT5682S_IF1_ADC1_SEL_SFT 14
+#define RT5682S_IF1_ADC2_SEL_SFT 12
+#define RT5682S_IF1_ADC3_SEL_SFT 10
+#define RT5682S_IF1_ADC4_SEL_SFT 8
+#define RT5682S_TDM_ADC_SEL_SFT 3
+
+/* TDM control 3 (0x007b) */
+#define RT5682S_TDM_EN (0x1 << 7)
+
+/* TDM/I2S control (0x007e) */
+#define RT5682S_TDM_S_BP_MASK (0x1 << 15)
+#define RT5682S_TDM_S_BP_SFT 15
+#define RT5682S_TDM_S_BP_NOR (0x0 << 15)
+#define RT5682S_TDM_S_BP_INV (0x1 << 15)
+#define RT5682S_TDM_S_LP_MASK (0x1 << 14)
+#define RT5682S_TDM_S_LP_SFT 14
+#define RT5682S_TDM_S_LP_NOR (0x0 << 14)
+#define RT5682S_TDM_S_LP_INV (0x1 << 14)
+#define RT5682S_TDM_DF_MASK (0x7 << 11)
+#define RT5682S_TDM_DF_SFT 11
+#define RT5682S_TDM_DF_I2S (0x0 << 11)
+#define RT5682S_TDM_DF_LEFT (0x1 << 11)
+#define RT5682S_TDM_DF_PCM_A (0x2 << 11)
+#define RT5682S_TDM_DF_PCM_B (0x3 << 11)
+#define RT5682S_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5682S_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5682S_TDM_BCLK_MS1_MASK (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_SFT 8
+#define RT5682S_TDM_BCLK_MS1_32 (0x0 << 8)
+#define RT5682S_TDM_BCLK_MS1_64 (0x1 << 8)
+#define RT5682S_TDM_BCLK_MS1_128 (0x2 << 8)
+#define RT5682S_TDM_BCLK_MS1_256 (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_16 (0x4 << 8)
+#define RT5682S_TDM_CL_MASK (0x3 << 4)
+#define RT5682S_TDM_CL_16 (0x0 << 4)
+#define RT5682S_TDM_CL_20 (0x1 << 4)
+#define RT5682S_TDM_CL_24 (0x2 << 4)
+#define RT5682S_TDM_CL_32 (0x3 << 4)
+#define RT5682S_TDM_M_BP_MASK (0x1 << 2)
+#define RT5682S_TDM_M_BP_SFT 2
+#define RT5682S_TDM_M_BP_NOR (0x0 << 2)
+#define RT5682S_TDM_M_BP_INV (0x1 << 2)
+#define RT5682S_TDM_M_LP_MASK (0x1 << 1)
+#define RT5682S_TDM_M_LP_SFT 1
+#define RT5682S_TDM_M_LP_NOR (0x0 << 1)
+#define RT5682S_TDM_M_LP_INV (0x1 << 1)
+#define RT5682S_TDM_MS_MASK (0x1 << 0)
+#define RT5682S_TDM_MS_SFT 0
+#define RT5682S_TDM_MS_S (0x0 << 0)
+#define RT5682S_TDM_MS_M (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5682S_SCLK_SRC_MASK (0x7 << 13)
+#define RT5682S_SCLK_SRC_SFT 13
+#define RT5682S_PLL_SRC_MASK (0x3 << 8)
+#define RT5682S_PLL_SRC_SFT 8
+#define RT5682S_PLL_SRC_MCLK (0x0 << 8)
+#define RT5682S_PLL_SRC_BCLK1 (0x1 << 8)
+#define RT5682S_PLL_SRC_RC (0x3 << 8)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5682S_DA_ASRC_MASK (0x1 << 13)
+#define RT5682S_DA_ASRC_SFT 13
+#define RT5682S_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5682S_DAC_STO1_ASRC_SFT 12
+#define RT5682S_AD_ASRC_MASK (0x1 << 8)
+#define RT5682S_AD_ASRC_SFT 8
+#define RT5682S_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5682S_AD_ASRC_SEL_SFT 4
+#define RT5682S_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5682S_DMIC_ASRC_SFT 3
+#define RT5682S_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5682S_ADC_STO1_ASRC_SFT 2
+#define RT5682S_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5682S_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5682S_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5682S_FILTER_CLK_SEL_SFT 12
+#define RT5682S_FILTER_CLK_DIV_MASK (0xf << 8)
+#define RT5682S_FILTER_CLK_DIV_SFT 8
+
+/* ASRC Control 4 (0x0086) */
+#define RT5682S_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5682S_ASRCIN_FTK_N1_SFT 14
+#define RT5682S_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5682S_ASRCIN_FTK_N2_SFT 12
+#define RT5682S_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5682S_ASRCIN_FTK_M1_SFT 8
+#define RT5682S_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5682S_ASRCIN_FTK_M2_SFT 4
+
+/* ASRC Control 11 (0x008c) */
+#define RT5682S_ASRCIN_AUTO_CLKOUT_MASK (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_EN (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_DIS (0x0 << 5)
+#define RT5682S_ASRCIN_AUTO_RST_MASK (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_EN (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_DIS (0x0 << 4)
+#define RT5682S_SEL_LRCK_DET_MASK (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV8 (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV4 (0x2)
+#define RT5682S_SEL_LRCK_DET_DIV2 (0x1)
+#define RT5682S_SEL_LRCK_DET_DIV1 (0x0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5682S_OUT_HP_L_EN (0x1 << 6)
+#define RT5682S_OUT_HP_R_EN (0x1 << 5)
+#define RT5682S_LDO_PUMP_EN (0x1 << 4)
+#define RT5682S_LDO_PUMP_EN_SFT 4
+#define RT5682S_PUMP_EN (0x1 << 3)
+#define RT5682S_PUMP_EN_SFT 3
+#define RT5682S_CAPLESS_L_EN (0x1 << 1)
+#define RT5682S_CAPLESS_L_EN_SFT 1
+#define RT5682S_CAPLESS_R_EN (0x1 << 0)
+#define RT5682S_CAPLESS_R_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5682S_RAMP_MASK (0x1 << 12)
+#define RT5682S_RAMP_SFT 12
+#define RT5682S_RAMP_DIS (0x0 << 12)
+#define RT5682S_RAMP_EN (0x1 << 12)
+#define RT5682S_BPS_MASK (0x1 << 11)
+#define RT5682S_BPS_SFT 11
+#define RT5682S_BPS_DIS (0x0 << 11)
+#define RT5682S_BPS_EN (0x1 << 11)
+#define RT5682S_FAST_UPDN_MASK (0x1 << 10)
+#define RT5682S_FAST_UPDN_SFT 10
+#define RT5682S_FAST_UPDN_DIS (0x0 << 10)
+#define RT5682S_FAST_UPDN_EN (0x1 << 10)
+#define RT5682S_VLO_MASK (0x1 << 7)
+#define RT5682S_VLO_SFT 7
+#define RT5682S_VLO_3V (0x0 << 7)
+#define RT5682S_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5682S_OSW_L_MASK (0x1 << 11)
+#define RT5682S_OSW_L_SFT 11
+#define RT5682S_OSW_L_DIS (0x0 << 11)
+#define RT5682S_OSW_L_EN (0x1 << 11)
+#define RT5682S_OSW_R_MASK (0x1 << 10)
+#define RT5682S_OSW_R_SFT 10
+#define RT5682S_OSW_R_DIS (0x0 << 10)
+#define RT5682S_OSW_R_EN (0x1 << 10)
+#define RT5682S_PM_HP_MASK (0x3 << 8)
+#define RT5682S_PM_HP_SFT 8
+#define RT5682S_PM_HP_LV (0x0 << 8)
+#define RT5682S_PM_HP_MV (0x1 << 8)
+#define RT5682S_PM_HP_HV (0x2 << 8)
+
+/* Micbias Control1 (0x93) */
+#define RT5682S_MIC1_OV_MASK (0x3 << 14)
+#define RT5682S_MIC1_OV_SFT 14
+#define RT5682S_MIC1_OV_2V7 (0x0 << 14)
+#define RT5682S_MIC1_OV_2V4 (0x1 << 14)
+#define RT5682S_MIC1_OV_2V25 (0x3 << 14)
+#define RT5682S_MIC1_OV_1V8 (0x4 << 14)
+#define RT5682S_MIC2_OV_MASK (0x3 << 8)
+#define RT5682S_MIC2_OV_SFT 8
+#define RT5682S_MIC2_OV_2V7 (0x0 << 8)
+#define RT5682S_MIC2_OV_2V4 (0x1 << 8)
+#define RT5682S_MIC2_OV_2V25 (0x3 << 8)
+#define RT5682S_MIC2_OV_1V8 (0x4 << 8)
+
+/* Micbias Control2 (0x0094) */
+#define RT5682S_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5682S_PWR_CLK25M_SFT 9
+#define RT5682S_PWR_CLK25M_PD (0x0 << 9)
+#define RT5682S_PWR_CLK25M_PU (0x1 << 9)
+#define RT5682S_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5682S_PWR_CLK1M_SFT 8
+#define RT5682S_PWR_CLK1M_PD (0x0 << 8)
+#define RT5682S_PWR_CLK1M_PU (0x1 << 8)
+
+/* PLL M/N/K Code Control 1 (0x0098) */
+#define RT5682S_PLLA_N_MASK (0x1ff << 0)
+
+/* PLL M/N/K Code Control 2 (0x0099) */
+#define RT5682S_PLLA_M_MASK (0x1f << 8)
+#define RT5682S_PLLA_M_SFT 8
+#define RT5682S_PLLA_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 3 (0x009a) */
+#define RT5682S_PLLB_N_MASK (0x3ff << 0)
+
+/* PLL M/N/K Code Control 4 (0x009b) */
+#define RT5682S_PLLB_M_MASK (0x1f << 8)
+#define RT5682S_PLLB_M_SFT 8
+#define RT5682S_PLLB_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 6 (0x009d) */
+#define RT5682S_PLLB_SEL_PS_MASK (0x1 << 13)
+#define RT5682S_PLLB_SEL_PS_SFT 13
+#define RT5682S_PLLB_BYP_PS_MASK (0x1 << 12)
+#define RT5682S_PLLB_BYP_PS_SFT 12
+#define RT5682S_PLLB_M_BP_MASK (0x1 << 11)
+#define RT5682S_PLLB_M_BP_SFT 11
+#define RT5682S_PLLB_K_BP_MASK (0x1 << 10)
+#define RT5682S_PLLB_K_BP_SFT 10
+#define RT5682S_PLLA_M_BP_MASK (0x1 << 7)
+#define RT5682S_PLLA_M_BP_SFT 7
+#define RT5682S_PLLA_K_BP_MASK (0x1 << 6)
+#define RT5682S_PLLA_K_BP_SFT 6
+
+/* PLL M/N/K Code Control 7 (0x009e) */
+#define RT5682S_PLLB_SRC_MASK (0x1)
+#define RT5682S_PLLB_SRC_DFIN (0x1)
+#define RT5682S_PLLB_SRC_PLLA (0x0)
+
+/* RC Clock Control (0x009f) */
+#define RT5682S_POW_IRQ (0x1 << 15)
+#define RT5682S_POW_JDH (0x1 << 14)
+
+/* I2S2 Master Mode Clock Control 1 (0x00a0) */
+#define RT5682S_I2S2_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S2_M_CLK_SRC_SFT 4
+#define RT5682S_I2S2_M_D_MASK (0xf << 0)
+#define RT5682S_I2S2_M_D_1 (0x0)
+#define RT5682S_I2S2_M_D_2 (0x1)
+#define RT5682S_I2S2_M_D_3 (0x2)
+#define RT5682S_I2S2_M_D_4 (0x3)
+#define RT5682S_I2S2_M_D_6 (0x4)
+#define RT5682S_I2S2_M_D_8 (0x5)
+#define RT5682S_I2S2_M_D_12 (0x6)
+#define RT5682S_I2S2_M_D_16 (0x7)
+#define RT5682S_I2S2_M_D_24 (0x8)
+#define RT5682S_I2S2_M_D_32 (0x9)
+#define RT5682S_I2S2_M_D_48 (0xa)
+#define RT5682S_I2S2_M_D_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5682S_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5682S_JD1_PULSE_EN_SFT 10
+#define RT5682S_JD1_PULSE_DIS (0x0 << 10)
+#define RT5682S_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5682S_JD1_EN_MASK (0x1 << 15)
+#define RT5682S_JD1_EN_SFT 15
+#define RT5682S_JD1_DIS (0x0 << 15)
+#define RT5682S_JD1_EN (0x1 << 15)
+#define RT5682S_JD1_POL_MASK (0x1 << 13)
+#define RT5682S_JD1_POL_NOR (0x0 << 13)
+#define RT5682S_JD1_POL_INV (0x1 << 13)
+#define RT5682S_JD1_IRQ_MASK (0x1 << 10)
+#define RT5682S_JD1_IRQ_LEV (0x0 << 10)
+#define RT5682S_JD1_IRQ_PUL (0x1 << 10)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5682S_IL_IRQ_MASK (0x1 << 7)
+#define RT5682S_IL_IRQ_DIS (0x0 << 7)
+#define RT5682S_IL_IRQ_EN (0x1 << 7)
+#define RT5682S_IL_IRQ_TYPE_MASK (0x1 << 4)
+#define RT5682S_IL_IRQ_LEV (0x0 << 4)
+#define RT5682S_IL_IRQ_PUL (0x1 << 4)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5682S_GP1_PIN_MASK (0x3 << 14)
+#define RT5682S_GP1_PIN_SFT 14
+#define RT5682S_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5682S_GP1_PIN_IRQ (0x1 << 14)
+#define RT5682S_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5682S_GP2_PIN_MASK (0x3 << 12)
+#define RT5682S_GP2_PIN_SFT 12
+#define RT5682S_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5682S_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5682S_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5682S_GP3_PIN_MASK (0x3 << 10)
+#define RT5682S_GP3_PIN_SFT 10
+#define RT5682S_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5682S_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5682S_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5682S_GP4_PIN_MASK (0x3 << 8)
+#define RT5682S_GP4_PIN_SFT 8
+#define RT5682S_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5682S_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5682S_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5682S_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5682S_GP5_PIN_MASK (0x3 << 6)
+#define RT5682S_GP5_PIN_SFT 6
+#define RT5682S_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5682S_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5682S_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5682S_GP6_PIN_MASK (0x1 << 5)
+#define RT5682S_GP6_PIN_SFT 5
+#define RT5682S_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5682S_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5682S_GP1_PF_MASK (0x1 << 15)
+#define RT5682S_GP1_PF_IN (0x0 << 15)
+#define RT5682S_GP1_PF_OUT (0x1 << 15)
+#define RT5682S_GP1_OUT_MASK (0x1 << 14)
+#define RT5682S_GP1_OUT_L (0x0 << 14)
+#define RT5682S_GP1_OUT_H (0x1 << 14)
+#define RT5682S_GP2_PF_MASK (0x1 << 13)
+#define RT5682S_GP2_PF_IN (0x0 << 13)
+#define RT5682S_GP2_PF_OUT (0x1 << 13)
+#define RT5682S_GP2_OUT_MASK (0x1 << 12)
+#define RT5682S_GP2_OUT_L (0x0 << 12)
+#define RT5682S_GP2_OUT_H (0x1 << 12)
+#define RT5682S_GP3_PF_MASK (0x1 << 11)
+#define RT5682S_GP3_PF_IN (0x0 << 11)
+#define RT5682S_GP3_PF_OUT (0x1 << 11)
+#define RT5682S_GP3_OUT_MASK (0x1 << 10)
+#define RT5682S_GP3_OUT_L (0x0 << 10)
+#define RT5682S_GP3_OUT_H (0x1 << 10)
+#define RT5682S_GP4_PF_MASK (0x1 << 9)
+#define RT5682S_GP4_PF_IN (0x0 << 9)
+#define RT5682S_GP4_PF_OUT (0x1 << 9)
+#define RT5682S_GP4_OUT_MASK (0x1 << 8)
+#define RT5682S_GP4_OUT_L (0x0 << 8)
+#define RT5682S_GP4_OUT_H (0x1 << 8)
+#define RT5682S_GP5_PF_MASK (0x1 << 7)
+#define RT5682S_GP5_PF_IN (0x0 << 7)
+#define RT5682S_GP5_PF_OUT (0x1 << 7)
+#define RT5682S_GP5_OUT_MASK (0x1 << 6)
+#define RT5682S_GP5_OUT_L (0x0 << 6)
+#define RT5682S_GP5_OUT_H (0x1 << 6)
+#define RT5682S_GP6_PF_MASK (0x1 << 5)
+#define RT5682S_GP6_PF_IN (0x0 << 5)
+#define RT5682S_GP6_PF_OUT (0x1 << 5)
+#define RT5682S_GP6_OUT_MASK (0x1 << 4)
+#define RT5682S_GP6_OUT_L (0x0 << 4)
+#define RT5682S_GP6_OUT_H (0x1 << 4)
+
+/* GPIO Status (0x00c2) */
+#define RT5682S_GP6_ST (0x1 << 6)
+#define RT5682S_GP5_ST (0x1 << 5)
+#define RT5682S_GP4_ST (0x1 << 4)
+#define RT5682S_GP3_ST (0x1 << 3)
+#define RT5682S_GP2_ST (0x1 << 2)
+#define RT5682S_GP1_ST (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5682S_ZCD_MASK (0x1 << 10)
+#define RT5682S_ZCD_SFT 10
+#define RT5682S_ZCD_PD (0x0 << 10)
+#define RT5682S_ZCD_PU (0x1 << 10)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5682S_4BTN_IL_MASK (0x1 << 15)
+#define RT5682S_4BTN_IL_EN (0x1 << 15)
+#define RT5682S_4BTN_IL_DIS (0x0 << 15)
+#define RT5682S_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5682S_4BTN_IL_NOR (0x1 << 14)
+#define RT5682S_4BTN_IL_RST (0x0 << 14)
+
+/* 4 Button Inline Command Control 3~6 (0x00e5~0x00e8) */
+#define RT5682S_4BTN_IL_HOLD_WIN_MASK (0x7f << 8)
+#define RT5682S_4BTN_IL_HOLD_WIN_SFT 8
+#define RT5682S_4BTN_IL_CLICK_WIN_MASK (0x7f)
+#define RT5682S_4BTN_IL_CLICK_WIN_SFT 0
+
+/* Analog JD Control (0x00f0) */
+#define RT5682S_JDH_RS_MASK (0x1 << 4)
+#define RT5682S_JDH_NO_PLUG (0x1 << 4)
+#define RT5682S_JDH_PLUG (0x0 << 4)
+
+/* Bias current control 7 (0x0110) */
+#define RT5682S_LDO_DACREF_MASK (0x3 << 4)
+#define RT5682S_LDO_DACREF_1_607V (0x0 << 4)
+#define RT5682S_LDO_DACREF_1_5V (0x1 << 4)
+#define RT5682S_LDO_DACREF_1_406V (0x2 << 4)
+#define RT5682S_LDO_DACREF_1_731V (0x3 << 4)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682S_CP_CLK_HP_MASK (0x3 << 4)
+#define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4)
+#define RT5682S_CP_CLK_HP_200KHZ (0x1 << 4)
+#define RT5682S_CP_CLK_HP_300KHZ (0x2 << 4)
+#define RT5682S_CP_CLK_HP_600KHZ (0x3 << 4)
+
+/* Pad Driving Control (0x0136) */
+#define RT5682S_PAD_DRV_GP1_MASK (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_HIGH (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_LOW (0x0 << 14)
+#define RT5682S_PAD_DRV_GP2_MASK (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_HIGH (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_LOW (0x0 << 12)
+#define RT5682S_PAD_DRV_GP3_MASK (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_HIGH (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_LOW (0x0 << 10)
+#define RT5682S_PAD_DRV_GP4_MASK (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_HIGH (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_LOW (0x0 << 8)
+#define RT5682S_PAD_DRV_GP5_MASK (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_HIGH (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_LOW (0x0 << 6)
+#define RT5682S_PAD_DRV_GP6_MASK (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_HIGH (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_LOW (0x0 << 4)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5682S_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_DAC1_SFT 13
+#define RT5682S_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5682S_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_ADC1_SFT 13
+#define RT5682S_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5682S_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5682S_AD2DA_LB_MASK (0x1 << 10)
+#define RT5682S_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5682S_NG2_EN_MASK (0x1 << 15)
+#define RT5682S_NG2_EN (0x1 << 15)
+#define RT5682S_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5682S_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5682S_DEB_80_MS (0x0 << 4)
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682S_HP_SIG_SRC_MASK (0x3)
+#define RT5682S_HP_SIG_SRC_1BIT_CTL (0x3)
+#define RT5682S_HP_SIG_SRC_REG (0x2)
+#define RT5682S_HP_SIG_SRC_IMPE_REG (0x1)
+#define RT5682S_HP_SIG_SRC_DC_CALI (0x0)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5682S_SAR_BUTDET_MASK (0x1 << 15)
+#define RT5682S_SAR_BUTDET_EN (0x1 << 15)
+#define RT5682S_SAR_BUTDET_DIS (0x0 << 15)
+#define RT5682S_SAR_BUTDET_POW_MASK (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5682S_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST_NORM (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST (0x0 << 13)
+#define RT5682S_SAR_POW_MASK (0x1 << 12)
+#define RT5682S_SAR_POW_EN (0x1 << 12)
+#define RT5682S_SAR_POW_DIS (0x0 << 12)
+#define RT5682S_SAR_RST_MASK (0x1 << 11)
+#define RT5682S_SAR_RST_NORMAL (0x1 << 11)
+#define RT5682S_SAR_RST (0x0 << 11)
+#define RT5682S_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5682S_SAR_BYPASS_EN (0x1 << 10)
+#define RT5682S_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5682S_SAR_SEL_MB1_2_MASK (0x3 << 8)
+#define RT5682S_SAR_SEL_MB1_2_SFT 8
+#define RT5682S_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5682S_SAR_SEL_MB1_2_CTL_MASK (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_AUTO (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_MANU (0x0 << 5)
+#define RT5682S_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 2 (0x0211) */
+#define RT5682S_SAR_ADC_PSV_MASK (0x1 << 4)
+#define RT5682S_SAR_ADC_PSV_ENTRY (0x1 << 4)
+
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5682S_SAR_SOUR_MASK (0x3f)
+#define RT5682S_SAR_SOUR_BTN (0x3f)
+#define RT5682S_SAR_SOUR_TYPE (0x0)
+
+/* Headphone Amp Detection Control 1 (0x3b00) */
+#define RT5682S_CP_SW_SIZE_MASK (0x7 << 4)
+#define RT5682S_CP_SW_SIZE_L (0x4 << 4)
+#define RT5682S_CP_SW_SIZE_M (0x2 << 4)
+#define RT5682S_CP_SW_SIZE_S (0x1 << 4)
+
+#define RT5682S_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5682S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+/* System Clock Source */
+enum {
+ RT5682S_SCLK_S_MCLK,
+ RT5682S_SCLK_S_PLL1,
+ RT5682S_SCLK_S_PLL2,
+ RT5682S_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5682S_PLL_S_MCLK,
+ RT5682S_PLL_S_BCLK1,
+ RT5682S_PLL_S_BCLK2,
+ RT5682S_PLL_S_RCCLK,
+};
+
+enum {
+ RT5682S_PLL1,
+ RT5682S_PLL2,
+ RT5682S_PLLS,
+};
+
+enum {
+ RT5682S_AIF1,
+ RT5682S_AIF2,
+ RT5682S_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5682S_DA_STEREO1_FILTER = 0x1,
+ RT5682S_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5682S_CLK_SEL_SYS,
+ RT5682S_CLK_SEL_I2S1_ASRC,
+ RT5682S_CLK_SEL_I2S2_ASRC,
+};
+
+enum {
+ USE_PLLA,
+ USE_PLLB,
+ USE_PLLAB,
+};
+
+struct pll_calc_map {
+ unsigned int freq_in;
+ unsigned int freq_out;
+ int m;
+ int n;
+ int k;
+ bool m_bp;
+ bool k_bp;
+ bool byp_ps;
+ bool sel_ps;
+};
+
+enum {
+ RT5682S_SUPPLY_AVDD,
+ RT5682S_SUPPLY_MICVDD,
+ RT5682S_SUPPLY_DBVDD,
+ RT5682S_SUPPLY_LDO1_IN,
+ RT5682S_NUM_SUPPLIES,
+};
+
+struct rt5682s_priv {
+ struct snd_soc_component *component;
+ struct rt5682s_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+ struct mutex sar_mutex;
+ struct mutex wclk_mutex;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS];
+ struct clk *mclk;
+#endif
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5682S_AIFS];
+ int bclk[RT5682S_AIFS];
+ int master[RT5682S_AIFS];
+
+ int pll_src[RT5682S_PLLS];
+ int pll_in[RT5682S_PLLS];
+ int pll_out[RT5682S_PLLS];
+ int pll_comb;
+
+ int jack_type;
+ unsigned int irq;
+ int irq_work_delay_time;
+ int wclk_enabled;
+};
+
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5682S_H__ */
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
new file mode 100644
index 000000000000..44543c0da177
--- /dev/null
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700-sdw.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt700.h"
+#include "rt700-sdw.h"
+
+static bool rt700_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2000 ... 0x200e:
+ case 0x2012 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2231:
+ case 0x3000 ... 0x3fff:
+ case 0x7000 ... 0x7fff:
+ case 0x8300 ... 0x83ff:
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0x75201a:
+ case 0x752045:
+ case 0x752046:
+ case 0x752048:
+ case 0x75204a:
+ case 0x75206b:
+ case 0x752080:
+ case 0x752081:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt700_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2009:
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x200b ... 0x200e: /* i2c read */
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0xff01:
+ case 0x75201a:
+ case 0x752046:
+ case 0x752080:
+ case 0x752081:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt700->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT700_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg3, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt700->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg2, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt700->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_3, &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_2, &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_1, &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_0, &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
+ __func__, reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt700->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT700_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg3, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == 0xff01) {
+ ret = regmap_write(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg2, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt700_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt700_readable_register,
+ .volatile_reg = rt700_volatile_register,
+ .max_register = 0x755800,
+ .reg_defaults = rt700_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt700_sdw_read,
+ .reg_write = rt700_sdw_write,
+};
+
+static const struct regmap_config rt700_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt700_readable_register,
+ .max_register = 0xff01,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt700_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt700->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt700->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt700_io_init(&slave->dev, slave);
+}
+
+static int rt700_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_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0xA; /* BITMAP: 00001010 */
+
+ 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;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt700_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt700->params, params, sizeof(*params));
+
+ ret = rt700_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+static int rt700_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt700->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt700->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_detect_work, msecs_to_jiffies(250));
+ }
+ mutex_unlock(&rt700->disable_irq_lock);
+
+ 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 rt700_slave_ops = {
+ .read_prop = rt700_read_prop,
+ .interrupt_callback = rt700_interrupt_callback,
+ .update_status = rt700_update_status,
+ .bus_config = rt700_bus_config,
+};
+
+static int rt700_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL,
+ &slave->dev, &rt700_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt700_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt700_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ if (rt700->hw_init) {
+ cancel_delayed_work_sync(&rt700->jack_detect_work);
+ cancel_delayed_work_sync(&rt700->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt700_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x700, 0x1, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt700_id);
+
+static int rt700_dev_suspend(struct device *dev)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+
+ if (!rt700->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt700->jack_detect_work);
+ cancel_delayed_work_sync(&rt700->jack_btn_check_work);
+
+ regcache_cache_only(rt700->regmap, true);
+
+ return 0;
+}
+
+static int rt700_dev_system_suspend(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!rt700->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt700->disable_irq_lock);
+ rt700->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt700->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt700_dev_suspend(dev);
+}
+
+#define RT700_PROBE_TIMEOUT 5000
+
+static int rt700_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt700->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT700_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(rt700->regmap, false);
+ regcache_sync_region(rt700->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt700->regmap, 0x752010, 0x75206b);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt700_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume)
+ RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL)
+};
+
+static struct sdw_driver rt700_sdw_driver = {
+ .driver = {
+ .name = "rt700",
+ .pm = pm_ptr(&rt700_pm),
+ },
+ .probe = rt700_sdw_probe,
+ .remove = rt700_sdw_remove,
+ .ops = &rt700_slave_ops,
+ .id_table = rt700_id,
+};
+module_sdw_driver(rt700_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT700 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h
new file mode 100644
index 000000000000..4ad0dcfd16fd
--- /dev/null
+++ b/sound/soc/codecs/rt700-sdw.h
@@ -0,0 +1,335 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt700-sdw.h -- RT700 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT700_SDW_H__
+#define __RT700_SDW_H__
+
+static const struct reg_default rt700_reg_defaults[] = {
+ { 0x0000, 0x0000 },
+ { 0x0001, 0x0000 },
+ { 0x0002, 0x0000 },
+ { 0x0003, 0x0000 },
+ { 0x0004, 0x0000 },
+ { 0x0005, 0x0001 },
+ { 0x0020, 0x0000 },
+ { 0x0022, 0x0000 },
+ { 0x0023, 0x0000 },
+ { 0x0024, 0x0000 },
+ { 0x0025, 0x0000 },
+ { 0x0026, 0x0000 },
+ { 0x0030, 0x0000 },
+ { 0x0032, 0x0000 },
+ { 0x0033, 0x0000 },
+ { 0x0034, 0x0000 },
+ { 0x0035, 0x0000 },
+ { 0x0036, 0x0000 },
+ { 0x0040, 0x0000 },
+ { 0x0041, 0x0000 },
+ { 0x0042, 0x0000 },
+ { 0x0043, 0x0000 },
+ { 0x0044, 0x0020 },
+ { 0x0045, 0x0001 },
+ { 0x0046, 0x0000 },
+ { 0x0050, 0x0000 },
+ { 0x0051, 0x0000 },
+ { 0x0052, 0x0000 },
+ { 0x0053, 0x0000 },
+ { 0x0054, 0x0000 },
+ { 0x0055, 0x0000 },
+ { 0x0060, 0x0000 },
+ { 0x0070, 0x0000 },
+ { 0x00e0, 0x0000 },
+ { 0x00f0, 0x0000 },
+ { 0x0100, 0x0000 },
+ { 0x0101, 0x0000 },
+ { 0x0102, 0x0000 },
+ { 0x0103, 0x0000 },
+ { 0x0104, 0x0000 },
+ { 0x0105, 0x0000 },
+ { 0x0120, 0x0000 },
+ { 0x0121, 0x0000 },
+ { 0x0122, 0x0000 },
+ { 0x0123, 0x0000 },
+ { 0x0124, 0x0000 },
+ { 0x0125, 0x0000 },
+ { 0x0126, 0x0000 },
+ { 0x0127, 0x0000 },
+ { 0x0130, 0x0000 },
+ { 0x0131, 0x0000 },
+ { 0x0132, 0x0000 },
+ { 0x0133, 0x0000 },
+ { 0x0134, 0x0000 },
+ { 0x0135, 0x0000 },
+ { 0x0136, 0x0000 },
+ { 0x0137, 0x0000 },
+ { 0x0200, 0x0000 },
+ { 0x0201, 0x0000 },
+ { 0x0202, 0x0000 },
+ { 0x0203, 0x0000 },
+ { 0x0204, 0x0000 },
+ { 0x0205, 0x0000 },
+ { 0x0220, 0x0000 },
+ { 0x0221, 0x0000 },
+ { 0x0222, 0x0000 },
+ { 0x0223, 0x0000 },
+ { 0x0224, 0x0000 },
+ { 0x0225, 0x0000 },
+ { 0x0226, 0x0000 },
+ { 0x0227, 0x0000 },
+ { 0x0230, 0x0000 },
+ { 0x0231, 0x0000 },
+ { 0x0232, 0x0000 },
+ { 0x0233, 0x0000 },
+ { 0x0234, 0x0000 },
+ { 0x0235, 0x0000 },
+ { 0x0236, 0x0000 },
+ { 0x0237, 0x0000 },
+ { 0x0300, 0x0000 },
+ { 0x0301, 0x0000 },
+ { 0x0302, 0x0000 },
+ { 0x0303, 0x0000 },
+ { 0x0304, 0x0000 },
+ { 0x0305, 0x0000 },
+ { 0x0320, 0x0000 },
+ { 0x0321, 0x0000 },
+ { 0x0322, 0x0000 },
+ { 0x0323, 0x0000 },
+ { 0x0324, 0x0000 },
+ { 0x0325, 0x0000 },
+ { 0x0326, 0x0000 },
+ { 0x0327, 0x0000 },
+ { 0x0330, 0x0000 },
+ { 0x0331, 0x0000 },
+ { 0x0332, 0x0000 },
+ { 0x0333, 0x0000 },
+ { 0x0334, 0x0000 },
+ { 0x0335, 0x0000 },
+ { 0x0336, 0x0000 },
+ { 0x0337, 0x0000 },
+ { 0x0400, 0x0000 },
+ { 0x0401, 0x0000 },
+ { 0x0402, 0x0000 },
+ { 0x0403, 0x0000 },
+ { 0x0404, 0x0000 },
+ { 0x0405, 0x0000 },
+ { 0x0420, 0x0000 },
+ { 0x0421, 0x0000 },
+ { 0x0422, 0x0000 },
+ { 0x0423, 0x0000 },
+ { 0x0424, 0x0000 },
+ { 0x0425, 0x0000 },
+ { 0x0426, 0x0000 },
+ { 0x0427, 0x0000 },
+ { 0x0430, 0x0000 },
+ { 0x0431, 0x0000 },
+ { 0x0432, 0x0000 },
+ { 0x0433, 0x0000 },
+ { 0x0434, 0x0000 },
+ { 0x0435, 0x0000 },
+ { 0x0436, 0x0000 },
+ { 0x0437, 0x0000 },
+ { 0x0500, 0x0000 },
+ { 0x0501, 0x0000 },
+ { 0x0502, 0x0000 },
+ { 0x0503, 0x0000 },
+ { 0x0504, 0x0000 },
+ { 0x0505, 0x0000 },
+ { 0x0520, 0x0000 },
+ { 0x0521, 0x0000 },
+ { 0x0522, 0x0000 },
+ { 0x0523, 0x0000 },
+ { 0x0524, 0x0000 },
+ { 0x0525, 0x0000 },
+ { 0x0526, 0x0000 },
+ { 0x0527, 0x0000 },
+ { 0x0530, 0x0000 },
+ { 0x0531, 0x0000 },
+ { 0x0532, 0x0000 },
+ { 0x0533, 0x0000 },
+ { 0x0534, 0x0000 },
+ { 0x0535, 0x0000 },
+ { 0x0536, 0x0000 },
+ { 0x0537, 0x0000 },
+ { 0x0600, 0x0000 },
+ { 0x0601, 0x0000 },
+ { 0x0602, 0x0000 },
+ { 0x0603, 0x0000 },
+ { 0x0604, 0x0000 },
+ { 0x0605, 0x0000 },
+ { 0x0620, 0x0000 },
+ { 0x0621, 0x0000 },
+ { 0x0622, 0x0000 },
+ { 0x0623, 0x0000 },
+ { 0x0624, 0x0000 },
+ { 0x0625, 0x0000 },
+ { 0x0626, 0x0000 },
+ { 0x0627, 0x0000 },
+ { 0x0630, 0x0000 },
+ { 0x0631, 0x0000 },
+ { 0x0632, 0x0000 },
+ { 0x0633, 0x0000 },
+ { 0x0634, 0x0000 },
+ { 0x0635, 0x0000 },
+ { 0x0636, 0x0000 },
+ { 0x0637, 0x0000 },
+ { 0x0700, 0x0000 },
+ { 0x0701, 0x0000 },
+ { 0x0702, 0x0000 },
+ { 0x0703, 0x0000 },
+ { 0x0704, 0x0000 },
+ { 0x0705, 0x0000 },
+ { 0x0720, 0x0000 },
+ { 0x0721, 0x0000 },
+ { 0x0722, 0x0000 },
+ { 0x0723, 0x0000 },
+ { 0x0724, 0x0000 },
+ { 0x0725, 0x0000 },
+ { 0x0726, 0x0000 },
+ { 0x0727, 0x0000 },
+ { 0x0730, 0x0000 },
+ { 0x0731, 0x0000 },
+ { 0x0732, 0x0000 },
+ { 0x0733, 0x0000 },
+ { 0x0734, 0x0000 },
+ { 0x0735, 0x0000 },
+ { 0x0736, 0x0000 },
+ { 0x0737, 0x0000 },
+ { 0x0800, 0x0000 },
+ { 0x0801, 0x0000 },
+ { 0x0802, 0x0000 },
+ { 0x0803, 0x0000 },
+ { 0x0804, 0x0000 },
+ { 0x0805, 0x0000 },
+ { 0x0820, 0x0000 },
+ { 0x0821, 0x0000 },
+ { 0x0822, 0x0000 },
+ { 0x0823, 0x0000 },
+ { 0x0824, 0x0000 },
+ { 0x0825, 0x0000 },
+ { 0x0826, 0x0000 },
+ { 0x0827, 0x0000 },
+ { 0x0830, 0x0000 },
+ { 0x0831, 0x0000 },
+ { 0x0832, 0x0000 },
+ { 0x0833, 0x0000 },
+ { 0x0834, 0x0000 },
+ { 0x0835, 0x0000 },
+ { 0x0836, 0x0000 },
+ { 0x0837, 0x0000 },
+ { 0x0f00, 0x0000 },
+ { 0x0f01, 0x0000 },
+ { 0x0f02, 0x0000 },
+ { 0x0f03, 0x0000 },
+ { 0x0f04, 0x0000 },
+ { 0x0f05, 0x0000 },
+ { 0x0f20, 0x0000 },
+ { 0x0f21, 0x0000 },
+ { 0x0f22, 0x0000 },
+ { 0x0f23, 0x0000 },
+ { 0x0f24, 0x0000 },
+ { 0x0f25, 0x0000 },
+ { 0x0f26, 0x0000 },
+ { 0x0f27, 0x0000 },
+ { 0x0f30, 0x0000 },
+ { 0x0f31, 0x0000 },
+ { 0x0f32, 0x0000 },
+ { 0x0f33, 0x0000 },
+ { 0x0f34, 0x0000 },
+ { 0x0f35, 0x0000 },
+ { 0x0f36, 0x0000 },
+ { 0x0f37, 0x0000 },
+ { 0x2000, 0x0000 },
+ { 0x2001, 0x0000 },
+ { 0x2002, 0x0000 },
+ { 0x2003, 0x0000 },
+ { 0x2004, 0x0000 },
+ { 0x2005, 0x0000 },
+ { 0x2006, 0x0000 },
+ { 0x2007, 0x0000 },
+ { 0x2008, 0x0000 },
+ { 0x2009, 0x0003 },
+ { 0x200a, 0x0003 },
+ { 0x200b, 0x0000 },
+ { 0x200c, 0x0000 },
+ { 0x200d, 0x0000 },
+ { 0x200e, 0x0000 },
+ { 0x2012, 0x0000 },
+ { 0x2013, 0x0000 },
+ { 0x2014, 0x0000 },
+ { 0x2015, 0x0000 },
+ { 0x2016, 0x0000 },
+ { 0x201a, 0x0000 },
+ { 0x201b, 0x0000 },
+ { 0x201c, 0x0000 },
+ { 0x201d, 0x0000 },
+ { 0x201e, 0x0000 },
+ { 0x201f, 0x0000 },
+ { 0x2020, 0x0000 },
+ { 0x2021, 0x0000 },
+ { 0x2022, 0x0000 },
+ { 0x2023, 0x0000 },
+ { 0x2024, 0x0000 },
+ { 0x2025, 0x0002 },
+ { 0x2026, 0x0000 },
+ { 0x2027, 0x0000 },
+ { 0x2029, 0x0000 },
+ { 0x202a, 0x0000 },
+ { 0x202d, 0x0000 },
+ { 0x202e, 0x0000 },
+ { 0x202f, 0x0000 },
+ { 0x2030, 0x0000 },
+ { 0x2031, 0x0000 },
+ { 0x2032, 0x0000 },
+ { 0x2033, 0x0000 },
+ { 0x2034, 0x0000 },
+ { 0x2200, 0x0000 },
+ { 0x2201, 0x0000 },
+ { 0x2202, 0x0000 },
+ { 0x2203, 0x0000 },
+ { 0x2204, 0x0000 },
+ { 0x2206, 0x0000 },
+ { 0x2207, 0x0000 },
+ { 0x2208, 0x0000 },
+ { 0x2209, 0x0000 },
+ { 0x220a, 0x0000 },
+ { 0x220b, 0x0000 },
+ { 0x220c, 0x0000 },
+ { 0x220d, 0x0000 },
+ { 0x220e, 0x0000 },
+ { 0x220f, 0x0000 },
+ { 0x2211, 0x0000 },
+ { 0x2212, 0x0000 },
+ { 0x2220, 0x0000 },
+ { 0x2221, 0x0000 },
+ { 0x2222, 0x0000 },
+ { 0x2223, 0x0000 },
+ { 0x2230, 0x0000 },
+ { 0x2231, 0x0000 },
+ { 0x3121, 0x0001 },
+ { 0x3122, 0x0000 },
+ { 0x3123, 0x0000 },
+ { 0x7303, 0x0057 },
+ { 0x7303, 0x0057 },
+ { 0x8383, 0x0057 },
+ { 0x7308, 0x0097 },
+ { 0x8388, 0x0097 },
+ { 0x7309, 0x0097 },
+ { 0x8389, 0x0097 },
+ { 0x7312, 0x0000 },
+ { 0x8392, 0x0000 },
+ { 0x7313, 0x0000 },
+ { 0x8393, 0x0000 },
+ { 0x7319, 0x0000 },
+ { 0x8399, 0x0000 },
+ { 0x75201a, 0x8003 },
+ { 0x752045, 0x5289 },
+ { 0x752048, 0xd049 },
+ { 0x75204a, 0xa83b },
+ { 0x75206b, 0x5064 },
+};
+
+#endif /* __RT700_H__ */
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
new file mode 100644
index 000000000000..816117c13aea
--- /dev/null
+++ b/sound/soc/codecs/rt700.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#include "rt700.h"
+
+static int rt700_index_write(struct regmap *regmap,
+ unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt700_index_read(struct regmap *regmap,
+ unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static unsigned int rt700_button_detect(struct rt700_priv *rt700)
+{
+ unsigned int btn_type = 0, val80, val81;
+ int ret;
+
+ ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE1, &val80);
+ if (ret < 0)
+ goto read_error;
+ ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE2, &val81);
+ if (ret < 0)
+ goto read_error;
+
+ val80 &= 0x0381;
+ val81 &= 0xff00;
+
+ switch (val80) {
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x0001:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+ switch (val81) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0200:
+ case 0x0100:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+read_error:
+ return btn_type;
+}
+
+static int rt700_headset_detect(struct rt700_priv *rt700)
+{
+ unsigned int buf, loop = 0;
+ int ret;
+ unsigned int jack_status = 0, reg;
+
+ ret = rt700_index_read(rt700->regmap,
+ RT700_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ while (loop < 500 &&
+ (buf & RT700_COMBOJACK_AUTO_DET_STATUS) == 0) {
+ loop++;
+
+ usleep_range(9000, 10000);
+ ret = rt700_index_read(rt700->regmap,
+ RT700_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if ((jack_status & (1 << 31)) == 0)
+ goto remove_error;
+ }
+
+ if (loop >= 500)
+ goto to_error;
+
+ if (buf & RT700_COMBOJACK_AUTO_DET_TRS)
+ rt700->jack_type = SND_JACK_HEADPHONE;
+ else if ((buf & RT700_COMBOJACK_AUTO_DET_CTIA) ||
+ (buf & RT700_COMBOJACK_AUTO_DET_OMTP))
+ rt700->jack_type = SND_JACK_HEADSET;
+
+ return 0;
+
+to_error:
+ ret = -ETIMEDOUT;
+ pr_err_ratelimited("Time-out error in %s\n", __func__);
+ return ret;
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+remove_error:
+ pr_err_ratelimited("Jack removal in %s\n", __func__);
+ return -ENODEV;
+}
+
+static void rt700_jack_detect_handler(struct work_struct *work)
+{
+ struct rt700_priv *rt700 =
+ container_of(work, struct rt700_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ if (!rt700->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt700->component->card))
+ return;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ /* jack in */
+ if (rt700->jack_type == 0) {
+ ret = rt700_headset_detect(rt700);
+ if (ret < 0)
+ return;
+ if (rt700->jack_type == SND_JACK_HEADSET)
+ btn_type = rt700_button_detect(rt700);
+ } else if (rt700->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt700_button_detect(rt700);
+ }
+ } else {
+ /* jack out */
+ rt700->jack_type = 0;
+ }
+
+ dev_dbg(&rt700->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt700->jack_type);
+ dev_dbg(&rt700->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt700_btn_check_handler(struct work_struct *work)
+{
+ struct rt700_priv *rt700 = container_of(work, struct rt700_priv,
+ jack_btn_check_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ if (rt700->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt700_button_detect(rt700);
+ }
+ } else {
+ rt700->jack_type = 0;
+ }
+
+ /* cbj comparator */
+ ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, &reg);
+ if (ret < 0)
+ goto io_error;
+
+ if ((reg & 0xf0) == 0xf0)
+ btn_type = 0;
+
+ dev_dbg(&rt700->slave->dev,
+ "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt700_jack_init(struct rt700_priv *rt700)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt700->component);
+
+ /* power on */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ if (rt700->hs_jack) {
+ /* Enable Jack Detection */
+ regmap_write(rt700->regmap,
+ RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82);
+ regmap_write(rt700->regmap,
+ RT700_SET_HP_UNSOLICITED_ENABLE, 0x81);
+ regmap_write(rt700->regmap,
+ RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x83);
+ rt700_index_write(rt700->regmap, 0x10, 0x2420);
+ rt700_index_write(rt700->regmap, 0x19, 0x2e11);
+
+ dev_dbg(&rt700->slave->dev, "in %s enable\n", __func__);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_detect_work, msecs_to_jiffies(250));
+ } else {
+ regmap_write(rt700->regmap,
+ RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt700->regmap,
+ RT700_SET_HP_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt700->regmap,
+ RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x00);
+
+ dev_dbg(&rt700->slave->dev, "in %s disable\n", __func__);
+ }
+
+ /* power off */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+}
+
+static int rt700_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt700->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt700->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt700_jack_init(rt700);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ /* R Channel */
+ *r_val = (val_h << 8);
+ regmap_read(rt700->regmap, addr_l, r_val);
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = (val_h << 8);
+ regmap_read(rt700->regmap, addr_h, l_val);
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl;
+ int i;
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ /* L Channel */
+ if (mc->invert) {
+ /* for mute */
+ val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7;
+ /* keep gain */
+ read_ll = read_ll & 0x7f;
+ val_ll |= read_ll;
+ } else {
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ read_ll = read_ll & 0x80;
+ val_ll |= read_ll;
+ }
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* R Channel */
+ if (mc->invert) {
+ /* for mute */
+ val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7;
+ /* keep gain */
+ read_rl = read_rl & 0x7f;
+ val_lr |= read_rl;
+ } else {
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ read_rl = read_rl & 0x80;
+ val_lr |= read_rl;
+ }
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt700->regmap,
+ addr_h, (val_h << 8 | val_ll));
+ regmap_write(rt700->regmap,
+ addr_l, (val_h << 8 | val_ll));
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt700->regmap,
+ addr_h, (val_h << 8 | val_ll));
+
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt700->regmap,
+ addr_l, (val_h << 8 | val_lr));
+ }
+ /* check result */
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return 0;
+}
+
+static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* for mute status */
+ read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT);
+ read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT);
+ } else {
+ /* for gain */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt700_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume",
+ RT700_SET_GAIN_DAC1_H, RT700_SET_GAIN_DAC1_L,
+ RT700_DIR_OUT_SFT, 0x57, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, out_vol_tlv),
+ SOC_DOUBLE_R_EXT("ADC 08 Capture Switch",
+ RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L,
+ RT700_DIR_IN_SFT, 1, 1,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT("ADC 09 Capture Switch",
+ RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L,
+ RT700_DIR_IN_SFT, 1, 1,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume",
+ RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L,
+ RT700_DIR_IN_SFT, 0x3f, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume",
+ RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L,
+ RT700_DIR_IN_SFT, 0x3f, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("AMIC Volume",
+ RT700_SET_GAIN_AMIC_H, RT700_SET_GAIN_AMIC_L,
+ RT700_DIR_IN_SFT, 3, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, mic_vol_tlv),
+};
+
+static int rt700_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val = 0, nid;
+ int ret;
+
+ if (strstr(ucontrol->id.name, "HPO Mux"))
+ nid = RT700_HP_OUT;
+ else if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT700_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT700_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* vid = 0xf01 */
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt700->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt700_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg, nid;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "HPO Mux"))
+ nid = RT700_HP_OUT;
+ else if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT700_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT700_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt700->regmap, reg, &val2);
+ if (ret < 0)
+ return ret;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ regmap_write(rt700->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt700_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static const struct snd_kcontrol_new rt700_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static const char * const out_mux_text[] = {
+ "Front",
+ "Surround",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_hp_enum, SND_SOC_NOPM, 0, out_mux_text);
+
+static const struct snd_kcontrol_new rt700_hp_mux =
+ SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static int rt700_dac_front_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_dac_surround_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_adc_09_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_adc_08_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_hpo_mux_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val_l = 0x00;
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT700_MUTE_SFT);
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ usleep_range(50000, 55000);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_spk_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val_l = 0x00;
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT700_MUTE_SFT);
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l));
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_DAC_E("DAC Front", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_dac_front_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_dac_surround_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux,
+ rt700_hpo_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt700_spk_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_adc_09_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_adc_08_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt700_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt700_adc23_mux),
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt700_audio_map[] = {
+ {"DAC Front", NULL, "DP1RX"},
+ {"DAC Surround", NULL, "DP3RX"},
+ {"DP2TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"HPO Mux", "Front", "DAC Front"},
+ {"HPO Mux", "Surround", "DAC Surround"},
+ {"HP", NULL, "HPO Mux"},
+ {"SPK PGA", NULL, "DAC Front"},
+ {"SPK", NULL, "SPK PGA"},
+};
+
+static int rt700_probe(struct snd_soc_component *component)
+{
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt700->component = component;
+
+ if (!rt700->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static int rt700_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
+ .probe = rt700_probe,
+ .set_bias_level = rt700_set_bias_level,
+ .controls = rt700_snd_controls,
+ .num_controls = ARRAY_SIZE(rt700_snd_controls),
+ .dapm_widgets = rt700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets),
+ .dapm_routes = rt700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt700_audio_map),
+ .set_jack = rt700_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt700_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 rt700_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt700_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 rt700_priv *rt700 = 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;
+ unsigned int val = 0;
+
+ 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 (!rt700->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* This code assumes port 1 for playback and port 2 for capture */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ switch (dai->id) {
+ case RT700_AIF1:
+ break;
+ case RT700_AIF2:
+ port_config.num += 2;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt700->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 48Khz */
+ regmap_write(rt700->regmap, RT700_DAC_FORMAT_H, val);
+ regmap_write(rt700->regmap, RT700_ADC_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt700_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt700->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt700->slave, sdw_stream);
+ return 0;
+}
+
+#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT700_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 rt700_ops = {
+ .hw_params = rt700_pcm_hw_params,
+ .hw_free = rt700_pcm_hw_free,
+ .set_stream = rt700_set_sdw_stream,
+ .shutdown = rt700_shutdown,
+};
+
+static struct snd_soc_dai_driver rt700_dai[] = {
+ {
+ .name = "rt700-aif1",
+ .id = RT700_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .ops = &rt700_ops,
+ },
+ {
+ .name = "rt700-aif2",
+ .id = RT700_AIF2,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .ops = &rt700_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT700_CLK_FREQ_9600000HZ 9600000
+#define RT700_CLK_FREQ_12000000HZ 12000000
+#define RT700_CLK_FREQ_6000000HZ 6000000
+#define RT700_CLK_FREQ_4800000HZ 4800000
+#define RT700_CLK_FREQ_2400000HZ 2400000
+#define RT700_CLK_FREQ_12288000HZ 12288000
+
+int rt700_clock_config(struct device *dev)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt700->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT700_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT700_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT700_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT700_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT700_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT700_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt700->regmap, 0xe0, value);
+ regmap_write(rt700->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+int rt700_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+
+{
+ struct rt700_priv *rt700;
+ int ret;
+
+ rt700 = devm_kzalloc(dev, sizeof(*rt700), GFP_KERNEL);
+ if (!rt700)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt700);
+ rt700->slave = slave;
+ rt700->sdw_regmap = sdw_regmap;
+ rt700->regmap = regmap;
+
+ regcache_cache_only(rt700->regmap, true);
+
+ mutex_init(&rt700->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt700->jack_detect_work,
+ rt700_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt700->jack_btn_check_work,
+ rt700_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt700->hw_init = false;
+ rt700->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt700,
+ rt700_dai,
+ ARRAY_SIZE(rt700_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+int rt700_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+
+ rt700->disable_irq = false;
+
+ if (rt700->hw_init)
+ return 0;
+
+ regcache_cache_only(rt700->regmap, false);
+ if (rt700->first_hw_init)
+ regcache_cache_bypass(rt700->regmap, true);
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!rt700->first_hw_init)
+ /* PM runtime status is marked as 'active' only when a Slave reports as Attached */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* reset */
+ regmap_write(rt700->regmap, 0xff01, 0x0000);
+ regmap_write(rt700->regmap, 0x7520, 0x001a);
+ regmap_write(rt700->regmap, 0x7420, 0xc003);
+
+ /* power on */
+ regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+ /* Set Pin Widget */
+ regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40);
+ regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40);
+ regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH);
+ regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20);
+
+ /* Set Configuration Default */
+ regmap_write(rt700->regmap, 0x4f12, 0x91);
+ regmap_write(rt700->regmap, 0x4e12, 0xd6);
+ regmap_write(rt700->regmap, 0x4d12, 0x11);
+ regmap_write(rt700->regmap, 0x4c12, 0x20);
+ regmap_write(rt700->regmap, 0x4f13, 0x91);
+ regmap_write(rt700->regmap, 0x4e13, 0xd6);
+ regmap_write(rt700->regmap, 0x4d13, 0x11);
+ regmap_write(rt700->regmap, 0x4c13, 0x21);
+
+ regmap_write(rt700->regmap, 0x4f19, 0x02);
+ regmap_write(rt700->regmap, 0x4e19, 0xa1);
+ regmap_write(rt700->regmap, 0x4d19, 0x90);
+ regmap_write(rt700->regmap, 0x4c19, 0x80);
+
+ /* Enable Line2 */
+ regmap_write(rt700->regmap, 0x371b, 0x40);
+ regmap_write(rt700->regmap, 0x731b, 0xb0);
+ regmap_write(rt700->regmap, 0x839b, 0x00);
+
+ /* Set index */
+ rt700_index_write(rt700->regmap, 0x4a, 0x201b);
+ rt700_index_write(rt700->regmap, 0x45, 0x5089);
+ rt700_index_write(rt700->regmap, 0x6b, 0x5064);
+ rt700_index_write(rt700->regmap, 0x48, 0xd249);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt700->hs_jack)
+ rt700_jack_init(rt700);
+
+ if (rt700->first_hw_init) {
+ regcache_cache_bypass(rt700->regmap, false);
+ regcache_mark_dirty(rt700->regmap);
+ } else
+ rt700->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt700->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT700 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h
new file mode 100644
index 000000000000..491774d207de
--- /dev/null
+++ b/sound/soc/codecs/rt700.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt700.h -- RT700 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT700_H__
+#define __RT700_H__
+
+extern const struct dev_pm_ops rt700_runtime_pm;
+
+struct rt700_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+};
+
+/* NID */
+#define RT700_AUDIO_FUNCTION_GROUP 0x01
+#define RT700_DAC_OUT1 0x02
+#define RT700_DAC_OUT2 0x03
+#define RT700_ADC_IN1 0x09
+#define RT700_ADC_IN2 0x08
+#define RT700_DMIC1 0x12
+#define RT700_DMIC2 0x13
+#define RT700_SPK_OUT 0x14
+#define RT700_MIC2 0x19
+#define RT700_LINE1 0x1a
+#define RT700_LINE2 0x1b
+#define RT700_BEEP 0x1d
+#define RT700_SPDIF 0x1e
+#define RT700_VENDOR_REGISTERS 0x20
+#define RT700_HP_OUT 0x21
+#define RT700_MIXER_IN1 0x22
+#define RT700_MIXER_IN2 0x23
+#define RT700_INLINE_CMD 0x55
+
+/* Index (NID:20h) */
+#define RT700_DAC_DC_CALI_CTL1 0x00
+#define RT700_PARA_VERB_CTL 0x1a
+#define RT700_COMBO_JACK_AUTO_CTL1 0x45
+#define RT700_COMBO_JACK_AUTO_CTL2 0x46
+#define RT700_INLINE_CMD_CTL 0x48
+#define RT700_DIGITAL_MISC_CTRL4 0x4a
+#define RT700_VREFOUT_CTL 0x6b
+#define RT700_FSM_CTL 0x6f
+#define RT700_IRQ_FLAG_TABLE1 0x80
+#define RT700_IRQ_FLAG_TABLE2 0x81
+#define RT700_IRQ_FLAG_TABLE3 0x82
+
+/* Verb */
+#define RT700_VERB_SET_CONNECT_SEL 0x3100
+#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT700_VERB_GET_CONNECT_SEL 0xb100
+#define RT700_VERB_SET_POWER_STATE 0x3500
+#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT700_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT700_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT700_VERB_GET_PIN_SENSE 0xb900
+
+#define RT700_READ_HDA_3 0x2012
+#define RT700_READ_HDA_2 0x2013
+#define RT700_READ_HDA_1 0x2014
+#define RT700_READ_HDA_0 0x2015
+#define RT700_PRIV_INDEX_W_H 0x7520
+#define RT700_PRIV_INDEX_W_L 0x85a0
+#define RT700_PRIV_DATA_W_H 0x7420
+#define RT700_PRIV_DATA_W_L 0x84a0
+#define RT700_PRIV_INDEX_R_H 0x9d20
+#define RT700_PRIV_INDEX_R_L 0xada0
+#define RT700_PRIV_DATA_R_H 0x9c20
+#define RT700_PRIV_DATA_R_L 0xaca0
+#define RT700_DAC_FORMAT_H 0x7203
+#define RT700_DAC_FORMAT_L 0x8283
+#define RT700_ADC_FORMAT_H 0x7209
+#define RT700_ADC_FORMAT_L 0x8289
+#define RT700_SET_AUDIO_POWER_STATE\
+ (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP)
+#define RT700_SET_PIN_DMIC1\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1)
+#define RT700_SET_PIN_DMIC2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2)
+#define RT700_SET_PIN_SPK\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT)
+#define RT700_SET_PIN_HP\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT)
+#define RT700_SET_PIN_MIC2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2)
+#define RT700_SET_PIN_LINE1\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1)
+#define RT700_SET_PIN_LINE2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2)
+#define RT700_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2)
+#define RT700_SET_HP_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT)
+#define RT700_SET_INLINE_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD)
+#define RT700_SET_STREAMID_DAC1\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1)
+#define RT700_SET_STREAMID_DAC2\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2)
+#define RT700_SET_STREAMID_ADC1\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1)
+#define RT700_SET_STREAMID_ADC2\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2)
+#define RT700_SET_GAIN_DAC1_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1)
+#define RT700_SET_GAIN_DAC1_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1)
+#define RT700_SET_GAIN_ADC1_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1)
+#define RT700_SET_GAIN_ADC1_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1)
+#define RT700_SET_GAIN_ADC2_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2)
+#define RT700_SET_GAIN_ADC2_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2)
+#define RT700_SET_GAIN_AMIC_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2)
+#define RT700_SET_GAIN_AMIC_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2)
+#define RT700_SET_GAIN_HP_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT)
+#define RT700_SET_GAIN_HP_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT)
+#define RT700_SET_GAIN_SPK_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT)
+#define RT700_SET_GAIN_SPK_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT)
+#define RT700_SET_EAPD_SPK\
+ (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+#define RT700_EAPD_HIGH 0x2
+#define RT700_EAPD_LOW 0x0
+#define RT700_MUTE_SFT 7
+#define RT700_DIR_IN_SFT 6
+#define RT700_DIR_OUT_SFT 7
+
+enum {
+ RT700_AIF1,
+ RT700_AIF2,
+ RT700_AIFS,
+};
+
+int rt700_io_init(struct device *dev, struct sdw_slave *slave);
+int rt700_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic);
+int rt700_clock_config(struct device *dev);
+#endif /* __RT700_H__ */
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
new file mode 100644
index 000000000000..6eb05871db37
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdw-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "rt711-sdca.h"
+#include "rt711-sdca-sdw.h"
+
+static bool rt711_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x2f01 ... 0x2f0f:
+ case 0x2f30 ... 0x2f36:
+ case 0x2f50 ... 0x2f5a:
+ case 0x2f60:
+ case 0x3200 ... 0x3212:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2200 ... 0x2212: /* i2c debug */
+ case RT711_RC_CAL_STATUS:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x20000ff:
+ case 0x5600000 ... 0x56000ff:
+ case 0x5700000 ... 0x57000ff:
+ case 0x5800000 ... 0x58000ff:
+ case 0x5900000 ... 0x59000ff:
+ case 0x5b00000 ... 0x5b000ff:
+ case 0x5f00000 ... 0x5f000ff:
+ case 0x6100000 ... 0x61000ff:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000046:
+ case 0x2000080:
+ case 0x2000081:
+ case 0x2000083:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x5f00001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt711_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt711_sdca_readable_register,
+ .volatile_reg = rt711_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt711_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt711_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt711_sdca_mbq_readable_register,
+ .volatile_reg = rt711_sdca_mbq_volatile_register,
+ .max_register = 0x40800f12,
+ .reg_defaults = rt711_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt711_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt711->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt711->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt711_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt711_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt711_sdca_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 = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0x8; /* BITMAP: 00001000 */
+
+ 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++;
+ }
+
+ prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop),
+ GFP_KERNEL);
+ if (!prop->dp0_prop)
+ return -ENOMEM;
+
+ prop->dp0_prop->simple_ch_prep_sm = true;
+ prop->dp0_prop->ch_prep_timeout = 10;
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 700;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt711_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x, sdca_cascade=%x", __func__,
+ status->control_port, status->sdca_cascade);
+
+ if (cancel_delayed_work_sync(&rt711->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt711->scp_sdca_stat2)
+ scp_sdca_stat2 = rt711->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt711->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt711->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt711->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt711->scp_sdca_stat2 |= scp_sdca_stat2;
+
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt711->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt711->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(30));
+
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt711->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt711_sdca_slave_ops = {
+ .read_prop = rt711_sdca_read_prop,
+ .interrupt_callback = rt711_sdca_interrupt_callback,
+ .update_status = rt711_sdca_update_status,
+};
+
+static int rt711_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt711_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt711_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt711_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt711_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (rt711->hw_init) {
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt711->calibrate_mutex);
+ mutex_destroy(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt711_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt711_sdca_id);
+
+static int rt711_sdca_dev_suspend(struct device *dev)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+
+ if (!rt711->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+
+ regcache_cache_only(rt711->regmap, true);
+ regcache_cache_only(rt711->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt711_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt711_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt711_sdca->disable_irq_lock);
+ rt711_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt711_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt711_sdca_dev_suspend(dev);
+}
+
+#define RT711_PROBE_TIMEOUT 5000
+
+static int rt711_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt711->disable_irq = false;
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT711_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt711->regmap, false);
+ regcache_sync(rt711->regmap);
+ regcache_cache_only(rt711->mbq_regmap, false);
+ regcache_sync(rt711->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt711_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt711-sdca",
+ .pm = pm_ptr(&rt711_sdca_pm),
+ },
+ .probe = rt711_sdca_sdw_probe,
+ .remove = rt711_sdca_sdw_remove,
+ .ops = &rt711_sdca_slave_ops,
+ .id_table = rt711_sdca_id,
+};
+module_sdw_driver(rt711_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h
new file mode 100644
index 000000000000..0d774e473ab9
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdw-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDW_SDCA_H__
+#define __RT711_SDW_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt711_sdca_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x14 },
+ { 0x2f0f, 0x00 },
+ { 0x2f50, 0x03 },
+ { 0x2f5a, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+};
+
+static const struct reg_default rt711_sdca_mbq_defaults[] = {
+ { 0x2000009, 0x1029 },
+ { 0x2000011, 0x007a },
+ { 0x200001a, 0x8003 },
+ { 0x2000045, 0x5289 },
+ { 0x2000048, 0x8049 },
+ { 0x200004a, 0xa83b },
+ { 0x200006b, 0x5064 },
+ { 0x200006f, 0x058b },
+ { 0x5800000, 0x0008 },
+ { 0x5800001, 0x0000 },
+ { 0x5f00001, 0x000a },
+ { 0x6100000, 0x6100 },
+ { 0x6100035, 0x0060 },
+ { 0x6100036, 0x0029 },
+ { 0x610003f, 0xff12 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+};
+
+#endif /* __RT711_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
new file mode 100644
index 000000000000..16c351779243
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -0,0 +1,1672 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <linux/bitops.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 <sound/tlv.h>
+#include <sound/jack.h>
+
+#include "rt711-sdca.h"
+
+static int rt711_sdca_index_write(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt711->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt711->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt711_sdca_index_read(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt711->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt711->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt711_sdca_index_update_bits(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt711_sdca_index_read(rt711, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt711_sdca_index_write(rt711, nid, reg, tmp);
+}
+
+static void rt711_sdca_reset(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET,
+ RT711_HIDDEN_REG_SW_RESET);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
+static void rt711_sdca_ge_force_jack_type(struct rt711_sdca_priv *rt711, unsigned int det_mode)
+{
+ switch (det_mode) {
+ case 0x00:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x0000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x10, 0x00);
+ break;
+ case 0x03:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x13);
+ break;
+ case 0x05:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8400);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x15);
+ break;
+ }
+}
+
+static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711)
+{
+ unsigned int val, loop_rc = 0, loop_dc = 0;
+ struct device *dev;
+ struct regmap *regmap = rt711->regmap;
+ int chk_cnt = 100;
+ int ret = 0;
+
+ mutex_lock(&rt711->calibrate_mutex);
+ dev = regmap_get_device(regmap);
+
+ regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+ /* RC calibration */
+ if (!(val & 0x40))
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+ RT711_MISC_POWER_CTL0, 0x0010, 0x0010);
+
+ for (loop_rc = 0; loop_rc < chk_cnt && !(val & 0x40); loop_rc++) {
+ usleep_range(10000, 11000);
+ ret = regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_rc == chk_cnt)
+ dev_err(dev, "%s, RC calibration time-out!\n", __func__);
+
+ /* HP calibration by manual mode setting */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0x2000, 0x2000);
+
+ /* Calibration manual mode */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0xf, RT711_CALI_CTL);
+
+ /* reset HP calibration */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST, 0x00);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST,
+ RT711_DAC_DC_FORCE_CALI_RST);
+
+ /* cal_clk_en_reg */
+ if (rt711->hw_ver == RT711_VER_VD0)
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_CLK_EN,
+ RT711_DAC_DC_CALI_CLK_EN);
+
+ /* trigger */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER,
+ RT711_DAC_DC_CALI_TRIGGER);
+
+ /* wait for calibration process */
+ rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT711_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+ ret = -ETIMEDOUT;
+
+_cali_fail_:
+ /* enable impedance sense */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, RT711_FSM_IMP_EN, RT711_FSM_IMP_EN);
+
+ /* release HP-JD and trigger FSM */
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_DIGITAL_MISC_CTRL4, 0x201b);
+
+ mutex_unlock(&rt711->calibrate_mutex);
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt711_sdca_button_detect(struct rt711_sdca_priv *rt711)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+ &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt711->regmap,
+ RT711_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01,
+ RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+
+ return btn_type;
+}
+
+static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711)
+{
+ unsigned int det_mode;
+ int ret;
+
+ rt711_sdca_ge_force_jack_type(rt711, rt711->ge_mode_override);
+
+ /* get detected_mode */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt711->jack_type = 0;
+ break;
+ case 0x03:
+ rt711->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt711->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0),
+ det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt711->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt711_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt711_sdca_priv *rt711 =
+ container_of(work, struct rt711_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt711->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt711->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt711_sdca_headset_detect(rt711);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt711->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt711_sdca_button_detect(rt711);
+
+ if (rt711->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt711->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt711->jack_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt711_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt711_sdca_priv *rt711 =
+ container_of(work, struct rt711_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt711->regmap,
+ RT711_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+ } else
+ rt711->jack_type = 0;
+
+ dev_dbg(&rt711->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711)
+{
+ mutex_lock(&rt711->calibrate_mutex);
+
+ if (rt711->hs_jack) {
+ /* Enable HID1 event & set button RTC mode */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL6, 0x80f0, 0x8000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL2, 0x11dd, 0x11dd);
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL7, 0xffff);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL9, 0xf000, 0x0000);
+
+ /* GE_mode_change_event_en & Hid1_push_button_event_en */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0c00);
+
+ switch (rt711->jd_src) {
+ case RT711_JD1:
+ /* default settings was already for JD1 */
+ break;
+ case RT711_JD2:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+ RT711_JD2_DIGITAL_MODE_SEL);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_100K:
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa47e);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+ RT711_JD2_DIGITAL_MODE_SEL);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_JD2_2PORT_100K_DECODE_MASK | RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_100K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI_OFF);
+ break;
+ default:
+ dev_warn(rt711->component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__);
+ } else {
+ /* disable HID 1/2 event */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0000);
+
+ dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__);
+ }
+
+ mutex_unlock(&rt711->calibrate_mutex);
+}
+
+static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt711_sdca_jack_init(rt711);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt711_sdca_set_gain_put(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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &rvalue);
+
+ /* control value to 2's complement value */
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+ read_l = gain_l_val;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = (gain_l_val * 10) << mc->shift;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag && gain_l_val > mc->shift)
+ gain_l_val = (gain_l_val - mc->shift) * 75;
+ else
+ gain_l_val = (mc->shift - gain_l_val) * 75;
+ gain_l_val <<= 8;
+ gain_l_val /= 100;
+ if (!(adc_vol_flag && read_l > mc->shift)) {
+ gain_l_val = ~gain_l_val;
+ gain_l_val += 1;
+ }
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+ read_r = gain_r_val;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = (gain_r_val * 10) << mc->shift;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag && gain_r_val > mc->shift)
+ gain_r_val = (gain_r_val - mc->shift) * 75;
+ else
+ gain_r_val = (mc->shift - gain_r_val) * 75;
+ gain_r_val <<= 8;
+ gain_r_val /= 100;
+ if (!(adc_vol_flag && read_r > mc->shift)) {
+ gain_r_val = ~gain_r_val;
+ gain_r_val += 1;
+ }
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ /* Lch*/
+ regmap_write(rt711->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt711->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ break;
+ }
+
+ return i == 3 ? -EIO : changed;
+}
+
+static int rt711_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0, neg_flag = 0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+
+ /* 2's complement value to control value */
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = (read_l >> mc->shift) / 10;
+ else { /* ADC/DAC gain */
+ ctl_l = read_l;
+ if (read_l & BIT(15)) {
+ ctl_l = 0xffff & ~(read_l - 1);
+ neg_flag = 1;
+ }
+ ctl_l *= 100;
+ ctl_l >>= 8;
+ if (adc_vol_flag) {
+ if (neg_flag)
+ ctl_l = mc->shift - (ctl_l / 75);
+ else
+ ctl_l = mc->shift + (ctl_l / 75);
+ } else
+ ctl_l = mc->max - (ctl_l / 75);
+ }
+
+ neg_flag = 0;
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = (read_r >> mc->shift) / 10;
+ else { /* ADC/DAC gain */
+ ctl_r = read_r;
+ if (read_r & BIT(15)) {
+ ctl_r = 0xffff & ~(read_r - 1);
+ neg_flag = 1;
+ }
+ ctl_r *= 100;
+ ctl_r >>= 8;
+ if (adc_vol_flag) {
+ if (neg_flag)
+ ctl_r = mc->shift - (ctl_r / 75);
+ else
+ ctl_r = mc->shift + (ctl_r / 75);
+ } else
+ ctl_r = mc->max - (ctl_r / 75);
+ }
+ } else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt711_sdca_set_fu0f_capture_ctl(struct rt711_sdca_priv *rt711)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+ RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+ RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt711_sdca_set_fu1e_capture_ctl(struct rt711_sdca_priv *rt711)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+ RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+ RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt711_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt711->fu1e_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt711->fu1e_mixer_r_mute;
+ return 0;
+}
+
+static int rt711_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt711->fu1e_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt711->fu1e_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt711->fu1e_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt711->fu1e_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt711_sdca_set_fu1e_capture_ctl(rt711);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt711_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt711->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt711->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt711_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt711->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt711->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt711->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt711->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt711_sdca_set_fu0f_capture_ctl(rt711);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt711_sdca_ge_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, item;
+
+ val = (rt711->ge_mode_override >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[0] = item;
+ return 0;
+}
+
+static int rt711_sdca_ge_select_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, change = 0;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ if (rt711->ge_mode_override != val) {
+ rt711->ge_mode_override = val;
+ change = 1;
+ }
+
+ return change;
+}
+
+static const char * const rt711_sdca_ge_select[] = {
+ "Auto",
+ "Headphone",
+ "Headset",
+};
+
+static int rt711_sdca_ge_select_values[] = {
+ 0,
+ 3,
+ 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt711_sdca_ge_mode_enum, SND_SOC_NOPM,
+ 0, 0x7, rt711_sdca_ge_select, rt711_sdca_ge_select_values);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x57, 0x57, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, out_vol_tlv),
+ SOC_DOUBLE_EXT("FU1E Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt711_sdca_fu1e_capture_get, rt711_sdca_fu1e_capture_put),
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt711_sdca_fu0f_capture_get, rt711_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x17, 0x3f, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x17, 0x3f, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU44 Gain Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+ 8, 3, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU15 Gain Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+ 8, 3, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+ SOC_ENUM_EXT("GE49 Selected Mode", rt711_sdca_ge_mode_enum,
+ rt711_sdca_ge_select_get, rt711_sdca_ge_select_put),
+};
+
+static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 10;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 13;
+ else
+ return -EINVAL;
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt711_sdca_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 10;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 13;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, &val2);
+ val2 = (val2 >> mask_sft) & 0x7;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, 0x7 << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt711_sdca_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum,
+ rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt711_sdca_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum,
+ rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static int rt711_sdca_fu05_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_L),
+ unmute);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_R),
+ unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_L),
+ mute);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_R),
+ mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_fu0f_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt711->fu0f_dapm_mute = false;
+ rt711_sdca_set_fu0f_capture_ctl(rt711);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt711->fu0f_dapm_mute = true;
+ rt711_sdca_set_fu0f_capture_ctl(rt711);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_fu1e_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt711->fu1e_dapm_mute = false;
+ rt711_sdca_set_fu1e_capture_ctl(rt711);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt711->fu1e_dapm_mute = true;
+ rt711_sdca_set_fu1e_capture_ctl(rt711);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde28_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde29_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde2a_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_line1_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ static unsigned int sel_mode = 0xffff;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ &sel_mode);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x1);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ 0x7);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x0);
+ if (sel_mode != 0xffff)
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ sel_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int rt711_sdca_line2_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x1);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x0);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt711_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_PGA_E("LINE1 Power", SND_SOC_NOPM,
+ 0, 0, NULL, 0, rt711_sdca_line1_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE2 Power", SND_SOC_NOPM,
+ 0, 0, NULL, 0, rt711_sdca_line2_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PDE 28", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde28_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 29", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde29_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 2A", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde2a_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu05_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu0f_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_sdca_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_sdca_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt711_sdca_audio_map[] = {
+ {"FU 05", NULL, "DP3RX"},
+ {"DP2TX", NULL, "FU 0F"},
+ {"DP4TX", NULL, "FU 1E"},
+
+ {"LINE1 Power", NULL, "LINE1"},
+ {"LINE2 Power", NULL, "LINE2"},
+ {"HP", NULL, "PDE 28"},
+ {"FU 0F", NULL, "PDE 29"},
+ {"FU 1E", NULL, "PDE 2A"},
+
+ {"FU 0F", NULL, "ADC 22 Mux"},
+ {"FU 1E", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1 Power"},
+ {"ADC 22 Mux", "LINE2", "LINE2 Power"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1 Power"},
+ {"ADC 23 Mux", "LINE2", "LINE2 Power"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+
+ {"HP", NULL, "FU 05"},
+};
+
+static int rt711_sdca_parse_dt(struct rt711_sdca_priv *rt711, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt711->jd_src);
+
+ return 0;
+}
+
+static int rt711_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711_sdca_parse_dt(rt711, &rt711->slave->dev);
+ rt711->component = component;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
+ .probe = rt711_sdca_probe,
+ .controls = rt711_sdca_snd_controls,
+ .num_controls = ARRAY_SIZE(rt711_sdca_snd_controls),
+ .dapm_widgets = rt711_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt711_sdca_dapm_widgets),
+ .dapm_routes = rt711_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
+ .set_jack = rt711_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt711_sdca_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 rt711_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt711_sdca_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 rt711_sdca_priv *rt711 = 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;
+ 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 (!rt711->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 3;
+ } else {
+ if (dai->id == RT711_AIF1)
+ port_config.num = 2;
+ else if (dai->id == RT711_AIF2)
+ port_config.num = 4;
+ else
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt711->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT711_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT711_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT711_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT711_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS11, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_CS1F, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
+ return 0;
+}
+
+#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt711_sdca_ops = {
+ .hw_params = rt711_sdca_pcm_hw_params,
+ .hw_free = rt711_sdca_pcm_hw_free,
+ .set_stream = rt711_sdca_set_sdw_stream,
+ .shutdown = rt711_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt711_sdca_dai[] = {
+ {
+ .name = "rt711-sdca-aif1",
+ .id = RT711_AIF1,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_sdca_ops,
+ },
+ {
+ .name = "rt711-sdca-aif2",
+ .id = RT711_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_sdca_ops,
+ }
+};
+
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711;
+ int ret;
+
+ rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL);
+ if (!rt711)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt711);
+ rt711->slave = slave;
+ rt711->regmap = regmap;
+ rt711->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt711->regmap, true);
+ regcache_cache_only(rt711->mbq_regmap, true);
+
+ mutex_init(&rt711->calibrate_mutex);
+ mutex_init(&rt711->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt711->hw_init = false;
+ rt711->first_hw_init = false;
+ rt711->fu0f_dapm_mute = true;
+ rt711->fu1e_dapm_mute = true;
+ rt711->fu0f_mixer_l_mute = rt711->fu0f_mixer_r_mute = true;
+ rt711->fu1e_mixer_l_mute = rt711->fu1e_mixer_r_mute = true;
+
+ /* JD source uses JD2 in default */
+ rt711->jd_src = RT711_JD2;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt711,
+ rt711_sdca_dai,
+ ARRAY_SIZE(rt711_sdca_dai));
+
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_GPIO_TEST_MODE_CTL2, 0x0e00);
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_GPIO_CTL, 0x0008);
+
+ regmap_write(rt711->regmap, 0x2f5a, 0x01);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_ADC27_VOL_SET, 0x8728);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa472);
+
+ regmap_write(rt711->regmap, 0x2f50, 0x02);
+
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+ RT711_MISC_POWER_CTL4, 0x6000, 0x6000);
+
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0x000c, 0x000c);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+ RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+static void rt711_sdca_vd1_io_init(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_UNSOLICITED_CTL, 0x0300, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa43e);
+
+ regmap_write(rt711->regmap, 0x2f5a, 0x05);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTRL6, 0x0500);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_DMIC_CTL1, 0x6173);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+ RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned int val;
+
+ rt711->disable_irq = false;
+
+ if (rt711->hw_init)
+ return 0;
+
+ regcache_cache_only(rt711->regmap, false);
+ regcache_cache_only(rt711->mbq_regmap, false);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, true);
+ regcache_cache_bypass(rt711->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt711_sdca_reset(rt711);
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_REG, RT711_JD_PRODUCT_NUM, &val);
+ rt711->hw_ver = val & 0xf;
+
+ if (rt711->hw_ver == RT711_VER_VD0)
+ rt711_sdca_vd0_io_init(rt711);
+ else
+ rt711_sdca_vd1_io_init(rt711);
+
+ /* DP4 mux select from 08_filter_Out_pri */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FILTER_SRC_SEL, 0x1800, 0x0800);
+
+ /* ge_exclusive_inbox_en disable */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL0, 0x20, 0x00);
+
+ /* calibration */
+ ret = rt711_sdca_calibration(rt711);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+
+ /* HP output enable */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_OT1, RT711_SDCA_CTL_VENDOR_DEF, 0), 0x4);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt711->hs_jack)
+ rt711_sdca_jack_init(rt711);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, false);
+ regcache_mark_dirty(rt711->regmap);
+ regcache_cache_bypass(rt711->mbq_regmap, false);
+ regcache_mark_dirty(rt711->mbq_regmap);
+ } else
+ rt711->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt711->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
new file mode 100644
index 000000000000..15263dcb0314
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDCA_H__
+#define __RT711_SDCA_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt711_sdca_priv {
+ struct regmap *regmap, *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ struct mutex disable_irq_lock; /* SDCA irq lock protection */
+ bool disable_irq;
+ int jack_type, jd_src;
+ unsigned int scp_sdca_stat1, scp_sdca_stat2;
+ int hw_ver;
+ bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute;
+ bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute;
+ unsigned int ge_mode_override;
+};
+
+/* NID */
+#define RT711_AUDIO_FUNCTION_GROUP 0x01
+#define RT711_DAC_OUT2 0x03
+#define RT711_ADC_IN1 0x09
+#define RT711_ADC_IN2 0x08
+#define RT711_DMIC1 0x12
+#define RT711_DMIC2 0x13
+#define RT711_MIC2 0x19
+#define RT711_LINE1 0x1a
+#define RT711_LINE2 0x1b
+#define RT711_BEEP 0x1d
+#define RT711_VENDOR_REG 0x20
+#define RT711_HP_OUT 0x21
+#define RT711_MIXER_IN1 0x22
+#define RT711_MIXER_IN2 0x23
+#define RT711_INLINE_CMD 0x55
+#define RT711_VENDOR_CALI 0x58
+#define RT711_VENDOR_IMS_DRE 0x5b
+#define RT711_VENDOR_VAD 0x5e
+#define RT711_VENDOR_ANALOG_CTL 0x5f
+#define RT711_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT711_JD_PRODUCT_NUM 0x00
+#define RT711_DMIC_CTL1 0x06
+#define RT711_JD_CTL1 0x08
+#define RT711_JD_CTL2 0x09
+#define RT711_CC_DET1 0x11
+#define RT711_PARA_VERB_CTL 0x1a
+#define RT711_COMBO_JACK_AUTO_CTL1 0x45
+#define RT711_COMBO_JACK_AUTO_CTL2 0x46
+#define RT711_COMBO_JACK_AUTO_CTL3 0x47
+#define RT711_INLINE_CMD_CTL 0x48
+#define RT711_DIGITAL_MISC_CTRL4 0x4a
+#define RT711_JD_CTRL6 0x6a
+#define RT711_VREFOUT_CTL 0x6b
+#define RT711_GPIO_TEST_MODE_CTL2 0x6d
+#define RT711_FSM_CTL 0x6f
+#define RT711_IRQ_FLAG_TABLE1 0x80
+#define RT711_IRQ_FLAG_TABLE2 0x81
+#define RT711_IRQ_FLAG_TABLE3 0x82
+#define RT711_HP_FSM_CTL 0x83
+#define RT711_TX_RX_MUX_CTL 0x91
+#define RT711_FILTER_SRC_SEL 0xb0
+#define RT711_ADC27_VOL_SET 0xb7
+
+/* Index (NID:58h) */
+#define RT711_DAC_DC_CALI_CTL1 0x00
+#define RT711_DAC_DC_CALI_CTL2 0x01
+
+/* Index (NID:5bh) */
+#define RT711_IMS_DIGITAL_CTL1 0x00
+#define RT711_HP_IMS_RESULT_L 0x20
+#define RT711_HP_IMS_RESULT_R 0x21
+
+/* Index (NID:5eh) */
+#define RT711_VAD_SRAM_CTL1 0x10
+
+/* Index (NID:5fh) */
+#define RT711_MISC_POWER_CTL0 0x01
+#define RT711_MISC_POWER_CTL4 0x05
+
+/* Index (NID:61h) */
+#define RT711_HDA_LEGACY_MUX_CTL1 0x00
+#define RT711_HDA_LEGACY_UNSOLICITED_CTL 0x03
+#define RT711_HDA_LEGACY_CONFIG_CTL 0x06
+#define RT711_HDA_LEGACY_RESET_CTL 0x08
+#define RT711_HDA_LEGACY_GPIO_CTL 0x0a
+#define RT711_ADC08_09_PDE_CTL 0x24
+#define RT711_GE_MODE_RELATED_CTL 0x35
+#define RT711_PUSH_BTN_INT_CTL0 0x36
+#define RT711_PUSH_BTN_INT_CTL1 0x37
+#define RT711_PUSH_BTN_INT_CTL2 0x38
+#define RT711_PUSH_BTN_INT_CTL6 0x3c
+#define RT711_PUSH_BTN_INT_CTL7 0x3d
+#define RT711_PUSH_BTN_INT_CTL9 0x3f
+
+/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
+#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
+#define RT711_DAC_DC_CALI_CLK_EN (0x1 << 14)
+#define RT711_DAC_DC_FORCE_CALI_RST (0x1 << 3)
+
+/* jack detect control 1 (0x08)(NID:20h) */
+#define RT711_JD2_DIGITAL_MODE_SEL (0x1 << 1)
+
+/* jack detect control 2 (0x09)(NID:20h) */
+#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_JD2_2PORT_100K_DECODE_MASK (0x1 << 12)
+#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12)
+#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
+#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
+
+/* CC DET1 (0x11)(NID:20h) */
+#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
+#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+#define RT711_POW_CC1_AGPI (0x1 << 5)
+#define RT711_POW_CC1_AGPI_ON (0x1 << 5)
+#define RT711_POW_CC1_AGPI_OFF (0x0 << 5)
+
+/* Parameter & Verb control (0x1a)(NID:20h) */
+#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* FSM control (0x6f)(NID:20h) */
+#define RT711_CALI_CTL (0x0 << 0)
+#define RT711_COMBOJACK_CTL (0x1 << 0)
+#define RT711_IMS_CTL (0x2 << 0)
+#define RT711_DEPOP_CTL (0x3 << 0)
+#define RT711_FSM_IMP_EN (0x1 << 6)
+
+/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
+#define RT711_TRIGGER_IMS (0x1 << 15)
+#define RT711_IMS_EN (0x1 << 6)
+
+#define RT711_EAPD_HIGH 0x2
+#define RT711_EAPD_LOW 0x0
+#define RT711_MUTE_SFT 7
+/* set input/output mapping to payload[14][15] separately */
+#define RT711_DIR_IN_SFT 6
+#define RT711_DIR_OUT_SFT 7
+
+/* RC Calibration register */
+#define RT711_RC_CAL_STATUS 0x320c
+
+/* Buffer address for HID */
+#define RT711_BUF_ADDR_HID1 0x44030000
+#define RT711_BUF_ADDR_HID2 0x44030020
+
+/* RT711 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+
+/* RT711 SDCA entity */
+#define RT711_SDCA_ENT_HID01 0x01
+#define RT711_SDCA_ENT_GE49 0x49
+#define RT711_SDCA_ENT_USER_FU05 0x05
+#define RT711_SDCA_ENT_USER_FU0F 0x0f
+#define RT711_SDCA_ENT_USER_FU1E 0x1e
+#define RT711_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT711_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT711_SDCA_ENT_PDE28 0x28
+#define RT711_SDCA_ENT_PDE29 0x29
+#define RT711_SDCA_ENT_PDE2A 0x2a
+#define RT711_SDCA_ENT_CS01 0x01
+#define RT711_SDCA_ENT_CS11 0x11
+#define RT711_SDCA_ENT_CS1F 0x1f
+#define RT711_SDCA_ENT_OT1 0x06
+#define RT711_SDCA_ENT_LINE1 0x09
+#define RT711_SDCA_ENT_LINE2 0x31
+#define RT711_SDCA_ENT_PDELINE2 0x36
+#define RT711_SDCA_ENT_USER_FU9 0x41
+
+/* RT711 SDCA control */
+#define RT711_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT711_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT711_SDCA_CTL_FU_MUTE 0x01
+#define RT711_SDCA_CTL_FU_VOLUME 0x02
+#define RT711_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT711_SDCA_CTL_SELECTED_MODE 0x01
+#define RT711_SDCA_CTL_DETECTED_MODE 0x02
+#define RT711_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT711_SDCA_CTL_VENDOR_DEF 0x30
+
+/* RT711 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* sample frequency index */
+#define RT711_SDCA_RATE_44100HZ 0x08
+#define RT711_SDCA_RATE_48000HZ 0x09
+#define RT711_SDCA_RATE_96000HZ 0x0b
+#define RT711_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT711_AIF1,
+ RT711_AIF2,
+ RT711_AIFS,
+};
+
+enum rt711_sdca_jd_src {
+ RT711_JD_NULL,
+ RT711_JD1,
+ RT711_JD2,
+ RT711_JD2_100K
+};
+
+enum rt711_sdca_ver {
+ RT711_VER_VD0,
+ RT711_VER_VD1
+};
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt711_sdca_jack_detect(struct rt711_sdca_priv *rt711, bool *hp, bool *mic);
+#endif /* __RT711_SDCA_H__ */
diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c
new file mode 100644
index 000000000000..93a5a89a96b1
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711-sdw.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt711.h"
+#include "rt711-sdw.h"
+
+static bool rt711_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2012 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2201 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x2f01 ... 0x2f0f:
+ case 0x3000 ... 0x3fff:
+ case 0x7000 ... 0x7fff:
+ case 0x8300 ... 0x83ff:
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0x752008:
+ case 0x752009:
+ case 0x75200b:
+ case 0x752011:
+ case 0x75201a:
+ case 0x752045:
+ case 0x752046:
+ case 0x752048:
+ case 0x75204a:
+ case 0x75206b:
+ case 0x75206f:
+ case 0x752080:
+ case 0x752081:
+ case 0x752091:
+ case 0x755800:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0xff01:
+ case 0x75201a:
+ case 0x752046:
+ case 0x752080:
+ case 0x752081:
+ case 0x755800:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt711->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT711_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg3, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt711->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg2, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt711->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_3, &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_2, &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_1, &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_0, &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
+ __func__, reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt711->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT711_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg3, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == RT711_FUNC_RESET) {
+ ret = regmap_write(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg2, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt711_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt711_readable_register,
+ .volatile_reg = rt711_volatile_register,
+ .max_register = 0x755800,
+ .reg_defaults = rt711_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt711_sdw_read,
+ .reg_write = rt711_sdw_write,
+};
+
+static const struct regmap_config rt711_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt711_readable_register,
+ .max_register = 0xff01,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt711_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt711->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt711_io_init(&slave->dev, slave);
+}
+
+static int rt711_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_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0x8; /* BITMAP: 00001000 */
+
+ 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;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt711_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt711->params, params, sizeof(*params));
+
+ ret = rt711_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return ret;
+}
+
+static int rt711_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt711->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt711->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(250));
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt711_slave_ops = {
+ .read_prop = rt711_read_prop,
+ .interrupt_callback = rt711_interrupt_callback,
+ .update_status = rt711_update_status,
+ .bus_config = rt711_bus_config,
+};
+
+static int rt711_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL,
+ &slave->dev, &rt711_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt711_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt711_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (rt711->hw_init) {
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ cancel_work_sync(&rt711->calibration_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt711->calibrate_mutex);
+ mutex_destroy(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt711_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt711_id);
+
+static int rt711_dev_suspend(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+
+ if (!rt711->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ cancel_work_sync(&rt711->calibration_work);
+
+ regcache_cache_only(rt711->regmap, true);
+
+ return 0;
+}
+
+static int rt711_dev_system_suspend(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!rt711->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt711->disable_irq_lock);
+ rt711->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt711_dev_suspend(dev);
+}
+
+#define RT711_PROBE_TIMEOUT 5000
+
+static int rt711_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF);
+ rt711->disable_irq = false;
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT711_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt711->regmap, false);
+ regcache_sync_region(rt711->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt711->regmap, 0x752009, 0x752091);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt711_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume)
+ RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdw_driver = {
+ .driver = {
+ .name = "rt711",
+ .pm = pm_ptr(&rt711_pm),
+ },
+ .probe = rt711_sdw_probe,
+ .remove = rt711_sdw_remove,
+ .ops = &rt711_slave_ops,
+ .id_table = rt711_id,
+};
+module_sdw_driver(rt711_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT711 SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h
new file mode 100644
index 000000000000..6acf9858330d
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdw.h
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt711-sdw.h -- RT711 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDW_H__
+#define __RT711_SDW_H__
+
+static const struct reg_default rt711_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 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5d },
+ { 0x0053, 0x07 },
+ { 0x0054, 0x11 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x0080, 0xc0 },
+ { 0x0088, 0x00 },
+ { 0x00e0, 0x00 },
+ { 0x00e1, 0x00 },
+ { 0x00e2, 0x00 },
+ { 0x00e3, 0x00 },
+ { 0x00e5, 0x00 },
+ { 0x00ee, 0x00 },
+ { 0x00ef, 0x00 },
+ { 0x00f0, 0x00 },
+ { 0x00f1, 0x00 },
+ { 0x00f2, 0x00 },
+ { 0x00f3, 0x00 },
+ { 0x00f4, 0x00 },
+ { 0x00f5, 0x00 },
+ { 0x00fe, 0x00 },
+ { 0x00ff, 0x00 },
+ { 0x0100, 0x00 },
+ { 0x0101, 0x00 },
+ { 0x0102, 0x00 },
+ { 0x0103, 0x00 },
+ { 0x0104, 0x00 },
+ { 0x0105, 0x00 },
+ { 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 },
+ { 0x0300, 0x00 },
+ { 0x0301, 0x00 },
+ { 0x0302, 0x20 },
+ { 0x0303, 0x00 },
+ { 0x0304, 0x00 },
+ { 0x0305, 0x03 },
+ { 0x0320, 0x00 },
+ { 0x0322, 0x00 },
+ { 0x0323, 0x00 },
+ { 0x0324, 0x00 },
+ { 0x0325, 0x00 },
+ { 0x0326, 0x00 },
+ { 0x0327, 0x00 },
+ { 0x0330, 0x00 },
+ { 0x0332, 0x00 },
+ { 0x0333, 0x00 },
+ { 0x0334, 0x00 },
+ { 0x0335, 0x00 },
+ { 0x0336, 0x00 },
+ { 0x0337, 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, 0x20 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0x03 },
+ { 0x0f06, 0x00 },
+ { 0x0f07, 0x00 },
+ { 0x0f08, 0x00 },
+ { 0x0f09, 0x00 },
+ { 0x0f10, 0x00 },
+ { 0x0f11, 0x00 },
+ { 0x0f12, 0x00 },
+ { 0x0f13, 0x00 },
+ { 0x0f14, 0x00 },
+ { 0x0f15, 0x00 },
+ { 0x0f16, 0x00 },
+ { 0x0f17, 0x00 },
+ { 0x0f18, 0x00 },
+ { 0x0f19, 0x00 },
+ { 0x0f1a, 0x00 },
+ { 0x0f1b, 0x00 },
+ { 0x0f1c, 0x00 },
+ { 0x0f1d, 0x00 },
+ { 0x0f1e, 0x00 },
+ { 0x0f1f, 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 },
+ { 0x2012, 0x00 },
+ { 0x2013, 0x00 },
+ { 0x2014, 0x00 },
+ { 0x2015, 0x00 },
+ { 0x2016, 0x00 },
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x0c },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2201, 0xc7 },
+ { 0x2202, 0x0c },
+ { 0x2203, 0x22 },
+ { 0x2204, 0x04 },
+ { 0x2206, 0x00 },
+ { 0x2207, 0x00 },
+ { 0x2208, 0x00 },
+ { 0x2209, 0x00 },
+ { 0x220a, 0x00 },
+ { 0x220b, 0x00 },
+ { 0x220c, 0x00 },
+ { 0x220d, 0x04 },
+ { 0x220e, 0x00 },
+ { 0x220f, 0x00 },
+ { 0x2211, 0x01 },
+ { 0x2212, 0x00 },
+ { 0x2220, 0x00 },
+ { 0x2221, 0x00 },
+ { 0x2222, 0x00 },
+ { 0x2223, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f07, 0xcf },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x00 },
+ { 0x2f0f, 0x00 },
+ { 0x3122, 0x00 },
+ { 0x3123, 0x00 },
+ { 0x7303, 0x57 },
+ { 0x8383, 0x57 },
+ { 0x7308, 0x97 },
+ { 0x8388, 0x97 },
+ { 0x7309, 0x97 },
+ { 0x8389, 0x97 },
+ { 0x7312, 0x00 },
+ { 0x8392, 0x00 },
+ { 0x7313, 0x00 },
+ { 0x8393, 0x00 },
+ { 0x7319, 0x00 },
+ { 0x8399, 0x00 },
+ { 0x752008, 0xa807 },
+ { 0x752009, 0x1029 },
+ { 0x75200b, 0x7770 },
+ { 0x752011, 0x007a },
+ { 0x75201a, 0x8003 },
+ { 0x752045, 0x5289 },
+ { 0x752048, 0xd049 },
+ { 0x75204a, 0xa83b },
+ { 0x75206b, 0x5064 },
+ { 0x75206f, 0x058b },
+ { 0x752091, 0x0000 },
+};
+
+#endif /* __RT711_SDW_H__ */
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
new file mode 100644
index 000000000000..af3a49aee618
--- /dev/null
+++ b/sound/soc/codecs/rt711.c
@@ -0,0 +1,1341 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#include "rt711.h"
+
+static int rt711_index_write(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt711_index_read(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt711_index_update_bits(struct regmap *regmap, unsigned int nid,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp, orig;
+ int ret;
+
+ ret = rt711_index_read(regmap, nid, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return rt711_index_write(regmap, nid, reg, tmp);
+}
+
+static void rt711_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT711_FUNC_RESET, 0);
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG,
+ RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET,
+ RT711_HIDDEN_REG_SW_RESET);
+}
+
+static int rt711_calibration(struct rt711_priv *rt711)
+{
+ unsigned int val, loop = 0;
+ struct device *dev;
+ struct regmap *regmap = rt711->regmap;
+ int ret = 0;
+
+ mutex_lock(&rt711->calibrate_mutex);
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ dev = regmap_get_device(regmap);
+
+ /* Calibration manual mode */
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL,
+ 0xf, 0x0);
+
+ /* trigger */
+ rt711_index_update_bits(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER,
+ RT711_DAC_DC_CALI_TRIGGER);
+
+ /* wait for calibration process */
+ rt711_index_read(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+
+ while (val & RT711_DAC_DC_CALI_TRIGGER) {
+ if (loop >= 500) {
+ pr_err("%s, calibration time-out!\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ loop++;
+
+ usleep_range(10000, 11000);
+ rt711_index_read(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+ }
+
+ /* depop mode */
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL);
+
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt711_button_detect(struct rt711_priv *rt711)
+{
+ unsigned int btn_type = 0, val80, val81;
+ int ret;
+
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_IRQ_FLAG_TABLE1, &val80);
+ if (ret < 0)
+ goto read_error;
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_IRQ_FLAG_TABLE2, &val81);
+ if (ret < 0)
+ goto read_error;
+
+ val80 &= 0x0381;
+ val81 &= 0xff00;
+
+ switch (val80) {
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x0001:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+ switch (val81) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0200:
+ case 0x0100:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+read_error:
+ return btn_type;
+}
+
+static int rt711_headset_detect(struct rt711_priv *rt711)
+{
+ unsigned int buf, loop = 0;
+ int ret;
+ unsigned int jack_status = 0, reg;
+
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ while (loop < 500 &&
+ (buf & RT711_COMBOJACK_AUTO_DET_STATUS) == 0) {
+ loop++;
+
+ usleep_range(9000, 10000);
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+ if ((jack_status & (1 << 31)) == 0)
+ goto remove_error;
+ }
+
+ if (loop >= 500)
+ goto to_error;
+
+ if (buf & RT711_COMBOJACK_AUTO_DET_TRS)
+ rt711->jack_type = SND_JACK_HEADPHONE;
+ else if ((buf & RT711_COMBOJACK_AUTO_DET_CTIA) ||
+ (buf & RT711_COMBOJACK_AUTO_DET_OMTP))
+ rt711->jack_type = SND_JACK_HEADSET;
+
+ return 0;
+
+to_error:
+ ret = -ETIMEDOUT;
+ pr_err_ratelimited("Time-out error in %s\n", __func__);
+ return ret;
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+remove_error:
+ pr_err_ratelimited("Jack removal in %s\n", __func__);
+ return -ENODEV;
+}
+
+static void rt711_jack_detect_handler(struct work_struct *work)
+{
+ struct rt711_priv *rt711 =
+ container_of(work, struct rt711_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ if (!rt711->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
+ return;
+
+ if (pm_runtime_status_suspended(rt711->slave->dev.parent)) {
+ dev_dbg(&rt711->slave->dev,
+ "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n",
+ __func__);
+ return;
+ }
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ /* jack in */
+ if (rt711->jack_type == 0) {
+ ret = rt711_headset_detect(rt711);
+ if (ret < 0)
+ return;
+ if (rt711->jack_type == SND_JACK_HEADSET)
+ btn_type = rt711_button_detect(rt711);
+ } else if (rt711->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt711_button_detect(rt711);
+ }
+ } else {
+ /* jack out */
+ rt711->jack_type = 0;
+ }
+
+ dev_dbg(&rt711->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt711->jack_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_btn_check_handler(struct work_struct *work)
+{
+ struct rt711_priv *rt711 = container_of(work, struct rt711_priv,
+ jack_btn_check_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ if (rt711->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt711_button_detect(rt711);
+ }
+ } else {
+ rt711->jack_type = 0;
+ }
+
+ /* cbj comparator */
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &reg);
+ if (ret < 0)
+ goto io_error;
+
+ if ((reg & 0xf0) == 0xf0)
+ btn_type = 0;
+
+ dev_dbg(&rt711->slave->dev,
+ "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_jack_init(struct rt711_priv *rt711)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt711->component);
+
+ mutex_lock(&rt711->calibrate_mutex);
+ /* power on */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ if (rt711->hs_jack) {
+ /* unsolicited response & IRQ control */
+ regmap_write(rt711->regmap,
+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82);
+ regmap_write(rt711->regmap,
+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81);
+ regmap_write(rt711->regmap,
+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ 0x10, 0x2420);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ 0x19, 0x2e11);
+
+ switch (rt711->jd_src) {
+ case RT711_JD1:
+ /* default settings was already for JD1 */
+ break;
+ case RT711_JD2:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_HP_JD_SEL_JD2);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_100K:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_100K_DECODE | RT711_JD2_1PORT_TYPE_DECODE |
+ RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_TYPE_100K_DECODE,
+ RT711_JD2_2PORT_100K_DECODE_HP | RT711_JD2_1PORT_JD_HP |
+ RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_JD_RESERVED);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_1P8V_1PORT:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_JD_MODE_SEL,
+ RT711_JD2_1_JD_MODE);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_1PORT_TYPE_DECODE |
+ RT711_HP_JD_SEL_JD2,
+ RT711_JD2_1PORT_JD_HP |
+ RT711_HP_JD_SEL_JD2);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL4, RT711_JD2_PAD_PULL_UP_MASK |
+ RT711_JD2_MODE_SEL_MASK,
+ RT711_JD2_PAD_PULL_UP |
+ RT711_JD2_MODE2_1P8V_1PORT);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ default:
+ dev_warn(rt711->component->dev, "%s: Wrong JD source\n", __func__);
+ break;
+ }
+
+ dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(250));
+ } else {
+ regmap_write(rt711->regmap,
+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt711->regmap,
+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt711->regmap,
+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x00);
+
+ dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__);
+ }
+
+ /* power off */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+}
+
+static int rt711_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt711_jack_init(rt711);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ /* R Channel */
+ *r_val = (val_h << 8);
+ regmap_read(rt711->regmap, addr_l, r_val);
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = (val_h << 8);
+ regmap_read(rt711->regmap, addr_h, l_val);
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl;
+ int i;
+
+ mutex_lock(&rt711->calibrate_mutex);
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ /* L Channel */
+ if (mc->invert) {
+ /* for mute/unmute */
+ val_ll = (mc->max - ucontrol->value.integer.value[0])
+ << RT711_MUTE_SFT;
+ /* keep gain */
+ read_ll = read_ll & 0x7f;
+ val_ll |= read_ll;
+ } else {
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ read_ll = read_ll & (1 << RT711_MUTE_SFT);
+ val_ll |= read_ll;
+ }
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* R Channel */
+ if (mc->invert) {
+ /* for mute/unmute */
+ val_lr = (mc->max - ucontrol->value.integer.value[1])
+ << RT711_MUTE_SFT;
+ /* keep gain */
+ read_rl = read_rl & 0x7f;
+ val_lr |= read_rl;
+ } else {
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ read_rl = read_rl & (1 << RT711_MUTE_SFT);
+ val_lr |= read_rl;
+ }
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt711->regmap,
+ addr_h, (val_h << 8 | val_ll));
+ regmap_write(rt711->regmap,
+ addr_l, (val_h << 8 | val_ll));
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt711->regmap,
+ addr_h, (val_h << 8 | val_ll));
+
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt711->regmap,
+ addr_l, (val_h << 8 | val_lr));
+ }
+ /* check result */
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ mutex_unlock(&rt711->calibrate_mutex);
+ return 0;
+}
+
+static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ /* switch to get command */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* mute/unmute for switch controls */
+ read_ll = !((read_ll & 0x80) >> RT711_MUTE_SFT);
+ read_rl = !((read_rl & 0x80) >> RT711_MUTE_SFT);
+ } else {
+ /* for gain volume controls */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt711_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume",
+ RT711_SET_GAIN_DAC2_H, RT711_SET_GAIN_DAC2_L,
+ RT711_DIR_OUT_SFT, 0x57, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, out_vol_tlv),
+ SOC_DOUBLE_R_EXT("ADC 08 Capture Switch",
+ RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L,
+ RT711_DIR_IN_SFT, 1, 1,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT("ADC 09 Capture Switch",
+ RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L,
+ RT711_DIR_IN_SFT, 1, 1,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume",
+ RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L,
+ RT711_DIR_IN_SFT, 0x3f, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume",
+ RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L,
+ RT711_DIR_IN_SFT, 0x3f, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("AMIC Volume",
+ RT711_SET_GAIN_AMIC_H, RT711_SET_GAIN_AMIC_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume",
+ RT711_SET_GAIN_DMIC1_H, RT711_SET_GAIN_DMIC1_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume",
+ RT711_SET_GAIN_DMIC2_H, RT711_SET_GAIN_DMIC2_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+};
+
+static int rt711_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val = 0, nid;
+ int ret;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT711_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT711_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* vid = 0xf01 */
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt711->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt711_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg, nid;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT711_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT711_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt711->regmap, reg, &val2);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ regmap_write(rt711->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt711_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum,
+ rt711_mux_get, rt711_mux_put);
+
+static const struct snd_kcontrol_new rt711_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum,
+ rt711_mux_get, rt711_mux_put);
+
+static int rt711_dac_surround_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT711_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_DAC2, 0x10);
+
+ val_l = 0x00;
+ regmap_write(rt711->regmap,
+ RT711_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT711_MUTE_SFT);
+ regmap_write(rt711->regmap,
+ RT711_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ usleep_range(50000, 55000);
+
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_DAC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_adc_09_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_adc_08_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt711_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_dac_surround_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_adc_09_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_adc_08_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt711_audio_map[] = {
+ {"DAC Surround", NULL, "DP3RX"},
+ {"DP2TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+
+ {"HP", NULL, "DAC Surround"},
+};
+
+static int rt711_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ mutex_lock(&rt711->calibrate_mutex);
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt711->jd_src);
+
+ return 0;
+}
+
+static int rt711_probe(struct snd_soc_component *component)
+{
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711_parse_dt(rt711, &rt711->slave->dev);
+ rt711->component = component;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
+ .probe = rt711_probe,
+ .set_bias_level = rt711_set_bias_level,
+ .controls = rt711_snd_controls,
+ .num_controls = ARRAY_SIZE(rt711_snd_controls),
+ .dapm_widgets = rt711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt711_dapm_widgets),
+ .dapm_routes = rt711_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt711_audio_map),
+ .set_jack = rt711_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt711_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 rt711_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt711_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 rt711_priv *rt711 = 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;
+ unsigned int val = 0;
+
+ 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 (!rt711->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 3;
+ } else {
+ if (dai->id == RT711_AIF1)
+ port_config.num = 4;
+ else if (dai->id == RT711_AIF2)
+ port_config.num = 2;
+ else
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt711->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 48Khz */
+ regmap_write(rt711->regmap, RT711_DAC_FORMAT_H, val);
+ regmap_write(rt711->regmap, RT711_ADC1_FORMAT_H, val);
+ regmap_write(rt711->regmap, RT711_ADC2_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt711_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
+ return 0;
+}
+
+#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT711_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 rt711_ops = {
+ .hw_params = rt711_pcm_hw_params,
+ .hw_free = rt711_pcm_hw_free,
+ .set_stream = rt711_set_sdw_stream,
+ .shutdown = rt711_shutdown,
+};
+
+static struct snd_soc_dai_driver rt711_dai[] = {
+ {
+ .name = "rt711-aif1",
+ .id = RT711_AIF1,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_ops,
+ },
+ {
+ .name = "rt711-aif2",
+ .id = RT711_AIF2,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_ops,
+ }
+};
+
+/* Bus clock frequency */
+#define RT711_CLK_FREQ_9600000HZ 9600000
+#define RT711_CLK_FREQ_12000000HZ 12000000
+#define RT711_CLK_FREQ_6000000HZ 6000000
+#define RT711_CLK_FREQ_4800000HZ 4800000
+#define RT711_CLK_FREQ_2400000HZ 2400000
+#define RT711_CLK_FREQ_12288000HZ 12288000
+
+int rt711_clock_config(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt711->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT711_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT711_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT711_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT711_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT711_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT711_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt711->regmap, 0xe0, value);
+ regmap_write(rt711->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static void rt711_calibration_work(struct work_struct *work)
+{
+ struct rt711_priv *rt711 =
+ container_of(work, struct rt711_priv, calibration_work);
+
+ rt711_calibration(rt711);
+}
+
+int rt711_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711;
+ int ret;
+
+ rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL);
+ if (!rt711)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt711);
+ rt711->slave = slave;
+ rt711->sdw_regmap = sdw_regmap;
+ rt711->regmap = regmap;
+
+ regcache_cache_only(rt711->regmap, true);
+
+ mutex_init(&rt711->calibrate_mutex);
+ mutex_init(&rt711->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_btn_check_handler);
+ INIT_WORK(&rt711->calibration_work, rt711_calibration_work);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt711->hw_init = false;
+ rt711->first_hw_init = false;
+
+ /* JD source uses JD2 in default */
+ rt711->jd_src = RT711_JD2;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt711,
+ rt711_dai,
+ ARRAY_SIZE(rt711_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+int rt711_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+
+ rt711->disable_irq = false;
+
+ if (rt711->hw_init)
+ return 0;
+
+ regcache_cache_only(rt711->regmap, false);
+ if (rt711->first_hw_init)
+ regcache_cache_bypass(rt711->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt711->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt711_reset(rt711->regmap);
+
+ /* power on */
+ regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* Set Pin Widget */
+ regmap_write(rt711->regmap, RT711_SET_PIN_MIC2, 0x25);
+ regmap_write(rt711->regmap, RT711_SET_PIN_HP, 0xc0);
+ regmap_write(rt711->regmap, RT711_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_LINE1, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_LINE2, 0x20);
+
+ /* Mute HP/ADC1/ADC2 */
+ regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0xa080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0x9080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x6080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x5080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x6080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x5080);
+
+ /* Set Configuration Default */
+ regmap_write(rt711->regmap, 0x4f12, 0x91);
+ regmap_write(rt711->regmap, 0x4e12, 0xd6);
+ regmap_write(rt711->regmap, 0x4d12, 0x11);
+ regmap_write(rt711->regmap, 0x4c12, 0x20);
+ regmap_write(rt711->regmap, 0x4f13, 0x91);
+ regmap_write(rt711->regmap, 0x4e13, 0xd6);
+ regmap_write(rt711->regmap, 0x4d13, 0x11);
+ regmap_write(rt711->regmap, 0x4c13, 0x21);
+ regmap_write(rt711->regmap, 0x4c21, 0xf0);
+ regmap_write(rt711->regmap, 0x4d21, 0x11);
+ regmap_write(rt711->regmap, 0x4e21, 0x11);
+ regmap_write(rt711->regmap, 0x4f21, 0x01);
+
+ /* Data port arrangement */
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_TX_RX_MUX_CTL, 0x0154);
+
+ /* Set index */
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_DIGITAL_MISC_CTRL4, 0x201b);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL1, 0x5089);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_VREFOUT_CTL, 0x5064);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_INLINE_CMD_CTL, 0xd249);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ if (rt711->first_hw_init)
+ rt711_calibration(rt711);
+ else
+ schedule_work(&rt711->calibration_work);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt711->hs_jack)
+ rt711_jack_init(rt711);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, false);
+ regcache_mark_dirty(rt711->regmap);
+ } else
+ rt711->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt711->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT711 SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h
new file mode 100644
index 000000000000..491e357191f9
--- /dev/null
+++ b/sound/soc/codecs/rt711.h
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt711.h -- RT711 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_H__
+#define __RT711_H__
+
+extern const struct dev_pm_ops rt711_runtime_pm;
+
+struct rt711_priv {
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct work_struct calibration_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ int jack_type, jd_src;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+};
+
+/* NID */
+#define RT711_AUDIO_FUNCTION_GROUP 0x01
+#define RT711_DAC_OUT2 0x03
+#define RT711_ADC_IN1 0x09
+#define RT711_ADC_IN2 0x08
+#define RT711_DMIC1 0x12
+#define RT711_DMIC2 0x13
+#define RT711_MIC2 0x19
+#define RT711_LINE1 0x1a
+#define RT711_LINE2 0x1b
+#define RT711_BEEP 0x1d
+#define RT711_VENDOR_REG 0x20
+#define RT711_HP_OUT 0x21
+#define RT711_MIXER_IN1 0x22
+#define RT711_MIXER_IN2 0x23
+#define RT711_INLINE_CMD 0x55
+#define RT711_VENDOR_CALI 0x58
+#define RT711_VENDOR_IMS_DRE 0x5b
+
+/* Index (NID:20h) */
+#define RT711_DAC_DC_CALI_CTL1 0x00
+#define RT711_JD_CTL1 0x08
+#define RT711_JD_CTL2 0x09
+#define RT711_JD_CTL4 0x0b
+#define RT711_CC_DET1 0x11
+#define RT711_PARA_VERB_CTL 0x1a
+#define RT711_COMBO_JACK_AUTO_CTL1 0x45
+#define RT711_COMBO_JACK_AUTO_CTL2 0x46
+#define RT711_INLINE_CMD_CTL 0x48
+#define RT711_DIGITAL_MISC_CTRL4 0x4a
+#define RT711_VREFOUT_CTL 0x6b
+#define RT711_FSM_CTL 0x6f
+#define RT711_IRQ_FLAG_TABLE1 0x80
+#define RT711_IRQ_FLAG_TABLE2 0x81
+#define RT711_IRQ_FLAG_TABLE3 0x82
+#define RT711_TX_RX_MUX_CTL 0x91
+
+/* Index (NID:5bh) */
+#define RT711_IMS_DIGITAL_CTL1 0x00
+#define RT711_HP_IMS_RESULT_L 0x20
+#define RT711_HP_IMS_RESULT_R 0x21
+
+/* Verb */
+#define RT711_VERB_SET_CONNECT_SEL 0x3100
+#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT711_VERB_GET_CONNECT_SEL 0xb100
+#define RT711_VERB_SET_POWER_STATE 0x3500
+#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT711_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT711_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT711_VERB_GET_POWER_STATE 0xb500
+#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600
+#define RT711_VERB_GET_PIN_SENSE 0xb900
+#define RT711_FUNC_RESET 0xff01
+
+#define RT711_READ_HDA_3 0x2012
+#define RT711_READ_HDA_2 0x2013
+#define RT711_READ_HDA_1 0x2014
+#define RT711_READ_HDA_0 0x2015
+#define RT711_PRIV_INDEX_W_H 0x7500
+#define RT711_PRIV_INDEX_W_L 0x8580
+#define RT711_PRIV_DATA_W_H 0x7400
+#define RT711_PRIV_DATA_W_L 0x8480
+#define RT711_PRIV_INDEX_R_H 0x9d00
+#define RT711_PRIV_INDEX_R_L 0xad80
+#define RT711_PRIV_DATA_R_H 0x9c00
+#define RT711_PRIV_DATA_R_L 0xac80
+#define RT711_DAC_FORMAT_H 0x7203
+#define RT711_DAC_FORMAT_L 0x8283
+#define RT711_ADC1_FORMAT_H 0x7209
+#define RT711_ADC1_FORMAT_L 0x8289
+#define RT711_ADC2_FORMAT_H 0x7208
+#define RT711_ADC2_FORMAT_L 0x8288
+
+#define RT711_SET_AUDIO_POWER_STATE\
+ (RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
+#define RT711_GET_AUDIO_POWER_STATE\
+ (RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
+#define RT711_SET_PIN_DMIC1\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1)
+#define RT711_SET_PIN_DMIC2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2)
+#define RT711_SET_PIN_HP\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT)
+#define RT711_SET_PIN_MIC2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2)
+#define RT711_SET_PIN_LINE1\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1)
+#define RT711_SET_PIN_LINE2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2)
+#define RT711_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2)
+#define RT711_SET_HP_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT)
+#define RT711_SET_INLINE_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD)
+#define RT711_SET_STREAMID_DAC2\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2)
+#define RT711_SET_STREAMID_ADC1\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1)
+#define RT711_SET_STREAMID_ADC2\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2)
+#define RT711_GET_STREAMID_DAC2\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2)
+#define RT711_GET_STREAMID_ADC1\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1)
+#define RT711_GET_STREAMID_ADC2\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2)
+#define RT711_SET_GAIN_DAC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2)
+#define RT711_SET_GAIN_DAC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2)
+#define RT711_SET_GAIN_ADC1_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1)
+#define RT711_SET_GAIN_ADC1_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1)
+#define RT711_SET_GAIN_ADC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2)
+#define RT711_SET_GAIN_ADC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2)
+#define RT711_SET_GAIN_AMIC_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2)
+#define RT711_SET_GAIN_AMIC_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2)
+#define RT711_SET_GAIN_DMIC1_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1)
+#define RT711_SET_GAIN_DMIC1_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1)
+#define RT711_SET_GAIN_DMIC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2)
+#define RT711_SET_GAIN_DMIC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2)
+#define RT711_SET_GAIN_HP_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT)
+#define RT711_SET_GAIN_HP_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT)
+
+/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
+#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
+
+/* jack detect control 1 (0x08)(NID:20h) */
+#define RT711_JD2_DIGITAL_JD_MODE_SEL (0x1 << 1)
+#define RT711_JD2_1_JD_MODE (0x0 << 1)
+#define RT711_JD2_2_JD_MODE (0x1 << 1)
+
+/* jack detect control 2 (0x09)(NID:20h) */
+#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_JD2_2PORT_100K_DECODE (0x1 << 12)
+#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12)
+#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
+#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
+#define RT711_JD2_1PORT_TYPE_DECODE (0x3 << 10)
+#define RT711_JD2_1PORT_JD_LINE2 (0x0 << 10)
+#define RT711_JD2_1PORT_JD_HP (0x1 << 10)
+#define RT711_JD2_1PORT_JD_LINE1 (0x2 << 10)
+#define RT711_JD1_2PORT_TYPE_100K_DECODE (0x1 << 0)
+#define RT711_JD1_2PORT_JD_RESERVED (0x0 << 0)
+#define RT711_JD1_2PORT_JD_LINE1 (0x1 << 0)
+
+/* jack detect control 4 (0x0b)(NID:20h) */
+#define RT711_JD2_PAD_PULL_UP_MASK (0x1 << 3)
+#define RT711_JD2_PAD_NOT_PULL_UP (0x0 << 3)
+#define RT711_JD2_PAD_PULL_UP (0x1 << 3)
+#define RT711_JD2_MODE_SEL_MASK (0x3 << 0)
+#define RT711_JD2_MODE0_2PORT (0x0 << 0)
+#define RT711_JD2_MODE1_3P3V_1PORT (0x1 << 0)
+#define RT711_JD2_MODE2_1P8V_1PORT (0x2 << 0)
+
+/* CC DET1 (0x11)(NID:20h) */
+#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
+#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+
+/* Parameter & Verb control (0x1a)(NID:20h) */
+#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* FSM control (0x6f)(NID:20h) */
+#define RT711_CALI_CTL (0x0 << 0)
+#define RT711_COMBOJACK_CTL (0x1 << 0)
+#define RT711_IMS_CTL (0x2 << 0)
+#define RT711_DEPOP_CTL (0x3 << 0)
+
+/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
+#define RT711_TRIGGER_IMS (0x1 << 15)
+#define RT711_IMS_EN (0x1 << 6)
+
+#define RT711_EAPD_HIGH 0x2
+#define RT711_EAPD_LOW 0x0
+#define RT711_MUTE_SFT 7
+/* set input/output mapping to payload[14][15] separately */
+#define RT711_DIR_IN_SFT 6
+#define RT711_DIR_OUT_SFT 7
+
+enum {
+ RT711_AIF1,
+ RT711_AIF2,
+ RT711_AIFS,
+};
+
+enum rt711_jd_src {
+ RT711_JD_NULL,
+ RT711_JD1,
+ RT711_JD2,
+ RT711_JD2_100K,
+ RT711_JD2_1P8V_1PORT
+};
+
+int rt711_io_init(struct device *dev, struct sdw_slave *slave);
+int rt711_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic);
+int rt711_clock_config(struct device *dev);
+#endif /* __RT711_H__ */
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
new file mode 100644
index 000000000000..42f8f7b8bed0
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -0,0 +1,987 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-dmic.c -- rt712 SDCA DMIC ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-dmic.h"
+
+static bool rt712_sdca_dmic_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f52:
+ case 0x2f58 ... 0x2f59:
+ case 0x3201:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x6100068:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000024:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_dmic_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_dmic_readable_register,
+ .volatile_reg = rt712_sdca_dmic_volatile_register,
+ .max_register = 0x40981300,
+ .reg_defaults = rt712_sdca_dmic_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_dmic_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_dmic_mbq_readable_register,
+ .volatile_reg = rt712_sdca_dmic_mbq_volatile_register,
+ .max_register = 0x40800f14,
+ .reg_defaults = rt712_sdca_dmic_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_update_bits(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_dmic_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_dmic_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_bypass(rt712->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0B_11_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC1_2_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_I2S_IN_OUT_PDE_FLOAT_CTL, 0x1155);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_ENT_FLOAT_CTL, 0x2626);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_ENT_FLOAT_CTL, 0x1e19);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_VOL_CH_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_CONFIG_CTL0, 0x0050);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_dmic_index_write(rt712, RT712_ULTRA_SOUND_DET,
+ RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ regmap_write(rt712->regmap, 0x2f52, 0x00);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * 0x0a00;
+ else { /* ADC gain */
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt712->slave->dev, "%s: 0x%08x can't be set\n",
+ __func__, p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_dmic_priv *rt712)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+ ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+ RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt712_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt712_sdca_fu_info, \
+ .get = rt712_sdca_dmic_fu1e_capture_get, \
+ .put = rt712_sdca_dmic_fu1e_capture_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt712_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+ RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4),
+ RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+ RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, mic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = (0x7 << mask_sft) & val2;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_dmic_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc25_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc26_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt712_adc25_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc26_mux =
+ SOC_DAPM_ENUM_EXT("ADC 26 Mux", rt712_adc26_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu1e_dapm_mute = false;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu1e_dapm_mute = true;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_dmic_pde11_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc25_mux),
+ SND_SOC_DAPM_MUX("ADC 26 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc26_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP2TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 25 Mux"},
+ {"FU 1E", NULL, "ADC 26 Mux"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 26 Mux", "DMIC1", "DMIC1"},
+ {"ADC 26 Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->component = component;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = {
+ .probe = rt712_sdca_dmic_probe,
+ .controls = rt712_sdca_dmic_snd_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+ .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+ .dapm_routes = rt712_sdca_dmic_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+ .endianness = 1,
+};
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, num_channels;
+ 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 (!rt712->slave)
+ return -EINVAL;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = SDW_DATA_DIR_TX;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 4) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT712_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT712_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
+ return 0;
+}
+
+#define RT712_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 RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_dmic_ops = {
+ .hw_params = rt712_sdca_dmic_hw_params,
+ .hw_free = rt712_sdca_dmic_hw_free,
+ .set_stream = rt712_sdca_dmic_set_sdw_stream,
+ .shutdown = rt712_sdca_dmic_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-dmic-aif1",
+ .id = RT712_AIF1,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_dmic_ops,
+ },
+};
+
+static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu1e_dapm_mute = true;
+ rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+ rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712_dmic,
+ rt712_sdca_dmic_dai,
+ ARRAY_SIZE(rt712_sdca_dmic_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+
+static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt712->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_dmic_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_dmic_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 = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = 0;
+
+ 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++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_dmic_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id);
+
+static int rt712_sdca_dmic_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev);
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ return rt712_sdca_dmic_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int rt712_sdca_dmic_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n",
+ __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_dmic_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL)
+};
+
+
+static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
+ .read_prop = rt712_sdca_dmic_read_prop,
+ .update_status = rt712_sdca_dmic_update_status,
+};
+
+static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_dmic_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_dmic_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static struct sdw_driver rt712_sdca_dmic_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca-dmic",
+ .pm = pm_ptr(&rt712_sdca_dmic_pm),
+ },
+ .probe = rt712_sdca_dmic_sdw_probe,
+ .remove = rt712_sdca_dmic_sdw_remove,
+ .ops = &rt712_sdca_dmic_slave_ops,
+ .id_table = rt712_sdca_dmic_id,
+};
+module_sdw_driver(rt712_sdca_dmic_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA DMIC SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h
new file mode 100644
index 000000000000..110154e74efe
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-dmic.h -- RT712 SDCA DMIC ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_DMIC_H__
+#define __RT712_SDW_DMIC_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+struct rt712_sdca_dmic_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+};
+
+struct rt712_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+
+static const struct reg_default rt712_sdca_dmic_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x01 },
+ { 0x2f35, 0x02 },
+ { 0x2f36, 0xcf },
+ { 0x2f52, 0x08 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x3201, 0x01 },
+ { 0x320c, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = {
+ { 0x0590001e, 0x0020 },
+ { 0x06100000, 0x0010 },
+ { 0x06100006, 0x0055 },
+ { 0x06100010, 0x2630 },
+ { 0x06100011, 0x152f },
+ { 0x06100013, 0x0102 },
+ { 0x06100015, 0x2219 },
+ { 0x06100018, 0x0102 },
+ { 0x06100026, 0x2c29 },
+ { 0x06100027, 0x2d2b },
+ { 0x0610002b, 0x2a32 },
+ { 0x0610002f, 0x3355 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+};
+
+#endif /* __RT712_SDW_DMIC_H__ */
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
new file mode 100644
index 000000000000..ea07131edfa2
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-sdw.h"
+
+static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case 0x3201:
+ case 0x320c:
+ case 0x3301 ... 0x3303:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x61000f1:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000020:
+ case 0x2000024:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_readable_register,
+ .volatile_reg = rt712_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt712_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_mbq_readable_register,
+ .volatile_reg = rt712_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt712_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt712->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt712->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt712_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_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;
+
+ sdw_slave_read_prop(slave);
+
+ 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(8) | BIT(4); /* BITMAP: 100010000 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ 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 = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt712_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x, sdca_cascade=%x", __func__,
+ status->control_port, status->sdca_cascade);
+
+ if (cancel_delayed_work_sync(&rt712->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt712->scp_sdca_stat2)
+ scp_sdca_stat2 = rt712->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt712->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt712->scp_sdca_stat2 |= scp_sdca_stat2;
+
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt712->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_detect_work, msecs_to_jiffies(30));
+
+ mutex_unlock(&rt712->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt712->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt712_sdca_slave_ops = {
+ .read_prop = rt712_sdca_read_prop,
+ .interrupt_callback = rt712_sdca_interrupt_callback,
+ .update_status = rt712_sdca_update_status,
+};
+
+static int rt712_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (rt712->hw_init) {
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt712->calibrate_mutex);
+ mutex_destroy(&rt712->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_id);
+
+static int rt712_sdca_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt712_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt712_sdca->disable_irq_lock);
+ rt712_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt712_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt712_sdca_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int rt712_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt712->disable_irq_lock);
+ if (rt712->disable_irq == true) {
+
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt712->disable_irq = false;
+ }
+ mutex_unlock(&rt712->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt712_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca",
+ .pm = pm_ptr(&rt712_sdca_pm),
+ },
+ .probe = rt712_sdca_sdw_probe,
+ .remove = rt712_sdca_sdw_remove,
+ .ops = &rt712_sdca_slave_ops,
+ .id_table = rt712_sdca_id,
+};
+module_sdw_driver(rt712_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h
new file mode 100644
index 000000000000..99fd2d67f04d
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-sdw.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_H__
+#define __RT712_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt712_sdca_reg_defaults[] = {
+
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+static const struct reg_default rt712_sdca_mbq_defaults[] = {
+ { 0x2000004, 0xaa01 },
+ { 0x200000e, 0x21e0 },
+ { 0x200004a, 0x8830 },
+ { 0x2000067, 0xf100 },
+ { 0x5800000, 0x1893 },
+ { 0x5b00000, 0x0407 },
+ { 0x5b00005, 0x0000 },
+ { 0x5b00029, 0x3fff },
+ { 0x5b0002a, 0xf000 },
+ { 0x6100000, 0x04e4 },
+ { 0x610000e, 0x0007 },
+ { 0x6100045, 0x0860 },
+ { 0x6100046, 0x0029 },
+ { 0x6100053, 0x3fff },
+ { 0x6100055, 0x0000 },
+ { 0x6100060, 0x0000 },
+ { 0x6100064, 0x8000 },
+ { 0x6100065, 0x0000 },
+ { 0x6100067, 0xff12 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+};
+
+#endif /* __RT712_SDW_H__ */
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
new file mode 100644
index 000000000000..0ebaae426e73
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -0,0 +1,1932 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdca.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+
+static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
+{
+ unsigned int val, loop_rc = 0, loop_dc = 0;
+ struct device *dev;
+ struct regmap *regmap = rt712->regmap;
+ int chk_cnt = 100;
+ int ret = 0;
+
+ mutex_lock(&rt712->calibrate_mutex);
+ dev = regmap_get_device(regmap);
+
+ /* Set HP-JD source from JD1 */
+ if (rt712->version_id == RT712_VA)
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+
+ /* FSM switch to calibration manual mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100);
+
+ /* Calibration setting */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0x7883);
+
+ /* W1C Trigger DC calibration (HP & Class-D) */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0xf893);
+
+ /* wait for calibration process */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+ ret = -ETIMEDOUT;
+
+_cali_fail_:
+ /* Enable Rldet in FSM */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4500);
+
+ /* Sensing Lch+Rch */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL1, 0x040f);
+
+ /* Sine gen path control */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL5, 0x0000);
+
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_DIGITAL_MISC_CTRL4, 0x0010);
+
+ mutex_unlock(&rt712->calibrate_mutex);
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+ &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0) {
+ /* set owner to device */
+ if (rt712->version_id == RT712_VA)
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+ else
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+ }
+
+ return btn_type;
+}
+
+static int rt712_sdca_headset_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int det_mode;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt712->jack_type = 0;
+ break;
+ case 0x03:
+ rt712->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt712->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0),
+ det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt712->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt712_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt712->hs_jack)
+ return;
+
+ if (!rt712->component->card || !rt712->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt712_sdca_headset_detect(rt712);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt712_sdca_button_detect(rt712);
+
+ if (rt712->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt712->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt712->jack_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt712_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+ } else {
+ rt712->jack_type = 0;
+ }
+
+ dev_dbg(&rt712->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712)
+{
+ mutex_lock(&rt712->calibrate_mutex);
+
+ if (rt712->hs_jack) {
+ /* Enable HID1 event & set button RTC mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL5, 0xfff0);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL0, 0x1100, 0x1100);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL7, 0xf000, 0x0000);
+
+ /* detected_mode_change_event_en & hid1_push_button_event_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0c00);
+ /* ge_inbox_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL2, 0x0020, 0x0000);
+
+ switch (rt712->jd_src) {
+ case RT712_JD1:
+ /* Set HP-JD source from JD1, VB uses JD1 in default */
+ if (rt712->version_id == RT712_VA)
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+ break;
+ default:
+ dev_warn(rt712->component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__);
+
+ /* trigger GE interrupt */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0080);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0000);
+ } else {
+ /* disable HID1 & detected_mode_change event */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0000);
+
+ dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__);
+ }
+
+ mutex_unlock(&rt712->calibrate_mutex);
+}
+
+static int rt712_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->hs_jack = hs_jack;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt712_sdca_jack_init(rt712);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt712_sdca_set_gain_put(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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue == gain_l_val && rvalue == gain_r_val)
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt712->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt712->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return 1;
+
+ return -EIO;
+}
+
+static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = read_l / tendB;
+ else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = read_r / tendB;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_01, ch_02;
+
+ ch_01 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt712->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int err;
+
+ if (rt712->fu0f_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu0f_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt712_sdca_set_fu0f_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+static int rt712_sdca_set_fu05_playback_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_01, ch_02;
+
+ ch_01 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt712->fu05_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu05_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int err;
+
+ if (rt712->fu05_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu05_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu05_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu05_mixer_r_mute = !ucontrol->value.integer.value[1];
+
+ err = rt712_sdca_set_fu05_playback_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x3f, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 8, 3, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv),
+ SOC_DOUBLE_EXT("FU05 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu05_playback_get, rt712_sdca_fu05_playback_put),
+};
+
+static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+};
+
+static int rt712_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask = 0x3300;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+
+ val = val & mask;
+ switch (val) {
+ case 0x3000:
+ val = 1;
+ break;
+ case 0x0300:
+ val = 0;
+ break;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt712_sdca_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int mask_sft;
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (ucontrol->value.enumerated.item[0] == 0)
+ mask_sft = 12;
+ else if (ucontrol->value.enumerated.item[0] == 1)
+ mask_sft = 8;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+ val = (val >> mask_sft) & 0x3;
+ if (!val)
+ return 0;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3fff);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3 << mask_sft, 0);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return 1;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt712_adc23_enum,
+ rt712_sdca_mux_get, rt712_sdca_mux_put);
+
+static int rt712_sdca_fu05_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu05_dapm_mute = false;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu05_dapm_mute = true;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_fu0f_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu0f_dapm_mute = false;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu0f_dapm_mute = true;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde40_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde12_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde23_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt712_spk_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt712_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 40", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde40_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde12_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu05_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu0f_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = {
+ { "FU 05", NULL, "DP1RX" },
+ { "DP4TX", NULL, "FU 0F" },
+
+ { "FU 0F", NULL, "PDE 12" },
+ { "FU 0F", NULL, "ADC 23 Mux" },
+ { "ADC 23 Mux", "LINE2", "LINE2" },
+ { "ADC 23 Mux", "MIC2", "MIC2" },
+
+ { "HP", NULL, "PDE 40" },
+ { "HP", NULL, "FU 05" },
+};
+
+static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt712_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt712_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = {
+ { "FU06", NULL, "DP3RX" },
+ { "FU06", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU06" },
+ { "OT23 R", "Switch", "FU06" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+};
+
+static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt712->jd_src);
+
+ return 0;
+}
+
+static int rt712_sdca_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712_sdca_parse_dt(rt712, &rt712->slave->dev);
+ rt712->component = component;
+
+ /* add SPK route */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ snd_soc_add_component_controls(component,
+ rt712_sdca_spk_controls, ARRAY_SIZE(rt712_sdca_spk_controls));
+ snd_soc_dapm_new_controls(dapm,
+ rt712_sdca_spk_dapm_widgets, ARRAY_SIZE(rt712_sdca_spk_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt712_sdca_spk_dapm_routes, ARRAY_SIZE(rt712_sdca_spk_dapm_routes));
+ }
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * 0x0a00;
+ else { /* ADC gain */
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+ ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+ RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt712_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt712_sdca_fu_info, \
+ .get = rt712_sdca_dmic_fu1e_capture_get, \
+ .put = rt712_sdca_dmic_fu1e_capture_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt712_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+ RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4),
+ RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+ RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, dmic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (((val >> mask_sft) & 0xf) == 0x4) ? 0 : 1;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = ((0xf << mask_sft) & val2) >> mask_sft;
+
+ if (val == 0)
+ val = 0x4;
+ else if (val >= 1)
+ val = 0xe;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, 0xf << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_dmic_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0a_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0b_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0a_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0A Mux", rt712_adc0a_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0b_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0B Mux", rt712_adc0b_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu1e_dapm_mute = false;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu1e_dapm_mute = true;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_dmic_pde11_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 rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 0A Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0a_mux),
+ SND_SOC_DAPM_MUX("ADC 0B Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0b_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP8TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 0A Mux"},
+ {"FU 1E", NULL, "ADC 0B Mux"},
+ {"ADC 0A Mux", "DMIC1", "DMIC1"},
+ {"ADC 0A Mux", "DMIC2", "DMIC2"},
+ {"ADC 0B Mux", "DMIC1", "DMIC1"},
+ {"ADC 0B Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->dmic_component = component;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712 = {
+ .probe = rt712_sdca_probe,
+ .controls = rt712_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_controls),
+ .dapm_widgets = rt712_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dapm_widgets),
+ .dapm_routes = rt712_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_audio_map),
+ .set_jack = rt712_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = {
+ .probe = rt712_sdca_dmic_probe,
+ .controls = rt712_sdca_dmic_snd_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+ .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+ .dapm_routes = rt712_sdca_dmic_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+ .endianness = 1,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "dmic",
+#endif
+};
+
+static int rt712_sdca_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 rt712_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_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 rt712_sdca_priv *rt712 = 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;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ /* VA doesn't support AIF3 */
+ if (dai->id == RT712_AIF3 && rt712->version_id == RT712_VA)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT712_AIF1)
+ port = 1;
+ else if (dai->id == RT712_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT712_AIF1)
+ port = 4;
+ else if (dai->id == RT712_AIF3)
+ port = 8;
+ else
+ return -EINVAL;
+ }
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ switch (dai->id) {
+ case RT712_AIF1:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ case RT712_AIF2:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ case RT712_AIF3:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ default:
+ dev_err(component->dev, "%s: Wrong DAI id\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
+ return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_ops = {
+ .hw_params = rt712_sdca_pcm_hw_params,
+ .hw_free = rt712_sdca_pcm_hw_free,
+ .set_stream = rt712_sdca_set_sdw_stream,
+ .shutdown = rt712_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dai[] = {
+ {
+ .name = "rt712-sdca-aif1",
+ .id = RT712_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ },
+ {
+ .name = "rt712-sdca-aif2",
+ .id = RT712_AIF2,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ }
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-aif3",
+ .id = RT712_AIF3,
+ .capture = {
+ .stream_name = "DP8 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ }
+};
+
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ mutex_init(&rt712->calibrate_mutex);
+ mutex_init(&rt712->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu0f_dapm_mute = true;
+ rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true;
+ rt712->fu1e_dapm_mute = true;
+ rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+ rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+ rt712->fu05_dapm_mute = true;
+ rt712->fu05_mixer_l_mute = rt712->fu05_mixer_r_mute = false;
+
+ /* JD source uses JD1 in default */
+ rt712->jd_src = RT712_JD1;
+
+ if (slave->id.part_id != RT712_PART_ID_713)
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, ARRAY_SIZE(rt712_sdca_dai));
+ else
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, 1);
+ if (ret < 0)
+ return ret;
+
+ /* only add the dmic component if a SMART_MIC function is exposed in ACPI */
+ if (sdca_device_quirk_match(slave, SDCA_QUIRKS_RT712_VB)) {
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712_dmic,
+ rt712_sdca_dmic_dai,
+ ARRAY_SIZE(rt712_sdca_dmic_dai));
+ if (ret < 0)
+ return ret;
+ rt712->dmic_function_found = true;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int hibernation_flag;
+ struct device *dev = &rt712->slave->dev;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_ANALOG_CTL, RT712_MISC_POWER_CTL7, 0x0000);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* calibration */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, &hibernation_flag);
+ if (!hibernation_flag) {
+ ret = rt712_sdca_calibration(rt712);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+ }
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3000, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIC2_LINE2_PDE_FLOAT_CTL, 0x3412);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DAC03_HP_PDE_FLOAT_CTL, 0x4040);
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL, 0x0001, 0x0000);
+ regmap_write(rt712->regmap, 0x2f50, 0x00);
+ regmap_write(rt712->regmap, 0x2f54, 0x00);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+
+ /* add SPK settings */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_AMP_PDE_FLOAT_CTL, 0x2323);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ }
+}
+
+static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int jack_func_status, mic_func_status, amp_func_status;
+ struct device *dev = &rt712->slave->dev;
+
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s jack/mic/amp func_status=0x%x, 0x%x, 0x%x\n",
+ __func__, jack_func_status, mic_func_status, amp_func_status);
+
+ /* DMIC */
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL, 0x1f1e);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0B_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_HDA_LEGACY_CONFIG_CTL0, 0x8010);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT11, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_ULTRA_SOUND_DET, RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* Jack */
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_SEL_VEE2_HP_CTL1, 0x042a);
+ rt712_sdca_index_write(rt712, RT712_CHARGE_PUMP, RT712_HP_DET_CTL3, 0x1fff);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec67);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL1, 0x2802);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL6, 0xf215);
+
+ /* calibration */
+ ret = rt712_sdca_calibration(rt712);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, 0x3000, 0x0000);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_MISC_CTL_FOR_UAJ, 0x0003);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* SPK */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec63);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL1, 0xfff5);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ }
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned int val;
+ struct sdw_slave_prop *prop = &slave->prop;
+
+ rt712->disable_irq = false;
+
+ if (rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_bypass(rt712->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val);
+ rt712->hw_id = (val & 0xf000) >> 12;
+ rt712->version_id = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id);
+
+ if (rt712->version_id == RT712_VA) {
+ if (rt712->dmic_function_found) {
+ dev_err(&slave->dev, "%s RT712 VA detected but SMART_MIC function exposed in ACPI\n",
+ __func__);
+ goto suspend;
+ }
+
+ rt712_sdca_va_io_init(rt712);
+ } else {
+ if (!rt712->dmic_function_found)
+ dev_warn(&slave->dev, "%s RT712 VB detected but no SMART_MIC function exposed in ACPI\n",
+ __func__);
+
+ /* multilanes and DMIC are supported by rt712vb */
+ prop->lane_control_support = true;
+ rt712_sdca_vb_io_init(rt712);
+ }
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt712->hs_jack)
+ rt712_sdca_jack_init(rt712);
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->hw_init = true;
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+suspend:
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
new file mode 100644
index 000000000000..7ab7d5feb50a
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_H__
+#define __RT712_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt712_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_component *dmic_component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ struct mutex disable_irq_lock; /* SDCA irq lock protection */
+ bool disable_irq;
+ int jack_type;
+ int jd_src;
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ unsigned int hw_id;
+ unsigned int version_id;
+ bool dmic_function_found;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+ bool fu05_dapm_mute;
+ bool fu05_mixer_l_mute;
+ bool fu05_mixer_r_mute;
+};
+
+struct rt712_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+
+/* NID */
+#define RT712_VENDOR_REG 0x20
+#define RT712_EQ_CTRL 0x53
+#define RT712_CHARGE_PUMP 0x57
+#define RT712_VENDOR_CALI 0x58
+#define RT712_ULTRA_SOUND_DET 0x59
+#define RT712_VENDOR_IMS_DRE 0x5b
+#define RT712_VENDOR_ANALOG_CTL 0x5f
+#define RT712_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT712_JD_PRODUCT_NUM 0x00
+#define RT712_ANALOG_BIAS_CTL3 0x04
+#define RT712_JD_CTL1 0x09
+#define RT712_IO_CTL 0x0c
+#define RT712_LDO2_3_CTL1 0x0e
+#define RT712_PARA_VERB_CTL 0x1a
+#define RT712_CC_DET1 0x24
+#define RT712_CLASSD_AMP_CTL1 0x37
+#define RT712_CLASSD_AMP_CTL6 0x3c
+#define RT712_COMBO_JACK_AUTO_CTL1 0x45
+#define RT712_COMBO_JACK_AUTO_CTL2 0x46
+#define RT712_COMBO_JACK_AUTO_CTL3 0x47
+#define RT712_DIGITAL_MISC_CTRL4 0x4a
+#define RT712_FSM_CTL 0x67
+#define RT712_SW_CONFIG1 0x8a
+#define RT712_SW_CONFIG2 0x8b
+
+/* Index (NID:57h) */
+#define RT712_HP_DET_CTL3 0x0c
+
+/* Index (NID:58h) */
+#define RT712_DAC_DC_CALI_CTL1 0x00
+#define RT712_DAC_DC_CALI_CTL2 0x01
+
+/* Index (NID:59h) */
+#define RT712_ULTRA_SOUND_DETECTOR6 0x1e
+
+/* Index (NID:5bh) */
+#define RT712_IMS_DIGITAL_CTL1 0x00
+#define RT712_IMS_DIGITAL_CTL5 0x05
+#define RT712_SEL_VEE2_HP_CTL1 0x23
+#define RT712_HP_DETECT_RLDET_CTL1 0x29
+#define RT712_HP_DETECT_RLDET_CTL2 0x2a
+
+/* Index (NID:5fh) */
+#define RT712_MISC_POWER_CTL0 0x00
+#define RT712_MISC_POWER_CTL7 0x08
+
+/* Index (NID:61h) */
+#define RT712_HDA_LEGACY_MUX_CTL0 0x00
+#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06
+#define RT712_HDA_LEGACY_RESET_CTL 0x08
+#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT712_DMIC_ENT_FLOAT_CTL 0x10
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL2 0x13
+#define RT712_ADC_ENT_FLOAT_CTL 0x15
+#define RT712_ADC_VOL_CH_FLOAT_CTL2 0x18
+#define RT712_DAC03_HP_PDE_FLOAT_CTL 0x22
+#define RT712_MIC2_LINE2_PDE_FLOAT_CTL 0x23
+#define RT712_ADC0A_08_PDE_FLOAT_CTL 0x26
+#define RT712_ADC0B_11_PDE_FLOAT_CTL 0x27
+#define RT712_DMIC1_2_PDE_FLOAT_CTL 0x2b
+#define RT712_AMP_PDE_FLOAT_CTL 0x2c
+#define RT712_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f
+#define RT712_GE_RELATED_CTL1 0x45
+#define RT712_GE_RELATED_CTL2 0x46
+#define RT712_MIXER_CTL0 0x52
+#define RT712_MIXER_CTL1 0x53
+#define RT712_EAPD_CTL 0x55
+#define RT712_UMP_HID_CTL0 0x60
+#define RT712_UMP_HID_CTL1 0x61
+#define RT712_UMP_HID_CTL2 0x62
+#define RT712_UMP_HID_CTL3 0x63
+#define RT712_UMP_HID_CTL4 0x64
+#define RT712_UMP_HID_CTL5 0x65
+#define RT712_UMP_HID_CTL6 0x66
+#define RT712_UMP_HID_CTL7 0x67
+#define RT712_UMP_HID_CTL8 0x68
+#define RT712_MISC_CTL_FOR_UAJ 0x72
+#define RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL 0xa2
+#define RT712_DMIC2_FU_IT_FLOAT_CTL 0xa6
+#define RT712_ADC0B_FU_CH12_FLOAT_CTL 0xb0
+#define RT712_DMIC2_FU_CH12_FLOAT_CTL 0xb1
+
+/* Parameter & Verb control 01 (0x1a)(NID:20h) */
+#define RT712_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT712_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT712_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT712_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT712_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* DAC DC offset calibration control-1 (0x00)(NID:58h) */
+#define RT712_DAC_DC_CALI_TRIGGER (0x1 << 15)
+
+#define RT712_EAPD_HIGH 0x2
+#define RT712_EAPD_LOW 0x0
+
+/* RC Calibration register */
+#define RT712_RC_CAL 0x3201
+
+/* Buffer address for HID */
+#define RT712_BUF_ADDR_HID1 0x44030000
+#define RT712_BUF_ADDR_HID2 0x44030020
+
+/* RT712 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT712 SDCA entity */
+#define RT712_SDCA_ENT0 0x00
+#define RT712_SDCA_ENT_HID01 0x01
+#define RT712_SDCA_ENT_GE49 0x49
+#define RT712_SDCA_ENT_USER_FU05 0x05
+#define RT712_SDCA_ENT_USER_FU06 0x06
+#define RT712_SDCA_ENT_USER_FU0F 0x0f
+#define RT712_SDCA_ENT_USER_FU10 0x19
+#define RT712_SDCA_ENT_USER_FU1E 0x1e
+#define RT712_SDCA_ENT_FU15 0x15
+#define RT712_SDCA_ENT_PDE23 0x23
+#define RT712_SDCA_ENT_PDE40 0x40
+#define RT712_SDCA_ENT_PDE11 0x11
+#define RT712_SDCA_ENT_PDE12 0x12
+#define RT712_SDCA_ENT_CS01 0x01
+#define RT712_SDCA_ENT_CS11 0x11
+#define RT712_SDCA_ENT_CS1F 0x1f
+#define RT712_SDCA_ENT_CS1C 0x1c
+#define RT712_SDCA_ENT_CS31 0x31
+#define RT712_SDCA_ENT_OT23 0x42
+#define RT712_SDCA_ENT_IT11 0x26
+#define RT712_SDCA_ENT_IT26 0x26
+#define RT712_SDCA_ENT_IT09 0x09
+#define RT712_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT712_SDCA_ENT_PLATFORM_FU44 0x44
+
+/* RT712 SDCA control */
+#define RT712_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT712_SDCA_CTL_FU_MUTE 0x01
+#define RT712_SDCA_CTL_FU_VOLUME 0x02
+#define RT712_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT712_SDCA_CTL_SELECTED_MODE 0x01
+#define RT712_SDCA_CTL_DETECTED_MODE 0x02
+#define RT712_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT712_SDCA_CTL_VENDOR_DEF 0x30
+#define RT712_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT712_SDCA_CTL_FUNC_STATUS 0x10
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+#define FUNCTION_HAS_BEEN_RESET BIT(6)
+#define FUNCTION_BUSY BIT(7)
+
+/* sample frequency index */
+#define RT712_SDCA_RATE_16000HZ 0x04
+#define RT712_SDCA_RATE_32000HZ 0x07
+#define RT712_SDCA_RATE_44100HZ 0x08
+#define RT712_SDCA_RATE_48000HZ 0x09
+#define RT712_SDCA_RATE_96000HZ 0x0b
+#define RT712_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT712_AIF1,
+ RT712_AIF2,
+ RT712_AIF3,
+};
+
+enum rt712_sdca_jd_src {
+ RT712_JD_NULL,
+ RT712_JD1,
+};
+
+enum rt712_sdca_hw_id {
+ RT712_DEV_ID_712 = 0x7,
+ RT712_DEV_ID_713 = 0x6,
+ RT712_DEV_ID_716 = 0x5,
+ RT712_DEV_ID_717 = 0x4,
+};
+
+#define RT712_PART_ID_713 0x713
+
+enum rt712_sdca_version {
+ RT712_VA,
+ RT712_VB,
+};
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt712_sdca_jack_detect(struct rt712_sdca_priv *rt712, bool *hp, bool *mic);
+#endif /* __RT712_H__ */
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
new file mode 100644
index 000000000000..ce7d8955efc3
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt715-sdca.h"
+#include "rt715-sdca-sdw.h"
+
+static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2230 ... 0x2239:
+ case 0x2f5b:
+ case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2200 ... 0x2212: /* i2c debug */
+ case 0x2f07:
+ case 0x2f1b ... 0x2f1e:
+ case 0x2f30 ... 0x2f34:
+ case 0x2f50 ... 0x2f51:
+ case 0x2f53 ... 0x2f59:
+ case 0x2f5c ... 0x2f5f:
+ case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200002b:
+ case 0x2000036:
+ case 0x2000037:
+ case 0x2000039:
+ case 0x2000044:
+ case 0x6100000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt715_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt715_sdca_readable_register,
+ .volatile_reg = rt715_sdca_volatile_register,
+ .max_register = 0x43ffffff,
+ .reg_defaults = rt715_reg_defaults_sdca,
+ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt715_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt715_sdca_mbq_readable_register,
+ .volatile_reg = rt715_sdca_mbq_volatile_register,
+ .max_register = 0x43ffffff,
+ .reg_defaults = rt715_mbq_reg_defaults_sdca,
+ .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt715_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt715_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt715_sdca_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->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x50;/* BITMAP: 01010000 */
+ prop->sink_ports = 0x0; /* BITMAP: 00000000 */
+
+ 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;
+
+ dpn = prop->src_dpn_prop;
+ i = 0;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt715_sdca_slave_ops = {
+ .read_prop = rt715_sdca_read_prop,
+ .update_status = rt715_sdca_update_status,
+};
+
+static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *mbq_regmap, *regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave);
+}
+
+static int rt715_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt715_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt715_sdca_id);
+
+static int rt715_dev_suspend(struct device *dev)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+
+ if (!rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, true);
+ regcache_mark_dirty(rt715->regmap);
+ regcache_cache_only(rt715->mbq_regmap, true);
+ regcache_mark_dirty(rt715->mbq_regmap);
+
+ return 0;
+}
+
+#define RT715_PROBE_TIMEOUT 5000
+
+static int rt715_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt715->regmap, false);
+ regcache_sync_region(rt715->regmap,
+ SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+ CH_00),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+ regcache_cache_only(rt715->mbq_regmap, false);
+ regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff);
+ regcache_sync_region(rt715->mbq_regmap,
+ SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+ CH_00),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt715_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715-sdca",
+ .pm = pm_ptr(&rt715_pm),
+ },
+ .probe = rt715_sdca_sdw_probe,
+ .remove = rt715_sdca_sdw_remove,
+ .ops = &rt715_sdca_slave_ops,
+ .id_table = rt715_sdca_id,
+};
+module_sdw_driver(rt715_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h
new file mode 100644
index 000000000000..0cbc14844f8c
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDW_SDCA_H__
+#define __RT715_SDW_SDCA_H__
+
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt715_reg_defaults_sdca[] = {
+ { 0x201a, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x0b },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0e },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x12 },
+ { 0x2f0f, 0x00 },
+ { 0x2f10, 0x00 },
+ { 0x2f11, 0x00 },
+ { 0x2f12, 0x00 },
+ { 0x2f13, 0x00 },
+ { 0x2f14, 0x00 },
+ { 0x2f15, 0x00 },
+ { 0x2f16, 0x00 },
+ { 0x2f17, 0x00 },
+ { 0x2f18, 0x00 },
+ { 0x2f19, 0x03 },
+ { 0x2f1a, 0x00 },
+ { 0x2f1f, 0x10 },
+ { 0x2f20, 0x00 },
+ { 0x2f21, 0x00 },
+ { 0x2f22, 0x00 },
+ { 0x2f23, 0x00 },
+ { 0x2f24, 0x00 },
+ { 0x2f25, 0x00 },
+ { 0x2f52, 0x01 },
+ { 0x2f5a, 0x02 },
+ { 0x2f5b, 0x05 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+ RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+};
+
+static const struct reg_default rt715_mbq_reg_defaults_sdca[] = {
+ { 0x200002b, 0x0420 },
+ { 0x2000036, 0x0000 },
+ { 0x2000037, 0x0000 },
+ { 0x2000039, 0xaa81 },
+ { 0x2000044, 0x0202 },
+ { 0x6100000, 0x0100 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+};
+#endif /* __RT715_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
new file mode 100644
index 000000000000..db7d43349d7d
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.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/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt715-sdca.h"
+
+static int rt715_sdca_index_write(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt715->mbq_regmap;
+ unsigned int addr;
+ int ret;
+
+ addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt715->slave->dev,
+ "%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt715_sdca_index_read(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ struct regmap *regmap = rt715->mbq_regmap;
+ unsigned int addr;
+ int ret;
+
+ addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt715->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt715_sdca_index_update_bits(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt715_sdca_index_read(rt715, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+
+ return rt715_sdca_index_write(rt715, nid, reg, tmp);
+}
+
+static inline unsigned int rt715_sdca_vol_gain(unsigned int u_ctrl_val,
+ unsigned int vol_max, unsigned int vol_gain_sft)
+{
+ unsigned int val;
+
+ if (u_ctrl_val > vol_max)
+ u_ctrl_val = vol_max;
+ val = u_ctrl_val;
+ u_ctrl_val =
+ ((abs(u_ctrl_val - vol_gain_sft) * RT715_SDCA_DB_STEP) << 8) / 1000;
+ if (val <= vol_gain_sft) {
+ u_ctrl_val = ~u_ctrl_val;
+ u_ctrl_val += 1;
+ }
+ u_ctrl_val &= 0xffff;
+
+ return u_ctrl_val;
+}
+
+static inline unsigned int rt715_sdca_boost_gain(unsigned int u_ctrl_val,
+ unsigned int b_max, unsigned int b_gain_sft)
+{
+ if (u_ctrl_val > b_max)
+ u_ctrl_val = b_max;
+
+ return (u_ctrl_val * 10) << b_gain_sft;
+}
+
+static inline unsigned int rt715_sdca_get_gain(unsigned int reg_val,
+ unsigned int gain_sft)
+{
+ unsigned int neg_flag = 0;
+
+ if (reg_val & BIT(15)) {
+ reg_val = ~(reg_val - 1) & 0xffff;
+ neg_flag = 1;
+ }
+ reg_val *= 1000;
+ reg_val >>= 8;
+ if (neg_flag)
+ reg_val = gain_sft - reg_val / RT715_SDCA_DB_STEP;
+ else
+ reg_val = gain_sft + reg_val / RT715_SDCA_DB_STEP;
+
+ return reg_val;
+}
+
+/* SDCA Volume/Boost control */
+static int rt715_sdca_set_amp_gain_put(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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val, i, k_changed = 0;
+ int ret;
+
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ rt715->kctl_2ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_vol_gain(ucontrol->value.integer.value[i], mc->max,
+ mc->shift);
+ ret = regmap_write(rt715->mbq_regmap, mc->reg + i, gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, mc->reg + i, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_4ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, k_changed = 0;
+ const unsigned int gain_sft = 0x2f;
+ unsigned int gain_val, i;
+ int ret;
+
+ for (i = 0; i < 4; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_4ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ rt715->kctl_4ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_vol_gain(ucontrol->value.integer.value[i], p->max,
+ gain_sft);
+ ret = regmap_write(rt715->mbq_regmap, reg_base + i,
+ gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, reg_base + i, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_8ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, i, k_changed = 0;
+ const unsigned int gain_sft = 8;
+ unsigned int gain_val, reg;
+ int ret;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ rt715->kctl_8ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_boost_gain(ucontrol->value.integer.value[i], p->max,
+ gain_sft);
+ reg = i < 7 ? reg_base + i : (reg_base - 1) | BIT(15);
+ ret = regmap_write(rt715->mbq_regmap, reg, gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, reg, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int val, i;
+ int ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_read(rt715->mbq_regmap, mc->reg + i, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, mc->reg + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, mc->shift);
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_set_amp_gain_4ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, i;
+ const unsigned int gain_sft = 0x2f;
+ unsigned int val;
+ int ret;
+
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg_base + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, gain_sft);
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base;
+ const unsigned int gain_sft = 8;
+ unsigned int val_l, val_r;
+ unsigned int i, reg;
+ int ret;
+
+ for (i = 0; i < 8; i += 2) {
+ ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val_l);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg_base + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = (val_l >> gain_sft) / 10;
+
+ reg = (i == 6) ? (reg_base - 1) | BIT(15) : reg_base + 1 + i;
+ ret = regmap_read(rt715->mbq_regmap, reg, &val_r);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i + 1] = (val_r >> gain_sft) / 10;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static int rt715_sdca_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base;
+ unsigned int invert = p->invert, i;
+ int val;
+
+ for (i = 0; i < p->count; i += 2) {
+ val = snd_soc_component_read(component, reg_base + i);
+ if (val < 0)
+ return -EINVAL;
+ ucontrol->value.integer.value[i] = invert ? p->max - val : val;
+
+ val = snd_soc_component_read(component, reg_base + 1 + i);
+ if (val < 0)
+ return -EINVAL;
+ ucontrol->value.integer.value[i + 1] =
+ invert ? p->max - val : val;
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int val[4] = {0}, val_mask, i, k_changed = 0;
+ unsigned int reg = p->reg_base;
+ unsigned int shift = p->shift;
+ unsigned int max = p->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = p->invert;
+ int err;
+
+ for (i = 0; i < 4; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_switch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ rt715->kctl_switch_orig[i * 2] = ucontrol->value.integer.value[i * 2];
+ val[i * 2] = ucontrol->value.integer.value[i * 2] & mask;
+ if (invert)
+ val[i * 2] = max - val[i * 2];
+ val_mask = mask << shift;
+ val[i * 2] <<= shift;
+
+ rt715->kctl_switch_orig[i * 2 + 1] =
+ ucontrol->value.integer.value[i * 2 + 1];
+ val[i * 2 + 1] =
+ ucontrol->value.integer.value[i * 2 + 1] & mask;
+ if (invert)
+ val[i * 2 + 1] = max - val[i * 2 + 1];
+
+ val[i * 2 + 1] <<= shift;
+
+ err = snd_soc_component_update_bits(component, reg + i * 2, val_mask,
+ val[i * 2]);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_component_update_bits(component, reg + 1 + i * 2,
+ val_mask, val[i * 2 + 1]);
+ if (err < 0)
+ return err;
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT715_SDCA_PR_VALUE(xreg_base, xcount, xmax, xshift, xinvert) \
+ ((unsigned long)&(struct rt715_sdca_kcontrol_private) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .shift = xshift, .invert = xinvert})
+
+#define RT715_SDCA_FU_CTRL(xname, reg_base, xshift, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt715_sdca_fu_info, \
+ .get = rt715_sdca_get_volsw, \
+ .put = rt715_sdca_put_volsw, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, \
+ xshift, xinvert)}
+
+#define RT715_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, tlv_array, xcount, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) }
+
+#define RT715_SDCA_BOOST_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, tlv_array, xcount, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) }
+
+static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = {
+ /* Capture switch */
+ SOC_DOUBLE_R("FU0A Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02),
+ 0, 1, 1),
+ RT715_SDCA_FU_CTRL("FU02 Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ 0, 1, 1, 4),
+ RT715_SDCA_FU_CTRL("FU06 Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ 0, 1, 1, 4),
+ /* Volume Control */
+ SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02),
+ 0x2f, 0x3f, 0,
+ rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+ in_vol_tlv),
+ RT715_SDCA_EXT_TLV("FU02 Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_4ch_get,
+ rt715_sdca_set_amp_gain_4ch_put,
+ in_vol_tlv, 4, 0x3f),
+ RT715_SDCA_EXT_TLV("FU06 Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_4ch_get,
+ rt715_sdca_set_amp_gain_4ch_put,
+ in_vol_tlv, 4, 0x3f),
+ /* MIC Boost Control */
+ RT715_SDCA_BOOST_EXT_TLV("FU0E Boost",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_8ch_get,
+ rt715_sdca_set_amp_gain_8ch_put,
+ mic_vol_tlv, 8, 3),
+ RT715_SDCA_BOOST_EXT_TLV("FU0C Boost",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_8ch_get,
+ rt715_sdca_set_amp_gain_8ch_put,
+ mic_vol_tlv, 8, 3),
+};
+
+static int rt715_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int val, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 12;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+ mask_sft = 4;
+ else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 0;
+ else
+ return -EINVAL;
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, &val);
+ val = (val >> mask_sft) & 0xf;
+
+ /*
+ * The first two indices of ADC Mux 24/25 are routed to the same
+ * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
+ * To have a unique set of inputs, we skip the index1 of the muxes.
+ */
+ if ((strstr(ucontrol->id.name, "ADC 24 Mux") ||
+ strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0)
+ val -= 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt715_sdca_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 12;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+ mask_sft = 4;
+ else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 0;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h, nid = e->reg */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, &val2);
+ val2 = (val2 >> mask_sft) & 0xf;
+
+ change = val != val2;
+
+ if (change)
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_22_23_mux_text[] = {
+ "MIC1",
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+/*
+ * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
+ * 1 will be connected to the same dmic source, therefore we skip index 1 to
+ * avoid misunderstanding on usage of dapm routing.
+ */
+static int rt715_adc_24_25_values[] = {
+ 0,
+ 2,
+ 3,
+ 4,
+ 5,
+};
+
+static const char * const adc_24_mux_text[] = {
+ "MIC2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static const char * const adc_25_mux_text[] = {
+ "MIC1",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0,
+ adc_22_23_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0,
+ adc_22_23_mux_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
+ SND_SOC_NOPM, 0, 0xf,
+ adc_24_mux_text, rt715_adc_24_25_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
+ SND_SOC_NOPM, 0, 0xf,
+ adc_25_mux_text, rt715_adc_24_25_values);
+
+static const struct snd_kcontrol_new rt715_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc24_mux =
+ SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static int rt715_sdca_pde23_24_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+ RT715_SDCA_REQ_POW_CTRL,
+ CH_00), 0x00);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+ RT715_SDCA_REQ_POW_CTRL,
+ CH_00), 0x03);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt715_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0,
+ rt715_sdca_pde23_24_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc23_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc25_mux),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = {
+ {"DP6TX", NULL, "ADC 09"},
+ {"DP6TX", NULL, "ADC 08"},
+ {"DP4TX", NULL, "ADC 07"},
+ {"DP4TX", NULL, "ADC 27"},
+ {"DP4TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+
+ {"LINE1", NULL, "PDE23_24"},
+ {"LINE2", NULL, "PDE23_24"},
+ {"MIC1", NULL, "PDE23_24"},
+ {"MIC2", NULL, "PDE23_24"},
+ {"DMIC1", NULL, "PDE23_24"},
+ {"DMIC2", NULL, "PDE23_24"},
+ {"DMIC3", NULL, "PDE23_24"},
+ {"DMIC4", NULL, "PDE23_24"},
+
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 07", NULL, "ADC 24 Mux"},
+ {"ADC 27", NULL, "ADC 25 Mux"},
+ {"ADC 22 Mux", "MIC1", "MIC1"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "DMIC1", "DMIC1"},
+ {"ADC 22 Mux", "DMIC2", "DMIC2"},
+ {"ADC 22 Mux", "DMIC3", "DMIC3"},
+ {"ADC 22 Mux", "DMIC4", "DMIC4"},
+ {"ADC 23 Mux", "MIC1", "MIC1"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "DMIC1", "DMIC1"},
+ {"ADC 23 Mux", "DMIC2", "DMIC2"},
+ {"ADC 23 Mux", "DMIC3", "DMIC3"},
+ {"ADC 23 Mux", "DMIC4", "DMIC4"},
+ {"ADC 24 Mux", "MIC2", "MIC2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1"},
+ {"ADC 24 Mux", "DMIC2", "DMIC2"},
+ {"ADC 24 Mux", "DMIC3", "DMIC3"},
+ {"ADC 24 Mux", "DMIC4", "DMIC4"},
+ {"ADC 25 Mux", "MIC1", "MIC1"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 25 Mux", "DMIC3", "DMIC3"},
+ {"ADC 25 Mux", "DMIC4", "DMIC4"},
+};
+
+static int rt715_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
+ .probe = rt715_sdca_probe,
+ .controls = rt715_sdca_snd_controls,
+ .num_controls = ARRAY_SIZE(rt715_sdca_snd_controls),
+ .dapm_widgets = rt715_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets),
+ .dapm_routes = rt715_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map),
+ .endianness = 1,
+};
+
+static int rt715_sdca_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 rt715_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt715_sdca_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 rt715_sdca_priv *rt715 = 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;
+ unsigned int val;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ switch (dai->id) {
+ case RT715_AIF1:
+ port_config.num = 6;
+ rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+ 0xa500);
+ break;
+ case RT715_AIF2:
+ port_config.num = 4;
+ rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+ 0xaf00);
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(component->dev, "%s: Unable to configure port, retval:%d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0x1;
+ break;
+ case 11025:
+ val = 0x2;
+ break;
+ case 12000:
+ val = 0x3;
+ break;
+ case 16000:
+ val = 0x4;
+ break;
+ case 22050:
+ val = 0x5;
+ break;
+ case 24000:
+ val = 0x6;
+ break;
+ case 32000:
+ val = 0x7;
+ break;
+ case 44100:
+ val = 0x8;
+ break;
+ case 48000:
+ val = 0x9;
+ break;
+ case 88200:
+ val = 0xa;
+ break;
+ case 96000:
+ val = 0xb;
+ break;
+ case 176400:
+ val = 0xc;
+ break;
+ case 192000:
+ val = 0xd;
+ break;
+ case 384000:
+ val = 0xe;
+ break;
+ case 768000:
+ val = 0xf;
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported sample rate %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN,
+ RT715_SDCA_FREQ_IND_CTRL, CH_00), val);
+
+ return 0;
+}
+
+static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
+ return 0;
+}
+
+#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT715_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 rt715_sdca_ops = {
+ .hw_params = rt715_sdca_pcm_hw_params,
+ .hw_free = rt715_sdca_pcm_hw_free,
+ .set_stream = rt715_sdca_set_sdw_stream,
+ .shutdown = rt715_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt715_sdca_dai[] = {
+ {
+ .name = "rt715-sdca-aif1",
+ .id = RT715_AIF1,
+ .capture = {
+ .stream_name = "DP6 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_sdca_ops,
+ },
+ {
+ .name = "rt715-sdca-aif2",
+ .id = RT715_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_sdca_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT715_CLK_FREQ_9600000HZ 9600000
+#define RT715_CLK_FREQ_12000000HZ 12000000
+#define RT715_CLK_FREQ_6000000HZ 6000000
+#define RT715_CLK_FREQ_4800000HZ 4800000
+#define RT715_CLK_FREQ_2400000HZ 2400000
+#define RT715_CLK_FREQ_12288000HZ 12288000
+
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt715_sdca_priv *rt715;
+ int ret;
+
+ rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
+ if (!rt715)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt715);
+ rt715->slave = slave;
+ rt715->regmap = regmap;
+ rt715->mbq_regmap = mbq_regmap;
+ rt715->hw_sdw_ver = slave->id.sdw_version;
+
+ regcache_cache_only(rt715->regmap, true);
+ regcache_cache_only(rt715->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt715->hw_init = false;
+ rt715->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt715_sdca,
+ rt715_sdca_dai,
+ ARRAY_SIZE(rt715_sdca_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int hw_ver;
+
+ if (rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, false);
+ regcache_cache_only(rt715->mbq_regmap, false);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt715->first_hw_init) {
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ rt715->first_hw_init = true;
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_REG,
+ RT715_PRODUCT_NUM, &hw_ver);
+ hw_ver = hw_ver & 0x000f;
+
+ /* set clock selector = external */
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+ RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1);
+ /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */
+ if (hw_ver == 0x0)
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_AD_FUNC_EN, 0x54, 0x54);
+ else if (hw_ver == 0x1) {
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_AD_FUNC_EN, 0x55, 0x55);
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_REV_1, 0x40, 0x40);
+ }
+ /* DFLL Calibration trigger */
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_DFLL_VAD, 0x1, 0x1);
+ /* trigger mode = VAD enable */
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2);
+ /* SMPU-1 interrupt enable mask */
+ regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1);
+
+ /* Mark Slave initialization complete */
+ rt715->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h
new file mode 100644
index 000000000000..e5d6928ecaba
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDCA_H__
+#define __RT715_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+struct rt715_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_codec *codec;
+ struct sdw_slave *slave;
+ struct delayed_work adc_mute_work;
+ int dbg_nid;
+ int dbg_vid;
+ int dbg_payload;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int l_is_unmute;
+ int r_is_unmute;
+ int hw_sdw_ver;
+ int kctl_switch_orig[4];
+ int kctl_2ch_orig[2];
+ int kctl_4ch_orig[4];
+ int kctl_8ch_orig[8];
+};
+
+struct rt715_sdca_kcontrol_private {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int shift;
+ unsigned int invert;
+};
+
+/* MIPI Register */
+#define RT715_INT_CTRL 0x005a
+#define RT715_INT_MASK 0x005e
+
+/* NID */
+#define RT715_AUDIO_FUNCTION_GROUP 0x01
+#define RT715_MIC_ADC 0x07
+#define RT715_LINE_ADC 0x08
+#define RT715_MIX_ADC 0x09
+#define RT715_DMIC1 0x12
+#define RT715_DMIC2 0x13
+#define RT715_MIC1 0x18
+#define RT715_MIC2 0x19
+#define RT715_LINE1 0x1a
+#define RT715_LINE2 0x1b
+#define RT715_DMIC3 0x1d
+#define RT715_DMIC4 0x29
+#define RT715_VENDOR_REG 0x20
+#define RT715_MUX_IN1 0x22
+#define RT715_MUX_IN2 0x23
+#define RT715_MUX_IN3 0x24
+#define RT715_MUX_IN4 0x25
+#define RT715_MIX_ADC2 0x27
+#define RT715_INLINE_CMD 0x55
+#define RT715_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT715_PRODUCT_NUM 0x0
+#define RT715_IRQ_CTRL 0x2b
+#define RT715_AD_FUNC_EN 0x36
+#define RT715_REV_1 0x37
+#define RT715_SDW_INPUT_SEL 0x39
+#define RT715_DFLL_VAD 0x44
+#define RT715_EXT_DMIC_CLK_CTRL2 0x54
+
+/* Index (NID:61h) */
+#define RT715_HDA_LEGACY_MUX_CTL1 0x00
+
+/* SDCA (Function) */
+#define FUN_JACK_CODEC 0x01
+#define FUN_MIC_ARRAY 0x02
+#define FUN_HID 0x03
+/* SDCA (Entity) */
+#define RT715_SDCA_ST_EN 0x00
+#define RT715_SDCA_CS_FREQ_IND_EN 0x01
+#define RT715_SDCA_FU_ADC8_9_VOL 0x02
+#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05
+#define RT715_SDCA_FU_ADC10_11_VOL 0x06
+#define RT715_SDCA_FU_ADC7_27_VOL 0x0a
+#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c
+#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e
+#define RT715_SDCA_CX_CLK_SEL_EN 0x10
+#define RT715_SDCA_CREQ_POW_EN 0x18
+/* SDCA (Control) */
+#define RT715_SDCA_ST_CTRL 0x00
+#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01
+#define RT715_SDCA_REQ_POW_CTRL 0x01
+#define RT715_SDCA_FU_MUTE_CTRL 0x01
+#define RT715_SDCA_FU_VOL_CTRL 0x02
+#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b
+#define RT715_SDCA_FREQ_IND_CTRL 0x10
+#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10
+#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11
+/* SDCA (Channel) */
+#define CH_00 0x00
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_05 0x05
+#define CH_06 0x06
+#define CH_07 0x07
+#define CH_08 0x08
+
+#define RT715_SDCA_DB_STEP 375
+
+enum {
+ RT715_AIF1,
+ RT715_AIF2,
+};
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+#endif /* __RT715_SDCA_H__ */
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
new file mode 100644
index 000000000000..a3df4bbedf86
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt715-sdw.c -- rt715 ALSA SoC audio driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
+ *
+ */
+#include <linux/delay.h>
+#include <linux/device.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/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt715.h"
+#include "rt715-sdw.h"
+
+static bool rt715_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0 ... 0x00e5:
+ case 0x00ee ... 0x00ef:
+ case 0x00f0 ... 0x00f5:
+ case 0x00fe ... 0x00ff:
+ case 0x02e0:
+ case 0x02f0:
+ case 0x04e0:
+ case 0x04f0:
+ case 0x06e0:
+ case 0x06f0:
+ case 0x2000 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x22f0 ... 0x22f3:
+ case 0x3122:
+ case 0x3123:
+ case 0x3124:
+ case 0x3125:
+ case 0x3607:
+ case 0x3608:
+ case 0x3609:
+ case 0x3610:
+ case 0x3611:
+ case 0x3627:
+ case 0x3712:
+ case 0x3713:
+ case 0x3718:
+ case 0x3719:
+ case 0x371a:
+ case 0x371b:
+ case 0x371d:
+ case 0x3729:
+ case 0x385e:
+ case 0x3859:
+ case 0x4c12:
+ case 0x4c13:
+ case 0x4c1d:
+ case 0x4c29:
+ case 0x4d12:
+ case 0x4d13:
+ case 0x4d1d:
+ case 0x4d29:
+ case 0x4e12:
+ case 0x4e13:
+ case 0x4e1d:
+ case 0x4e29:
+ case 0x4f12:
+ case 0x4f13:
+ case 0x4f1d:
+ case 0x4f29:
+ case 0x7207:
+ case 0x7208:
+ case 0x7209:
+ case 0x7227:
+ case 0x7307:
+ case 0x7308:
+ case 0x7309:
+ case 0x7312:
+ case 0x7313:
+ case 0x7318:
+ case 0x7319:
+ case 0x731a:
+ case 0x731b:
+ case 0x731d:
+ case 0x7327:
+ case 0x7329:
+ case 0x8287:
+ case 0x8288:
+ case 0x8289:
+ case 0x82a7:
+ case 0x8387:
+ case 0x8388:
+ case 0x8389:
+ case 0x8392:
+ case 0x8393:
+ case 0x8398:
+ case 0x8399:
+ case 0x839a:
+ case 0x839b:
+ case 0x839d:
+ case 0x83a7:
+ case 0x83a9:
+ case 0x752001:
+ case 0x752039:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e5:
+ case 0x00f0:
+ case 0x00f3:
+ case 0x00f5:
+ case 0x2009:
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2023:
+ case 0x2230:
+ case 0x200b ... 0x200e: /* i2c read */
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt715->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT715_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt715->sdw_regmap, reg3,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt715->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt715->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3,
+ &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2,
+ &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1,
+ &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0,
+ &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__,
+ reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt715->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT715_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt715->sdw_regmap, reg3,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == RT715_FUNC_RESET) {
+ ret = regmap_write(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt715_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt715_readable_register, /* Readable registers */
+ .volatile_reg = rt715_volatile_register, /* volatile register */
+ .max_register = 0x752039, /* Maximum number of register */
+ .reg_defaults = rt715_reg_defaults, /* Defaults */
+ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt715_sdw_read,
+ .reg_write = rt715_sdw_write,
+};
+
+static const struct regmap_config rt715_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32, /* Total register space for SDW */
+ .val_bits = 8, /* Total number of bits in register */
+ .max_register = 0xff01, /* Maximum number of register */
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt715_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt715_io_init(&slave->dev, slave);
+}
+
+static int rt715_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_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x50;/* BITMAP: 01010000 */
+ prop->sink_ports = 0x0; /* BITMAP: 00000000 */
+
+ 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;
+
+ dpn = prop->src_dpn_prop;
+ i = 0;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt715_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt715->params, params, sizeof(*params));
+
+ ret = rt715_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt715_slave_ops = {
+ .read_prop = rt715_read_prop,
+ .update_status = rt715_update_status,
+ .bus_config = rt715_bus_config,
+};
+
+static int rt715_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev,
+ &rt715_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt715_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt715_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt715_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt715_id);
+
+static int rt715_dev_suspend(struct device *dev)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+
+ if (!rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, true);
+
+ return 0;
+}
+
+#define RT715_PROBE_TIMEOUT 5000
+
+static int rt715_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt715->regmap, false);
+ regcache_sync_region(rt715->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt715->regmap, 0x752039, 0x752039);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt715_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715",
+ .pm = pm_ptr(&rt715_pm),
+ },
+ .probe = rt715_sdw_probe,
+ .remove = rt715_sdw_remove,
+ .ops = &rt715_slave_ops,
+ .id_table = rt715_id,
+};
+module_sdw_driver(rt715_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT715 driver SDW");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h
new file mode 100644
index 000000000000..5d7661e335ae
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdw.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt715-sdw.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDW_H__
+#define __RT715_SDW_H__
+
+static const struct reg_default rt715_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, 0x00 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5d },
+ { 0x0053, 0x07 },
+ { 0x0054, 0x15 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x0080, 0x00 },
+ { 0x0088, 0x10 },
+ { 0x00e0, 0x00 },
+ { 0x00e1, 0x00 },
+ { 0x00e2, 0x00 },
+ { 0x00e3, 0x00 },
+ { 0x00e4, 0x00 },
+ { 0x00e5, 0x00 },
+ { 0x00ee, 0x00 },
+ { 0x00ef, 0x00 },
+ { 0x00f0, 0x00 },
+ { 0x00f1, 0x00 },
+ { 0x00f2, 0x00 },
+ { 0x00f3, 0x00 },
+ { 0x00f4, 0x00 },
+ { 0x00f5, 0x00 },
+ { 0x00fe, 0x00 },
+ { 0x00ff, 0x00 },
+ { 0x0200, 0x00 },
+ { 0x0201, 0x00 },
+ { 0x0202, 0x20 },
+ { 0x0203, 0x00 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x03 },
+ { 0x0220, 0x00 },
+ { 0x0221, 0x00 },
+ { 0x0222, 0x00 },
+ { 0x0223, 0x00 },
+ { 0x0224, 0x00 },
+ { 0x0225, 0x00 },
+ { 0x0226, 0x00 },
+ { 0x0227, 0x00 },
+ { 0x0230, 0x00 },
+ { 0x0231, 0x00 },
+ { 0x0232, 0x00 },
+ { 0x0233, 0x00 },
+ { 0x0234, 0x00 },
+ { 0x0235, 0x00 },
+ { 0x0236, 0x00 },
+ { 0x0237, 0x00 },
+ { 0x02e0, 0x00 },
+ { 0x02f0, 0x00 },
+ { 0x0400, 0x00 },
+ { 0x0401, 0x00 },
+ { 0x0402, 0x20 },
+ { 0x0403, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x0f },
+ { 0x0420, 0x00 },
+ { 0x0421, 0x00 },
+ { 0x0422, 0x00 },
+ { 0x0423, 0x00 },
+ { 0x0424, 0x00 },
+ { 0x0425, 0x00 },
+ { 0x0426, 0x00 },
+ { 0x0427, 0x00 },
+ { 0x0430, 0x00 },
+ { 0x0431, 0x00 },
+ { 0x0432, 0x00 },
+ { 0x0433, 0x00 },
+ { 0x0434, 0x00 },
+ { 0x0435, 0x00 },
+ { 0x0436, 0x00 },
+ { 0x0437, 0x00 },
+ { 0x04e0, 0x00 },
+ { 0x04f0, 0x00 },
+ { 0x0600, 0x00 },
+ { 0x0601, 0x00 },
+ { 0x0602, 0x20 },
+ { 0x0603, 0x00 },
+ { 0x0604, 0x00 },
+ { 0x0605, 0xff },
+ { 0x0620, 0x00 },
+ { 0x0621, 0x00 },
+ { 0x0622, 0x00 },
+ { 0x0623, 0x00 },
+ { 0x0624, 0x00 },
+ { 0x0625, 0x00 },
+ { 0x0626, 0x00 },
+ { 0x0627, 0x00 },
+ { 0x0630, 0x00 },
+ { 0x0631, 0x00 },
+ { 0x0632, 0x00 },
+ { 0x0633, 0x00 },
+ { 0x0634, 0x00 },
+ { 0x0635, 0x00 },
+ { 0x0636, 0x00 },
+ { 0x0637, 0x00 },
+ { 0x06e0, 0x00 },
+ { 0x06f0, 0x00 },
+ { 0x0f00, 0x00 },
+ { 0x0f01, 0x00 },
+ { 0x0f02, 0x00 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0xff },
+ { 0x0f06, 0x00 },
+ { 0x0f07, 0x00 },
+ { 0x0f08, 0x00 },
+ { 0x0f09, 0x00 },
+ { 0x0f0a, 0x00 },
+ { 0x0f0b, 0x00 },
+ { 0x0f0c, 0x00 },
+ { 0x0f0d, 0x00 },
+ { 0x0f0e, 0x00 },
+ { 0x0f0f, 0x00 },
+ { 0x0f10, 0x00 },
+ { 0x0f11, 0x00 },
+ { 0x0f12, 0x00 },
+ { 0x0f13, 0x00 },
+ { 0x0f14, 0x00 },
+ { 0x0f15, 0x00 },
+ { 0x0f16, 0x00 },
+ { 0x0f17, 0x00 },
+ { 0x0f18, 0x00 },
+ { 0x0f19, 0x00 },
+ { 0x0f1a, 0x00 },
+ { 0x0f1b, 0x00 },
+ { 0x0f1c, 0x00 },
+ { 0x0f1d, 0x00 },
+ { 0x0f1e, 0x00 },
+ { 0x0f1f, 0x00 },
+ { 0x0f20, 0x00 },
+ { 0x0f21, 0x00 },
+ { 0x0f22, 0x00 },
+ { 0x0f23, 0x00 },
+ { 0x0f24, 0x00 },
+ { 0x0f25, 0x00 },
+ { 0x0f26, 0x00 },
+ { 0x0f27, 0x00 },
+ { 0x0f30, 0x00 },
+ { 0x0f31, 0x00 },
+ { 0x0f32, 0x00 },
+ { 0x0f33, 0x00 },
+ { 0x0f34, 0x00 },
+ { 0x0f35, 0x00 },
+ { 0x0f36, 0x00 },
+ { 0x0f37, 0x00 },
+ { 0x2000, 0x00 },
+ { 0x2001, 0x00 },
+ { 0x2002, 0x00 },
+ { 0x2003, 0x00 },
+ { 0x2004, 0x00 },
+ { 0x2005, 0x00 },
+ { 0x2006, 0x00 },
+ { 0x2007, 0x00 },
+ { 0x2008, 0x00 },
+ { 0x2009, 0x03 },
+ { 0x200a, 0x00 },
+ { 0x200b, 0x00 },
+ { 0x200c, 0x00 },
+ { 0x200d, 0x00 },
+ { 0x200e, 0x00 },
+ { 0x200f, 0x10 },
+ { 0x2010, 0x00 },
+ { 0x2011, 0x00 },
+ { 0x2012, 0x00 },
+ { 0x2013, 0x00 },
+ { 0x2014, 0x00 },
+ { 0x2015, 0x00 },
+ { 0x2016, 0x00 },
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2200, 0x00 },
+ { 0x2201, 0x00 },
+ { 0x2202, 0x00 },
+ { 0x2203, 0x00 },
+ { 0x2204, 0x00 },
+ { 0x2206, 0x00 },
+ { 0x2207, 0x00 },
+ { 0x2208, 0x00 },
+ { 0x2209, 0x00 },
+ { 0x220a, 0x00 },
+ { 0x220b, 0x00 },
+ { 0x220c, 0x00 },
+ { 0x220d, 0x00 },
+ { 0x220e, 0x00 },
+ { 0x220f, 0x00 },
+ { 0x2210, 0x00 },
+ { 0x2211, 0x00 },
+ { 0x2212, 0x00 },
+ { 0x2220, 0x00 },
+ { 0x2221, 0x00 },
+ { 0x2222, 0x00 },
+ { 0x2223, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x0f },
+ { 0x2232, 0x00 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x22f0, 0x00 },
+ { 0x22f1, 0x00 },
+ { 0x22f2, 0x00 },
+ { 0x22f3, 0x00 },
+ { 0x3122, 0x02 },
+ { 0x3123, 0x03 },
+ { 0x3124, 0x00 },
+ { 0x3125, 0x01 },
+ { 0x3607, 0x00 },
+ { 0x3608, 0x00 },
+ { 0x3609, 0x00 },
+ { 0x3610, 0x00 },
+ { 0x3611, 0x00 },
+ { 0x3627, 0x00 },
+ { 0x3712, 0x00 },
+ { 0x3713, 0x00 },
+ { 0x3718, 0x00 },
+ { 0x3719, 0x00 },
+ { 0x371a, 0x00 },
+ { 0x371b, 0x00 },
+ { 0x371d, 0x00 },
+ { 0x3729, 0x00 },
+ { 0x385e, 0x00 },
+ { 0x3859, 0x00 },
+ { 0x4c12, 0x411111f0 },
+ { 0x4c13, 0x411111f0 },
+ { 0x4c1d, 0x411111f0 },
+ { 0x4c29, 0x411111f0 },
+ { 0x4d12, 0x411111f0 },
+ { 0x4d13, 0x411111f0 },
+ { 0x4d1d, 0x411111f0 },
+ { 0x4d29, 0x411111f0 },
+ { 0x4e12, 0x411111f0 },
+ { 0x4e13, 0x411111f0 },
+ { 0x4e1d, 0x411111f0 },
+ { 0x4e29, 0x411111f0 },
+ { 0x4f12, 0x411111f0 },
+ { 0x4f13, 0x411111f0 },
+ { 0x4f1d, 0x411111f0 },
+ { 0x4f29, 0x411111f0 },
+ { 0x7207, 0x00 },
+ { 0x8287, 0x00 },
+ { 0x7208, 0x00 },
+ { 0x8288, 0x00 },
+ { 0x7209, 0x00 },
+ { 0x8289, 0x00 },
+ { 0x7227, 0x00 },
+ { 0x82a7, 0x00 },
+ { 0x7307, 0x97 },
+ { 0x8387, 0x97 },
+ { 0x7308, 0x97 },
+ { 0x8388, 0x97 },
+ { 0x7309, 0x97 },
+ { 0x8389, 0x97 },
+ { 0x7312, 0x00 },
+ { 0x8392, 0x00 },
+ { 0x7313, 0x00 },
+ { 0x8393, 0x00 },
+ { 0x7318, 0x00 },
+ { 0x8398, 0x00 },
+ { 0x7319, 0x00 },
+ { 0x8399, 0x00 },
+ { 0x731a, 0x00 },
+ { 0x839a, 0x00 },
+ { 0x731b, 0x00 },
+ { 0x839b, 0x00 },
+ { 0x731d, 0x00 },
+ { 0x839d, 0x00 },
+ { 0x7327, 0x97 },
+ { 0x83a7, 0x97 },
+ { 0x7329, 0x00 },
+ { 0x83a9, 0x00 },
+ { 0x752039, 0xa500 },
+};
+
+#endif /* __RT715_H__ */
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
new file mode 100644
index 000000000000..7e1628a5c9d1
--- /dev/null
+++ b/sound/soc/codecs/rt715.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt715.c -- rt715 ALSA SoC audio driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.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 <sound/tlv.h>
+#include <sound/hda_verbs.h>
+
+#include "rt715.h"
+
+static int rt715_index_write(struct regmap *regmap, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0) {
+ pr_err("%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+ }
+
+ return ret;
+}
+
+static int rt715_index_write_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt715_index_read_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp, orig;
+ int ret;
+
+ ret = rt715_index_read_nid(regmap, nid, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return rt715_index_write_nid(regmap, nid, reg, tmp);
+}
+
+static void rt715_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT715_FUNC_RESET, 0);
+ rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS,
+ RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG,
+ RT715_CLEAR_HIDDEN_REG);
+}
+
+
+static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ int ret;
+ /* R Channel */
+ *r_val = val_h << 8;
+ ret = regmap_read(rt715->regmap, addr_l, r_val);
+ if (ret < 0)
+ pr_err("Failed to get R channel gain.\n");
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = val_h << 8;
+ ret = regmap_read(rt715->regmap, addr_h, l_val);
+ if (ret < 0)
+ pr_err("Failed to get L channel gain.\n");
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl, i;
+ unsigned int k_vol_changed = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_vol_ori[i]) {
+ k_vol_changed = 1;
+ break;
+ }
+ }
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ rt715->kctl_2ch_vol_ori[0] = ucontrol->value.integer.value[0];
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ val_ll |= read_ll & 0x80;
+
+ /* R Channel */
+ rt715->kctl_2ch_vol_ori[1] = ucontrol->value.integer.value[1];
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ val_lr |= read_rl & 0x80;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ /* check result */
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_vol_changed;
+}
+
+static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* for mute status */
+ read_ll = !(read_ll & 0x80);
+ read_rl = !(read_rl & 0x80);
+ } else {
+ /* for gain */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static int rt715_set_main_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr;
+ unsigned int k_shift = RT715_DIR_IN_SFT, k_changed = 0;
+ unsigned int read_ll, read_rl, i, j, loop_cnt = 4;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_switch_ori[i])
+ k_changed = 1;
+ }
+
+ for (j = 0; j < loop_cnt; j++) {
+ /* Can't use update bit function, so read the original value first */
+ addr_h = capture_reg_H[j];
+ addr_l = capture_reg_L[j];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ /* for mute */
+ rt715->kctl_8ch_switch_ori[j * 2] =
+ ucontrol->value.integer.value[j * 2];
+ val_ll = (!ucontrol->value.integer.value[j * 2]) << 7;
+ /* keep gain */
+ val_ll |= read_ll & 0x7f;
+
+ /* R Channel */
+ /* for mute */
+ rt715->kctl_8ch_switch_ori[j * 2 + 1] =
+ ucontrol->value.integer.value[j * 2 + 1];
+ val_lr = (!ucontrol->value.integer.value[j * 2 + 1]) << 7;
+ /* keep gain */
+ val_lr |= read_rl & 0x7f;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << k_shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << k_shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << k_shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ val_h = 0x0;
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_changed;
+}
+
+static int rt715_set_main_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4;
+ unsigned int read_ll, read_rl;
+
+ for (i = 0; i < loop_cnt; i++) {
+ addr_h = capture_reg_H[i];
+ addr_l = capture_reg_L[i];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ ucontrol->value.integer.value[i * 2] = !(read_ll & 0x80);
+ ucontrol->value.integer.value[i * 2 + 1] = !(read_rl & 0x80);
+ }
+
+ return 0;
+}
+
+static int rt715_set_main_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L};
+ unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr;
+ unsigned int read_ll, read_rl, i, j, loop_cnt = 4, k_changed = 0;
+ unsigned int k_shift = RT715_DIR_IN_SFT, k_max = 0x3f;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_vol_ori[i])
+ k_changed = 1;
+ }
+
+ for (j = 0; j < loop_cnt; j++) {
+ addr_h = capture_reg_H[j];
+ addr_l = capture_reg_L[j];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ /* for gain */
+ rt715->kctl_8ch_vol_ori[j * 2] = ucontrol->value.integer.value[j * 2];
+ val_ll = ((ucontrol->value.integer.value[j * 2]) & 0x7f);
+ if (val_ll > k_max)
+ val_ll = k_max;
+ /* keep mute status */
+ val_ll |= read_ll & 0x80;
+
+ /* R Channel */
+ /* for gain */
+ rt715->kctl_8ch_vol_ori[j * 2 + 1] =
+ ucontrol->value.integer.value[j * 2 + 1];
+ val_lr = ((ucontrol->value.integer.value[j * 2 + 1]) & 0x7f);
+ if (val_lr > k_max)
+ val_lr = k_max;
+ /* keep mute status */
+ val_lr |= read_rl & 0x80;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << k_shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << k_shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << k_shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ val_h = 0x0;
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_changed;
+}
+
+static int rt715_set_main_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4;
+ unsigned int read_ll, read_rl;
+
+ for (i = 0; i < loop_cnt; i++) {
+ addr_h = capture_reg_H[i];
+ addr_l = capture_reg_L[i];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ ucontrol->value.integer.value[i * 2] = read_ll & 0x7f;
+ ucontrol->value.integer.value[i * 2 + 1] = read_rl & 0x7f;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static int rt715_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int rt715_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x3f;
+ return 0;
+}
+
+#define RT715_MAIN_SWITCH_EXT(xname, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt715_switch_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+}
+
+#define RT715_MAIN_VOL_EXT_TLV(xname, xhandler_get, xhandler_put, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_vol_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+}
+
+static const struct snd_kcontrol_new rt715_snd_controls[] = {
+ /* Capture switch */
+ RT715_MAIN_SWITCH_EXT("Capture Switch",
+ rt715_set_main_switch_get, rt715_set_main_switch_put),
+ /* Volume Control */
+ RT715_MAIN_VOL_EXT_TLV("Capture Volume",
+ rt715_set_main_vol_get, rt715_set_main_vol_put, in_vol_tlv),
+ /* MIC Boost Control */
+ SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H,
+ RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H,
+ RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H,
+ RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H,
+ RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H,
+ RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H,
+ RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H,
+ RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H,
+ RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+};
+
+static int rt715_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg, val;
+ int ret;
+
+ /* nid = e->reg, vid = 0xf01 */
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ ret = regmap_read(rt715->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * The first two indices of ADC Mux 24/25 are routed to the same
+ * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
+ * To have a unique set of inputs, we skip the index1 of the muxes.
+ */
+ if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0))
+ val -= 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt715_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 rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ /* Verb ID = 0x701h, nid = e->reg */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ ret = regmap_read(rt715->regmap, reg, &val2);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ regmap_write(rt715->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_22_23_mux_text[] = {
+ "MIC1",
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+/*
+ * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
+ * 1 will be connected to the same dmic source, therefore we skip index 1 to
+ * avoid misunderstanding on usage of dapm routing.
+ */
+static const unsigned int rt715_adc_24_25_values[] = {
+ 0,
+ 2,
+ 3,
+ 4,
+ 5,
+};
+
+static const char * const adc_24_mux_text[] = {
+ "MIC2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static const char * const adc_25_mux_text[] = {
+ "MIC1",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
+ RT715_MUX_IN3, 0, 0xf,
+ adc_24_mux_text, rt715_adc_24_25_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
+ RT715_MUX_IN4, 0, 0xf,
+ adc_25_mux_text, rt715_adc_24_25_values);
+
+static const struct snd_kcontrol_new rt715_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc24_mux =
+ SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc23_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc25_mux),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt715_audio_map[] = {
+ {"DP6TX", NULL, "ADC 09"},
+ {"DP6TX", NULL, "ADC 08"},
+ {"DP4TX", NULL, "ADC 07"},
+ {"DP4TX", NULL, "ADC 27"},
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 07", NULL, "ADC 24 Mux"},
+ {"ADC 27", NULL, "ADC 25 Mux"},
+ {"ADC 22 Mux", "MIC1", "MIC1"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "DMIC1", "DMIC1"},
+ {"ADC 22 Mux", "DMIC2", "DMIC2"},
+ {"ADC 22 Mux", "DMIC3", "DMIC3"},
+ {"ADC 22 Mux", "DMIC4", "DMIC4"},
+ {"ADC 23 Mux", "MIC1", "MIC1"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "DMIC1", "DMIC1"},
+ {"ADC 23 Mux", "DMIC2", "DMIC2"},
+ {"ADC 23 Mux", "DMIC3", "DMIC3"},
+ {"ADC 23 Mux", "DMIC4", "DMIC4"},
+ {"ADC 24 Mux", "MIC2", "MIC2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1"},
+ {"ADC 24 Mux", "DMIC2", "DMIC2"},
+ {"ADC 24 Mux", "DMIC3", "DMIC3"},
+ {"ADC 24 Mux", "DMIC4", "DMIC4"},
+ {"ADC 25 Mux", "MIC1", "MIC1"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 25 Mux", "DMIC3", "DMIC3"},
+ {"ADC 25 Mux", "DMIC4", "DMIC4"},
+};
+
+static int rt715_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 rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(RT715_POWER_UP_DELAY_MS);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt715_probe(struct snd_soc_component *component)
+{
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt715 = {
+ .probe = rt715_probe,
+ .set_bias_level = rt715_set_bias_level,
+ .controls = rt715_snd_controls,
+ .num_controls = ARRAY_SIZE(rt715_snd_controls),
+ .dapm_widgets = rt715_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
+ .dapm_routes = rt715_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
+ .endianness = 1,
+};
+
+static int rt715_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 rt715_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt715_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 rt715_priv *rt715 = 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;
+ unsigned int val = 0;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ switch (dai->id) {
+ case RT715_AIF1:
+ port_config.num = 6;
+ rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500);
+ break;
+ case RT715_AIF2:
+ port_config.num = 4;
+ rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000);
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */
+ case 44100:
+ val |= 0x40 << 8;
+ break;
+ case 48000:
+ val |= 0x0 << 8;
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported sample rate %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
+ return 0;
+}
+
+#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT715_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 rt715_ops = {
+ .hw_params = rt715_pcm_hw_params,
+ .hw_free = rt715_pcm_hw_free,
+ .set_stream = rt715_set_sdw_stream,
+ .shutdown = rt715_shutdown,
+};
+
+static struct snd_soc_dai_driver rt715_dai[] = {
+ {
+ .name = "rt715-aif1",
+ .id = RT715_AIF1,
+ .capture = {
+ .stream_name = "DP6 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_ops,
+ },
+ {
+ .name = "rt715-aif2",
+ .id = RT715_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT715_CLK_FREQ_9600000HZ 9600000
+#define RT715_CLK_FREQ_12000000HZ 12000000
+#define RT715_CLK_FREQ_6000000HZ 6000000
+#define RT715_CLK_FREQ_4800000HZ 4800000
+#define RT715_CLK_FREQ_2400000HZ 2400000
+#define RT715_CLK_FREQ_12288000HZ 12288000
+
+int rt715_clock_config(struct device *dev)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt715->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT715_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT715_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT715_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT715_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT715_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT715_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap, 0xe0, value);
+ regmap_write(rt715->regmap, 0xf0, value);
+
+ return 0;
+}
+
+int rt715_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt715_priv *rt715;
+ int ret;
+
+ rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
+ if (!rt715)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt715);
+ rt715->slave = slave;
+ rt715->regmap = regmap;
+ rt715->sdw_regmap = sdw_regmap;
+
+ regcache_cache_only(rt715->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt715->hw_init = false;
+ rt715->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt715,
+ rt715_dai,
+ ARRAY_SIZE(rt715_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
+}
+
+int rt715_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+
+ if (rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, false);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt715->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt715_reset(rt715->regmap);
+
+ /* Mute nid=08h/09h */
+ regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080);
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080);
+ /* Mute nid=07h/27h */
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080);
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080);
+
+ /* Set Pin Widget */
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20);
+ /* Set Converter Stream */
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10);
+ /* Set Configuration Default */
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ if (rt715->first_hw_init)
+ regcache_mark_dirty(rt715->regmap);
+ else
+ rt715->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt715->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver");
+MODULE_DESCRIPTION("ASoC rt715 driver SDW");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
new file mode 100644
index 000000000000..a0c56aa1003a
--- /dev/null
+++ b/sound/soc/codecs/rt715.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt715.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_H__
+#define __RT715_H__
+
+#include <linux/regulator/consumer.h>
+
+struct rt715_priv {
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_codec *codec;
+ struct sdw_slave *slave;
+ int dbg_nid;
+ int dbg_vid;
+ int dbg_payload;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ unsigned int kctl_2ch_vol_ori[2];
+ unsigned int kctl_8ch_switch_ori[8];
+ unsigned int kctl_8ch_vol_ori[8];
+};
+
+/* NID */
+#define RT715_AUDIO_FUNCTION_GROUP 0x01
+#define RT715_MIC_ADC 0x07
+#define RT715_LINE_ADC 0x08
+#define RT715_MIX_ADC 0x09
+#define RT715_DMIC1 0x12
+#define RT715_DMIC2 0x13
+#define RT715_MIC1 0x18
+#define RT715_MIC2 0x19
+#define RT715_LINE1 0x1a
+#define RT715_LINE2 0x1b
+#define RT715_DMIC3 0x1d
+#define RT715_DMIC4 0x29
+#define RT715_VENDOR_REGISTERS 0x20
+#define RT715_MUX_IN1 0x22
+#define RT715_MUX_IN2 0x23
+#define RT715_MUX_IN3 0x24
+#define RT715_MUX_IN4 0x25
+#define RT715_MIX_ADC2 0x27
+#define RT715_INLINE_CMD 0x55
+
+/* Index (NID:20h) */
+#define RT715_VD_CLEAR_CTRL 0x01
+#define RT715_SDW_INPUT_SEL 0x39
+#define RT715_EXT_DMIC_CLK_CTRL2 0x54
+
+/* Verb */
+#define RT715_VERB_SET_CONNECT_SEL 0x3100
+#define RT715_VERB_GET_CONNECT_SEL 0xb100
+#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT715_VERB_SET_POWER_STATE 0x3500
+#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00
+#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00
+#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00
+#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00
+#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT715_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT715_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT715_READ_HDA_3 0x2012
+#define RT715_READ_HDA_2 0x2013
+#define RT715_READ_HDA_1 0x2014
+#define RT715_READ_HDA_0 0x2015
+#define RT715_PRIV_INDEX_W_H 0x7520
+#define RT715_PRIV_INDEX_W_L 0x85a0
+#define RT715_PRIV_INDEX_W_H_2 0x7500
+#define RT715_PRIV_INDEX_W_L_2 0x8580
+#define RT715_PRIV_DATA_W_H 0x7420
+#define RT715_PRIV_DATA_W_L 0x84a0
+#define RT715_PRIV_INDEX_R_H 0x9d20
+#define RT715_PRIV_INDEX_R_L 0xada0
+#define RT715_PRIV_DATA_R_H 0x9c20
+#define RT715_PRIV_DATA_R_L 0xaca0
+#define RT715_MIC_ADC_FORMAT_H 0x7207
+#define RT715_MIC_ADC_FORMAT_L 0x8287
+#define RT715_MIC_LINE_FORMAT_H 0x7208
+#define RT715_MIC_LINE_FORMAT_L 0x8288
+#define RT715_MIX_ADC_FORMAT_H 0x7209
+#define RT715_MIX_ADC_FORMAT_L 0x8289
+#define RT715_MIX_ADC2_FORMAT_H 0x7227
+#define RT715_MIX_ADC2_FORMAT_L 0x82a7
+#define RT715_FUNC_RESET 0xff01
+
+#define RT715_SET_AUDIO_POWER_STATE\
+ (RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP)
+#define RT715_SET_PIN_DMIC1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1)
+#define RT715_SET_PIN_DMIC2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2)
+#define RT715_SET_PIN_DMIC3\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3)
+#define RT715_SET_PIN_DMIC4\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4)
+#define RT715_SET_PIN_MIC1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1)
+#define RT715_SET_PIN_MIC2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2)
+#define RT715_SET_PIN_LINE1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1)
+#define RT715_SET_PIN_LINE2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2)
+#define RT715_SET_MIC1_UNSOLICITED_ENABLE\
+ (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1)
+#define RT715_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2)
+#define RT715_SET_STREAMID_MIC_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC)
+#define RT715_SET_STREAMID_LINE_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC)
+#define RT715_SET_STREAMID_MIX_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC)
+#define RT715_SET_STREAMID_MIX_ADC2\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_MIC_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC)
+#define RT715_SET_GAIN_MIC_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC)
+#define RT715_SET_GAIN_LINE_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC)
+#define RT715_SET_GAIN_LINE_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC)
+#define RT715_SET_GAIN_MIX_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC)
+#define RT715_SET_GAIN_MIX_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC)
+#define RT715_SET_GAIN_MIX_ADC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_MIX_ADC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_DMIC1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1)
+#define RT715_SET_GAIN_DMIC1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1)
+#define RT715_SET_GAIN_DMIC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2)
+#define RT715_SET_GAIN_DMIC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2)
+#define RT715_SET_GAIN_DMIC3_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3)
+#define RT715_SET_GAIN_DMIC3_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3)
+#define RT715_SET_GAIN_DMIC4_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4)
+#define RT715_SET_GAIN_DMIC4_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4)
+#define RT715_SET_GAIN_MIC1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1)
+#define RT715_SET_GAIN_MIC1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1)
+#define RT715_SET_GAIN_MIC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2)
+#define RT715_SET_GAIN_MIC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2)
+#define RT715_SET_GAIN_LINE1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1)
+#define RT715_SET_GAIN_LINE1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1)
+#define RT715_SET_GAIN_LINE2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2)
+#define RT715_SET_GAIN_LINE2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4)
+
+/* vendor register clear ctrl-1 (0x01)(NID:20h) */
+#define RT715_CLEAR_HIDDEN_REG (0x1 << 15)
+
+
+#define RT715_MUTE_SFT 7
+#define RT715_DIR_IN_SFT 6
+#define RT715_DIR_OUT_SFT 7
+
+enum {
+ RT715_AIF1,
+ RT715_AIF2,
+};
+
+#define RT715_POWER_UP_DELAY_MS 400
+
+int rt715_io_init(struct device *dev, struct sdw_slave *slave);
+int rt715_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt715_clock_config(struct device *dev);
+#endif /* __RT715_H__ */
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
new file mode 100644
index 000000000000..4d8a12b13015
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca-sdw.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt721-sdca.h"
+#include "rt721-sdca-sdw.h"
+#include "rt-sdw-common.h"
+
+static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35:
+ case 0x2f50:
+ case 0x2f51:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01:
+ case 0x2f51:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0900004 ... 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x0310100:
+ case 0x2000000 ... 0x2000003:
+ case 0x2000013:
+ case 0x200002c:
+ case 0x200003c:
+ case 0x2000046:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ case 0x5f00045:
+ case 0x5f00048:
+ case 0x6100000:
+ case 0x6100005:
+ case 0x6100006:
+ case 0x610000d:
+ case 0x6100010:
+ case 0x6100011:
+ case 0x6100013:
+ case 0x6100015:
+ case 0x6100017:
+ case 0x6100025:
+ case 0x6100029:
+ case 0x610002c ... 0x610002f:
+ case 0x6100053 ... 0x6100055:
+ case 0x6100057:
+ case 0x610005a:
+ case 0x610005b:
+ case 0x610006a:
+ case 0x610006d:
+ case 0x6100092:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0310100:
+ case 0x0900005:
+ case 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x200002c:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt721_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt721_sdca_readable_register,
+ .volatile_reg = rt721_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt721_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt721_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt721_sdca_mbq_readable_register,
+ .volatile_reg = rt721_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt721_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt721_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt721->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt721->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt721_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt721->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt721_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt721_sdca_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;
+
+ sdw_slave_read_prop(slave);
+ 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;
+
+ /*
+ * port = 1 for headphone playback
+ * port = 2 for headset-mic capture
+ * port = 3 for speaker playback
+ * port = 6 for digital-mic capture
+ */
+ prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ 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 = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt721-sdca codec */
+ prop->lane_control_support = true;
+
+ return 0;
+}
+
+static int rt721_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ if (cancel_delayed_work_sync(&rt721->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt721->scp_sdca_stat2)
+ scp_sdca_stat2 = rt721->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt721->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt721->scp_sdca_stat2 |= scp_sdca_stat2;
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt721->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+
+ if (status->sdca_cascade && !rt721->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_detect_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&rt721->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt721->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt721_sdca_slave_ops = {
+ .read_prop = rt721_sdca_read_prop,
+ .interrupt_callback = rt721_sdca_interrupt_callback,
+ .update_status = rt721_sdca_update_status,
+};
+
+static int rt721_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt721_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt721_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt721_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (rt721->hw_init) {
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+ }
+
+ if (rt721->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt721->calibrate_mutex);
+ mutex_destroy(&rt721->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt721_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt721_sdca_id);
+
+static int rt721_sdca_dev_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ if (!rt721->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt721_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt721_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt721_sdca->disable_irq_lock);
+ rt721_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt721_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt721_sdca_dev_suspend(dev);
+}
+
+#define RT721_PROBE_TIMEOUT 5000
+
+static int rt721_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt721->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt721->disable_irq_lock);
+ if (rt721->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt721->disable_irq = false;
+ }
+ mutex_unlock(&rt721->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT721_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(rt721->regmap, false);
+ regcache_sync(rt721->regmap);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ regcache_sync(rt721->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt721_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt721_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt721-sdca",
+ .owner = THIS_MODULE,
+ .pm = pm_ptr(&rt721_sdca_pm),
+ },
+ .probe = rt721_sdca_sdw_probe,
+ .remove = rt721_sdca_sdw_remove,
+ .ops = &rt721_sdca_slave_ops,
+ .id_table = rt721_sdca_id,
+};
+module_sdw_driver(rt721_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h
new file mode 100644
index 000000000000..214b31b82583
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca-sdw.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_SDW_H__
+#define __RT721_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt721_sdca_reg_defaults[] = {
+ { 0x202d, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x08 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0e },
+ { 0x2f06, 0x01 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f35, 0x00 },
+ { 0x2f50, 0xf0 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x2f5a, 0x00 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x27 },
+ { 0x2f5d, 0x07 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+};
+
+static const struct reg_default rt721_sdca_mbq_defaults[] = {
+ { 0x0900007, 0xc004 },
+ { 0x2000001, 0x0000 },
+ { 0x2000002, 0x0000 },
+ { 0x2000003, 0x0000 },
+ { 0x2000013, 0x8001 },
+ { 0x200003c, 0x0000 },
+ { 0x2000046, 0x3400 },
+ { 0x5f00044, 0x6040 },
+ { 0x5f00045, 0x3333 },
+ { 0x5f00048, 0x0000 },
+ { 0x6100005, 0x0005 },
+ { 0x6100006, 0x0000 },
+ { 0x610000d, 0x0051 },
+ { 0x6100010, 0x0180 },
+ { 0x6100011, 0x0000 },
+ { 0x6100013, 0x0000 },
+ { 0x6100015, 0x0000 },
+ { 0x6100017, 0x8049 },
+ { 0x6100025, 0x1000 },
+ { 0x6100029, 0x0809 },
+ { 0x610002c, 0x2828 },
+ { 0x610002d, 0x2929 },
+ { 0x610002e, 0x3529 },
+ { 0x610002f, 0x2901 },
+ { 0x6100053, 0x2630 },
+ { 0x6100054, 0x2a2a },
+ { 0x6100055, 0x152f },
+ { 0x6100057, 0x2200 },
+ { 0x610005a, 0x2a4b },
+ { 0x610005b, 0x2a00 },
+ { 0x610006a, 0x0102 },
+ { 0x610006d, 0x0102 },
+ { 0x6100092, 0x4f61 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_L), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_R), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_03),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_04),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x0000 },
+};
+
+#endif /* __RT721_SDW_H__ */
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
new file mode 100644
index 000000000000..a4bd29d7220b
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -0,0 +1,1560 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt721-sdca.h"
+#include "rt-sdw-common.h"
+
+static void rt721_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_sdca_priv, jack_detect_work.work);
+ int btn_type = 0;
+
+ if (!rt721->hs_jack)
+ return;
+
+ if (!rt721->component->card || !rt721->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
+ if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ rt721->jack_type = rt_sdca_headset_detect(rt721->regmap,
+ RT721_SDCA_ENT_GE49);
+ if (rt721->jack_type < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt721->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt_sdca_button_detect(rt721->regmap,
+ RT721_SDCA_ENT_HID01, RT721_BUF_ADDR_HID1,
+ RT721_SDCA_HID_ID);
+
+ if (rt721->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt721->slave->dev,
+ "in %s, jack_type=%d\n", __func__, rt721->jack_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt721_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt721->regmap,
+ RT721_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID1 */
+ if (buf[0] == 0x11)
+ btn_type = rt_sdca_btn_type(&buf[1]);
+ } else
+ rt721->jack_type = 0;
+
+ dev_dbg(&rt721->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL9, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL10, 0x2a00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL6, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL5, 0x2626);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL8, 0x1e00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL7, 0x1515);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL3, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL4, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_CTL1, 0x0000);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ regmap_write(rt721->regmap, 0x2f5c, 0x25);
+}
+
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL6, 0x5561);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_GPIO_PAD_CTRL5, 0x8003);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+}
+
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x8011);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL3, 0xcf00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL4, 0x000f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL1, 0x1100);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL5, 0x0c12);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+ RT721_JD_1PIN_GAT_CTRL2, 0xc002);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3333);
+ regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
+ regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL1, 0x0140);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL18, 0x5522);
+ regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
+ regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL4, 0xa105);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3b33);
+ regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3f33);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6048);
+ regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+ regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+ regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON17, 0x0008);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL3, 0x55ff);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL4, 0xcc00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_MBIAS_LV_CTRL2, 0x6677);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF2_LV_CTRL1, 0x7600);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL2, 0x1234);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL3, 0x3512);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL1, 0x4040);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL4, 0x1201);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL,
+ RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a);
+ regmap_write(rt721->regmap, 0x2f58, 0x07);
+}
+
+static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721)
+{
+ mutex_lock(&rt721->calibrate_mutex);
+ if (rt721->hs_jack) {
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt721->slave->dev, "in %s enable\n", __func__);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_UAJ_CTL, 0x036E);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU03,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU0D,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_XU_REL_CTRL, 0x0000);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x4000, 0x4000);
+ }
+ mutex_unlock(&rt721->calibrate_mutex);
+}
+
+static int rt721_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721->hs_jack = hs_jack;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt721_sdca_jack_init(rt721);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt721_sdca_set_gain_put(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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendA = 0x200;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_l_val == 0)
+ gain_l_val = 0x8000;
+ else
+ gain_l_val = (gain_l_val - 1) * tendA;
+ } else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_r_val == 0)
+ gain_r_val = 0x8000;
+ else
+ gain_r_val = (gain_r_val - 1) * tendA;
+ } else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt721->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt721->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return changed;
+
+ return -EIO;
+}
+
+static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendA = 0x200;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ ctl_l = read_l / tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_l == 0x8000 || read_l == 0xfe00)
+ ctl_l = 0;
+ else
+ ctl_l = read_l / tendA + 1;
+ } else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) {
+ /* boost gain */
+ ctl_r = read_r / tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_r == 0x8000 || read_r == 0xfe00)
+ ctl_r = 0;
+ else
+ ctl_r = read_r / tendA + 1;
+ } else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else {
+ ctl_r = ctl_l;
+ }
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt721_sdca_set_fu1e_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt721->fu1e_mixer_mute); i++) {
+ ch_mute = rt721->fu1e_dapm_mute || rt721->fu1e_mixer_mute[i];
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt721->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt721->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt721->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt721_sdca_set_fu1e_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_set_fu0f_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt721->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt721->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt721->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt721->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt721->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt721->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt721_sdca_set_fu0f_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * boost_step;
+ else { /* ADC gain */
+ gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt721->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt721->slave->dev, "%#08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0);
+
+static const struct snd_kcontrol_new rt721_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* Headset mic capture settings */
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* DMIC capture settings */
+ RT_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4, rt721_sdca_fu_info,
+ rt721_sdca_fu1e_capture_get, rt721_sdca_fu1e_capture_put),
+ RT_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_VOLUME, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 0x3f, mic_vol_tlv, rt721_sdca_fu_info),
+ RT_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 3, boost_vol_tlv, rt721_sdca_fu_info),
+};
+
+static int rt721_sdca_adc_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft, mask;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & mask;
+
+ return 0;
+}
+
+static int rt721_sdca_adc_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft, mask;
+ unsigned int check;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val2);
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux"))
+ val2 = (val2 >> mask_sft) & 0x7;
+ else
+ val2 = (val2 >> mask_sft) & 0x3;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &check);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, mask << mask_sft,
+ val << mask_sft);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc09_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1 RE",
+ "DMIC1 FE",
+ "DMIC2 RE",
+ "DMIC2 FE",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt721_adc09_enum, SND_SOC_NOPM, 0, adc09_mux_text);
+static SOC_ENUM_SINGLE_DECL(
+ rt721_dmic_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt721_sdca_adc09_mux =
+ SOC_DAPM_ENUM_EXT("ADC 09 Mux", rt721_adc09_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+
+
+static int rt721_sdca_fu42_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(100);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu21_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu23_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu113_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu1e_dapm_mute = false;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu1e_dapm_mute = true;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu36_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu0f_dapm_mute = false;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu0f_dapm_mute = true;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde47_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde41_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde11_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde34_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt721_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_INPUT("DMIC1_2"),
+ SND_SOC_DAPM_INPUT("DMIC3_4"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 41", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde41_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde47_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde34_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 23", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu42_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu36_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu113_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 09 Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc09_mux),
+ SND_SOC_DAPM_MUX("ADC 08 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_r_mux),
+ SND_SOC_DAPM_MUX("ADC 08 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_l_mux),
+ SND_SOC_DAPM_MUX("ADC 10 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_r_mux),
+ SND_SOC_DAPM_MUX("ADC 10 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_l_mux),
+ SND_SOC_DAPM_MUX("ADC 07 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_r_mux),
+ SND_SOC_DAPM_MUX("ADC 07 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_l_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt721_sdca_audio_map[] = {
+ {"FU 42", NULL, "DP1RX"},
+ {"FU 21", NULL, "DP3RX"},
+ {"FU 23", NULL, "DP3RX"},
+
+ {"ADC 09 Mux", "MIC2", "MIC2"},
+ {"ADC 09 Mux", "LINE1", "LINE1"},
+ {"ADC 09 Mux", "LINE2", "LINE2"},
+ {"ADC 07 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"FU 36", NULL, "PDE 34"},
+ {"FU 36", NULL, "ADC 09 Mux"},
+ {"FU 113", NULL, "PDE 11"},
+ {"FU 113", NULL, "ADC 07 R Mux"},
+ {"FU 113", NULL, "ADC 07 L Mux"},
+ {"FU 113", NULL, "ADC 10 R Mux"},
+ {"FU 113", NULL, "ADC 10 L Mux"},
+ {"DP2TX", NULL, "FU 36"},
+ {"DP6TX", NULL, "FU 113"},
+
+ {"HP", NULL, "PDE 47"},
+ {"HP", NULL, "FU 42"},
+ {"SPK", NULL, "PDE 41"},
+ {"SPK", NULL, "FU 21"},
+ {"SPK", NULL, "FU 23"},
+};
+
+static int rt721_sdca_parse_dt(struct rt721_sdca_priv *rt721, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt721->jd_src);
+
+ return 0;
+}
+
+static int rt721_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721_sdca_parse_dt(rt721, &rt721->slave->dev);
+ rt721->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt721 = {
+ .probe = rt721_sdca_probe,
+ .controls = rt721_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt721_sdca_controls),
+ .dapm_widgets = rt721_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt721_sdca_dapm_widgets),
+ .dapm_routes = rt721_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt721_sdca_audio_map),
+ .set_jack = rt721_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt721_sdca_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 rt721_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt721_sdca_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 rt721_sdca_priv *rt721 = 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;
+ 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 (!rt721->slave)
+ return -EINVAL;
+
+ /*
+ * RT721_AIF1 with port = 1 for headphone playback
+ * RT721_AIF1 with port = 2 for headset-mic capture
+ * RT721_AIF2 with port = 3 for speaker playback
+ * RT721_AIF3 with port = 6 for digital-mic capture
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT721_AIF1)
+ port = 1;
+ else if (dai->id == RT721_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT721_AIF1)
+ port = 2;
+ else if (dai->id == RT721_AIF3)
+ port = 6;
+ else
+ return -EINVAL;
+ }
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt721->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = RT721_SDCA_RATE_8000HZ;
+ break;
+ case 16000:
+ sampling_rate = RT721_SDCA_RATE_16000HZ;
+ break;
+ case 24000:
+ sampling_rate = RT721_SDCA_RATE_24000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT721_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT721_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT721_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT721_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT721_SDCA_RATE_192000HZ;
+ break;
+ case 384000:
+ sampling_rate = RT721_SDCA_RATE_384000HZ;
+ break;
+ case 768000:
+ sampling_rate = RT721_SDCA_RATE_768000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT721_AIF1) {
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ }
+
+ if (dai->id == RT721_AIF2)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ if (dai->id == RT721_AIF3)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ return 0;
+}
+
+static int rt721_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt721->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt721->slave, sdw_stream);
+ return 0;
+}
+
+#define RT721_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT721_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt721_sdca_ops = {
+ .hw_params = rt721_sdca_pcm_hw_params,
+ .hw_free = rt721_sdca_pcm_hw_free,
+ .set_stream = rt721_sdca_set_sdw_stream,
+ .shutdown = rt721_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt721_sdca_dai[] = {
+ {
+ .name = "rt721-sdca-aif1",
+ .id = RT721_AIF1,
+ .playback = {
+ .stream_name = "DP1 Headphone Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif2",
+ .id = RT721_AIF2,
+ .playback = {
+ .stream_name = "DP3 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif3",
+ .id = RT721_AIF3,
+ .capture = {
+ .stream_name = "DP6 DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ }
+};
+
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721;
+
+ rt721 = devm_kzalloc(dev, sizeof(*rt721), GFP_KERNEL);
+ if (!rt721)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt721);
+ rt721->slave = slave;
+ rt721->regmap = regmap;
+ rt721->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ mutex_init(&rt721->calibrate_mutex);
+ mutex_init(&rt721->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt721->jack_detect_work, rt721_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt721->jack_btn_check_work, rt721_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt721->hw_init = false;
+ rt721->first_hw_init = false;
+ rt721->fu1e_dapm_mute = true;
+ rt721->fu0f_dapm_mute = true;
+ rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true;
+ rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] =
+ rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true;
+
+ return devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
+}
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ rt721->disable_irq = false;
+
+ if (rt721->hw_init)
+ return 0;
+
+ regcache_cache_only(rt721->regmap, false);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, true);
+ regcache_cache_bypass(rt721->mbq_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);
+ rt721_sdca_dmic_preset(rt721);
+ rt721_sdca_amp_preset(rt721);
+ rt721_sdca_jack_preset(rt721);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, false);
+ regcache_mark_dirty(rt721->regmap);
+ regcache_cache_bypass(rt721->mbq_regmap, false);
+ regcache_mark_dirty(rt721->mbq_regmap);
+ } else
+ rt721->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt721->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h
new file mode 100644
index 000000000000..71fac9cd8739
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.h
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_H__
+#define __RT721_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt721_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct mutex calibrate_mutex;
+ struct mutex disable_irq_lock;
+ bool disable_irq;
+ /* For Headset jack & Headphone */
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ int jd_src;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ /* For DMIC */
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+};
+
+struct rt721_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* NID */
+#define RT721_ANA_POW_PART 0x01
+#define RT721_DAC_CTRL 0x04
+#define RT721_JD_CTRL 0x09
+#define RT721_CBJ_CTRL 0x0a
+#define RT721_CAP_PORT_CTRL 0x0c
+#define RT721_CLASD_AMP_CTRL 0x0d
+#define RT721_BOOST_CTRL 0x0f
+#define RT721_VENDOR_REG 0x20
+#define RT721_RC_CALIB_CTRL 0x40
+#define RT721_VENDOR_EQ_L 0x53
+#define RT721_VENDOR_EQ_R 0x54
+#define RT721_VENDOR_HP_CALI 0x56
+#define RT721_VENDOR_CHARGE_PUMP 0x57
+#define RT721_VENDOR_CLASD_CALI 0x58
+#define RT721_VENDOR_IMS_DRE 0x5b
+#define RT721_VENDOR_SPK_EFUSE 0x5c
+#define RT721_VENDOR_LEVEL_CTRL 0x5d
+#define RT721_VENDOR_ANA_CTL 0x5f
+#define RT721_HDA_SDCA_FLOAT 0x61
+
+/* Index (NID:01h) */
+#define RT721_MBIAS_LV_CTRL2 0x07
+#define RT721_VREF1_HV_CTRL1 0x0a
+#define RT721_VREF2_LV_CTRL1 0x0b
+
+/* Index (NID:04h) */
+#define RT721_DAC_2CH_CTRL3 0x02
+#define RT721_DAC_2CH_CTRL4 0x03
+
+/* Index (NID:09h) */
+#define RT721_JD_1PIN_GAT_CTRL2 0x07
+
+/* Index (NID:0ah) */
+#define RT721_CBJ_A0_GAT_CTRL1 0x04
+#define RT721_CBJ_A0_GAT_CTRL2 0x05
+
+/* Index (NID:0Ch) */
+#define RT721_HP_AMP_2CH_CAL1 0x05
+#define RT721_HP_AMP_2CH_CAL4 0x08
+#define RT721_HP_AMP_2CH_CAL18 0x1b
+
+/* Index (NID:0dh) */
+#define RT721_CLASD_AMP_2CH_CAL 0x14
+
+/* Index (NID:0fh) */
+#define RT721_BST_4CH_TOP_GATING_CTRL1 0x05
+
+/* Index (NID:20h) */
+#define RT721_JD_PRODUCT_NUM 0x00
+#define RT721_ANALOG_BIAS_CTL3 0x04
+#define RT721_JD_CTRL1 0x09
+#define RT721_LDO2_3_CTL1 0x0e
+#define RT721_GPIO_PAD_CTRL5 0x13
+#define RT721_LDO1_CTL 0x1a
+#define RT721_HP_JD_CTRL 0x24
+#define RT721_VD_HIDDEN_CTRL 0x26
+#define RT721_CLSD_CTRL6 0x3c
+#define RT721_COMBO_JACK_AUTO_CTL1 0x45
+#define RT721_COMBO_JACK_AUTO_CTL2 0x46
+#define RT721_COMBO_JACK_AUTO_CTL3 0x47
+#define RT721_DIGITAL_MISC_CTRL4 0x4a
+#define RT721_VREFO_GAT 0x63
+#define RT721_FSM_CTL 0x67
+#define RT721_SDCA_INTR_REC 0x82
+#define RT721_SW_CONFIG1 0x8a
+#define RT721_SW_CONFIG2 0x8b
+
+/* Index (NID:40h) */
+#define RT721_RC_CALIB_CTRL0 0x00
+
+/* Index (NID:58h) */
+#define RT721_DAC_DC_CALI_CTL1 0x01
+#define RT721_DAC_DC_CALI_CTL2 0x02
+#define RT721_DAC_DC_CALI_CTL3 0x03
+
+/* Index (NID:5fh) */
+#define RT721_MISC_POWER_CTL0 0x00
+#define RT721_MISC_POWER_CTL31 0x31
+#define RT721_UAJ_TOP_TCON13 0x44
+#define RT721_UAJ_TOP_TCON14 0x45
+#define RT721_UAJ_TOP_TCON17 0x48
+
+/* Index (NID:61h) */
+#define RT721_HDA_LEGACY_MUX_CTL0 0x00
+#define RT721_HDA_LEGACY_UAJ_CTL 0x02
+#define RT721_HDA_LEGACY_CTL1 0x05
+#define RT721_HDA_LEGACY_RESET_CTL 0x06
+#define RT721_XU_REL_CTRL 0x0c
+#define RT721_GE_REL_CTRL1 0x0d
+#define RT721_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT721_GE_SDCA_RST_CTRL 0x10
+#define RT721_INT_RST_EN_CTRL 0x11
+#define RT721_XU_EVENT_EN 0x13
+#define RT721_INLINE_CTL2 0x17
+#define RT721_UMP_HID_CTRL1 0x18
+#define RT721_UMP_HID_CTRL2 0x19
+#define RT721_UMP_HID_CTRL3 0x1a
+#define RT721_UMP_HID_CTRL4 0x1b
+#define RT721_UMP_HID_CTRL5 0x1c
+#define RT721_FUNC_FLOAT_CTL0 0x22
+#define RT721_FUNC_FLOAT_CTL1 0x23
+#define RT721_FUNC_FLOAT_CTL2 0x24
+#define RT721_FUNC_FLOAT_CTL3 0x25
+#define RT721_ENT_FLOAT_CTL0 0x29
+#define RT721_ENT_FLOAT_CTL1 0x2c
+#define RT721_ENT_FLOAT_CTL2 0x2d
+#define RT721_ENT_FLOAT_CTL3 0x2e
+#define RT721_ENT_FLOAT_CTL4 0x2f
+#define RT721_CH_FLOAT_CTL1 0x45
+#define RT721_CH_FLOAT_CTL2 0x46
+#define RT721_ENT_FLOAT_CTL5 0x53
+#define RT721_ENT_FLOAT_CTL6 0x54
+#define RT721_ENT_FLOAT_CTL7 0x55
+#define RT721_ENT_FLOAT_CTL8 0x57
+#define RT721_ENT_FLOAT_CTL9 0x5a
+#define RT721_ENT_FLOAT_CTL10 0x5b
+#define RT721_CH_FLOAT_CTL3 0x6a
+#define RT721_CH_FLOAT_CTL4 0x6d
+#define RT721_CH_FLOAT_CTL5 0x70
+#define RT721_CH_FLOAT_CTL6 0x92
+
+/* Parameter & Verb control 01 (0x26)(NID:20h) */
+#define RT721_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* Buffer address for HID */
+#define RT721_BUF_ADDR_HID1 0x44030000
+#define RT721_BUF_ADDR_HID2 0x44030020
+
+/* RT721 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT721 SDCA entity */
+#define RT721_SDCA_ENT_HID01 0x01
+#define RT721_SDCA_ENT_XUV 0x03
+#define RT721_SDCA_ENT_GE49 0x49
+#define RT721_SDCA_ENT_USER_FU05 0x05
+#define RT721_SDCA_ENT_USER_FU06 0x06
+#define RT721_SDCA_ENT_USER_FU0F 0x0f
+#define RT721_SDCA_ENT_USER_FU10 0x19
+#define RT721_SDCA_ENT_USER_FU1E 0x1e
+#define RT721_SDCA_ENT_FU15 0x15
+#define RT721_SDCA_ENT_PDE23 0x23
+#define RT721_SDCA_ENT_PDE40 0x40
+#define RT721_SDCA_ENT_PDE41 0x41
+#define RT721_SDCA_ENT_PDE11 0x11
+#define RT721_SDCA_ENT_PDE12 0x12
+#define RT721_SDCA_ENT_PDE2A 0x2a
+#define RT721_SDCA_ENT_CS01 0x01
+#define RT721_SDCA_ENT_CS11 0x11
+#define RT721_SDCA_ENT_CS1F 0x1f
+#define RT721_SDCA_ENT_CS1C 0x1c
+#define RT721_SDCA_ENT_CS31 0x31
+#define RT721_SDCA_ENT_OT23 0x42
+#define RT721_SDCA_ENT_IT26 0x26
+#define RT721_SDCA_ENT_IT09 0x09
+#define RT721_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT721_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT721_SDCA_ENT_XU03 0x03
+#define RT721_SDCA_ENT_XU0D 0x0d
+#define RT721_SDCA_ENT_FU55 0x55
+
+/* RT721 SDCA control */
+#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT721_SDCA_CTL_FU_MUTE 0x01
+#define RT721_SDCA_CTL_FU_VOLUME 0x02
+#define RT721_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT721_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT721_SDCA_CTL_SELECTED_MODE 0x01
+#define RT721_SDCA_CTL_DETECTED_MODE 0x02
+#define RT721_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT721_SDCA_CTL_VENDOR_DEF 0x30
+#define RT721_SDCA_CTL_XUV 0x34
+#define RT721_SDCA_CTL_FU_CH_GAIN 0x0b
+
+/* RT721 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_08 0x08
+#define CH_09 0x09
+#define CH_0A 0x0a
+
+/* sample frequency index */
+#define RT721_SDCA_RATE_8000HZ 0x01
+#define RT721_SDCA_RATE_11025HZ 0x02
+#define RT721_SDCA_RATE_12000HZ 0x03
+#define RT721_SDCA_RATE_16000HZ 0x04
+#define RT721_SDCA_RATE_22050HZ 0x05
+#define RT721_SDCA_RATE_24000HZ 0x06
+#define RT721_SDCA_RATE_32000HZ 0x07
+#define RT721_SDCA_RATE_44100HZ 0x08
+#define RT721_SDCA_RATE_48000HZ 0x09
+#define RT721_SDCA_RATE_88200HZ 0x0a
+#define RT721_SDCA_RATE_96000HZ 0x0b
+#define RT721_SDCA_RATE_176400HZ 0x0c
+#define RT721_SDCA_RATE_192000HZ 0x0d
+#define RT721_SDCA_RATE_384000HZ 0x0e
+#define RT721_SDCA_RATE_768000HZ 0x0f
+
+/* RT721 HID ID */
+#define RT721_SDCA_HID_ID 0x11
+
+enum {
+ RT721_AIF1, /* For headset mic and headphone */
+ RT721_AIF2, /* For speaker */
+ RT721_AIF3, /* For dmic */
+ RT721_AIFS,
+};
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+#endif /* __RT721_H__ */
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
new file mode 100644
index 000000000000..5ea40c1b159a
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt722-sdca.h"
+#include "rt722-sdca-sdw.h"
+
+static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50 ... 0x2f52:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, RT722_SDCA_CTL_SELECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ return 1;
+ case 0x2000000 ... 0x2000024:
+ case 0x2000029 ... 0x200004a:
+ case 0x2000051 ... 0x2000052:
+ case 0x200005a ... 0x200005b:
+ case 0x2000061 ... 0x2000069:
+ case 0x200006b:
+ case 0x2000070:
+ case 0x200007f:
+ case 0x2000082 ... 0x200008e:
+ case 0x2000090 ... 0x2000094:
+ case 0x3110000:
+ case 0x5300000 ... 0x5300002:
+ case 0x5400002:
+ case 0x5600000 ... 0x5600007:
+ case 0x5700000 ... 0x5700004:
+ case 0x5800000 ... 0x5800004:
+ case 0x5810000:
+ case 0x5b00003:
+ case 0x5c00011:
+ case 0x5d00006:
+ case 0x5f00000 ... 0x5f0000d:
+ case 0x5f00030:
+ case 0x6100000 ... 0x6100051:
+ case 0x6100055 ... 0x6100057:
+ case 0x6100060:
+ case 0x6100062:
+ case 0x6100064 ... 0x6100065:
+ case 0x6100067:
+ case 0x6100070 ... 0x610007c:
+ case 0x6100080:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_R):
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_sdw_mbq_cfg rt722_mbq_config = {
+ .mbq_size = rt722_sdca_mbq_size,
+};
+
+static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ return rt722_sdca_mbq_size(dev, reg) > 0;
+}
+
+static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01:
+ case 0x2f54:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
+ 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x3110000:
+ case 0x5800003:
+ case 0x5810000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt722_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt722_sdca_readable_register,
+ .volatile_reg = rt722_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt722_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt722_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt722->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt722->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt722_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt722->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt722_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt722_sdca_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;
+
+ sdw_slave_read_lane_mapping(slave);
+
+ 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;
+
+ /*
+ * port = 1 for headphone playback
+ * port = 2 for headset-mic capture
+ * port = 3 for speaker playback
+ * port = 6 for digital-mic capture
+ */
+ prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ 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 = 900;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt722-sdca codec */
+ prop->lane_control_support = true;
+
+ return 0;
+}
+
+static int rt722_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ if (cancel_delayed_work_sync(&rt722->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt722->scp_sdca_stat2)
+ scp_sdca_stat2 = rt722->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt722->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt722->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt722->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt722->scp_sdca_stat2 |= scp_sdca_stat2;
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt722->scp_sdca_stat1, rt722->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt722->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_detect_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&rt722->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt722->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt722_sdca_slave_ops = {
+ .read_prop = rt722_sdca_read_prop,
+ .interrupt_callback = rt722_sdca_interrupt_callback,
+ .update_status = rt722_sdca_update_status,
+};
+
+static int rt722_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw_mbq_cfg(slave, &rt722_sdca_regmap, &rt722_mbq_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt722_sdca_init(&slave->dev, regmap, slave);
+}
+
+static int rt722_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+
+ if (rt722->hw_init) {
+ cancel_delayed_work_sync(&rt722->jack_detect_work);
+ cancel_delayed_work_sync(&rt722->jack_btn_check_work);
+ }
+
+ if (rt722->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt722->calibrate_mutex);
+ mutex_destroy(&rt722->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt722_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt722_sdca_id);
+
+static int rt722_sdca_dev_suspend(struct device *dev)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+
+ if (!rt722->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt722->jack_detect_work);
+ cancel_delayed_work_sync(&rt722->jack_btn_check_work);
+
+ regcache_cache_only(rt722->regmap, true);
+
+ return 0;
+}
+
+static int rt722_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt722_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt722_sdca->disable_irq_lock);
+ rt722_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt722_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt722_sdca_dev_suspend(dev);
+}
+
+#define RT722_PROBE_TIMEOUT 5000
+
+static int rt722_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt722->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt722->disable_irq_lock);
+ if (rt722->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt722->disable_irq = false;
+ }
+ mutex_unlock(&rt722->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT722_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(rt722->regmap, false);
+ regcache_sync(rt722->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt722_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt722_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt722-sdca",
+ .pm = pm_ptr(&rt722_sdca_pm),
+ },
+ .probe = rt722_sdca_sdw_probe,
+ .remove = rt722_sdca_sdw_remove,
+ .ops = &rt722_sdca_slave_ops,
+ .id_table = rt722_sdca_id,
+};
+module_sdw_driver(rt722_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h
new file mode 100644
index 000000000000..c5dd472a2c00
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT722_SDW_H__
+#define __RT722_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt722_sdca_reg_defaults[] = {
+ { 0x202d, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f35, 0x00 },
+ { 0x2f36, 0x00 },
+ { 0x2f50, 0xf0 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x2f5a, 0x07 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x27 },
+ { 0x2f5d, 0x07 },
+ { 0x200003c, 0xc214 },
+ { 0x2000046, 0x8004 },
+ { 0x5810000, 0x702d },
+ { 0x6100000, 0x0201 },
+ { 0x6100006, 0x0005 },
+ { 0x6100010, 0x2630 },
+ { 0x6100011, 0x152f },
+ { 0x6100013, 0x0102 },
+ { 0x6100015, 0x2200 },
+ { 0x6100017, 0x0102 },
+ { 0x6100025, 0x2a29 },
+ { 0x6100026, 0x2a00 },
+ { 0x6100028, 0x2a2a },
+ { 0x6100029, 0x4141 },
+ { 0x6100055, 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0),
+ 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+#endif /* __RT722_SDW_H__ */
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
new file mode 100644
index 000000000000..79b8b7e70a33
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt722-sdca.h"
+
+#define RT722_NID_ADDR(nid, reg) ((nid) << 20 | (reg))
+
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+ int ret;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt722_sdca_index_read(rt722, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt722_sdca_index_write(rt722, nid, reg, tmp);
+}
+
+static int rt722_sdca_btn_type(unsigned char *buffer)
+{
+ if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) ||
+ (*(buffer + 1) == 0x10))
+ return SND_JACK_BTN_2;
+ else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) ||
+ (*(buffer + 1) == 0x20))
+ return SND_JACK_BTN_3;
+ else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) ||
+ (*(buffer + 1) == 0x40))
+ return SND_JACK_BTN_0;
+ else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) ||
+ (*(buffer + 1) == 0x80))
+ return SND_JACK_BTN_1;
+
+ return 0;
+}
+
+static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt722->regmap,
+ RT722_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11)
+ btn_type = rt722_sdca_btn_type(&buf[1]);
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+
+static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722)
+{
+ unsigned int det_mode;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt722->jack_type = 0;
+ break;
+ case 0x03:
+ rt722->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt722->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt722->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt722_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt722_sdca_priv *rt722 =
+ container_of(work, struct rt722_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt722->hs_jack)
+ return;
+
+ if (!rt722->component->card || !rt722->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt722_sdca_headset_detect(rt722);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt722_sdca_button_detect(rt722);
+
+ if (rt722->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt722->slave->dev,
+ "in %s, jack_type=%d\n", __func__, rt722->jack_type);
+ dev_dbg(&rt722->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt722->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt722->scp_sdca_stat1, rt722->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt722_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt722_sdca_priv *rt722 =
+ container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt722->regmap,
+ RT722_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11)
+ btn_type = rt722_sdca_btn_type(&buf[1]);
+ } else
+ rt722->jack_type = 0;
+
+ dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722)
+{
+ mutex_lock(&rt722->calibrate_mutex);
+ if (rt722->hs_jack) {
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_UNSOL_CTL, 0x016E);
+ /* set XU(et03h) & XU(et0Dh) to Not bypassed */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), 0);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), 0);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL1, 0x0000);
+ /* trigger GE interrupt */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_GE_RELATED_CTL2, 0x4000, 0x4000);
+ }
+ mutex_unlock(&rt722->calibrate_mutex);
+}
+
+static int rt722_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt722->hs_jack = hs_jack;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt722_sdca_jack_init(rt722);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt722_sdca_set_gain_put(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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt722->regmap, mc->reg, &lvalue);
+ regmap_read(rt722->regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt722->regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt722->regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return changed;
+
+ return -EIO;
+}
+
+static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = read_l / tendB;
+ else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = read_r / tendB;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else {
+ ctl_r = ctl_l;
+ }
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) {
+ ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i];
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt722_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt722_sdca_set_fu1e_capture_ctl(rt722);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt722_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt722->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt722_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt722->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt722_sdca_set_fu0f_capture_ctl(rt722);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * boost_step;
+ else { /* ADC gain */
+ gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt722->regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n",
+ __func__, p->reg_base + i);
+ }
+
+ return changed;
+}
+
+#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt722_sdca_fu_info, \
+ .get = rt722_sdca_fu1e_capture_get, \
+ .put = rt722_sdca_fu1e_capture_put, \
+ .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt722_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt722_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv),
+ /* Headset mic capture settings */
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv),
+ /* DMIC capture settings */
+ RT722_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4),
+ RT722_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_VOLUME, CH_01),
+ rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put,
+ 4, 0x3f, mic_vol_tlv),
+ RT722_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put,
+ 4, 3, boost_vol_tlv),
+};
+
+static const char * const adc22_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc22_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 12, adc22_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc24_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 4, adc07_10_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc25_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt722_sdca_adc22_mux =
+ SOC_DAPM_ENUM("ADC 22 Mux", rt722_adc22_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc24_mux =
+ SOC_DAPM_ENUM("ADC 24 Mux", rt722_adc24_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc25_mux =
+ SOC_DAPM_ENUM("ADC 25 Mux", rt722_adc25_enum);
+
+static int rt722_sdca_fu42_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu21_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu113_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt722->fu1e_dapm_mute = false;
+ rt722_sdca_set_fu1e_capture_ctl(rt722);
+ usleep_range(150000, 160000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt722->fu1e_dapm_mute = true;
+ rt722_sdca_set_fu1e_capture_ctl(rt722);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu36_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt722->fu0f_dapm_mute = false;
+ rt722_sdca_set_fu0f_capture_ctl(rt722);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt722->fu0f_dapm_mute = true;
+ rt722_sdca_set_fu0f_capture_ctl(rt722);
+ break;
+ }
+ return 0;
+}
+
+static void rt722_pde_transition_delay(struct rt722_sdca_priv *rt722, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 1000, val;
+
+ pm_runtime_mark_last_busy(&rt722->slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(func, entity, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt722->slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ }
+}
+
+static int rt722_sdca_pde47_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde23_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde11_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde12_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_INPUT("DMIC1_2"),
+ SND_SOC_DAPM_INPUT("DMIC3_4"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde47_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde12_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu42_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu36_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu113_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc25_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = {
+ {"FU 42", NULL, "DP1RX"},
+ {"FU 21", NULL, "DP3RX"},
+
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1_2"},
+ {"ADC 24 Mux", "DMIC2", "DMIC3_4"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1_2"},
+ {"ADC 25 Mux", "DMIC2", "DMIC3_4"},
+ {"FU 36", NULL, "PDE 12"},
+ {"FU 36", NULL, "ADC 22 Mux"},
+ {"FU 113", NULL, "PDE 11"},
+ {"FU 113", NULL, "ADC 24 Mux"},
+ {"FU 113", NULL, "ADC 25 Mux"},
+ {"DP2TX", NULL, "FU 36"},
+ {"DP6TX", NULL, "FU 113"},
+
+ {"HP", NULL, "PDE 47"},
+ {"HP", NULL, "FU 42"},
+ {"SPK", NULL, "PDE 23"},
+ {"SPK", NULL, "FU 21"},
+};
+
+static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src);
+
+ return 0;
+}
+
+static int rt722_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt722_sdca_parse_dt(rt722, &rt722->slave->dev);
+ rt722->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt722 = {
+ .probe = rt722_sdca_probe,
+ .controls = rt722_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt722_sdca_controls),
+ .dapm_widgets = rt722_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets),
+ .dapm_routes = rt722_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map),
+ .set_jack = rt722_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt722_sdca_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 rt722_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt722_sdca_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 rt722_sdca_priv *rt722 = 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;
+ 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 (!rt722->slave)
+ return -EINVAL;
+
+ /*
+ * RT722_AIF1 with port = 1 for headphone playback
+ * RT722_AIF1 with port = 2 for headset-mic capture
+ * RT722_AIF2 with port = 3 for speaker playback
+ * RT722_AIF3 with port = 6 for digital-mic capture
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT722_AIF1)
+ port = 1;
+ else if (dai->id == RT722_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT722_AIF1)
+ port = 2;
+ else if (dai->id == RT722_AIF3)
+ port = 6;
+ else
+ return -EINVAL;
+ }
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt722->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT722_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT722_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT722_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT722_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT722_AIF1) {
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ }
+
+ if (dai->id == RT722_AIF2)
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ if (dai->id == RT722_AIF3)
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ return 0;
+}
+
+static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt722->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt722->slave, sdw_stream);
+ return 0;
+}
+
+#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt722_sdca_ops = {
+ .hw_params = rt722_sdca_pcm_hw_params,
+ .hw_free = rt722_sdca_pcm_hw_free,
+ .set_stream = rt722_sdca_set_sdw_stream,
+ .shutdown = rt722_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt722_sdca_dai[] = {
+ {
+ .name = "rt722-sdca-aif1",
+ .id = RT722_AIF1,
+ .playback = {
+ .stream_name = "DP1 Headphone Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ },
+ {
+ .name = "rt722-sdca-aif2",
+ .id = RT722_AIF2,
+ .playback = {
+ .stream_name = "DP3 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ },
+ {
+ .name = "rt722-sdca-aif3",
+ .id = RT722_AIF3,
+ .capture = {
+ .stream_name = "DP6 DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ }
+};
+
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722;
+
+ rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL);
+ if (!rt722)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt722);
+ rt722->slave = slave;
+ rt722->regmap = regmap;
+
+ regcache_cache_only(rt722->regmap, true);
+
+ mutex_init(&rt722->calibrate_mutex);
+ mutex_init(&rt722->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt722->hw_init = false;
+ rt722->first_hw_init = false;
+ rt722->fu1e_dapm_mute = true;
+ rt722->fu0f_dapm_mute = true;
+ rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true;
+ rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] =
+ rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true;
+
+ return devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai));
+}
+
+static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722)
+{
+ unsigned int mic_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ dev_dbg(dev, "%s mic func_status=0x%x\n", __func__, mic_func_status);
+
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set AD07 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29);
+ /* Set AD10 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC10_PDE_FLOAT_CTL, 0x2a00);
+ /* Set DMIC1/DMIC2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a);
+ /* Set DMIC2 IT entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_ENT_FLOAT_CTL, 0x2626);
+ /* Set AD10 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_ENT_FLOAT_CTL, 0x1e00);
+ /* Set DMIC2 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ /* Set AD10 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304);
+ /* Set DMIC2 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000);
+ /* Enable vf707_r12_05/vf707_r13_05 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ /* Fine tune PDE2A latency */
+ regmap_write(rt722->regmap, 0x2f5c, 0x25);
+ /* PHYtiming TDZ/TZD control */
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f52, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
+{
+ unsigned int amp_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* Reset dc_cal_top */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0x702c);
+ /* W1C Trigger Calibration */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0xf02d);
+ /* Set DAC02/ClassD power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL,
+ 0x2323);
+ /* Set EAPD high */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL,
+ 0x0002);
+ /* Enable vf707_r14 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f54, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
+{
+ int loop_check, chk_cnt = 100, ret;
+ unsigned int calib_status = 0;
+ unsigned int jack_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ dev_dbg(dev, "%s jack func_status=0x%x\n", __func__, jack_func_status);
+
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Config analog bias */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3,
+ 0xa081);
+ /* GE related settings */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2,
+ 0xa009);
+ /* Button A, B, C, D bypass mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4,
+ 0xcf00);
+ /* HID1 slot enable */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5,
+ 0x000f);
+ /* Report ID for HID1 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0,
+ 0x1100);
+ /* OSC/OOC for slot 2, 3 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7,
+ 0x0c12);
+ /* Set JD de-bounce clock control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1,
+ 0x7002);
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* FSM switch to calibration manual mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL,
+ 0x4100);
+ /* W1C Trigger DC calibration (HP) */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3,
+ 0x008d);
+ /* check HP calibration FSM status */
+ for (loop_check = 0; loop_check < chk_cnt; loop_check++) {
+ usleep_range(10000, 11000);
+ ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
+ RT722_DAC_DC_CALI_CTL3, &calib_status);
+ if (ret < 0)
+ dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
+ if ((calib_status & 0x0040) == 0x0)
+ break;
+ }
+
+ if (loop_check == chk_cnt)
+ dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__);
+
+ /* Set ADC09 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL,
+ 0x2a12);
+ /* Set MIC2 and LINE1 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL,
+ 0x3429);
+ /* Set ET41h and LINE2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL,
+ 0x4112);
+ /* Set DAC03 and HP power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL,
+ 0x4040);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1,
+ 0x4141);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1,
+ 0x0101);
+ /* Fine tune PDE40 latency */
+ regmap_write(rt722->regmap, 0x2f58, 0x07);
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+ /* MIC VRefo */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200);
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_VREFO_GAT, 0x4000, 0x4000);
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4,
+ 0x0010);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f51, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ rt722->disable_irq = false;
+
+ if (rt722->hw_init)
+ return 0;
+
+ regcache_cache_only(rt722->regmap, false);
+ if (rt722->first_hw_init) {
+ regcache_cache_bypass(rt722->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);
+
+ rt722_sdca_index_read(rt722, RT722_VENDOR_REG, RT722_JD_PRODUCT_NUM, &val);
+ rt722->hw_vid = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid);
+
+ rt722_sdca_dmic_preset(rt722);
+ rt722_sdca_amp_preset(rt722);
+ rt722_sdca_jack_preset(rt722);
+
+ if (rt722->first_hw_init) {
+ regcache_cache_bypass(rt722->regmap, false);
+ regcache_mark_dirty(rt722->regmap);
+ } else
+ rt722->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt722->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
new file mode 100644
index 000000000000..823abee9ab76
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT722_H__
+#define __RT722_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt722_sdca_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct mutex calibrate_mutex;
+ struct mutex disable_irq_lock;
+ bool disable_irq;
+ /* For Headset jack & Headphone */
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ int jd_src;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ /* For DMIC */
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+ int hw_vid;
+};
+
+struct rt722_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* NID */
+#define RT722_VENDOR_REG 0x20
+#define RT722_VENDOR_CALI 0x58
+#define RT722_VENDOR_SPK_EFUSE 0x5c
+#define RT722_VENDOR_IMS_DRE 0x5b
+#define RT722_VENDOR_ANALOG_CTL 0x5f
+#define RT722_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT722_JD_PRODUCT_NUM 0x00
+#define RT722_ANALOG_BIAS_CTL3 0x04
+#define RT722_JD_CTRL1 0x09
+#define RT722_LDO2_3_CTL1 0x0e
+#define RT722_LDO1_CTL 0x1a
+#define RT722_HP_JD_CTRL 0x24
+#define RT722_CLSD_CTRL6 0x3c
+#define RT722_COMBO_JACK_AUTO_CTL1 0x45
+#define RT722_COMBO_JACK_AUTO_CTL2 0x46
+#define RT722_COMBO_JACK_AUTO_CTL3 0x47
+#define RT722_DIGITAL_MISC_CTRL4 0x4a
+#define RT722_VREFO_GAT 0x63
+#define RT722_FSM_CTL 0x67
+#define RT722_SDCA_INTR_REC 0x82
+#define RT722_SW_CONFIG1 0x8a
+#define RT722_SW_CONFIG2 0x8b
+
+/* Index (NID:58h) */
+#define RT722_DAC_DC_CALI_CTL0 0x00
+#define RT722_DAC_DC_CALI_CTL1 0x01
+#define RT722_DAC_DC_CALI_CTL2 0x02
+#define RT722_DAC_DC_CALI_CTL3 0x03
+
+/* Index (NID:59h) */
+#define RT722_ULTRA_SOUND_DETECTOR6 0x1e
+
+/* Index (NID:5bh) */
+#define RT722_IMS_DIGITAL_CTL1 0x00
+#define RT722_IMS_DIGITAL_CTL5 0x05
+#define RT722_HP_DETECT_RLDET_CTL1 0x29
+#define RT722_HP_DETECT_RLDET_CTL2 0x2a
+
+/* Index (NID:5fh) */
+#define RT722_MISC_POWER_CTL0 0x00
+#define RT722_MISC_POWER_CTL7 0x08
+
+/* Index (NID:61h) */
+#define RT722_HDA_LEGACY_MUX_CTL0 0x00
+#define RT722_HDA_LEGACY_UNSOL_CTL 0x03
+#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06
+#define RT722_HDA_LEGACY_RESET_CTL 0x08
+#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT722_DMIC_ENT_FLOAT_CTL 0x10
+#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
+#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13
+#define RT722_ADC_ENT_FLOAT_CTL 0x15
+#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17
+#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18
+#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22
+#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23
+#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24
+#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25
+#define RT722_ADC10_PDE_FLOAT_CTL 0x26
+#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28
+#define RT722_AMP_PDE_FLOAT_CTL 0x29
+#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f
+#define RT722_GE_RELATED_CTL1 0x45
+#define RT722_GE_RELATED_CTL2 0x46
+#define RT722_MIXER_CTL0 0x52
+#define RT722_MIXER_CTL1 0x53
+#define RT722_EAPD_CTL 0x55
+#define RT722_UMP_HID_CTL0 0x60
+#define RT722_UMP_HID_CTL1 0x61
+#define RT722_UMP_HID_CTL2 0x62
+#define RT722_UMP_HID_CTL3 0x63
+#define RT722_UMP_HID_CTL4 0x64
+#define RT722_UMP_HID_CTL5 0x65
+#define RT722_UMP_HID_CTL6 0x66
+#define RT722_UMP_HID_CTL7 0x67
+#define RT722_UMP_HID_CTL8 0x68
+#define RT722_FLOAT_CTRL_1 0x70
+#define RT722_ENT_FLOAT_CTRL_1 0x76
+
+/* Parameter & Verb control 01 (0x1a)(NID:20h) */
+#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* DAC calibration control (0x00)(NID:58h) */
+#define RT722_DC_CALIB_CTRL (0x1 << 16)
+/* DAC DC offset calibration control-1 (0x01)(NID:58h) */
+#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15)
+
+#define RT722_EAPD_HIGH 0x2
+#define RT722_EAPD_LOW 0x0
+
+/* Buffer address for HID */
+#define RT722_BUF_ADDR_HID1 0x44030000
+#define RT722_BUF_ADDR_HID2 0x44030020
+
+/* RT722 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT722 SDCA entity */
+#define RT722_SDCA_ENT_HID01 0x01
+#define RT722_SDCA_ENT_GE49 0x49
+#define RT722_SDCA_ENT_USER_FU05 0x05
+#define RT722_SDCA_ENT_USER_FU06 0x06
+#define RT722_SDCA_ENT_USER_FU0F 0x0f
+#define RT722_SDCA_ENT_USER_FU10 0x19
+#define RT722_SDCA_ENT_USER_FU1E 0x1e
+#define RT722_SDCA_ENT_FU15 0x15
+#define RT722_SDCA_ENT_PDE23 0x23
+#define RT722_SDCA_ENT_PDE40 0x40
+#define RT722_SDCA_ENT_PDE11 0x11
+#define RT722_SDCA_ENT_PDE12 0x12
+#define RT722_SDCA_ENT_PDE2A 0x2a
+#define RT722_SDCA_ENT_CS01 0x01
+#define RT722_SDCA_ENT_CS11 0x11
+#define RT722_SDCA_ENT_CS1F 0x1f
+#define RT722_SDCA_ENT_CS1C 0x1c
+#define RT722_SDCA_ENT_CS31 0x31
+#define RT722_SDCA_ENT_OT23 0x42
+#define RT722_SDCA_ENT_IT26 0x26
+#define RT722_SDCA_ENT_IT09 0x09
+#define RT722_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT722_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT722_SDCA_ENT_XU03 0x03
+#define RT722_SDCA_ENT_XU0D 0x0d
+#define RT722_SDCA_ENT0 0x00
+
+/* RT722 SDCA control */
+#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT722_SDCA_CTL_FU_MUTE 0x01
+#define RT722_SDCA_CTL_FU_VOLUME 0x02
+#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT722_SDCA_CTL_SELECTED_MODE 0x01
+#define RT722_SDCA_CTL_DETECTED_MODE 0x02
+#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT722_SDCA_CTL_VENDOR_DEF 0x30
+#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT722_SDCA_CTL_FUNC_STATUS 0x10
+#define RT722_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+
+/* RT722 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_08 0x08
+
+/* sample frequency index */
+#define RT722_SDCA_RATE_16000HZ 0x04
+#define RT722_SDCA_RATE_32000HZ 0x07
+#define RT722_SDCA_RATE_44100HZ 0x08
+#define RT722_SDCA_RATE_48000HZ 0x09
+#define RT722_SDCA_RATE_96000HZ 0x0b
+#define RT722_SDCA_RATE_192000HZ 0x0d
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+enum {
+ RT722_AIF1, /* For headset mic and headphone */
+ RT722_AIF2, /* For speaker */
+ RT722_AIF3, /* For dmic */
+ RT722_AIFS,
+};
+
+enum rt722_sdca_jd_src {
+ RT722_JD_NULL,
+ RT722_JD1,
+};
+
+enum rt722_sdca_version {
+ RT722_VA,
+ RT722_VB,
+};
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave);
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value);
+int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int *value);
+
+int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic);
+#endif /* __RT722_H__ */
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
new file mode 100644
index 000000000000..97f56af25577
--- /dev/null
+++ b/sound/soc/codecs/rt9120.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RT9120_REG_DEVID 0x00
+#define RT9120_REG_I2SFMT 0x02
+#define RT9120_REG_I2SWL 0x03
+#define RT9120_REG_SDIOSEL 0x04
+#define RT9120_REG_SYSCTL 0x05
+#define RT9120_REG_SPKGAIN 0x07
+#define RT9120_REG_VOLRAMP 0x0A
+#define RT9120_REG_ERRRPT 0x10
+#define RT9120_REG_MSVOL 0x20
+#define RT9120_REG_SWRESET 0x40
+#define RT9120_REG_INTERCFG 0x63
+#define RT9120_REG_INTERNAL0 0x65
+#define RT9120_REG_INTERNAL1 0x69
+#define RT9120_REG_UVPOPT 0x6C
+#define RT9120_REG_DIGCFG 0xF8
+
+#define RT9120_VID_MASK GENMASK(15, 8)
+#define RT9120_SWRST_MASK BIT(7)
+#define RT9120_MUTE_MASK GENMASK(5, 4)
+#define RT9120_I2SFMT_MASK GENMASK(4, 2)
+#define RT9120_I2SFMT_SHIFT 2
+#define RT9120_CFG_FMT_I2S 0
+#define RT9120_CFG_FMT_LEFTJ 1
+#define RT9120_CFG_FMT_RIGHTJ 2
+#define RT9120_CFG_FMT_DSPA 3
+#define RT9120_CFG_FMT_DSPB 7
+#define RT9120_AUDBIT_MASK GENMASK(1, 0)
+#define RT9120_CFG_AUDBIT_16 0
+#define RT9120_CFG_AUDBIT_20 1
+#define RT9120_CFG_AUDBIT_24 2
+#define RT9120_AUDWL_MASK GENMASK(5, 0)
+#define RT9120_CFG_WORDLEN_16 16
+#define RT9120_CFG_WORDLEN_24 24
+#define RT9120_CFG_WORDLEN_32 32
+#define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4)
+#define RT9120_AUTOSYNC_MASK BIT(6)
+
+#define RT9120_VENDOR_ID 0x42
+#define RT9120S_VENDOR_ID 0x43
+#define RT9120_RESET_WAITMS 20
+#define RT9120_CHIPON_WAITMS 20
+#define RT9120_AMPON_WAITMS 50
+#define RT9120_AMPOFF_WAITMS 100
+#define RT9120_LVAPP_THRESUV 2000000
+
+/* 8000 to 192000 supported , only 176400 not support */
+#define RT9120_RATES_MASK (SNDRV_PCM_RATE_8000_192000 &\
+ ~SNDRV_PCM_RATE_176400)
+#define RT9120_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum {
+ CHIP_IDX_RT9120 = 0,
+ CHIP_IDX_RT9120S,
+ CHIP_IDX_MAX
+};
+
+struct rt9120_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pwdnn_gpio;
+ int chip_idx;
+};
+
+/* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -1039375, 625, 1);
+
+/* {6, 8, 10, 12, 13, 14, 15, 16}dB */
+static const DECLARE_TLV_DB_RANGE(classd_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(600, 200, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(1300, 100, 0)
+);
+
+static const char * const sdo_select_text[] = {
+ "None", "INTF", "Final", "RMS Detect"
+};
+
+static const struct soc_enum sdo_select_enum =
+ SOC_ENUM_SINGLE(RT9120_REG_SDIOSEL, 4, ARRAY_SIZE(sdo_select_text),
+ sdo_select_text);
+
+static const struct snd_kcontrol_new rt9120_snd_controls[] = {
+ SOC_SINGLE_TLV("MS Volume", RT9120_REG_MSVOL, 0, 2047, 1, digital_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RT9120_REG_SPKGAIN, 0, 7, 0, classd_tlv),
+ SOC_SINGLE("PBTL Switch", RT9120_REG_SYSCTL, 3, 1, 0),
+ SOC_ENUM("SDO Select", sdo_select_enum),
+};
+
+static int internal_power_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(comp, RT9120_REG_ERRRPT, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(RT9120_AMPON_WAITMS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(RT9120_AMPOFF_WAITMS);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9120_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("DMIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("LDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("RDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("PWND", RT9120_REG_SYSCTL, 6, 1,
+ internal_power_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA("SPKL PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKR PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route rt9120_dapm_routes[] = {
+ { "DMIX", NULL, "AIF Playback" },
+ /* SPKL */
+ { "LDAC", NULL, "PWND" },
+ { "LDAC", NULL, "DMIX" },
+ { "SPKL PA", NULL, "LDAC" },
+ { "SPKL", NULL, "SPKL PA" },
+ /* SPKR */
+ { "RDAC", NULL, "PWND" },
+ { "RDAC", NULL, "DMIX" },
+ { "SPKR PA", NULL, "RDAC" },
+ { "SPKR", NULL, "SPKR PA" },
+ /* Cap */
+ { "AIF Capture", NULL, "LDAC" },
+ { "AIF Capture", NULL, "RDAC" },
+};
+
+static int rt9120_codec_probe(struct snd_soc_component *comp)
+{
+ struct rt9120_data *data = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, data->regmap);
+
+ pm_runtime_get_sync(comp->dev);
+
+ /* Internal setting */
+ if (data->chip_idx == CHIP_IDX_RT9120S) {
+ snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde);
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x66);
+ } else
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04);
+
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
+ return 0;
+}
+
+static int rt9120_codec_suspend(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_suspend(comp->dev);
+}
+
+static int rt9120_codec_resume(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_resume(comp->dev);
+}
+
+static const struct snd_soc_component_driver rt9120_component_driver = {
+ .probe = rt9120_codec_probe,
+ .suspend = rt9120_codec_suspend,
+ .resume = rt9120_codec_resume,
+ .controls = rt9120_snd_controls,
+ .num_controls = ARRAY_SIZE(rt9120_snd_controls),
+ .dapm_widgets = rt9120_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets),
+ .dapm_routes = rt9120_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes),
+ .endianness = 1,
+};
+
+static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int format;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = RT9120_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = RT9120_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = RT9120_CFG_FMT_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = RT9120_CFG_FMT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = RT9120_CFG_FMT_DSPB;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_I2SFMT_MASK,
+ format << RT9120_I2SFMT_SHIFT);
+ return 0;
+}
+
+static int rt9120_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int param_width, param_slot_width, auto_sync;
+ int width, fs;
+
+ switch (width = params_width(param)) {
+ case 16:
+ param_width = RT9120_CFG_AUDBIT_16;
+ break;
+ case 20:
+ param_width = RT9120_CFG_AUDBIT_20;
+ break;
+ case 24:
+ case 32:
+ param_width = RT9120_CFG_AUDBIT_24;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported data width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_AUDBIT_MASK, param_width);
+
+ switch (width = params_physical_width(param)) {
+ case 16:
+ param_slot_width = RT9120_CFG_WORDLEN_16;
+ break;
+ case 24:
+ param_slot_width = RT9120_CFG_WORDLEN_24;
+ break;
+ case 32:
+ param_slot_width = RT9120_CFG_WORDLEN_32;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported slot width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SWL,
+ RT9120_AUDWL_MASK, param_slot_width);
+
+ fs = width * params_channels(param);
+ /* If fs is divided by 48, disable auto sync */
+ if (fs % 48 == 0)
+ auto_sync = 0;
+ else
+ auto_sync = RT9120_AUTOSYNC_MASK;
+
+ snd_soc_component_update_bits(comp, RT9120_REG_DIGCFG,
+ RT9120_AUTOSYNC_MASK, auto_sync);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9120_dai_ops = {
+ .set_fmt = rt9120_set_fmt,
+ .hw_params = rt9120_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9120_dai = {
+ .name = "rt9120_aif",
+ .playback = {
+ .stream_name = "AIF Playback",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9120_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct regmap_range rt9120_rd_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x0C),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x40),
+ regmap_reg_range(0x63, 0x63),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C),
+ regmap_reg_range(0xF8, 0xF8)
+};
+
+static const struct regmap_access_table rt9120_rd_table = {
+ .yes_ranges = rt9120_rd_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_rd_yes_ranges),
+};
+
+static const struct regmap_range rt9120_wr_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x00),
+ regmap_reg_range(0x02, 0x0A),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x3D),
+ regmap_reg_range(0x40, 0x40),
+ regmap_reg_range(0x63, 0x63),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C),
+ regmap_reg_range(0xF8, 0xF8)
+};
+
+static const struct regmap_access_table rt9120_wr_table = {
+ .yes_ranges = rt9120_wr_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges),
+};
+
+static bool rt9120_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x01:
+ case 0x10:
+ case 0x30 ... 0x40:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt9120_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x00:
+ case 0x20 ... 0x27:
+ return 2;
+ case 0x30 ... 0x3D:
+ return 3;
+ case 0x3E ... 0x3F:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ u8 raw[4] = {0};
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, raw);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ switch (size) {
+ case 4:
+ *val = be32_to_cpup((__be32 *)raw);
+ break;
+ case 3:
+ *val = raw[0] << 16 | raw[1] << 8 | raw[2];
+ break;
+ case 2:
+ *val = be16_to_cpup((__be16 *)raw);
+ break;
+ default:
+ *val = raw[0];
+ }
+
+ return 0;
+}
+
+static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ __be32 be32_val;
+ u8 *rawp = (u8 *)&be32_val;
+ int offs = 4 - size;
+
+ be32_val = cpu_to_be32(val);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs);
+}
+
+static const struct reg_default rt9120_reg_defaults[] = {
+ { .reg = 0x02, .def = 0x02 },
+ { .reg = 0x03, .def = 0xf2 },
+ { .reg = 0x04, .def = 0x01 },
+ { .reg = 0x05, .def = 0xc0 },
+ { .reg = 0x06, .def = 0x28 },
+ { .reg = 0x07, .def = 0x04 },
+ { .reg = 0x08, .def = 0xff },
+ { .reg = 0x09, .def = 0x01 },
+ { .reg = 0x0a, .def = 0x01 },
+ { .reg = 0x0b, .def = 0x00 },
+ { .reg = 0x0c, .def = 0x04 },
+ { .reg = 0x11, .def = 0x30 },
+ { .reg = 0x12, .def = 0x08 },
+ { .reg = 0x13, .def = 0x12 },
+ { .reg = 0x14, .def = 0x09 },
+ { .reg = 0x15, .def = 0x00 },
+ { .reg = 0x20, .def = 0x7ff },
+ { .reg = 0x21, .def = 0x180 },
+ { .reg = 0x22, .def = 0x180 },
+ { .reg = 0x23, .def = 0x00 },
+ { .reg = 0x24, .def = 0x80 },
+ { .reg = 0x25, .def = 0x180 },
+ { .reg = 0x26, .def = 0x640 },
+ { .reg = 0x27, .def = 0x180 },
+ { .reg = 0x63, .def = 0x5e },
+ { .reg = 0x65, .def = 0x66 },
+ { .reg = 0x6c, .def = 0xe0 },
+ { .reg = 0xf8, .def = 0x44 },
+};
+
+static const struct regmap_config rt9120_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT9120_REG_DIGCFG,
+ .reg_defaults = rt9120_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .reg_read = rt9120_reg_read,
+ .reg_write = rt9120_reg_write,
+
+ .volatile_reg = rt9120_volatile_reg,
+ .wr_table = &rt9120_wr_table,
+ .rd_table = &rt9120_rd_table,
+};
+
+static int rt9120_check_vendor_info(struct rt9120_data *data)
+{
+ unsigned int devid;
+ int ret;
+
+ ret = regmap_read(data->regmap, RT9120_REG_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ devid = FIELD_GET(RT9120_VID_MASK, devid);
+ switch (devid) {
+ case RT9120_VENDOR_ID:
+ data->chip_idx = CHIP_IDX_RT9120;
+ break;
+ case RT9120S_VENDOR_ID:
+ data->chip_idx = CHIP_IDX_RT9120S;
+ break;
+ default:
+ dev_err(data->dev, "DEVID not correct [0x%0x]\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt9120_do_register_reset(struct rt9120_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, RT9120_REG_SWRESET,
+ RT9120_SWRST_MASK);
+ if (ret)
+ return ret;
+
+ msleep(RT9120_RESET_WAITMS);
+ return 0;
+}
+
+static int rt9120_probe(struct i2c_client *i2c)
+{
+ struct rt9120_data *data;
+ struct regulator *dvdd_supply;
+ int dvdd_supply_volt, ret;
+
+ data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &i2c->dev;
+ i2c_set_clientdata(i2c, data);
+
+ data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->pwdnn_gpio)) {
+ dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n");
+ return PTR_ERR(data->pwdnn_gpio);
+ } else if (data->pwdnn_gpio) {
+ dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n");
+ msleep(RT9120_CHIPON_WAITMS);
+ }
+
+ data->regmap = devm_regmap_init(&i2c->dev, NULL, data,
+ &rt9120_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap [%d]\n", ret);
+ return ret;
+ }
+
+ ret = rt9120_check_vendor_info(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to check vendor info\n");
+ return ret;
+ }
+
+ ret = rt9120_do_register_reset(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to do register reset\n");
+ return ret;
+ }
+
+ dvdd_supply = devm_regulator_get(&i2c->dev, "dvdd");
+ if (IS_ERR(dvdd_supply)) {
+ dev_err(&i2c->dev, "No dvdd regulator found\n");
+ return PTR_ERR(dvdd_supply);
+ }
+
+ dvdd_supply_volt = regulator_get_voltage(dvdd_supply);
+ if (dvdd_supply_volt <= RT9120_LVAPP_THRESUV) {
+ dev_dbg(&i2c->dev, "dvdd low voltage design\n");
+ ret = regmap_update_bits(data->regmap, RT9120_REG_UVPOPT,
+ RT9120_DVDD_UVSEL_MASK, 0);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to config dvdd uvsel\n");
+ return ret;
+ }
+ }
+
+ pm_runtime_set_autosuspend_delay(&i2c->dev, 1000);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_mark_last_busy(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &rt9120_component_driver,
+ &rt9120_dai, 1);
+}
+
+static void rt9120_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+ pm_runtime_set_suspended(&i2c->dev);
+}
+
+static int rt9120_runtime_suspend(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ regcache_cache_only(data->regmap, true);
+ regcache_mark_dirty(data->regmap);
+ gpiod_set_value(data->pwdnn_gpio, 0);
+ }
+
+ return 0;
+}
+
+static int rt9120_runtime_resume(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ gpiod_set_value(data->pwdnn_gpio, 1);
+ msleep(RT9120_CHIPON_WAITMS);
+ regcache_cache_only(data->regmap, false);
+ regcache_sync(data->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt9120_pm_ops = {
+ RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL)
+};
+
+static const struct of_device_id __maybe_unused rt9120_device_table[] = {
+ { .compatible = "richtek,rt9120", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt9120_device_table);
+
+static struct i2c_driver rt9120_driver = {
+ .driver = {
+ .name = "rt9120",
+ .of_match_table = rt9120_device_table,
+ .pm = pm_ptr(&rt9120_pm_ops),
+ },
+ .probe = rt9120_probe,
+ .remove = rt9120_remove,
+};
+module_i2c_driver(rt9120_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RT9120 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
new file mode 100644
index 000000000000..b162824526d6
--- /dev/null
+++ b/sound/soc/codecs/rt9123.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123.c -- RT9123 (SW I2C Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#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/tlv.h>
+
+#define RT9123_REG_AMPCTRL 0x01
+#define RT9123_REG_I2SOPT 0x02
+#define RT9123_REG_TDMRX 0x03
+#define RT9123_REG_SILVOLEN 0x04
+#define RT9123_REG_VOLGAIN 0x12
+#define RT9123_REG_ANAFLAG 0x36
+#define RT9123_REG_COMBOID 0xF7
+
+#define RT9123_MASK_SWRST BIT(15)
+#define RT9123_MASK_SWMUTE BIT(14)
+#define RT9123_MASK_AMPON BIT(12)
+#define RT9123_MASK_AUDBIT GENMASK(14, 12)
+#define RT9123_MASK_AUDFMT GENMASK(11, 8)
+#define RT9123_MASK_TDMRXLOC GENMASK(4, 0)
+#define RT9123_MASK_VENID GENMASK(15, 4)
+
+#define RT9123_FIXED_VENID 0x340
+
+struct rt9123_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rt9123_enable_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 device *dev = comp->dev;
+ unsigned int enable;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */
+ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-1200, 0, 0),
+ 1, 9, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(1400, 0, 0));
+static const char * const pwmfreq_text[] = { "300KHz", "325KHz", "350KHz", "375KHz" };
+static const struct soc_enum rt9123_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_AMPCTRL, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rt9123_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_I2SOPT, 4, ARRAY_SIZE(i2sch_text), i2sch_text);
+
+static int rt9123_kcontrol_name_comp(struct snd_kcontrol *kcontrol, const char *s)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ const char *kctlname = kcontrol->id.name;
+
+ if (comp && comp->name_prefix)
+ kctlname += strlen(comp->name_prefix) + 1;
+
+ return strcmp(kctlname, s);
+}
+
+static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG read
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to get control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG write
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to put control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static const struct snd_kcontrol_new rt9123_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RT9123_REG_VOLGAIN, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_EXT_TLV("Speaker Volume", RT9123_REG_AMPCTRL, 0, 10, 0, rt9123_xhandler_get,
+ rt9123_xhandler_put, ana_tlv),
+ SOC_ENUM_EXT("PWM Frequency Select", rt9123_pwm_freq_enum, rt9123_xhandler_get,
+ rt9123_xhandler_put),
+ SOC_ENUM("I2S CH Select", rt9123_i2sch_select_enum),
+ SOC_SINGLE("Silence Detect Switch", RT9123_REG_SILVOLEN, 14, 1, 0),
+};
+
+static const struct snd_soc_component_driver rt9123_comp_driver = {
+ .controls = rt9123_controls,
+ .num_controls = ARRAY_SIZE(rt9123_controls),
+ .dapm_widgets = rt9123_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123_dapm_widgets),
+ .dapm_routes = rt9123_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt9123_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+
+ rt9123->dai_fmt = fmt;
+ return 0;
+}
+
+static int rt9123_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int rx_loc;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slots % 2 || slot_width % 8 ||
+ slots * slot_width > 256) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || ffs(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure rx channel data location */
+ rx_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write_field(comp, RT9123_REG_TDMRX, RT9123_MASK_TDMRXLOC, rx_loc);
+
+ rt9123->tdm_slots = slots;
+ rt9123->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rt9123_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rt9123->dai_fmt);
+ if (rt9123->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rt9123->tdm_slots ? 4 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rt9123->tdm_slots ? 12 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ audbit = 2;
+ break;
+ case 32:
+ audbit = 3;
+ break;
+ case 8:
+ audbit = 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+ if (rt9123->tdm_slots && slot_width > rt9123->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDFMT, audfmt);
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDBIT, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9123_dai_ops = {
+ .set_fmt = rt9123_dai_set_format,
+ .set_tdm_slot = rt9123_dai_set_tdm_slot,
+ .hw_params = rt9123_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9123_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_22050 | SNDRV_PCM_RATE_24000 |
+ 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 = &rt9123_dai_ops,
+};
+
+static bool rt9123_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01:
+ case 0x20:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt9123_regmap_config = {
+ .name = "rt9123",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .readable_reg = rt9123_readable_reg,
+ .writeable_reg = rt9123_writeable_reg,
+ .volatile_reg = rt9123_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = RT9123_REG_ANAFLAG + 1,
+};
+
+static int rt9123_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rt9123_priv *rt9123;
+ struct regmap *regmap;
+ __be16 value;
+ u16 venid;
+ int ret;
+
+ rt9123 = devm_kzalloc(dev, sizeof(*rt9123), GFP_KERNEL);
+ if (!rt9123)
+ return -ENOMEM;
+
+ rt9123->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt9123->enable))
+ return PTR_ERR(rt9123->enable);
+ else if (rt9123->enable)
+ usleep_range(250, 350);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_i2c_block_data(i2c, RT9123_REG_COMBOID, sizeof(value), (u8 *)&value);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read vendor-id\n");
+
+ venid = be16_to_cpu(value);
+ if ((venid & RT9123_MASK_VENID) != RT9123_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect vendor-id 0x%04x\n", venid);
+
+ /* Trigger RG reset before regmap init cache */
+ value = cpu_to_be16(RT9123_MASK_SWRST);
+ ret = i2c_smbus_write_i2c_block_data(i2c, RT9123_REG_AMPCTRL, sizeof(value), (u8 *)&value);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init_i2c(i2c, &rt9123_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ i2c_set_clientdata(i2c, rt9123);
+
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rt9123_comp_driver, &rt9123_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rt9123_runtime_suspend(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rt9123->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rt9123->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rt9123_runtime_resume(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rt9123->enable) {
+ gpiod_set_value(rt9123->enable, 1);
+ usleep_range(250, 350);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rt9123_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rt9123_runtime_suspend, rt9123_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123_device_id[] = {
+ { .compatible = "richtek,rt9123" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123_acpi_match[] = {
+ { "RT9123", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123_acpi_match);
+#endif
+
+static struct i2c_driver rt9123_i2c_driver = {
+ .driver = {
+ .name = "rt9123",
+ .of_match_table = of_match_ptr(rt9123_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123_acpi_match),
+ .pm = pm_ptr(&rt9123_dev_pm_ops),
+ },
+ .probe = rt9123_i2c_probe,
+};
+module_i2c_driver(rt9123_i2c_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c
new file mode 100644
index 000000000000..d509659e735b
--- /dev/null
+++ b/sound/soc/codecs/rt9123p.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123p.c -- RT9123 (HW Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.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 <linux/property.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt9123p_priv {
+ struct gpio_desc *enable;
+ unsigned int enable_delay;
+ int enable_switch;
+};
+
+static int rt9123p_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (!rt9123p->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mdelay(rt9123p->enable_delay);
+ if (rt9123p->enable_switch) {
+ gpiod_set_value(rt9123p->enable, 1);
+ dev_dbg(comp->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(rt9123p->enable, 0);
+ dev_dbg(comp->dev, "set enable to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt9123p_enable_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 rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ rt9123p->enable_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ rt9123p->enable_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123p_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123p_dapm_routes[] = {
+ {"Amp Drv", NULL, "HiFi Playback"},
+ {"SPK", NULL, "Amp Drv"},
+};
+
+static const struct snd_soc_component_driver rt9123p_comp_driver = {
+ .dapm_widgets = rt9123p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123p_dapm_widgets),
+ .dapm_routes = rt9123p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt9123p_dai_ops = {
+ .trigger = rt9123p_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver rt9123p_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_22050 | SNDRV_PCM_RATE_24000 |
+ 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 = &rt9123p_dai_ops,
+};
+
+static int rt9123p_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rt9123p_priv *rt9123p;
+ int ret;
+
+ rt9123p = devm_kzalloc(dev, sizeof(*rt9123p), GFP_KERNEL);
+ if (!rt9123p)
+ return -ENOMEM;
+
+ rt9123p->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(rt9123p->enable))
+ return PTR_ERR(rt9123p->enable);
+
+ ret = device_property_read_u32(dev, "enable-delay-ms", &rt9123p->enable_delay);
+ if (ret) {
+ rt9123p->enable_delay = 0;
+ dev_dbg(dev, "no optional property 'enable-delay-ms' found, default: no delay\n");
+ }
+
+ platform_set_drvdata(pdev, rt9123p);
+
+ return devm_snd_soc_register_component(dev, &rt9123p_comp_driver, &rt9123p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123p_device_id[] = {
+ { .compatible = "richtek,rt9123p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123p_acpi_match[] = {
+ { "RT9123P", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123p_acpi_match);
+#endif
+
+static struct platform_driver rt9123p_platform_driver = {
+ .driver = {
+ .name = "rt9123p",
+ .of_match_table = of_match_ptr(rt9123p_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123p_acpi_match),
+ },
+ .probe = rt9123p_platform_probe,
+};
+module_platform_driver(rt9123p_platform_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123p Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
new file mode 100644
index 000000000000..186904b31434
--- /dev/null
+++ b/sound/soc/codecs/rtq9124.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rtq9124.c -- RTQ9124 ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#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/tlv.h>
+
+#define RTQ9124_REG_SDI_SEL 0x00
+#define RTQ9124_REG_SDO_SEL 0x01
+#define RTQ9124_REG_I2S_OPT 0x02
+#define RTQ9124_REG_AMP_OPT 0x03
+#define RTQ9124_REG_STATE_CTRL 0x04
+#define RTQ9124_REG_PWM_PHASE 0x05
+#define RTQ9124_REG_SIL_CTRL 0x06
+#define RTQ9124_REG_PWM_SS_OPT 0x07
+#define RTQ9124_REG_ERR_INT_0 0x10
+#define RTQ9124_REG_ERR_MASK6 0x26
+#define RTQ9124_REG_TDM_TX_CH0 0x32
+#define RTQ9124_REG_TDM_RX_CH0 0x34
+#define RTQ9124_REG_VOL_OPT 0x38
+#define RTQ9124_REG_DCR_TH 0x4B
+#define RTQ9124_REG_ERR_TH 0x4C
+#define RTQ9124_REG_PROT_EN 0x5B
+#define RTQ9124_REG_PRJ_CODE 0xF9
+
+#define RTQ9124_MASK_CS_DATA_INV BIT(9)
+#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8)
+#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4)
+#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0)
+#define RTQ9124_MASK_CH_STATE GENMASK(1, 0)
+#define RTQ9124_MASK_SF_RESET BIT(15)
+
+#define RTQ9124_FIXED_VENID 0x9124
+
+struct rtq9124_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rtq9124_enable_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);
+ unsigned int i, chan_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Change state to normal */
+ chan_state = 0;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Change state to HiZ */
+ chan_state = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Before amp turn on, clear old events first */
+ for (i = 0; !chan_state && i < 8; i++)
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff);
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE,
+ chan_state);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0));
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rtq9124_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text);
+static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" };
+static const struct soc_enum rtq9124_sdo_vselect_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text);
+static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" };
+static const struct soc_enum rtq9124_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" };
+static const struct soc_enum rtq9124_out_angle_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text);
+static const char * const sdo_select_text[] = {
+ "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin",
+ "DCR",
+};
+static const struct soc_enum rtq9124_sdo_select_enum =
+ SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text);
+static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" };
+static const struct soc_enum rtq9124_ulqm_dcvt_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text);
+
+static const struct snd_kcontrol_new rtq9124_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv),
+ SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum),
+ SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum),
+ SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum),
+ SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum),
+ SOC_ENUM("SDO Select", rtq9124_sdo_select_enum),
+ SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum),
+ SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0),
+ SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0),
+};
+
+static int rtq9124_comp_probe(struct snd_soc_component *comp)
+{
+ /* CS Data INV */
+ snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1);
+
+ /* RTLD */
+ snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff);
+ snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9124_comp_driver = {
+ .probe = rtq9124_comp_probe,
+ .controls = rtq9124_controls,
+ .num_controls = ARRAY_SIZE(rtq9124_controls),
+ .dapm_widgets = rtq9124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets),
+ .dapm_routes = rtq9124_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+
+ rtq9124->dai_fmt = fmt;
+ return 0;
+}
+
+static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int byte_loc, i;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) {
+ dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure tx channel data location */
+ for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) {
+ byte_loc = (ffs(tx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc);
+ }
+
+ /* Configure rx channel data location */
+ byte_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc);
+
+ rtq9124->tdm_slots = slots;
+ rtq9124->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width, bitrate;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt);
+ if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A &&
+ fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rtq9124->tdm_slots ? 7 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rtq9124->tdm_slots ? 15 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ if (rtq9124->tdm_slots) {
+ slot_width = params_physical_width(param);
+ if (slot_width > rtq9124->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate);
+ return -EINVAL;
+ }
+ }
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt);
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rtq9124_dai_ops = {
+ .set_fmt = rtq9124_dai_set_format,
+ .set_tdm_slot = rtq9124_dai_set_tdm_slot,
+ .hw_params = rtq9124_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rtq9124_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_48000 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rtq9124_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static bool rtq9124_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30 ... 0x3D:
+ case 0x40 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x09:
+ case 0x0C ... 0x0E:
+ case 0x10 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30:
+ case 0x32 ... 0x3D:
+ case 0x40 ... 0x4E:
+ case 0x50 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0A ... 0x0B:
+ case 0x0F ... 0x17:
+ case 0x31:
+ case 0x4F:
+ case 0x51:
+ case 0x53 ... 0x57:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline u8 rtq9124_get_reg_len(unsigned int reg)
+{
+ return (reg >= 0x40 && reg <= 0x47) ? 4 : 2;
+}
+
+static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)reg_buf;
+ u8 size = rtq9124_get_reg_len(reg);
+ u32 *val = val_buf;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf);
+
+ return 0;
+}
+
+static int rtq9124_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)data, *vbuf;
+ u8 size = rtq9124_get_reg_len(reg);
+ __be16 val16 = cpu_to_be16p(data + 1);
+ __be32 val32 = cpu_to_be32p(data + 1);
+
+ vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16;
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf);
+}
+
+static const struct regmap_config rtq9124_regmap_config = {
+ .name = "rtq9124",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .read = rtq9124_regmap_read,
+ .write = rtq9124_regmap_write,
+ .readable_reg = rtq9124_readable_reg,
+ .writeable_reg = rtq9124_writeable_reg,
+ .volatile_reg = rtq9124_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = 0xFD + 1,
+ .use_single_read = 1,
+ .use_single_write = 1,
+};
+
+static const struct reg_sequence rtq9124_init_regs[] = {
+ { 0xfb, 0x0065 },
+ { 0x93, 0x2000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rtq9124_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9124_priv *rtq9124;
+ struct regmap *regmap;
+ int ret;
+
+ rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL);
+ if (!rtq9124)
+ return -ENOMEM;
+
+ rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rtq9124->enable))
+ return PTR_ERR(rtq9124->enable);
+ else if (rtq9124->enable)
+ usleep_range(6000, 7000);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read project code\n");
+ else if (ret != RTQ9124_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret);
+
+ /* Trigger RG reset before regmap init */
+ ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register regmap patch\n");
+
+ i2c_set_clientdata(i2c, rtq9124);
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rtq9124_runtime_suspend(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rtq9124->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rtq9124->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rtq9124_runtime_resume(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rtq9124->enable) {
+ gpiod_set_value(rtq9124->enable, 1);
+ usleep_range(6000, 7000);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rtq9124_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtq9124_device_id[] = {
+ { .compatible = "richtek,rtq9124" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9124_device_id);
+#endif
+
+static struct i2c_driver rtq9124_driver = {
+ .driver = {
+ .name = "rtq9124",
+ .of_match_table = of_match_ptr(rtq9124_device_id),
+ .pm = pm_ptr(&rtq9124_dev_pm_ops),
+ },
+ .probe = rtq9124_probe,
+};
+module_i2c_driver(rtq9124_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC RTQ9124 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
new file mode 100644
index 000000000000..391cc03d687f
--- /dev/null
+++ b/sound/soc/codecs/rtq9128.c
@@ -0,0 +1,789 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2023 Richtek Technology Corp.
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+//
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RTQ9128_REG_SDI_SEL 0x00
+#define RTQ9128_REG_SDO_SEL 0x01
+#define RTQ9128_REG_I2S_OPT 0x02
+#define RTQ9128_REG_MISC 0x03
+#define RTQ9128_REG_STATE_CTRL 0x04
+#define RTQ9128_REG_PLLTRI_GEN1 0x05
+#define RTQ9128_REG_PLLTRI_GEN2 0x06
+#define RTQ9128_REG_PWM_SS_OPT 0x07
+#define RTQ9128_REG_DSP_EN 0x08
+#define RTQ9128_REG_TDM_TX_CH1 0x21
+#define RTQ9128_REG_TDM_RX_CH1 0x25
+#define RTQ9128_REG_MS_VOL 0x30
+#define RTQ9128_REG_CH1_VOL 0x31
+#define RTQ9128_REG_CH2_VOL 0x32
+#define RTQ9128_REG_CH3_VOL 0x33
+#define RTQ9128_REG_CH4_VOL 0x34
+#define RTQ9128_REG_PROT_OPT 0x71
+#define RTQ9128_REG_EFUSE_DATA 0xE0
+#define RTQ9128_REG_VENDOR_ID 0xF9
+
+#define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0)
+#define RTQ9128_DOLEN_MASK GENMASK(7, 6)
+#define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4)
+#define RTQ9128_AUDBIT_MASK GENMASK(5, 4)
+#define RTQ9128_AUDFMT_MASK GENMASK(3, 0)
+#define RTQ9128_MSMUTE_MASK BIT(0)
+#define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0)
+#define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8)
+
+#define RTQ9128_SOFT_RESET_VAL 0x80
+#define RTQ9128_VENDOR_ID_VAL 0x470
+#define RTQ9128_ALLCH_HIZ_VAL 0x55
+#define RTQ9128_ALLCH_ULQM_VAL 0xFF
+#define RTQ9128_TKA470B_VAL 0
+#define RTQ9128_RTQ9128DH_VAL 0x0F
+#define RTQ9128_RTQ9128DL_VAL 0x10
+
+struct rtq9128_data {
+ struct gpio_desc *enable;
+ unsigned int daifmt;
+ int tdm_slots;
+ int tdm_slot_width;
+ bool tdm_input_data2_select;
+};
+
+struct rtq9128_init_reg {
+ unsigned int reg;
+ unsigned int val;
+};
+
+static int rtq9128_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x5C ... 0x6F:
+ case 0x98 ... 0x9F:
+ case 0xC0 ... 0xC3:
+ case 0xC8 ... 0xCF:
+ case 0xDF ... 0xE5:
+ case 0xF9:
+ return 4;
+ case 0x40 ... 0x4F:
+ return 3;
+ case 0x30 ... 0x35:
+ case 0x8C ... 0x97:
+ case 0xC4 ... 0xC7:
+ case 0xD7 ... 0xDA:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int rtq9128_i2c_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)data;
+ int rg_size;
+
+ if (count != 5) {
+ dev_err(dev, "Invalid write for data length (%d)\n", (int)count);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, rg_size, data + count - rg_size);
+}
+
+static int rtq9128_i2c_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)reg_buf;
+ u8 data_tmp[4] = {};
+ int rg_size, ret;
+
+ if (reg_size != 1 || val_size != 4) {
+ dev_err(dev, "Invalid read for reg_size (%d) or val_size (%d)\n", (int)reg_size,
+ (int)val_size);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, rg_size, data_tmp);
+ if (ret < 0)
+ return ret;
+ else if (ret != rg_size)
+ return -EIO;
+
+ memset(val_buf, 0, val_size - rg_size);
+ memcpy(val_buf + val_size - rg_size, data_tmp, rg_size);
+
+ return 0;
+}
+
+static const struct regmap_bus rtq9128_regmap_bus = {
+ .write = rtq9128_i2c_write,
+ .read = rtq9128_i2c_read,
+ .max_raw_read = 4,
+ .max_raw_write = 4,
+};
+
+static bool rtq9128_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0 ... 0xE5:
+ case 0xF0 ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x1F:
+ case 0x21 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0x8B:
+ case 0xA0 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0:
+ case 0xD0 ... 0xDE:
+ case 0xE0 ... 0xE5:
+ case 0xF0 ... 0xF3:
+ case 0xF6 ... 0xF8:
+ case 0xFA ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0F ... 0x17:
+ case 0x20:
+ case 0x53:
+ case 0x55:
+ case 0x5C ... 0x6F:
+ case 0x8C ... 0x9F:
+ case 0xC0 ... 0xCF:
+ case 0xDF:
+ case 0xF0 ... 0xF1:
+ case 0xF4 ... 0xF5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rtq9128_regmap_config = {
+ .name = "rtq9128",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+
+ .readable_reg = rtq9128_is_readable_reg,
+ .writeable_reg = rtq9128_is_writeable_reg,
+ .volatile_reg = rtq9128_is_volatile_reg,
+ .num_reg_defaults_raw = RTQ9128_REG_VENDOR_ID + 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+
+static const DECLARE_TLV_DB_RANGE(spkgain_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1500, 300, 0),
+);
+
+static const char * const source_select_text[] = { "CH1", "CH2", "CH3", "CH4" };
+static const char * const pwmfreq_select_text[] = { "8fs", "10fs", "40fs", "44fs", "48fs" };
+static const char * const phase_select_text[] = {
+ "0 degree", "45 degree", "90 degree", "135 degree",
+ "180 degree", "225 degree", "270 degree", "315 degree",
+};
+static const char * const dvdduv_select_text[] = { "1P4V", "1P5V", "2P1V", "2P3V" };
+
+static const struct soc_enum rtq9128_ch1_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch2_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch3_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch4_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 4, ARRAY_SIZE(pwmfreq_select_text),
+ pwmfreq_select_text);
+static const struct soc_enum rtq9128_out2_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out3_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out4_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+
+/*
+ * In general usage, DVDD could be 1P8V, 3P0V or 3P3V.
+ * This DVDD undervoltage protection is to prevent from the abnormal power
+ * lose case while the amplifier is operating. Due to the different DVDD
+ * application, treat this threshold as a user choosable option.
+ */
+static const struct soc_enum rtq9128_dvdduv_select_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PROT_OPT, 6, ARRAY_SIZE(dvdduv_select_text),
+ dvdduv_select_text);
+
+static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = {
+ SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", RTQ9128_REG_CH1_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", RTQ9128_REG_CH2_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", RTQ9128_REG_CH3_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", RTQ9128_REG_CH4_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv),
+ SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 5, 1, 0),
+ SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 4, 1, 0),
+ SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0),
+ SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0),
+ SOC_ENUM("CH1 SI Select", rtq9128_ch1_si_enum),
+ SOC_ENUM("CH2 SI Select", rtq9128_ch2_si_enum),
+ SOC_ENUM("CH3 SI Select", rtq9128_ch3_si_enum),
+ SOC_ENUM("CH4 SI Select", rtq9128_ch4_si_enum),
+ SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum),
+ SOC_ENUM("OUT2 Phase Select", rtq9128_out2_phase_enum),
+ SOC_ENUM("OUT3 Phase Select", rtq9128_out3_phase_enum),
+ SOC_ENUM("OUT4 Phase Select", rtq9128_out4_phase_enum),
+ SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
+};
+
+static int rtq9128_dac_power_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);
+ unsigned int shift, mask;
+ int ret;
+
+ dev_dbg(comp->dev, "%s: %s event %d\n", __func__, w->name, event);
+
+ if (snd_soc_dapm_widget_name_cmp(w, "DAC1") == 0)
+ shift = 6;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC2") == 0)
+ shift = 4;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC3") == 0)
+ shift = 2;
+ else
+ shift = 0;
+
+ mask = RTQ9128_CHSTAT_VAL_MASK << shift;
+
+ /* Turn channel state to Normal or HiZ */
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_STATE_CTRL, mask,
+ event != SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * For each channel turns on, HW will trigger DC load detect and DC
+ * offset calibration, the time is needed for all the actions done.
+ */
+ if (event == SND_SOC_DAPM_POST_PMU)
+ msleep(25);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9128_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC1", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC4", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+};
+
+static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+ { "DAC3", NULL, "Playback" },
+ { "DAC4", NULL, "Playback" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+ { "OUT4", NULL, "DAC4" },
+ { "Capture", NULL, "DAC1" },
+ { "Capture", NULL, "DAC2" },
+ { "Capture", NULL, "DAC3" },
+ { "Capture", NULL, "DAC4" },
+};
+
+static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
+ { 0xA0, 0xEF },
+ { 0x0D, 0x00 },
+ { 0x03, 0x05 },
+ { 0x05, 0x31 },
+ { 0x06, 0x23 },
+ { 0x70, 0x11 },
+ { 0x75, 0x1F },
+ { 0xB6, 0x03 },
+ { 0xB9, 0x03 },
+ { 0xB8, 0x03 },
+ { 0xC1, 0xFF },
+ { 0xF8, 0x72 },
+ { 0x30, 0x180 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0xB2, 0xFF },
+ { 0xB3, 0xFF },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static int rtq9128_component_probe(struct snd_soc_component *comp)
+{
+ const struct rtq9128_init_reg *table, *curr;
+ size_t table_size;
+ unsigned int val;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "Failed to resume device (%d)\n", ret);
+ return ret;
+ }
+
+ val = snd_soc_component_read(comp, RTQ9128_REG_EFUSE_DATA);
+
+ switch (FIELD_GET(RTQ9128_DIE_CHECK_MASK, val)) {
+ case RTQ9128_TKA470B_VAL:
+ table = rtq9128_tka470b_tables;
+ table_size = ARRAY_SIZE(rtq9128_tka470b_tables);
+ break;
+ case RTQ9128_RTQ9128DH_VAL:
+ table = rtq9128_dh_tables;
+ table_size = ARRAY_SIZE(rtq9128_dh_tables);
+ break;
+ default:
+ table = rtq9128_dl_tables;
+ table_size = ARRAY_SIZE(rtq9128_dl_tables);
+ break;
+ }
+
+ for (i = 0, curr = table; i < table_size; i++, curr++) {
+ ret = snd_soc_component_write(comp, curr->reg, curr->val);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9128_comp_driver = {
+ .probe = rtq9128_component_probe,
+ .controls = rtq9128_snd_ctrls,
+ .num_controls = ARRAY_SIZE(rtq9128_snd_ctrls),
+ .dapm_widgets = rtq9128_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets),
+ .dapm_routes = rtq9128_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->dev;
+
+ dev_dbg(dev, "%s: fmt 0x%8x\n", __func__, fmt);
+
+ /* Only support bitclock & framesync as consumer */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
+ dev_err(dev, "Only support BCK and LRCK as consumer\n");
+ return -EINVAL;
+ }
+
+ /* Store here and will be used in runtime hw_params for DAI format setting */
+ data->daifmt = fmt;
+
+ return 0;
+}
+
+static int rtq9128_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int mask, start_loc, srcin_select;
+ int i, frame_length, ret;
+
+ dev_dbg(dev, "%s: slot %d slot_width %d, tx/rx mask 0x%x 0x%x\n", __func__, slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slot_width % 8) {
+ dev_err(dev, "Invalid slot numbers (%d) or width (%d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ /* HW supported maximum frame length 512 */
+ frame_length = slots * slot_width;
+ if (frame_length > 512) {
+ dev_err(dev, "frame length exceed the maximum (%d)\n", frame_length);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(tx_mask) > slots || hweight_long(rx_mask) > slots ||
+ fls(tx_mask) > slots || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid tx/rx mask (0x%x/0x%x)\n", tx_mask, rx_mask);
+ return -EINVAL;
+ }
+
+ for (mask = tx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_TX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign tx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ for (mask = rx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_RX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign rx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ srcin_select = data->tdm_input_data2_select ? RTQ9128_TDMSRCIN_MASK : 0;
+ ret = snd_soc_component_update_bits(comp, RTQ9128_REG_SDO_SEL, RTQ9128_TDMSRCIN_MASK,
+ srcin_select);
+ if (ret < 0) {
+ dev_err(dev, "Failed to configure TDM source input select\n");
+ return ret;
+ }
+
+ data->tdm_slots = slots;
+ data->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9128_dai_hw_params(struct snd_pcm_substream *stream, struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ unsigned int width, slot_width, bitrate, audbit, dolen;
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int fmtval, audfmt;
+ int ret;
+
+ dev_dbg(dev, "%s: width %d\n", __func__, params_width(param));
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, data->daifmt);
+ if (data->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM is used, format only support DSP_A or DSP_B\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 10;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = data->tdm_slots ? 12 : 11;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = data->tdm_slots ? 4 : 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported format 0x%8x\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 18:
+ audbit = 1;
+ break;
+ case 20:
+ audbit = 2;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width (%d)\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+
+ if (data->tdm_slots) {
+ if (slot_width > data->tdm_slot_width) {
+ dev_err(dev, "slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ /* Check BCK not exceed the maximum supported rate 24.576MHz */
+ bitrate = data->tdm_slots * data->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "bitrate exceed the maximum (%d)\n", bitrate);
+ return -EINVAL;
+ }
+
+ /* If TDM is used, configure slot width as TDM slot witdh */
+ slot_width = data->tdm_slot_width;
+ }
+
+ switch (slot_width) {
+ case 16:
+ dolen = 0;
+ break;
+ case 24:
+ dolen = 1;
+ break;
+ case 32:
+ dolen = 2;
+ break;
+ default:
+ dev_err(dev, "Unsupported slot width (%d)\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDFMT_MASK, audfmt);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDBIT_MASK, audbit);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_SDO_SEL, RTQ9128_DOLEN_MASK, dolen);
+ return ret < 0 ? ret : 0;
+}
+
+static int rtq9128_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ int ret;
+
+ dev_dbg(dev, "%s: mute (%d), stream (%d)\n", __func__, mute, stream);
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_DSP_EN, RTQ9128_MSMUTE_MASK,
+ mute ? 1 : 0);
+ return ret < 0 ? ret : 0;
+}
+
+static const struct snd_soc_dai_ops rtq9128_dai_ops = {
+ .set_fmt = rtq9128_dai_set_fmt,
+ .set_tdm_slot = rtq9128_dai_set_tdm_slot,
+ .hw_params = rtq9128_dai_hw_params,
+ .mute_stream = rtq9128_dai_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define RTQ9128_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rtq9128_dai = {
+ .name = "rtq9128-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &rtq9128_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int rtq9128_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9128_data *data;
+ struct regmap *regmap;
+ unsigned int venid;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->enable))
+ return dev_err_probe(dev, PTR_ERR(data->enable), "Failed to get 'enable' gpio\n");
+ else if (data->enable)
+ usleep_range(10000, 11000);
+
+ data->tdm_input_data2_select = device_property_read_bool(dev,
+ "richtek,tdm-input-data2-select");
+
+ i2c_set_clientdata(i2c, data);
+
+ /*
+ * Due to the bad design to combine SOFT_RESET bit with other function,
+ * directly use generic i2c API to trigger SOFT_RESET.
+ */
+ ret = i2c_smbus_write_byte_data(i2c, RTQ9128_REG_MISC, RTQ9128_SOFT_RESET_VAL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger software reset\n");
+
+ /* After trigger soft reset, have to wait 10ms for digital reset done */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, &rtq9128_regmap_bus, dev, &rtq9128_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get vendor id\n");
+
+ venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid);
+ if (venid != RTQ9128_VENDOR_ID_VAL)
+ return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1);
+}
+
+static int rtq9128_pm_runtime_suspend(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to ultra low quiescent */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_ULQM_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 0);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int rtq9128_pm_runtime_resume(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to default Hi-Z */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_HIZ_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 1);
+
+ /* Wait digital block to be ready */
+ usleep_range(10000, 11000);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static const struct dev_pm_ops rtq9128_pm_ops = {
+ RUNTIME_PM_OPS(rtq9128_pm_runtime_suspend, rtq9128_pm_runtime_resume, NULL)
+};
+
+static const struct of_device_id rtq9128_device_table[] = {
+ { .compatible = "richtek,rtq9128" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9128_device_table);
+
+static struct i2c_driver rtq9128_driver = {
+ .driver = {
+ .name = "rtq9128",
+ .of_match_table = rtq9128_device_table,
+ .pm = pm_ptr(&rtq9128_pm_ops),
+ },
+ .probe = rtq9128_probe,
+};
+module_i2c_driver(rtq9128_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RTQ9128 4CH Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
new file mode 100644
index 000000000000..574c08b14f0c
--- /dev/null
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// sdw-mockup.c -- a mockup SoundWire codec for tests where only the host
+// drives the bus.
+//
+// Copyright(c) 2021 Intel Corporation
+//
+//
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+struct sdw_mockup_priv {
+ struct sdw_slave *slave;
+};
+
+static int sdw_mockup_component_probe(struct snd_soc_component *component)
+{
+ return 0;
+}
+
+static void sdw_mockup_component_remove(struct snd_soc_component *component)
+{
+}
+
+static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
+ .probe = sdw_mockup_component_probe,
+ .remove = sdw_mockup_component_remove,
+ .endianness = 1,
+};
+
+static int sdw_mockup_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 sdw_mockup_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int sdw_mockup_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 sdw_mockup_priv *sdw_mockup = 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 = snd_soc_dai_get_dma_data(dai, substream);
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!sdw_mockup->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 8;
+
+ ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret)
+ dev_err(dai->dev, "Unable to configure port\n");
+
+ return ret;
+}
+
+static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_mockup->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(sdw_mockup->slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sdw_mockup_ops = {
+ .hw_params = sdw_mockup_pcm_hw_params,
+ .hw_free = sdw_mockup_pcm_hw_free,
+ .set_stream = sdw_mockup_set_sdw_stream,
+ .shutdown = sdw_mockup_shutdown,
+};
+
+static struct snd_soc_dai_driver sdw_mockup_dai[] = {
+ {
+ .name = "sdw-mockup-aif1",
+ .id = 1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "DP8 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &sdw_mockup_ops,
+ },
+};
+
+static int sdw_mockup_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ return 0;
+}
+
+static int sdw_mockup_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->paging_support = false;
+
+ /*
+ * first we need to allocate memory for set bits in port lists
+ * the port allocation is completely arbitrary:
+ * DP0 is not supported
+ * DP1 is sink
+ * DP8 is source
+ */
+ prop->source_ports = BIT(8);
+ 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;
+ 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;
+ j++;
+ }
+
+ prop->simple_clk_stop_capable = true;
+
+ /* wake-up event */
+ prop->wake_capable = 0;
+
+ return 0;
+}
+
+static int sdw_mockup_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ return 0;
+}
+
+static int sdw_mockup_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ return 0;
+}
+
+static const struct sdw_slave_ops sdw_mockup_slave_ops = {
+ .read_prop = sdw_mockup_read_prop,
+ .interrupt_callback = sdw_mockup_interrupt_callback,
+ .update_status = sdw_mockup_update_status,
+ .bus_config = sdw_mockup_bus_config,
+};
+
+static int sdw_mockup_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &slave->dev;
+ struct sdw_mockup_priv *sdw_mockup;
+ int ret;
+
+ sdw_mockup = devm_kzalloc(dev, sizeof(*sdw_mockup), GFP_KERNEL);
+ if (!sdw_mockup)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sdw_mockup);
+ sdw_mockup->slave = slave;
+
+ slave->is_mockup_device = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &snd_soc_sdw_mockup_component,
+ sdw_mockup_dai,
+ ARRAY_SIZE(sdw_mockup_dai));
+
+ return ret;
+}
+
+static int sdw_mockup_sdw_remove(struct sdw_slave *slave)
+{
+ return 0;
+}
+
+/*
+ * Intel reserved parts ID with the following mapping expected:
+ * 0xAAAA: generic full-duplex codec
+ * 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex
+ * 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with
+ * IV feedback
+ * 0x5555: mic codec (mock-up of RT715) - capture-only
+ */
+static const struct sdw_device_id sdw_mockup_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, sdw_mockup_id);
+
+static struct sdw_driver sdw_mockup_sdw_driver = {
+ .driver = {
+ .name = "sdw-mockup",
+ },
+ .probe = sdw_mockup_sdw_probe,
+ .remove = sdw_mockup_sdw_remove,
+ .ops = &sdw_mockup_slave_ops,
+ .id_table = sdw_mockup_id,
+};
+module_sdw_driver(sdw_mockup_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC SDW mockup codec driver");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index e1ab5537d27a..2cc8efe3d896 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1,12 +1,8 @@
-/*
- * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
- *
- * Copyright 2010-2011 Freescale Semiconductor, 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 version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
+//
+// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -17,11 +13,11 @@
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/log2.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
@@ -35,6 +31,13 @@
#define SGTL5000_DAP_REG_OFFSET 0x0100
#define SGTL5000_MAX_REG_OFFSET 0x013A
+/* Delay for the VAG ramp up */
+#define SGTL5000_VAG_POWERUP_DELAY 500 /* ms */
+/* Delay for the VAG ramp down */
+#define SGTL5000_VAG_POWERDOWN_DELAY 500 /* ms */
+
+#define SGTL5000_OUTPUTS_MUTE (SGTL5000_HP_MUTE | SGTL5000_LINE_OUT_MUTE)
+
/* default value of sgtl5000 registers */
static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_DIG_POWER, 0x0000 },
@@ -68,7 +71,7 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
{ SGTL5000_DAP_MAIN_CHAN, 0x8000 },
{ SGTL5000_DAP_MIX_CHAN, 0x0000 },
- { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_CTRL, 0x5100 },
{ SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
{ SGTL5000_DAP_AVC_ATTACK, 0x0028 },
{ SGTL5000_DAP_AVC_DECAY, 0x0050 },
@@ -120,6 +123,20 @@ enum {
I2S_LRCLK_STRENGTH_HIGH,
};
+enum {
+ I2S_SCLK_STRENGTH_DISABLE,
+ I2S_SCLK_STRENGTH_LOW,
+ I2S_SCLK_STRENGTH_MEDIUM,
+ I2S_SCLK_STRENGTH_HIGH,
+};
+
+enum {
+ HP_POWER_EVENT,
+ DAC_POWER_EVENT,
+ ADC_POWER_EVENT,
+ LAST_POWER_EVENT = ADC_POWER_EVENT
+};
+
/* sgtl5000 private structure in codec */
struct sgtl5000_priv {
int sysclk; /* sysclk rate */
@@ -133,8 +150,110 @@ struct sgtl5000_priv {
u8 micbias_resistor;
u8 micbias_voltage;
u8 lrclk_strength;
+ u8 sclk_strength;
+ u16 mute_state[LAST_POWER_EVENT + 1];
};
+static inline int hp_sel_input(struct snd_soc_component *component)
+{
+ return (snd_soc_component_read(component, SGTL5000_CHIP_ANA_CTRL) &
+ SGTL5000_HP_SEL_MASK) >> SGTL5000_HP_SEL_SHIFT;
+}
+
+static inline u16 mute_output(struct snd_soc_component *component,
+ u16 mute_mask)
+{
+ u16 mute_reg = snd_soc_component_read(component,
+ SGTL5000_CHIP_ANA_CTRL);
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ mute_mask, mute_mask);
+ return mute_reg;
+}
+
+static inline void restore_output(struct snd_soc_component *component,
+ u16 mute_mask, u16 mute_reg)
+{
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ mute_mask, mute_reg);
+}
+
+static void vag_power_on(struct snd_soc_component *component, u32 source)
+{
+ if (snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER) &
+ SGTL5000_VAG_POWERUP)
+ return;
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+
+ /* When VAG powering on to get local loop from Line-In, the sleep
+ * is required to avoid loud pop.
+ */
+ if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN &&
+ source == HP_POWER_EVENT)
+ msleep(SGTL5000_VAG_POWERUP_DELAY);
+}
+
+static int vag_power_consumers(struct snd_soc_component *component,
+ u16 ana_pwr_reg, u32 source)
+{
+ int consumers = 0;
+
+ /* count dac/adc consumers unconditional */
+ if (ana_pwr_reg & SGTL5000_DAC_POWERUP)
+ consumers++;
+ if (ana_pwr_reg & SGTL5000_ADC_POWERUP)
+ consumers++;
+
+ /*
+ * If the event comes from HP and Line-In is selected,
+ * current action is 'DAC to be powered down'.
+ * As HP_POWERUP is not set when HP muxed to line-in,
+ * we need to keep VAG power ON.
+ */
+ if (source == HP_POWER_EVENT) {
+ if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN)
+ consumers++;
+ } else {
+ if (ana_pwr_reg & SGTL5000_HP_POWERUP)
+ consumers++;
+ }
+
+ return consumers;
+}
+
+static void vag_power_off(struct snd_soc_component *component, u32 source)
+{
+ u16 ana_pwr = snd_soc_component_read(component,
+ SGTL5000_CHIP_ANA_POWER);
+
+ if (!(ana_pwr & SGTL5000_VAG_POWERUP))
+ return;
+
+ /*
+ * This function calls when any of VAG power consumers is disappearing.
+ * Thus, if there is more than one consumer at the moment, as minimum
+ * one consumer will definitely stay after the end of the current
+ * event.
+ * Don't clear VAG_POWERUP if 2 or more consumers of VAG present:
+ * - LINE_IN (for HP events) / HP (for DAC/ADC events)
+ * - DAC
+ * - ADC
+ * (the current consumer is disappearing right now)
+ */
+ if (vag_power_consumers(component, ana_pwr, source) >= 2)
+ return;
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, 0);
+ /* In power down case, we need wait 400-1000 ms
+ * when VAG fully ramped down.
+ * As longer we wait, as smaller pop we've got.
+ */
+ msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+}
+
/*
* mic_bias power on/off share the same register bits with
* output impedance of mic bias, when power on mic bias, we
@@ -147,55 +266,65 @@ struct sgtl5000_priv {
static int mic_bias_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* change mic bias resistor */
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK, 0);
break;
}
return 0;
}
-/*
- * As manual described, ADC/DAC only works when VAG powerup,
- * So enabled VAG before ADC/DAC up.
- * In power down case, we need wait 400ms when vag fully ramped down.
- */
-static int power_vag_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int vag_and_mute_control(struct snd_soc_component *component,
+ int event, int event_source)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP;
+ static const u16 mute_mask[] = {
+ /*
+ * Mask for HP_POWER_EVENT.
+ * Muxing Headphones have to be wrapped with mute/unmute
+ * headphones only.
+ */
+ SGTL5000_HP_MUTE,
+ /*
+ * Masks for DAC_POWER_EVENT/ADC_POWER_EVENT.
+ * Muxing DAC or ADC block have to wrapped with mute/unmute
+ * both headphones and line-out.
+ */
+ SGTL5000_OUTPUTS_MUTE,
+ SGTL5000_OUTPUTS_MUTE
+ };
+
+ struct sgtl5000_priv *sgtl5000 =
+ snd_soc_component_get_drvdata(component);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sgtl5000->mute_state[event_source] =
+ mute_output(component, mute_mask[event_source]);
+ break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
- msleep(400);
+ vag_power_on(component, event_source);
+ restore_output(component, mute_mask[event_source],
+ sgtl5000->mute_state[event_source]);
break;
-
case SND_SOC_DAPM_PRE_PMD:
- /*
- * Don't clear VAG_POWERUP, when both DAC and ADC are
- * operational to prevent inadvertently starving the
- * other one of them.
- */
- if ((snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER) &
- mask) != mask) {
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, 0);
- msleep(400);
- }
+ sgtl5000->mute_state[event_source] =
+ mute_output(component, mute_mask[event_source]);
+ vag_power_off(component, event_source);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ restore_output(component, mute_mask[event_source],
+ sgtl5000->mute_state[event_source]);
break;
default:
break;
@@ -204,6 +333,41 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
return 0;
}
+/*
+ * Mute Headphone when power it up/down.
+ * Control VAG power on HP power path.
+ */
+static int headphone_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);
+
+ return vag_and_mute_control(component, event, HP_POWER_EVENT);
+}
+
+/* As manual describes, ADC/DAC powering up/down requires
+ * to mute outputs to avoid pops.
+ * Control VAG power on ADC/DAC power path.
+ */
+static int adc_updown_depop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ return vag_and_mute_control(component, event, ADC_POWER_EVENT);
+}
+
+static int dac_updown_depop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ return vag_and_mute_control(component, event, DAC_POWER_EVENT);
+}
+
/* input sources for ADC */
static const char *adc_mux_text[] = {
"MIC_IN", "LINE_IN"
@@ -216,17 +380,54 @@ static SOC_ENUM_SINGLE_DECL(adc_enum,
static const struct snd_kcontrol_new adc_mux =
SOC_DAPM_ENUM("Capture Mux", adc_enum);
+/* input sources for headphone */
+static const char *hp_mux_text[] = {
+ "DAC", "LINE_IN"
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_enum,
+ SGTL5000_CHIP_ANA_CTRL, 6,
+ hp_mux_text);
+
+static const struct snd_kcontrol_new hp_mux =
+SOC_DAPM_ENUM("Headphone Mux", hp_enum);
+
/* input sources for DAC */
static const char *dac_mux_text[] = {
- "DAC", "LINE_IN"
+ "ADC", "I2S", "Rsvrd", "DAP"
};
static SOC_ENUM_SINGLE_DECL(dac_enum,
- SGTL5000_CHIP_ANA_CTRL, 6,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT,
dac_mux_text);
static const struct snd_kcontrol_new dac_mux =
-SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+SOC_DAPM_ENUM("Digital Input Mux", dac_enum);
+
+/* input sources for DAP */
+static const char *dap_mux_text[] = {
+ "ADC", "I2S"
+};
+
+static SOC_ENUM_SINGLE_DECL(dap_enum,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_SEL_SHIFT,
+ dap_mux_text);
+
+static const struct snd_kcontrol_new dap_mux =
+SOC_DAPM_ENUM("DAP Mux", dap_enum);
+
+/* input sources for DAP mix */
+static const char *dapmix_mux_text[] = {
+ "ADC", "I2S"
+};
+
+static SOC_ENUM_SINGLE_DECL(dapmix_enum,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_MIX_SEL_SHIFT,
+ dapmix_mux_text);
+
+static const struct snd_kcontrol_new dapmix_mux =
+SOC_DAPM_ENUM("DAP MIX Mux", dapmix_enum);
+
static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINE_IN"),
@@ -239,11 +440,19 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
mic_bias_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
+ headphone_pga_event,
+ SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
- SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+ SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux),
+ SND_SOC_DAPM_MUX("Digital Input Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+ SND_SOC_DAPM_MUX("DAP Mux", SGTL5000_DAP_CTRL, 0, 0, &dap_mux),
+ SND_SOC_DAPM_MUX("DAP MIX Mux", SGTL5000_DAP_CTRL, 4, 0, &dapmix_mux),
+ SND_SOC_DAPM_MIXER("DAP", SGTL5000_CHIP_DIG_POWER, 4, 0, NULL, 0),
+
/* aif for i2s input */
SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
@@ -255,11 +464,12 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
- SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
-
- SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
- SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0,
+ adc_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0,
+ dac_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
};
/* routes for sgtl5000 */
@@ -270,7 +480,19 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
+ {"DAP Mux", "ADC", "ADC"}, /* adc --> DAP mux */
+ {"DAP Mux", NULL, "AIFIN"}, /* i2s --> DAP mux */
+ {"DAP", NULL, "DAP Mux"}, /* DAP mux --> dap */
+
+ {"DAP MIX Mux", "ADC", "ADC"}, /* adc --> DAP MIX mux */
+ {"DAP MIX Mux", NULL, "AIFIN"}, /* i2s --> DAP MIX mux */
+ {"DAP", NULL, "DAP MIX Mux"}, /* DAP MIX mux --> dap */
+
+ {"Digital Input Mux", "ADC", "ADC"}, /* adc --> audio mux */
+ {"Digital Input Mux", NULL, "AIFIN"}, /* i2s --> audio mux */
+ {"Digital Input Mux", NULL, "DAP"}, /* dap --> audio mux */
+ {"DAC", NULL, "Digital Input Mux"}, /* audio mux --> dac */
+
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
@@ -318,12 +540,12 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol,
static int dac_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
- reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+ reg = snd_soc_component_read(component, SGTL5000_CHIP_DAC_VOL);
/* get left channel volume */
l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
@@ -371,7 +593,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol,
static int dac_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
@@ -391,7 +613,7 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+ snd_soc_component_write(component, SGTL5000_CHIP_DAC_VOL, reg);
return 0;
}
@@ -403,15 +625,15 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
* avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==>
* dB = ( fls(register_value) - 14.347 ) * 6.02
*
- * As this calculation is expensive and the threshold dB values may not exeed
+ * As this calculation is expensive and the threshold dB values may not exceed
* 0 to 96 we use pre-calculated values.
*/
static int avc_get_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int db, i;
- u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD);
+ u16 reg = snd_soc_component_read(component, SGTL5000_DAP_AVC_THRESHOLD);
/* register value 0 => -96dB */
if (!reg) {
@@ -436,13 +658,13 @@ static int avc_get_threshold(struct snd_kcontrol *kcontrol,
*
* The register value is calculated by following formula:
* register_value = 10^(dB/20) * 0.636 * 2^15
- * As this calculation is expensive and the threshold dB values may not exeed
+ * As this calculation is expensive and the threshold dB values may not exceed
* 0 to 96 we use pre-calculated values.
*/
static int avc_put_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int db;
u16 reg;
@@ -450,7 +672,7 @@ static int avc_put_threshold(struct snd_kcontrol *kcontrol,
if (db < 0 || db > 96)
return -EINVAL;
reg = avc_thr_db2reg[db];
- snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg);
+ snd_soc_component_write(component, SGTL5000_DAP_AVC_THRESHOLD, reg);
return 0;
}
@@ -463,6 +685,12 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
);
+/* tlv for DAP channels, 0% - 100% - 200% */
+static const DECLARE_TLV_DB_SCALE(dap_volume, 0, 1, 0);
+
+/* tlv for bass bands, -11.75db to 12.0db, step .25db */
+static const DECLARE_TLV_DB_SCALE(bass_band, -1175, 25, 0);
+
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
@@ -492,6 +720,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
SGTL5000_CHIP_ANA_ADC_CTRL,
8, 1, 0, capture_6db_attenuate),
SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
+ SOC_SINGLE("Capture Switch", SGTL5000_CHIP_ANA_CTRL, 0, 1, 1),
SOC_DOUBLE_TLV("Headphone Playback Volume",
SGTL5000_CHIP_ANA_HP_CTRL,
@@ -514,6 +743,11 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
lineout_volume),
SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1),
+ SOC_SINGLE_TLV("DAP Main channel", SGTL5000_DAP_MAIN_CHAN,
+ 0, 0xffff, 0, dap_volume),
+
+ SOC_SINGLE_TLV("DAP Mix channel", SGTL5000_DAP_MIX_CHAN,
+ 0, 0xffff, 0, dap_volume),
/* Automatic Volume Control (DAP AVC) */
SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0),
SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0),
@@ -523,16 +757,36 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD,
0, 96, 0, avc_get_threshold, avc_put_threshold,
avc_threshold),
+
+ SOC_SINGLE_TLV("BASS 0", SGTL5000_DAP_EQ_BASS_BAND0,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 1", SGTL5000_DAP_EQ_BASS_BAND1,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 2", SGTL5000_DAP_EQ_BASS_BAND2,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 3", SGTL5000_DAP_EQ_BASS_BAND3,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 4", SGTL5000_DAP_EQ_BASS_BAND4,
+ 0, 0x5F, 0, bass_band),
};
/* mute the codec used by alsa core */
-static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int sgtl5000_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 i2s_pwr = SGTL5000_I2S_IN_POWERUP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
- adcdac_ctrl, mute ? adcdac_ctrl : 0);
+ /*
+ * During 'digital mute' do not mute DAC
+ * because LINE_IN would be muted aswell. We want to mute
+ * only I2S block - this can be done by powering it off
+ */
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_DIG_POWER,
+ i2s_pwr, mute ? 0 : i2s_pwr);
return 0;
}
@@ -540,8 +794,8 @@ static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
/* set codec format */
static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
u16 i2sctl = 0;
sgtl5000->master = 0;
@@ -552,9 +806,9 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
* - clock and frame master
*/
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
i2sctl |= SGTL5000_I2S_MASTER;
sgtl5000->master = 1;
break;
@@ -599,7 +853,7 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_I2S_CTRL, i2sctl);
return 0;
}
@@ -608,8 +862,8 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int sgtl5000_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case SGTL5000_SYSCLK:
@@ -635,9 +889,9 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
* and above.
* 3. usage of sys_mclk is preferred over pll to save power.
*/
-static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+static int sgtl5000_set_clock(struct snd_soc_component *component, int frame_rate)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int clk_ctl = 0;
int sys_fs; /* sample freq */
@@ -690,7 +944,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
break;
default:
- dev_err(codec->dev, "frame rate %d not supported\n",
+ dev_err(component->dev, "frame rate %d not supported\n",
frame_rate);
return -EINVAL;
}
@@ -719,9 +973,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
SGTL5000_MCLK_FREQ_SHIFT;
} else {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PLL not supported in slave mode\n");
- dev_err(codec->dev, "%d ratio is not supported. "
+ dev_err(component->dev, "%d ratio is not supported. "
"SYS_MCLK needs to be 256, 384 or 512 * fs\n",
sgtl5000->sysclk / frame_rate);
return -EINVAL;
@@ -754,31 +1008,31 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
if (div2)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
SGTL5000_INPUT_FREQ_DIV2);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
0);
/* power up pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
/* if using pll, clk_ctrl must be set after pll power up */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
/* otherwise, clk_ctrl must be set before pll power down */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
/* power down pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
0);
}
@@ -794,8 +1048,8 @@ static int sgtl5000_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int channels = params_channels(params);
int i2s_ctl = 0;
int stereo;
@@ -803,7 +1057,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
/* sysclk should already set */
if (!sgtl5000->sysclk) {
- dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+ dev_err(component->dev, "%s: set sysclk first!\n", __func__);
return -EFAULT;
}
@@ -813,11 +1067,11 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
stereo = SGTL5000_ADC_STEREO;
/* set mono to save power */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, stereo,
channels == 1 ? 0 : stereo);
/* set codec clock base on lrclk */
- ret = sgtl5000_set_clock(codec, params_rate(params));
+ ret = sgtl5000_set_clock(component, params_rate(params));
if (ret)
return ret;
@@ -851,7 +1105,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL,
SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK,
i2s_ctl);
@@ -868,19 +1122,30 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
* stop:
* on --> prepare --> standby
*/
-static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+static int sgtl5000_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct sgtl5000_priv *sgtl = snd_soc_component_get_drvdata(component);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ regcache_cache_only(sgtl->regmap, false);
+ ret = regcache_sync(sgtl->regmap);
+ if (ret) {
+ regcache_cache_only(sgtl->regmap, true);
+ return ret;
+ }
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP,
SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ regcache_cache_only(sgtl->regmap, true);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP, 0);
break;
}
@@ -895,9 +1160,10 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops sgtl5000_ops = {
.hw_params = sgtl5000_pcm_hw_params,
- .digital_mute = sgtl5000_digital_mute,
+ .mute_stream = sgtl5000_mute_stream,
.set_fmt = sgtl5000_set_dai_fmt,
.set_sysclk = sgtl5000_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver sgtl5000_dai = {
@@ -921,7 +1187,7 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.formats = SGTL5000_FORMATS,
},
.ops = &sgtl5000_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
@@ -1020,7 +1286,7 @@ static const u8 vol_quot_table[] = {
* 1. vddd provided by external or not
* 2. vdda and vddio voltage value. > 3.1v or not
*/
-static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+static int sgtl5000_set_power_regs(struct snd_soc_component *component)
{
int vddd;
int vdda;
@@ -1032,7 +1298,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
int vol_quot;
int lo_vol;
size_t i;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
@@ -1045,14 +1311,14 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
vddd = vddd / 1000;
if (vdda <= 0 || vddio <= 0 || vddd < 0) {
- dev_err(codec->dev, "regulator voltage not set correctly\n");
+ dev_err(component->dev, "regulator voltage not set correctly\n");
return -EINVAL;
}
/* according to datasheet, maximum voltage of supplies */
if (vdda > 3600 || vddio > 3600 || vddd > 1980) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n",
vdda, vddio, vddd);
@@ -1060,30 +1326,36 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
}
/* reset value */
- ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr = snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER);
ana_pwr |= SGTL5000_DAC_STEREO |
SGTL5000_ADC_STEREO |
SGTL5000_REFTOP_POWERUP;
- lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL);
+ lreg_ctrl = snd_soc_component_read(component, SGTL5000_CHIP_LINREG_CTRL);
if (vddio < 3100 && vdda < 3100) {
/* enable internal oscillator used for charge pump */
- snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INT_OSC_EN,
SGTL5000_INT_OSC_EN);
/* Enable VDDC charge pump */
ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
- } else if (vddio >= 3100 && vdda >= 3100) {
+ } else {
ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP;
- /* VDDC use VDDIO rail */
- lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
- lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
- SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ /*
+ * if vddio == vdda the source of charge pump should be
+ * assigned manually to VDDIO
+ */
+ if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer,
+ sgtl5000->supplies[VDDIO].consumer)) {
+ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+ lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+ SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ }
}
- snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
+ snd_soc_component_write(component, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
- snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ snd_soc_component_write(component, SGTL5000_CHIP_ANA_POWER, ana_pwr);
/*
* set ADC/DAC VAG to vdda / 2,
@@ -1098,7 +1370,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
else
vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL,
SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT);
/* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
@@ -1112,7 +1384,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) /
SGTL5000_LINE_OUT_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_CTRL,
SGTL5000_LINE_OUT_CURRENT_MASK |
SGTL5000_LINE_OUT_GND_MASK,
lo_vag << SGTL5000_LINE_OUT_GND_SHIFT |
@@ -1126,7 +1398,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
* Searching for a suitable index solving this formula:
* idx = 40 * log10(vag_val / lo_cagcntrl) + 15
*/
- vol_quot = (vag * 100) / lo_vag;
+ vol_quot = lo_vag ? (vag * 100) / lo_vag : 0;
lo_vol = 0;
for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
if (vol_quot >= vol_quot_table[i])
@@ -1135,7 +1407,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_VOL,
SGTL5000_LINE_OUT_VOL_RIGHT_MASK |
SGTL5000_LINE_OUT_VOL_LEFT_MASK,
lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT |
@@ -1183,59 +1455,59 @@ static int sgtl5000_enable_regulators(struct i2c_client *client)
return ret;
}
-static int sgtl5000_probe(struct snd_soc_codec *codec)
+static int sgtl5000_probe(struct snd_soc_component *component)
{
int ret;
u16 reg;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
+ unsigned int zcd_mask = SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN;
/* power up sgtl5000 */
- ret = sgtl5000_set_power_regs(codec);
+ ret = sgtl5000_set_power_regs(component);
if (ret)
goto err;
/* enable small pop, introduce 400ms delay in turning off */
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP, 1);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL,
+ SGTL5000_SMALL_POP, SGTL5000_SMALL_POP);
/* disable short cut detector */
- snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+ snd_soc_component_write(component, SGTL5000_CHIP_SHORT_CTRL, 0);
- /*
- * set i2s as default input of sound switch
- * TODO: add sound switch to control and dapm widge.
- */
- snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
- SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
- snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER,
+ snd_soc_component_write(component, SGTL5000_CHIP_DIG_POWER,
SGTL5000_ADC_EN | SGTL5000_DAC_EN);
/* enable dac volume ramp by default */
- snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ snd_soc_component_write(component, SGTL5000_CHIP_ADCDAC_CTRL,
SGTL5000_DAC_VOL_RAMP_EN |
SGTL5000_DAC_MUTE_RIGHT |
SGTL5000_DAC_MUTE_LEFT);
- reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f);
- snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, reg);
+ reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT |
+ (sgtl5000->sclk_strength) << SGTL5000_PAD_I2S_SCLK_SHIFT |
+ 0x1f);
+ snd_soc_component_write(component, SGTL5000_CHIP_PAD_STRENGTH, reg);
- snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
- SGTL5000_HP_ZCD_EN |
- SGTL5000_ADC_ZCD_EN);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ zcd_mask, zcd_mask);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_VOLT_MASK,
sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT);
/*
- * disable DAP
+ * enable DAP Graphic EQ
* TODO:
- * Enable DAP in kcontrol and dapm.
+ * Add control for changing between PEQ/Tone Control/GEQ
*/
- snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+ snd_soc_component_write(component, SGTL5000_DAP_AUDIO_EQ, SGTL5000_DAP_SEL_GEQ);
+
+ /* Unmute DAC after start */
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ADCDAC_CTRL,
+ SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT, 0);
return 0;
@@ -1243,24 +1515,27 @@ err:
return ret;
}
-static int sgtl5000_remove(struct snd_soc_codec *codec)
+static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
{
+ /* return dai id 0, whatever the endpoint index */
return 0;
}
-static const struct snd_soc_codec_driver sgtl5000_driver = {
- .probe = sgtl5000_probe,
- .remove = sgtl5000_remove,
- .set_bias_level = sgtl5000_set_bias_level,
- .suspend_bias_off = true,
- .component_driver = {
- .controls = sgtl5000_snd_controls,
- .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
- .dapm_widgets = sgtl5000_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
- .dapm_routes = sgtl5000_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
- },
+static const struct snd_soc_component_driver sgtl5000_driver = {
+ .probe = sgtl5000_probe,
+ .set_bias_level = sgtl5000_set_bias_level,
+ .controls = sgtl5000_snd_controls,
+ .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
+ .dapm_widgets = sgtl5000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
+ .dapm_routes = sgtl5000_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
+ .of_xlate_dai_id = sgtl5000_of_xlate_dai_id,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config sgtl5000_regmap = {
@@ -1303,8 +1578,7 @@ static void sgtl5000_fill_defaults(struct i2c_client *client)
}
}
-static int sgtl5000_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int sgtl5000_i2c_probe(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000;
int ret, reg, rev;
@@ -1336,9 +1610,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
if (ret == -ENOENT)
ret = -EPROBE_DEFER;
- if (ret != -EPROBE_DEFER)
- dev_err(&client->dev, "Failed to get mclock: %d\n",
- ret);
+ dev_err_probe(&client->dev, ret, "Failed to get mclock\n");
+
goto disable_regs;
}
@@ -1378,6 +1651,40 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_err(&client->dev,
"Error %d initializing CHIP_CLK_CTRL\n", ret);
+ /* Mute everything to avoid pop from the following power-up */
+ ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_CHIP_ANA_CTRL_DEFAULT);
+ if (ret) {
+ dev_err(&client->dev,
+ "Error %d muting outputs via CHIP_ANA_CTRL\n", ret);
+ goto disable_clk;
+ }
+
+ /*
+ * If VAG is powered-on (e.g. from previous boot), it would be disabled
+ * by the write to ANA_POWER in later steps of the probe code. This
+ * may create a loud pop even with all outputs muted. The proper way
+ * to circumvent this is disabling the bit first and waiting the proper
+ * cool-down time.
+ */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret);
+ goto disable_clk;
+ }
+ if (value & SGTL5000_VAG_POWERUP) {
+ ret = regmap_update_bits(sgtl5000->regmap,
+ SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP,
+ 0);
+ if (ret) {
+ dev_err(&client->dev, "Error %d disabling VAG\n", ret);
+ goto disable_clk;
+ }
+
+ msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+ }
+
/* Follow section 2.2.1.1 of AN3663 */
ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
if (sgtl5000->num_supplies <= VDDD) {
@@ -1456,10 +1763,17 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
sgtl5000->lrclk_strength = value;
}
+ sgtl5000->sclk_strength = I2S_SCLK_STRENGTH_LOW;
+ if (!of_property_read_u32(np, "sclk-strength", &value)) {
+ if (value > I2S_SCLK_STRENGTH_HIGH)
+ value = I2S_SCLK_STRENGTH_LOW;
+ sgtl5000->sclk_strength = value;
+ }
+
/* Ensure sgtl5000 will start with sane register values */
sgtl5000_fill_defaults(client);
- ret = snd_soc_register_codec(&client->dev,
+ ret = devm_snd_soc_register_component(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
if (ret)
goto disable_clk;
@@ -1476,20 +1790,26 @@ disable_regs:
return ret;
}
-static int sgtl5000_i2c_remove(struct i2c_client *client)
+static void sgtl5000_i2c_remove(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_CLK_CTRL, SGTL5000_CHIP_CLK_CTRL_DEFAULT);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_DIG_POWER, SGTL5000_DIG_POWER_DEFAULT);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, SGTL5000_ANA_POWER_DEFAULT);
+
clk_disable_unprepare(sgtl5000->mclk);
regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+}
- return 0;
+static void sgtl5000_i2c_shutdown(struct i2c_client *client)
+{
+ sgtl5000_i2c_remove(client);
}
static const struct i2c_device_id sgtl5000_id[] = {
- {"sgtl5000", 0},
+ {"sgtl5000"},
{},
};
@@ -1503,11 +1823,12 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
static struct i2c_driver sgtl5000_i2c_driver = {
.driver = {
- .name = "sgtl5000",
- .of_match_table = sgtl5000_dt_ids,
- },
+ .name = "sgtl5000",
+ .of_match_table = sgtl5000_dt_ids,
+ },
.probe = sgtl5000_i2c_probe,
.remove = sgtl5000_i2c_remove,
+ .shutdown = sgtl5000_i2c_shutdown,
.id_table = sgtl5000_id,
};
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 22f3442af982..3a808c762299 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* sgtl5000.h - SGTL5000 audio codec interface
*
* Copyright 2010-2011 Freescale Semiconductor, 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 _SGTL5000_H
@@ -83,6 +80,7 @@
/*
* SGTL5000_CHIP_DIG_POWER
*/
+#define SGTL5000_DIG_POWER_DEFAULT 0x0000
#define SGTL5000_ADC_EN 0x0040
#define SGTL5000_DAC_EN 0x0020
#define SGTL5000_DAP_POWERUP 0x0010
@@ -236,6 +234,7 @@
/*
* SGTL5000_CHIP_ANA_CTRL
*/
+#define SGTL5000_CHIP_ANA_CTRL_DEFAULT 0x0133
#define SGTL5000_LINE_OUT_MUTE 0x0100
#define SGTL5000_HP_SEL_MASK 0x0040
#define SGTL5000_HP_SEL_SHIFT 6
@@ -276,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 1
+#define SGTL5000_SMALL_POP 0x0001
/*
* SGTL5000_CHIP_MIC_CTRL
@@ -399,4 +398,11 @@
#define SGTL5000_SYSCLK 0x00
#define SGTL5000_LRCLK 0x01
+/*
+ * SGTL5000_DAP_AUDIO_EQ
+ */
+#define SGTL5000_DAP_SEL_PEQ 1
+#define SGTL5000_DAP_SEL_TONE_CTRL 2
+#define SGTL5000_DAP_SEL_GEQ 3
+
#endif
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index 7b91ee267b4e..d87141ba8438 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
*
@@ -5,16 +6,6 @@
* Copyright (C) 2013 Andrey Smirnov
*
* Author: Andrey Smirnov <andrew.smirnov@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; 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.
- *
*/
#include <linux/module.h>
@@ -78,7 +69,7 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
int err;
u16 format = 0;
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -140,14 +131,14 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
si476x_core_lock(core);
- err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
format);
si476x_core_unlock(core);
if (err < 0) {
- dev_err(codec_dai->codec->dev, "Failed to set output format\n");
+ dev_err(codec_dai->component->dev, "Failed to set output format\n");
return err;
}
@@ -163,7 +154,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
rate = params_rate(params);
if (rate < 32000 || rate > 48000) {
- dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
+ dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
return -EINVAL;
}
@@ -186,19 +177,19 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
si476x_core_lock(core);
- err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
+ err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
rate);
if (err < 0) {
- dev_err(dai->codec->dev, "Failed to set sample rate\n");
+ dev_err(dai->component->dev, "Failed to set sample rate\n");
goto out;
}
- err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
(width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
(width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
if (err < 0) {
- dev_err(dai->codec->dev, "Failed to set output width\n");
+ dev_err(dai->component->dev, "Failed to set output width\n");
goto out;
}
@@ -239,28 +230,24 @@ static int si476x_probe(struct snd_soc_component *component)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_si476x = {
- .component_driver = {
- .probe = si476x_probe,
- .dapm_widgets = si476x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
- .dapm_routes = si476x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_si476x = {
+ .probe = si476x_probe,
+ .dapm_widgets = si476x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+ .dapm_routes = si476x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int si476x_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_si476x,
&si476x_dai, 1);
}
-static int si476x_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
MODULE_ALIAS("platform:si476x-codec");
static struct platform_driver si476x_platform_driver = {
@@ -268,7 +255,6 @@ static struct platform_driver si476x_platform_driver = {
.name = "si476x-codec",
},
.probe = si476x_platform_probe,
- .remove = si476x_platform_remove,
};
module_platform_driver(si476x_platform_driver);
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
index d374c18d4db7..07c9d89ab24a 100644
--- a/sound/soc/codecs/sigmadsp-i2c.c
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c
index 912861be5b87..ba9a6795e470 100644
--- a/sound/soc/codecs/sigmadsp-regmap.c
+++ b/sound/soc/codecs/sigmadsp-regmap.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/regmap.h>
@@ -27,7 +26,7 @@ static int sigmadsp_read_regmap(void *control_data,
}
/**
- * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * devm_sigmadsp_init_regmap() - Initialize SigmaDSP instance
* @dev: The parent device
* @regmap: Regmap instance to use
* @ops: The sigmadsp_ops to use for this instance
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index d53680ac78e4..201f74e3a7ae 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/crc32.h>
@@ -25,6 +24,8 @@
#define SIGMA_FW_CHUNK_TYPE_CONTROL 1
#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
+#define READBACK_CTRL_NAME "ReadBack"
+
struct sigmadsp_control {
struct list_head head;
uint32_t samplerates;
@@ -32,6 +33,7 @@ struct sigmadsp_control {
unsigned int num_bytes;
const char *name;
struct snd_kcontrol *kcontrol;
+ bool is_readback;
bool cached;
uint8_t cache[];
};
@@ -41,7 +43,7 @@ struct sigmadsp_data {
uint32_t samplerates;
unsigned int addr;
unsigned int length;
- uint8_t data[];
+ uint8_t data[] __counted_by(length);
};
struct sigma_fw_chunk {
@@ -117,8 +119,7 @@ static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp,
struct sigmadsp_control *ctrl, void *data)
{
/* safeload loads up to 20 bytes in a atomic operation */
- if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
- sigmadsp->ops->safeload)
+ if (ctrl->num_bytes <= 20 && sigmadsp->ops && sigmadsp->ops->safeload)
return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
ctrl->num_bytes);
else
@@ -143,7 +144,8 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
if (ret == 0) {
memcpy(ctrl->cache, data, ctrl->num_bytes);
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
}
mutex_unlock(&sigmadsp->lock);
@@ -166,7 +168,8 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
}
if (ret == 0) {
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
memcpy(ucontrol->value.bytes.data, ctrl->cache,
ctrl->num_bytes);
}
@@ -224,15 +227,22 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
if (!ctrl)
return -ENOMEM;
- name = kzalloc(name_len + 1, GFP_KERNEL);
+ name = kmemdup_nul(ctrl_chunk->name, name_len, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_free_ctrl;
}
- memcpy(name, ctrl_chunk->name, name_len);
- name[name_len] = '\0';
ctrl->name = name;
+ /*
+ * Readbacks doesn't work with non-volatile controls, since the
+ * firmware updates the control value without driver interaction. Mark
+ * the readbacks to ensure that the values are not cached.
+ */
+ if (ctrl->name && strncmp(ctrl->name, READBACK_CTRL_NAME,
+ (sizeof(READBACK_CTRL_NAME) - 1)) == 0)
+ ctrl->is_readback = true;
+
ctrl->addr = le16_to_cpu(ctrl_chunk->addr);
ctrl->num_bytes = num_bytes;
ctrl->samplerates = le32_to_cpu(chunk->samplerates);
@@ -260,7 +270,7 @@ static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
length -= sizeof(*data_chunk);
- data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, length), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -403,7 +413,8 @@ static int process_sigma_action(struct sigmadsp *sigmadsp,
if (len < 3)
return -EINVAL;
- data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, size_sub(len, 2)),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -659,36 +670,19 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
{
struct snd_card *card = sigmadsp->component->card->snd_card;
- struct snd_kcontrol_volatile *vd;
- struct snd_ctl_elem_id id;
bool active;
- bool changed = false;
+ int changed;
active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
-
- down_write(&card->controls_rwsem);
- if (!ctrl->kcontrol) {
- up_write(&card->controls_rwsem);
+ if (!ctrl->kcontrol)
return;
- }
-
- id = ctrl->kcontrol->id;
- vd = &ctrl->kcontrol->vd[0];
- if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
- vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- changed = true;
- }
- up_write(&card->controls_rwsem);
-
- if (active && changed) {
+ changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active);
+ if (active && changed > 0) {
mutex_lock(&sigmadsp->lock);
if (ctrl->cached)
sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache);
mutex_unlock(&sigmadsp->lock);
}
-
- if (changed)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
}
/**
@@ -811,4 +805,5 @@ int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
}
EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
+MODULE_DESCRIPTION("Analog Devices SigmaStudio firmware helpers");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index 614475cbb823..2783eff633a1 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Load firmware files from Analog Devices SigmaStudio
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __SIGMA_FIRMWARE_H__
@@ -45,7 +44,6 @@ struct sigmadsp {
struct sigmadsp *devm_sigmadsp_init(struct device *dev,
const struct sigmadsp_ops *ops, const char *firmware_name);
-void sigmadsp_reset(struct sigmadsp *sigmadsp);
int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
struct snd_pcm_substream *substream);
@@ -60,7 +58,7 @@ struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
int sigmadsp_attach(struct sigmadsp *sigmadsp,
struct snd_soc_component *component);
-int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate);
void sigmadsp_reset(struct sigmadsp *sigmadsp);
#endif
diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c
new file mode 100644
index 000000000000..d306c585b52b
--- /dev/null
+++ b/sound/soc/codecs/simple-amplifier.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "simple-amplifier"
+
+struct simple_amp {
+ struct gpio_desc *gpiod_enable;
+};
+
+static int drv_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 simple_amp *priv = snd_soc_component_get_drvdata(c);
+ int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ default:
+ WARN(1, "Unexpected event");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_enable, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
+ (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 20, 0),
+};
+
+static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = {
+ { "DRV", NULL, "INL" },
+ { "DRV", NULL, "INR" },
+ { "OUTL", NULL, "VCC" },
+ { "OUTR", NULL, "VCC" },
+ { "OUTL", NULL, "DRV" },
+ { "OUTR", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver simple_amp_component_driver = {
+ .dapm_widgets = simple_amp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets),
+ .dapm_routes = simple_amp_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes),
+};
+
+static int simple_amp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct simple_amp *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_enable))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable),
+ "Failed to get 'enable' gpio");
+
+ return devm_snd_soc_register_component(dev,
+ &simple_amp_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id simple_amp_ids[] = {
+ { .compatible = "dioo,dio2125", },
+ { .compatible = "simple-audio-amplifier", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, simple_amp_ids);
+#endif
+
+static struct platform_driver simple_amp_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(simple_amp_ids),
+ },
+ .probe = simple_amp_probe,
+};
+
+module_platform_driver(simple_amp_driver);
+
+MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c
new file mode 100644
index 000000000000..390696440155
--- /dev/null
+++ b/sound/soc/codecs/simple-mux.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 Bootlin SA
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux/driver.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define MUX_TEXT_SIZE 2
+#define MUX_WIDGET_SIZE 4
+#define MUX_ROUTE_SIZE 3
+struct simple_mux {
+ struct gpio_desc *gpiod_mux;
+ unsigned int mux;
+ const char *mux_texts[MUX_TEXT_SIZE];
+ unsigned int idle_state;
+ struct soc_enum mux_enum;
+ struct snd_kcontrol_new mux_mux;
+ struct snd_soc_dapm_widget mux_widgets[MUX_WIDGET_SIZE];
+ struct snd_soc_dapm_route mux_routes[MUX_ROUTE_SIZE];
+ struct snd_soc_component_driver mux_driver;
+};
+
+static const char * const simple_mux_texts[MUX_TEXT_SIZE] = {
+ "Input 1", "Input 2"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(simple_mux_enum, simple_mux_texts);
+
+static int simple_mux_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.enumerated.item[0] = priv->mux;
+
+ return 0;
+}
+
+static int simple_mux_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ if (ucontrol->value.enumerated.item[0] > e->items)
+ return -EINVAL;
+
+ if (priv->mux == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ priv->mux = ucontrol->value.enumerated.item[0];
+
+ if (priv->idle_state != MUX_IDLE_AS_IS && dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ return 0;
+
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux);
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static unsigned int simple_mux_read(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ struct simple_mux *priv = snd_soc_component_get_drvdata(component);
+
+ return priv->mux;
+}
+
+static const struct snd_kcontrol_new simple_mux_mux =
+ SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put);
+
+static int simple_mux_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 simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ if (priv->idle_state != MUX_IDLE_AS_IS) {
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->idle_state);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[MUX_WIDGET_SIZE] = {
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_MUX_E("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux, // see simple_mux_probe()
+ simple_mux_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route simple_mux_dapm_routes[MUX_ROUTE_SIZE] = {
+ { "OUT", NULL, "MUX" },
+ { "MUX", "Input 1", "IN1" }, // see simple_mux_probe()
+ { "MUX", "Input 2", "IN2" }, // see simple_mux_probe()
+};
+
+static int simple_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct simple_mux *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_mux))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux),
+ "Failed to get 'mux' gpio");
+
+ /* Copy default settings */
+ memcpy(&priv->mux_texts, &simple_mux_texts, sizeof(priv->mux_texts));
+ memcpy(&priv->mux_enum, &simple_mux_enum, sizeof(priv->mux_enum));
+ memcpy(&priv->mux_mux, &simple_mux_mux, sizeof(priv->mux_mux));
+ memcpy(&priv->mux_widgets, &simple_mux_dapm_widgets, sizeof(priv->mux_widgets));
+ memcpy(&priv->mux_routes, &simple_mux_dapm_routes, sizeof(priv->mux_routes));
+
+ priv->mux_driver.dapm_widgets = priv->mux_widgets;
+ priv->mux_driver.num_dapm_widgets = MUX_WIDGET_SIZE;
+ priv->mux_driver.dapm_routes = priv->mux_routes;
+ priv->mux_driver.num_dapm_routes = MUX_ROUTE_SIZE;
+ priv->mux_driver.read = simple_mux_read;
+
+ /* Overwrite text ("Input 1", "Input 2") if property exists */
+ of_property_read_string_array(np, "state-labels", priv->mux_texts, MUX_TEXT_SIZE);
+
+ ret = of_property_read_u32(np, "idle-state", &priv->idle_state);
+ if (ret < 0) {
+ priv->idle_state = MUX_IDLE_AS_IS;
+ } else if (priv->idle_state != MUX_IDLE_AS_IS && priv->idle_state >= 2) {
+ dev_err(dev, "invalid idle-state %u\n", priv->idle_state);
+ return -EINVAL;
+ }
+
+ /* switch to use priv data instead of default */
+ priv->mux_enum.texts = priv->mux_texts;
+ priv->mux_mux.private_value = (unsigned long)&priv->mux_enum;
+ priv->mux_widgets[2].kcontrol_news = &priv->mux_mux;
+ priv->mux_routes[1].control = priv->mux_texts[0]; // "Input 1"
+ priv->mux_routes[2].control = priv->mux_texts[1]; // "Input 2"
+
+ return devm_snd_soc_register_component(dev, &priv->mux_driver, NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id simple_mux_ids[] = {
+ { .compatible = "simple-audio-mux", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, simple_mux_ids);
+#endif
+
+static struct platform_driver simple_mux_driver = {
+ .driver = {
+ .name = "simple-mux",
+ .of_match_table = of_match_ptr(simple_mux_ids),
+ },
+ .probe = simple_mux_probe,
+};
+
+module_platform_driver(simple_mux_driver);
+
+MODULE_DESCRIPTION("ASoC Simple Audio Mux driver");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
deleted file mode 100644
index 7ae8c181d1a4..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ /dev/null
@@ -1,583 +0,0 @@
-/*
- * SiRF audio codec driver
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/regmap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "sirf-audio-codec.h"
-
-struct sirf_audio_codec {
- struct clk *clk;
- struct regmap *regmap;
- u32 reg_ctrl0, reg_ctrl1;
-};
-
-static const char * const input_mode_mux[] = {"Single-ended",
- "Differential"};
-
-static const struct soc_enum input_mode_mux_enum =
- SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux);
-
-static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control =
- SOC_DAPM_ENUM("Route", input_mode_mux_enum);
-
-static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0);
-static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0);
-static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6,
- 0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0),
- 0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0),
-);
-
-static struct snd_kcontrol_new volume_controls_atlas6[] = {
- SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
- 0x3F, 0, capture_vol_tlv_atlas6),
-};
-
-static struct snd_kcontrol_new volume_controls_prima2[] = {
- SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
- 0x1F, 0, capture_vol_tlv_prima2),
-};
-
-static struct snd_kcontrol_new left_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0),
- SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0),
-};
-
-static struct snd_kcontrol_new right_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0),
- SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0),
-};
-
-static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0);
-
-/* After enable adc, Delay 200ms to avoid pop noise */
-static int adc_enable_delay_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- msleep(200);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static void enable_and_reset_codec(struct regmap *regmap,
- u32 codec_enable_bits, u32 codec_reset_bits)
-{
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_enable_bits | codec_reset_bits,
- codec_enable_bits);
- msleep(20);
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_reset_bits, codec_reset_bits);
-}
-
-static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
-#define ATLAS6_CODEC_RESET_BITS (1 << 28)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
-#define PRIMA2_CODEC_RESET_BITS (1 << 26)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 26, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 27, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 23, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 24, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- atlas6_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- prima2_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0),
- SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0),
- SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
-
- SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &left_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &right_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0,
- NULL, 0),
-
- SND_SOC_DAPM_OUTPUT("HPOUTL"),
- SND_SOC_DAPM_OUTPUT("HPOUTR"),
- SND_SOC_DAPM_OUTPUT("SPKOUT"),
-
- SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0,
- &left_input_path_controls[0],
- ARRAY_SIZE(left_input_path_controls)),
- SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0,
- &right_input_path_controls[0],
- ARRAY_SIZE(right_input_path_controls)),
-
- SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0,
- &sirf_audio_codec_input_mode_control),
- SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0),
- SND_SOC_DAPM_INPUT("MICIN1"),
- SND_SOC_DAPM_INPUT("MICIN2"),
- SND_SOC_DAPM_INPUT("LINEIN1"),
- SND_SOC_DAPM_INPUT("LINEIN2"),
-
- SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0,
- 30, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_route sirf_audio_codec_map[] = {
- {"SPKOUT", NULL, "Speaker Driver"},
- {"Speaker Driver", NULL, "Speaker amp driver"},
- {"Speaker amp driver", NULL, "Left dac to speaker lineout"},
- {"Speaker amp driver", NULL, "Right dac to speaker lineout"},
- {"Left dac to speaker lineout", "Switch", "DAC left"},
- {"Right dac to speaker lineout", "Switch", "DAC right"},
- {"HPOUTL", NULL, "HP Left Driver"},
- {"HPOUTR", NULL, "HP Right Driver"},
- {"HP Left Driver", NULL, "HP amp left driver"},
- {"HP Right Driver", NULL, "HP amp right driver"},
- {"HP amp left driver", NULL, "Right dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"HP amp left driver", NULL, "Left dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"Right dac to hp left amp", "Switch", "DAC left"},
- {"Right dac to hp right amp", "Switch", "DAC right"},
- {"Left dac to hp left amp", "Switch", "DAC left"},
- {"Left dac to hp right amp", "Switch", "DAC right"},
- {"DAC left", NULL, "codecclk"},
- {"DAC right", NULL, "codecclk"},
- {"DAC left", NULL, "Playback"},
- {"DAC right", NULL, "Playback"},
- {"DAC left", NULL, "HSL Phase Opposite"},
- {"DAC right", NULL, "HSL Phase Opposite"},
-
- {"Capture", NULL, "ADC left"},
- {"Capture", NULL, "ADC right"},
- {"ADC left", NULL, "codecclk"},
- {"ADC right", NULL, "codecclk"},
- {"ADC left", NULL, "Left PGA mixer"},
- {"ADC right", NULL, "Right PGA mixer"},
- {"Left PGA mixer", "Line Left Switch", "LINEIN2"},
- {"Right PGA mixer", "Line Right Switch", "LINEIN1"},
- {"Left PGA mixer", "Mic Left Switch", "MICIN2"},
- {"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"},
- {"Mic input mode mux", "Single-ended", "MICIN1"},
- {"Mic input mode mux", "Differential", "MICIN1"},
-};
-
-static void sirf_audio_codec_tx_enable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_tx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, ~IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_rx_enable(struct sirf_audio_codec *sirf_audio_codec,
- int channels)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- if (channels == 1)
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
- else
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
-}
-
-static void sirf_audio_codec_rx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
-}
-
-static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
- int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-
- /*
- * This is a workaround, When stop playback,
- * need disable HP amp, avoid the current noise.
- */
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (playback) {
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, 0);
- sirf_audio_codec_tx_disable(sirf_audio_codec);
- } else
- sirf_audio_codec_rx_disable(sirf_audio_codec);
- break;
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (playback) {
- sirf_audio_codec_tx_enable(sirf_audio_codec);
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, IC_HSLEN | IC_HSREN);
- } else
- sirf_audio_codec_rx_enable(sirf_audio_codec,
- substream->runtime->channels);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
- .trigger = sirf_audio_codec_trigger,
-};
-
-static struct snd_soc_dai_driver sirf_audio_codec_dai = {
- .name = "sirf-audio-codec",
- .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 = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &sirf_audio_codec_dai_ops,
-};
-
-static int sirf_audio_codec_probe(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
-
- pm_runtime_enable(codec->dev);
-
- if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- prima2_output_driver_dapm_widgets,
- ARRAY_SIZE(prima2_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &prima2_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_prima2,
- ARRAY_SIZE(volume_controls_prima2));
- }
- if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- atlas6_output_driver_dapm_widgets,
- ARRAY_SIZE(atlas6_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &atlas6_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_atlas6,
- ARRAY_SIZE(volume_controls_atlas6));
- }
-
- return -EINVAL;
-}
-
-static int sirf_audio_codec_remove(struct snd_soc_codec *codec)
-{
- pm_runtime_disable(codec->dev);
- return 0;
-}
-
-static const struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = {
- .probe = sirf_audio_codec_probe,
- .remove = sirf_audio_codec_remove,
- .component_driver = {
- .dapm_widgets = sirf_audio_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets),
- .dapm_routes = sirf_audio_codec_map,
- .num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map),
- },
- .idle_bias_off = true,
-};
-
-static const struct of_device_id sirf_audio_codec_of_match[] = {
- { .compatible = "sirf,prima2-audio-codec" },
- { .compatible = "sirf,atlas6-audio-codec" },
- {}
-};
-MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match);
-
-static const struct regmap_config sirf_audio_codec_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
- .cache_type = REGCACHE_NONE,
-};
-
-static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
-{
- int ret;
- struct sirf_audio_codec *sirf_audio_codec;
- void __iomem *base;
- struct resource *mem_res;
- const struct of_device_id *match;
-
- match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
-
- sirf_audio_codec = devm_kzalloc(&pdev->dev,
- sizeof(struct sirf_audio_codec), GFP_KERNEL);
- if (!sirf_audio_codec)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, sirf_audio_codec);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &sirf_audio_codec_regmap_config);
- if (IS_ERR(sirf_audio_codec->regmap))
- return PTR_ERR(sirf_audio_codec->regmap);
-
- sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(sirf_audio_codec->clk)) {
- dev_err(&pdev->dev, "Get clock failed.\n");
- return PTR_ERR(sirf_audio_codec->clk);
- }
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret) {
- dev_err(&pdev->dev, "Enable clock failed.\n");
- return ret;
- }
-
- ret = snd_soc_register_codec(&(pdev->dev),
- &soc_codec_device_sirf_audio_codec,
- &sirf_audio_codec_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
- goto err_clk_put;
- }
-
- /*
- * Always open charge pump, if not, when the charge pump closed the
- * adc will not stable
- */
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- IC_CPFREQ, IC_CPFREQ);
-
- if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec"))
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN);
- return 0;
-
-err_clk_put:
- clk_disable_unprepare(sirf_audio_codec->clk);
- return ret;
-}
-
-static int sirf_audio_codec_driver_remove(struct platform_device *pdev)
-{
- struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(sirf_audio_codec->clk);
- snd_soc_unregister_codec(&(pdev->dev));
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int sirf_audio_codec_suspend(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
-
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- &sirf_audio_codec->reg_ctrl0);
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- &sirf_audio_codec->reg_ctrl1);
- clk_disable_unprepare(sirf_audio_codec->clk);
-
- return 0;
-}
-
-static int sirf_audio_codec_resume(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret)
- return ret;
-
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- sirf_audio_codec->reg_ctrl0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- sirf_audio_codec->reg_ctrl1);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops sirf_audio_codec_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume)
-};
-
-static struct platform_driver sirf_audio_codec_driver = {
- .driver = {
- .name = "sirf-audio-codec",
- .of_match_table = sirf_audio_codec_of_match,
- .pm = &sirf_audio_codec_pm_ops,
- },
- .probe = sirf_audio_codec_driver_probe,
- .remove = sirf_audio_codec_driver_remove,
-};
-
-module_platform_driver(sirf_audio_codec_driver);
-
-MODULE_DESCRIPTION("SiRF audio codec driver");
-MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sirf-audio-codec.h b/sound/soc/codecs/sirf-audio-codec.h
deleted file mode 100644
index ba1adc03839f..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * SiRF inner codec controllers define
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#ifndef _SIRF_AUDIO_CODEC_H
-#define _SIRF_AUDIO_CODEC_H
-
-
-#define AUDIO_IC_CODEC_PWR (0x00E0)
-#define AUDIO_IC_CODEC_CTRL0 (0x00E4)
-#define AUDIO_IC_CODEC_CTRL1 (0x00E8)
-#define AUDIO_IC_CODEC_CTRL2 (0x00EC)
-#define AUDIO_IC_CODEC_CTRL3 (0x00F0)
-
-#define MICBIASEN (1 << 3)
-
-#define IC_RDACEN (1 << 0)
-#define IC_LDACEN (1 << 1)
-#define IC_HSREN (1 << 2)
-#define IC_HSLEN (1 << 3)
-#define IC_SPEN (1 << 4)
-#define IC_CPEN (1 << 5)
-
-#define IC_HPRSELR (1 << 6)
-#define IC_HPLSELR (1 << 7)
-#define IC_HPRSELL (1 << 8)
-#define IC_HPLSELL (1 << 9)
-#define IC_SPSELR (1 << 10)
-#define IC_SPSELL (1 << 11)
-
-#define IC_MONOR (1 << 12)
-#define IC_MONOL (1 << 13)
-
-#define IC_RXOSRSEL (1 << 28)
-#define IC_CPFREQ (1 << 29)
-#define IC_HSINVEN (1 << 30)
-
-#define IC_MICINREN (1 << 0)
-#define IC_MICINLEN (1 << 1)
-#define IC_MICIN1SEL (1 << 2)
-#define IC_MICIN2SEL (1 << 3)
-#define IC_MICDIFSEL (1 << 4)
-#define IC_LINEIN1SEL (1 << 5)
-#define IC_LINEIN2SEL (1 << 6)
-#define IC_RADCEN (1 << 7)
-#define IC_LADCEN (1 << 8)
-#define IC_ALM (1 << 9)
-
-#define IC_DIGMICEN (1 << 22)
-#define IC_DIGMICFREQ (1 << 23)
-#define IC_ADC14B_12 (1 << 24)
-#define IC_FIRDAC_HSL_EN (1 << 25)
-#define IC_FIRDAC_HSR_EN (1 << 26)
-#define IC_FIRDAC_LOUT_EN (1 << 27)
-#define IC_POR (1 << 28)
-#define IC_CODEC_CLK_EN (1 << 29)
-#define IC_HP_3DB_BOOST (1 << 30)
-
-#define IC_ADC_LEFT_GAIN_SHIFT 16
-#define IC_ADC_RIGHT_GAIN_SHIFT 10
-#define IC_ADC_GAIN_MASK 0x3F
-#define IC_MIC_MAX_GAIN 0x39
-
-#define IC_RXPGAR_MASK 0x3F
-#define IC_RXPGAR_SHIFT 14
-#define IC_RXPGAL_MASK 0x3F
-#define IC_RXPGAL_SHIFT 21
-#define IC_RXPGAR 0x7B
-#define IC_RXPGAL 0x7B
-
-#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK 0x3F
-#define AUDIO_PORT_TX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_TX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_TX_FIFO_HC_OFFSET 20
-
-#define TX_FIFO_SC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_SC_OFFSET)
-#define TX_FIFO_LC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_LC_OFFSET)
-#define TX_FIFO_HC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_HC_OFFSET)
-
-#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK 0x0F
-#define AUDIO_PORT_RX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_RX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_RX_FIFO_HC_OFFSET 20
-
-#define RX_FIFO_SC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_SC_OFFSET)
-#define RX_FIFO_LC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_LC_OFFSET)
-#define RX_FIFO_HC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_HC_OFFSET)
-#define AUDIO_PORT_IC_CODEC_TX_CTRL (0x00F4)
-#define AUDIO_PORT_IC_CODEC_RX_CTRL (0x00F8)
-
-#define AUDIO_PORT_IC_TXFIFO_OP (0x00FC)
-#define AUDIO_PORT_IC_TXFIFO_LEV_CHK (0x0100)
-#define AUDIO_PORT_IC_TXFIFO_STS (0x0104)
-#define AUDIO_PORT_IC_TXFIFO_INT (0x0108)
-#define AUDIO_PORT_IC_TXFIFO_INT_MSK (0x010C)
-
-#define AUDIO_PORT_IC_RXFIFO_OP (0x0110)
-#define AUDIO_PORT_IC_RXFIFO_LEV_CHK (0x0114)
-#define AUDIO_PORT_IC_RXFIFO_STS (0x0118)
-#define AUDIO_PORT_IC_RXFIFO_INT (0x011C)
-#define AUDIO_PORT_IC_RXFIFO_INT_MSK (0x0120)
-
-#define AUDIO_FIFO_START (1 << 0)
-#define AUDIO_FIFO_RESET (1 << 1)
-
-#define AUDIO_FIFO_FULL (1 << 0)
-#define AUDIO_FIFO_EMPTY (1 << 1)
-#define AUDIO_FIFO_OFLOW (1 << 2)
-#define AUDIO_FIFO_UFLOW (1 << 3)
-
-#define IC_TX_ENABLE (0x03)
-#define IC_RX_ENABLE_MONO (0x01)
-#define IC_RX_ENABLE_STEREO (0x03)
-
-#endif /*__SIRF_AUDIO_CODEC_H*/
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
new file mode 100644
index 000000000000..980c48cbc348
--- /dev/null
+++ b/sound/soc/codecs/sma1303.c
@@ -0,0 +1,1820 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// sma1303.c -- sma1303 ALSA SoC Audio driver
+//
+// Copyright 2023 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuhwa.park@irondevice.com>
+// Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/mod_devicetable.h>
+#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 <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 "sma1303.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#define MAX_CONTROL_NAME 48
+
+#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
+ _post_n, _n, _vco, _p_cp)\
+{\
+ .input_clk_name = _input_clk_name,\
+ .output_clk_name = _output_clk_name,\
+ .input_clk = _input_clk,\
+ .post_n = _post_n,\
+ .n = _n,\
+ .vco = _vco,\
+ .p_cp = _p_cp,\
+}
+
+enum sma1303_type {
+ SMA1303,
+};
+
+struct sma1303_pll_match {
+ char *input_clk_name;
+ char *output_clk_name;
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int vco;
+ unsigned int p_cp;
+};
+
+struct sma1303_priv {
+ enum sma1303_type devtype;
+ struct attribute_group *attr_grp;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct regmap *regmap;
+ struct sma1303_pll_match *pll_matches;
+ bool amp_power_status;
+ bool force_mute_status;
+ int num_of_pll_matches;
+ int retry_cnt;
+ unsigned int amp_mode;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int last_ocp_val;
+ unsigned int last_over_temp;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot_rx;
+ unsigned int tdm_slot_tx;
+ unsigned int tsdw_cnt;
+ long check_fault_period;
+ long check_fault_status;
+};
+
+static struct sma1303_pll_match sma1303_pll_matches[] = {
+PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03),
+PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03),
+PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03),
+PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07),
+PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B),
+PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A),
+PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F),
+};
+
+static int sma1303_startup(struct snd_soc_component *);
+static int sma1303_shutdown(struct snd_soc_component *);
+
+static const struct reg_default sma1303_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x11 },
+ { 0x04, 0x17 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x31 },
+ { 0x0B, 0x98 },
+ { 0x0C, 0x84 },
+ { 0x0D, 0x07 },
+ { 0x0E, 0x3F },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x14, 0x5C },
+ { 0x15, 0x01 },
+ { 0x16, 0x0F },
+ { 0x17, 0x0F },
+ { 0x18, 0x0F },
+ { 0x19, 0x00 },
+ { 0x1A, 0x00 },
+ { 0x1B, 0x00 },
+ { 0x23, 0x19 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x04 },
+ { 0x33, 0x00 },
+ { 0x36, 0x92 },
+ { 0x37, 0x27 },
+ { 0x3B, 0x5A },
+ { 0x3C, 0x20 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x03 },
+ { 0x3F, 0x0C },
+ { 0x8B, 0x07 },
+ { 0x8C, 0x70 },
+ { 0x8D, 0x8B },
+ { 0x8E, 0x6F },
+ { 0x8F, 0x03 },
+ { 0x90, 0x26 },
+ { 0x91, 0x42 },
+ { 0x92, 0xE0 },
+ { 0x94, 0x35 },
+ { 0x95, 0x0C },
+ { 0x96, 0x42 },
+ { 0x97, 0x95 },
+ { 0xA0, 0x00 },
+ { 0xA1, 0x3B },
+ { 0xA2, 0xC8 },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x00 },
+};
+
+static bool sma1303_readable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1:
+ case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL:
+ case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_volatile_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ switch (reg) {
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0);
+
+static int sma1303_regmap_write(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_write(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to write [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int mask, unsigned int val, bool *change)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_update_bits_check(sma1303->regmap, reg,
+ mask, val, change);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to update [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_read(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_read(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to read [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static const char * const sma1303_aif_in_source_text[] = {
+ "Mono", "Left", "Right"};
+static const char * const sma1303_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post",
+ "Clk_PLL", "Clk_OSC"};
+static const char * const sma1303_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"};
+
+static const struct soc_enum sma1303_aif_in_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text),
+ sma1303_aif_in_source_text);
+static const struct soc_enum sma1303_aif_out_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text),
+ sma1303_aif_out_source_text);
+static const struct soc_enum sma1303_tdm_slot_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_tdm_slot_text),
+ sma1303_tdm_slot_text);
+
+static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status;
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1303->force_mute_status == val)
+ change = false;
+ else {
+ change = true;
+ sma1303->force_mute_status = val;
+ }
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x7E) >> 1;
+
+ return 0;
+}
+
+static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A5_TDM1, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_rx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A6_TDM2, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_tx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_startup(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp);
+ if (temp == true)
+ change = true;
+
+ if (sma1303->amp_mode == SMA1303_MONO) {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_MONO,
+ &temp);
+ if (temp == true)
+ change = true;
+
+ } else {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_STEREO,
+ &temp);
+ if (temp == true)
+ change = true;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ sma1303->amp_power_status = true;
+
+ return change;
+}
+
+static int sma1303_shutdown(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp);
+ if (temp == true)
+ change = true;
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303->amp_power_status = false;
+
+ return change;
+}
+
+static int sma1303_aif_in_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_ON,
+ &change);
+ sma1303->amp_mode = SMA1303_MONO;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_NORMAL,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_SWAP,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_in_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_aif_out_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_OUT_SEL_DISABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_FORMAT_CONVERTER,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_MIXER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 3:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_SPEAKER_PATH,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_POSTSCALER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 5:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_PLL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 6:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_OSC_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_out_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_sdo_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_OUTPUT_PORT_ENABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_NORMAL_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_INPUT_PORT_ONLY,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_HIGH_Z_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_post_scaler_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_EN_POST_SCALER,
+ &change);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_BYP_POST_SCALER,
+ &change);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMU\n", __func__);
+ ret = sma1303_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMD\n", __func__);
+ ret = sma1303_shutdown(component);
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_kcontrol_new sma1303_aif_in_source_control =
+ SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum);
+static const struct snd_kcontrol_new sma1303_aif_out_source_control =
+ SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum);
+static const struct snd_kcontrol_new sma1303_sdo_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_post_scaler_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_enable_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new sma1303_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL,
+ 0, 167, 1, sma1303_spk_tlv),
+ SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0,
+ sma1303_force_mute_get, sma1303_force_mute_put),
+ SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0,
+ sma1303_postscaler_get, sma1303_postscaler_put),
+ SOC_ENUM_EXT("TDM RX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_rx_get, sma1303_tdm_slot_rx_put),
+ SOC_ENUM_EXT("TDM TX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_tx_get, sma1303_tdm_slot_tx_put),
+};
+
+static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_in_source_control,
+ sma1303_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_out_source_control,
+ sma1303_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1303_sdo_control,
+ sma1303_sdo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1,
+ &sma1303_post_scaler_control,
+ sma1303_post_scaler_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1303_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1,
+ &sma1303_enable_control),
+
+ /* stream domain */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sma1303_audio_map[] = {
+ /* Playback */
+ {"AIF IN Source", "Mono", "AIF IN"},
+ {"AIF IN Source", "Left", "AIF IN"},
+ {"AIF IN Source", "Right", "AIF IN"},
+
+ {"SDO Enable", "Switch", "AIF IN"},
+ {"AIF OUT Source", "Disable", "SDO Enable"},
+ {"AIF OUT Source", "After_FmtC", "SDO Enable"},
+ {"AIF OUT Source", "After_Mixer", "SDO Enable"},
+ {"AIF OUT Source", "After_DSP", "SDO Enable"},
+ {"AIF OUT Source", "After_Post", "SDO Enable"},
+ {"AIF OUT Source", "Clk_PLL", "SDO Enable"},
+ {"AIF OUT Source", "Clk_OSC", "SDO Enable"},
+
+ {"Entry", NULL, "AIF OUT Source"},
+ {"Entry", NULL, "AIF IN Source"},
+
+ {"Post Scaler", "Switch", "Entry"},
+ {"AMP Power", NULL, "Entry"},
+ {"AMP Power", NULL, "Entry"},
+
+ {"AMP Enable", "Switch", "AMP Power"},
+ {"SPK", NULL, "AMP Enable"},
+
+ /* Capture */
+ {"AIF OUT", NULL, "AMP Enable"},
+};
+
+static int sma1303_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ int i = 0, ret = 0;
+
+ dev_dbg(component->dev, "%s : BCLK = %dHz\n",
+ __func__, bclk);
+
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) {
+ dev_dbg(component->dev, "%s : MCLK is not supported\n",
+ __func__);
+ } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1303->num_of_pll_matches; i++) {
+ if (sma1303->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1303->num_of_pll_matches) {
+ dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK,
+ SMA1303_PLL_OPERATION|SMA1303_PLL_SCK,
+ NULL);
+ }
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8B_PLL_POST_N,
+ sma1303->pll_matches[i].post_n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8C_PLL_N,
+ sma1303->pll_matches[i].n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8D_PLL_A_SETTING,
+ sma1303->pll_matches[i].vco);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8F_PLL_P_CP,
+ sma1303->pll_matches[i].p_cp);
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+ int ret = 0;
+
+ if (sma1303->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1303->frame_size;
+ else
+ bclk = params_rate(params) * params_physical_width(params)
+ * params_channels(params);
+
+ dev_dbg(component->dev,
+ "%s : rate = %d : bit size = %d : channel = %d\n",
+ __func__, params_rate(params), params_width(params),
+ params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ if (sma1303->last_bclk != bclk) {
+ sma1303_setup_pll(component, bclk);
+ sma1303->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 96000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_DISABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_LOW_FIRST_CH,
+ NULL);
+ break;
+
+ case 192000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_ENABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev, "%s not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ } else {
+
+ switch (params_format(params)) {
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S16_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_32FS,
+ NULL);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S24_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S32_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1303->format) {
+ case SND_SOC_DAIFMT_I2S:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_I2S,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_LJ,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_LJ,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_16BIT,
+ NULL);
+ break;
+ case 24:
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_24BIT,
+ NULL);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+ break;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1303_EXTERNAL_CLOCK_19_2:
+ break;
+ case SMA1303_EXTERNAL_CLOCK_24_576:
+ break;
+ case SMA1303_PLL_CLKIN_MCLK:
+ break;
+ case SMA1303_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
+ return -EINVAL;
+ }
+ sma1303->sys_clk_id = clk_id;
+ return 0;
+}
+
+static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return ret;
+
+ if (mute) {
+ dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE");
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_MUTE,
+ NULL);
+
+ /* Need to wait time for mute slope */
+ msleep(55);
+ } else {
+ if (!sma1303->force_mute_status) {
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "UNMUTE");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_UNMUTE,
+ NULL);
+ } else {
+ dev_dbg(sma1303->dev,
+ "%s : FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Device mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_DEVICE_MODE,
+ NULL);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Controller mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_CONTROLLER_MODE,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "Unsupported Controller/Device : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Audio Interface Format : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Normal Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_SCK_RISING_MASK,
+ SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK,
+ SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Normal Frame");
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Bit & Frameclock : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_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 sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1303->frame_size = slot_width * slots;
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+
+ switch (slot_width) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_16,
+ NULL);
+ break;
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_32,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slot_width\n",
+ __func__, slot_width);
+ break;
+ }
+
+ switch (slots) {
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_4,
+ NULL);
+ break;
+ case 8:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_8,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slots\n",
+ __func__, slots);
+ break;
+ }
+
+ if (sma1303->tdm_slot_rx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_SLOT1_RX_POS_MASK,
+ (sma1303->tdm_slot_rx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n",
+ __func__, sma1303->tdm_slot_rx);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_CLK_POL_MASK,
+ SMA1303_TDM_CLK_POL_RISE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_TX_MODE_MASK,
+ SMA1303_TDM_TX_MONO,
+ NULL);
+
+ if (sma1303->tdm_slot_tx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_SLOT1_TX_POS_MASK,
+ (sma1303->tdm_slot_tx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n",
+ __func__, sma1303->tdm_slot_tx);
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1303_dai_ops_amp = {
+ .set_sysclk = sma1303_dai_set_sysclk_amp,
+ .set_fmt = sma1303_dai_set_fmt_amp,
+ .hw_params = sma1303_dai_hw_params_amp,
+ .mute_stream = sma1303_dai_mute,
+ .set_tdm_slot = sma1303_dai_set_tdm_slot,
+};
+
+#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000
+#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1303_dai[] = {
+ {
+ .name = "sma1303-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .ops = &sma1303_dai_ops_amp,
+ },
+};
+
+static void sma1303_check_fault_worker(struct work_struct *work)
+{
+ struct sma1303_priv *sma1303 =
+ container_of(work, struct sma1303_priv, check_fault_work.work);
+ int ret = 0;
+ unsigned int over_temp, ocp_val, uvlo_val;
+
+ if (sma1303->tsdw_cnt)
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->cur_vol);
+ else
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->init_vol);
+
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_0A_SPK_VOL : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FA_STATUS1 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FB_STATUS2 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret);
+ return;
+ }
+
+ if (~over_temp & SMA1303_OT1_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT1(Over Temperature Level 1)\n", __func__);
+
+ if ((sma1303->cur_vol + 6) <= 0xFF)
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6);
+
+ sma1303->tsdw_cnt++;
+ } else if (sma1303->tsdw_cnt) {
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->init_vol);
+ sma1303->tsdw_cnt = 0;
+ sma1303->cur_vol = sma1303->init_vol;
+ }
+
+ if (~over_temp & SMA1303_OT2_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT2(Over Temperature Level 2)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_SPK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_SPK(Over Current Protect SPK)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_BST(Over Current Protect Boost)\n", __func__);
+ }
+ if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) {
+ dev_crit(sma1303->dev,
+ "%s : CLK_FAULT(No clock input)\n", __func__);
+ }
+ if (uvlo_val & SMA1303_UVLO_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : UVLO(Under Voltage Lock Out)\n", __func__);
+ }
+
+ if ((over_temp != sma1303->last_over_temp) ||
+ (ocp_val != sma1303->last_ocp_val)) {
+
+ dev_crit(sma1303->dev, "Please check AMP status");
+ dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n",
+ over_temp, ocp_val);
+ sma1303->last_over_temp = over_temp;
+ sma1303->last_ocp_val = ocp_val;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ if (!(~over_temp & SMA1303_OT1_OK_STATUS)
+ && !(~over_temp & SMA1303_OT2_OK_STATUS)
+ && !(ocp_val & SMA1303_OCP_SPK_STATUS)
+ && !(ocp_val & SMA1303_OCP_BST_STATUS)
+ && !(ocp_val & SMA1303_CLK_MON_STATUS)
+ && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) {
+ }
+}
+
+static int sma1303_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static void sma1303_remove(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct snd_soc_component_driver sma1303_component = {
+ .probe = sma1303_probe,
+ .remove = sma1303_remove,
+ .controls = sma1303_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1303_snd_controls),
+ .dapm_widgets = sma1303_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets),
+ .dapm_routes = sma1303_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
+};
+
+static const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1303_FF_DEVICE_INDEX,
+ .readable_reg = sma1303_readable_register,
+ .writeable_reg = sma1303_writeable_register,
+ .volatile_reg = sma1303_volatile_register,
+
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = sma1303_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def),
+};
+
+static ssize_t check_fault_period_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period);
+}
+
+static ssize_t check_fault_period_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_period);
+
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_period);
+
+static ssize_t check_fault_status_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status);
+}
+
+static ssize_t check_fault_status_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_status);
+
+ if (ret)
+ return -EINVAL;
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_status);
+
+static struct attribute *sma1303_attr[] = {
+ &dev_attr_check_fault_period.attr,
+ &dev_attr_check_fault_status.attr,
+ NULL,
+};
+
+static struct attribute_group sma1303_attr_group = {
+ .attrs = sma1303_attr,
+};
+
+static int sma1303_i2c_probe(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303;
+ int ret, i = 0;
+ unsigned int device_info, status, otp_stat;
+
+ sma1303 = devm_kzalloc(&client->dev,
+ sizeof(struct sma1303_priv), GFP_KERNEL);
+ if (!sma1303)
+ return -ENOMEM;
+ sma1303->dev = &client->dev;
+
+ sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1303->regmap)) {
+ ret = PTR_ERR(sma1303->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map: %d\n", ret);
+
+ return ret;
+ }
+
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_FF_DEVICE_INDEX, &device_info);
+
+ if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) {
+ dev_err(&client->dev, "device initialization error (%d 0x%02X)",
+ ret, device_info);
+ }
+ dev_dbg(&client->dev, "chip version 0x%02X\n", device_info);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_00_SYSTEM_CTRL,
+ SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET,
+ NULL);
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status);
+ sma1303->rev_num = status & SMA1303_REV_NUM_STATUS;
+ if (sma1303->rev_num == SMA1303_REV_NUM_TV0)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n");
+ else if (sma1303->rev_num == SMA1303_REV_NUM_TV1)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n");
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "failed to read, register: %02X, ret: %d\n",
+ SMA1303_FF_DEVICE_INDEX, ret);
+
+ if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) ||
+ ((sma1303->rev_num != SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1)))
+ dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n");
+ else
+ dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n");
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++)
+ ret += sma1303_regmap_write(sma1303,
+ sma1303_reg_def[i].reg,
+ sma1303_reg_def[i].def);
+
+ sma1303->amp_mode = SMA1303_MONO;
+ sma1303->amp_power_status = false;
+ sma1303->check_fault_period = CHECK_PERIOD_TIME;
+ sma1303->check_fault_status = true;
+ sma1303->force_mute_status = false;
+ sma1303->init_vol = 0x31;
+ sma1303->cur_vol = sma1303->init_vol;
+ sma1303->last_bclk = 0;
+ sma1303->last_ocp_val = 0x08;
+ sma1303->last_over_temp = 0xC0;
+ sma1303->tsdw_cnt = 0;
+ sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT;
+ sma1303->tdm_slot_rx = 0;
+ sma1303->tdm_slot_tx = 0;
+ sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK;
+
+ sma1303->dev = &client->dev;
+ sma1303->kobj = &client->dev.kobj;
+
+ INIT_DELAYED_WORK(&sma1303->check_fault_work,
+ sma1303_check_fault_worker);
+
+ i2c_set_clientdata(client, sma1303);
+
+ sma1303->pll_matches = sma1303_pll_matches;
+ sma1303->num_of_pll_matches =
+ ARRAY_SIZE(sma1303_pll_matches);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1303_component, sma1303_dai, 1);
+ if (ret) {
+ dev_err(&client->dev, "Failed to register component");
+
+ return ret;
+ }
+
+ sma1303->attr_grp = &sma1303_attr_group;
+ ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to create attribute group [%d]\n", ret);
+ sma1303->attr_grp = NULL;
+ }
+
+ return ret;
+}
+
+static void sma1303_i2c_remove(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303 =
+ (struct sma1303_priv *) i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct i2c_device_id sma1303_i2c_id[] = {
+ {"sma1303"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id);
+
+static const struct of_device_id sma1303_of_match[] = {
+ { .compatible = "irondevice,sma1303", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sma1303_of_match);
+
+static struct i2c_driver sma1303_i2c_driver = {
+ .driver = {
+ .name = "sma1303",
+ .of_match_table = sma1303_of_match,
+ },
+ .probe = sma1303_i2c_probe,
+ .remove = sma1303_i2c_remove,
+ .id_table = sma1303_i2c_id,
+};
+
+module_i2c_driver(sma1303_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1303 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("Kiseok Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h
new file mode 100644
index 000000000000..ae70f207adde
--- /dev/null
+++ b/sound/soc/codecs/sma1303.h
@@ -0,0 +1,609 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * sma1303.h -- sma1303 ALSA SoC Audio driver
+ *
+ * Copyright 2023 Iron Device Corporation
+ *
+ * Author: Kiseok Jo <kiseok.jo@irondevice.com>
+ *
+ */
+
+#ifndef _SMA1303_H
+#define _SMA1303_H
+
+#define SMA1303_I2C_ADDR_00 0x1e
+#define SMA1303_I2C_ADDR_01 0x3e
+#define SMA1303_I2C_ADDR_10 0x5e
+#define SMA1303_I2C_ADDR_11 0x7e
+
+#define SMA1303_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1303_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1303_PLL_CLKIN_MCLK 0x02
+#define SMA1303_PLL_CLKIN_BCLK 0x03
+
+#define SMA1303_MONO 0x00
+#define SMA1303_STEREO 0x01
+
+#define SMA1303_I2C_RETRY_COUNT 3
+
+/*
+ * SMA1303 Register Definition
+ */
+
+/* SMA1303 Register Addresses */
+#define SMA1303_00_SYSTEM_CTRL 0x00
+#define SMA1303_01_INPUT1_CTRL1 0x01
+#define SMA1303_02_INPUT1_CTRL2 0x02
+#define SMA1303_03_INPUT1_CTRL3 0x03
+#define SMA1303_04_INPUT1_CTRL4 0x04
+/* 0x05 ~ 0x08 : Reserved */
+#define SMA1303_09_OUTPUT_CTRL 0x09
+#define SMA1303_0A_SPK_VOL 0x0a
+#define SMA1303_0B_BST_TEST 0x0b
+#define SMA1303_0C_BST_TEST1 0x0c
+#define SMA1303_0D_SPK_TEST 0x0d
+#define SMA1303_0E_MUTE_VOL_CTRL 0x0e
+/* 0x0F : Reserved */
+#define SMA1303_10_SYSTEM_CTRL1 0x10
+#define SMA1303_11_SYSTEM_CTRL2 0x11
+#define SMA1303_12_SYSTEM_CTRL3 0x12
+/* 0x13 : Reserved */
+#define SMA1303_14_MODULATOR 0x14
+#define SMA1303_15_BASS_SPK1 0x15
+#define SMA1303_16_BASS_SPK2 0x16
+#define SMA1303_17_BASS_SPK3 0x17
+#define SMA1303_18_BASS_SPK4 0x18
+#define SMA1303_19_BASS_SPK5 0x19
+#define SMA1303_1A_BASS_SPK6 0x1a
+#define SMA1303_1B_BASS_SPK7 0x1b
+/* 0x1C ~ 0x22 : Reserved */
+#define SMA1303_23_COMP_LIM1 0x23
+#define SMA1303_24_COMP_LIM2 0x24
+#define SMA1303_25_COMP_LIM3 0x25
+#define SMA1303_26_COMP_LIM4 0x26
+/* 0x27 ~ 0x32 : Reserved */
+#define SMA1303_33_SDM_CTRL 0x33
+#define SMA1303_34_OTP_DATA1 0x34
+/* 0x35 : Reserved */
+#define SMA1303_36_PROTECTION 0x36
+#define SMA1303_37_SLOPE_CTRL 0x37
+#define SMA1303_38_OTP_TRM0 0x38
+/* 0x39 ~ 0x3A : Reserved */
+#define SMA1303_3B_TEST1 0x3b
+#define SMA1303_3C_TEST2 0x3c
+#define SMA1303_3D_TEST3 0x3d
+#define SMA1303_3E_ATEST1 0x3e
+#define SMA1303_3F_ATEST2 0x3f
+/* 0x40 ~ 0x8A : Reserved */
+#define SMA1303_8B_PLL_POST_N 0x8b
+#define SMA1303_8C_PLL_N 0x8c
+#define SMA1303_8D_PLL_A_SETTING 0x8d
+#define SMA1303_8E_PLL_CTRL 0x8e
+#define SMA1303_8F_PLL_P_CP 0x8f
+#define SMA1303_90_POSTSCALER 0x90
+#define SMA1303_91_CLASS_G_CTRL 0x91
+#define SMA1303_92_FDPEC_CTRL 0x92
+/* 0x93 : Reserved */
+#define SMA1303_94_BOOST_CTRL1 0x94
+#define SMA1303_95_BOOST_CTRL2 0x95
+#define SMA1303_96_BOOST_CTRL3 0x96
+#define SMA1303_97_BOOST_CTRL4 0x97
+/* 0x98 ~ 0x9F : Reserved */
+#define SMA1303_A0_PAD_CTRL0 0xa0
+#define SMA1303_A1_PAD_CTRL1 0xa1
+#define SMA1303_A2_TOP_MAN1 0xa2
+#define SMA1303_A3_TOP_MAN2 0xa3
+#define SMA1303_A4_TOP_MAN3 0xa4
+#define SMA1303_A5_TDM1 0xa5
+#define SMA1303_A6_TDM2 0xa6
+#define SMA1303_A7_CLK_MON 0xa7
+/* 0xA8 ~ 0xF9 : Reserved */
+#define SMA1303_FA_STATUS1 0xfa
+#define SMA1303_FB_STATUS2 0xfb
+/* 0xFC ~ 0xFE : Reserved */
+#define SMA1303_FF_DEVICE_INDEX 0xff
+
+/* SMA1303 Registers Bit Fields */
+
+/* SYSTEM_CTRL : 0x00 */
+#define SMA1303_RESETBYI2C_MASK (1<<1)
+#define SMA1303_RESETBYI2C_NORMAL (0<<1)
+#define SMA1303_RESETBYI2C_RESET (1<<1)
+
+#define SMA1303_POWER_MASK (1<<0)
+#define SMA1303_POWER_OFF (0<<0)
+#define SMA1303_POWER_ON (1<<0)
+
+/* INTPUT CTRL1 : 0x01 */
+#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7)
+#define SMA1303_DEVICE_MODE (0<<7)
+#define SMA1303_CONTROLLER_MODE (1<<7)
+
+#define SMA1303_I2S_MODE_MASK (7<<4)
+#define SMA1303_STANDARD_I2S (0<<4)
+#define SMA1303_LJ (1<<4)
+#define SMA1303_RJ_16BIT (4<<4)
+#define SMA1303_RJ_18BIT (5<<4)
+#define SMA1303_RJ_20BIT (6<<4)
+#define SMA1303_RJ_24BIT (7<<4)
+
+#define SMA1303_LEFTPOL_MASK (1<<3)
+#define SMA1303_LOW_FIRST_CH (0<<3)
+#define SMA1303_HIGH_FIRST_CH (1<<3)
+
+#define SMA1303_SCK_RISING_MASK (1<<2)
+#define SMA1303_SCK_FALLING_EDGE (0<<2)
+#define SMA1303_SCK_RISING_EDGE (1<<2)
+
+/* INTPUT CTRL2 : 0x02 */
+#define SMA1303_IMODE_MASK (3<<6)
+#define SMA1303_I2S (0<<6)
+#define SMA1303_PCM_SHORT (1<<6)
+#define SMA1303_PCM_LONG (2<<6)
+
+#define RSMA1303_IGHT_FIRST_MASK (1<<5)
+#define SMA1303_LEFT_NORMAL (0<<5)
+#define SMA1303_RIGHT_INVERTED (1<<5)
+
+#define SMA1303_PCM_ALAW_MASK (1<<4)
+#define SMA1303_PCM_U_DECODING (0<<4)
+#define SMA1303_PCM_A_DECODING (1<<4)
+
+#define SMA1303_PCM_COMP_MASK (1<<3)
+#define SMA1303_PCM_LINEAR (0<<3)
+#define SMA1303_PCM_COMPANDING (1<<3)
+
+#define SMA1303_INPUTSEL_MASK (1<<2)
+#define SMA1303_PCM_8KHZ (0<<2)
+#define SMA1303_PCM_16KHZ (1<<2)
+
+#define SMA1303_PCM_STEREO_MASK (1<<1)
+#define SMA1303_PCM_MONO (0<<1)
+#define SMA1303_PCM_STEREO (1<<1)
+
+#define SMA1303_PCM_DL_MASK (1<<0)
+#define SMA1303_PCM_8BIT (0<<0)
+#define SMA1303_PCM_16BIT (1<<0)
+
+/* INTPUT CTRL3 : 0x03 */
+#define SMA1303_PCM_N_SLOT_MASK (15<<0)
+#define SMA1303_PCM_N_SLOT1 (0<<0)
+#define SMA1303_PCM_N_SLOT2 (1<<0)
+#define SMA1303_PCM_N_SLOT3 (2<<0)
+#define SMA1303_PCM_N_SLOT4 (3<<0)
+#define SMA1303_PCM_N_SLOT5 (4<<0)
+#define SMA1303_PCM_N_SLOT6 (5<<0)
+#define SMA1303_PCM_N_SLOT7 (6<<0)
+#define SMA1303_PCM_N_SLOT8 (7<<0)
+#define SMA1303_PCM_N_SLOT9 (8<<0)
+#define SMA1303_PCM_N_SLOT10 (9<<0)
+#define SMA1303_PCM_N_SLOT11 (10<<0)
+#define SMA1303_PCM_N_SLOT12 (11<<0)
+#define SMA1303_PCM_N_SLOT13 (12<<0)
+#define SMA1303_PCM_N_SLOT14 (13<<0)
+#define SMA1303_PCM_N_SLOT15 (14<<0)
+#define SMA1303_PCM_N_SLOT16 (15<<0)
+
+/* INTPUT CTRL4 : 0x04 */
+#define SMA1303_PCM1_SLOT_MASK (15<<4)
+#define SMA1303_PCM1_SLOT1 (0<<4)
+#define SMA1303_PCM1_SLOT2 (1<<4)
+#define SMA1303_PCM1_SLOT3 (2<<4)
+#define SMA1303_PCM1_SLOT4 (3<<4)
+#define SMA1303_PCM1_SLOT5 (4<<4)
+#define SMA1303_PCM1_SLOT6 (5<<4)
+#define SMA1303_PCM1_SLOT7 (6<<4)
+#define SMA1303_PCM1_SLOT8 (7<<4)
+#define SMA1303_PCM1_SLOT9 (8<<4)
+#define SMA1303_PCM1_SLOT10 (9<<4)
+#define SMA1303_PCM1_SLOT11 (10<<4)
+#define SMA1303_PCM1_SLOT12 (11<<4)
+#define SMA1303_PCM1_SLOT13 (12<<4)
+#define SMA1303_PCM1_SLOT14 (13<<4)
+#define SMA1303_PCM1_SLOT15 (14<<4)
+#define SMA1303_PCM1_SLOT16 (15<<4)
+
+#define SMA1303_PCM2_SLOT_MASK (15<<0)
+#define SMA1303_PCM2_SLOT1 (0<<0)
+#define SMA1303_PCM2_SLOT2 (1<<0)
+#define SMA1303_PCM2_SLOT3 (2<<0)
+#define SMA1303_PCM2_SLOT4 (3<<0)
+#define SMA1303_PCM2_SLOT5 (4<<0)
+#define SMA1303_PCM2_SLOT6 (5<<0)
+#define SMA1303_PCM2_SLOT7 (6<<0)
+#define SMA1303_PCM2_SLOT8 (7<<0)
+#define SMA1303_PCM2_SLOT9 (8<<0)
+#define SMA1303_PCM2_SLOT10 (9<<0)
+#define SMA1303_PCM2_SLOT11 (10<<0)
+#define SMA1303_PCM2_SLOT12 (11<<0)
+#define SMA1303_PCM2_SLOT13 (12<<0)
+#define SMA1303_PCM2_SLOT14 (13<<0)
+#define SMA1303_PCM2_SLOT15 (14<<0)
+#define SMA1303_PCM2_SLOT16 (15<<0)
+
+/* OUTPUT CTRL : 0x09 */
+#define SMA1303_PORT_CONFIG_MASK (3<<5)
+#define SMA1303_INPUT_PORT_ONLY (0<<5)
+#define SMA1303_OUTPUT_PORT_ENABLE (2<<5)
+
+#define SMA1303_PORT_OUT_SEL_MASK (7<<0)
+#define SMA1303_OUT_SEL_DISABLE (0<<0)
+#define SMA1303_FORMAT_CONVERTER (1<<0)
+#define SMA1303_MIXER_OUTPUT (2<<0)
+#define SMA1303_SPEAKER_PATH (3<<0)
+#define SMA1303_POSTSCALER_OUTPUT (4<<0)
+
+/* BST_TEST : 0x0B */
+#define SMA1303_BST_OFF_SLOPE_MASK (3<<6)
+#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6)
+#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6)
+#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6)
+#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6)
+
+#define SMA1303_OCP_TEST_MASK (1<<5)
+#define SMA1303_OCP_NORMAL_MODE (0<<5)
+#define SMA1303_OCP_TEST_MODE (1<<5)
+
+#define SMA1303_BST_FAST_LEBN_MASK (1<<4)
+#define SMA1303_BST_SHORT_LEB (0<<4)
+#define SMA1303_BST_LONG_LEB (1<<4)
+
+#define SMA1303_HIGH_PGAIN_MASK (1<<3)
+#define SMA1303_NORMAL_P_GAIN (0<<3)
+#define SMA1303_HIGH_P_GAIN (1<<3)
+
+#define SMA1303_VCOMP_MASK (1<<2)
+#define SMA1303_VCOMP_NORMAL_MODE (0<<2)
+#define SMA1303_VCOMP_V_MON_MODE (1<<2)
+
+#define SMA1303_PMOS_ON_MASK (1<<1)
+#define SMA1303_PMOS_NORMAL_MODE (0<<1)
+#define SMA1303_PMOS_TEST_MODE (1<<1)
+
+#define SMA1303_NMOS_ON_MASK (1<<0)
+#define SMA1303_NMOS_NORMAL_MODE (0<<0)
+#define SMA1303_NMOS_TEST_MODE (1<<0)
+
+/* BST_TEST1 : 0x0C */
+#define SMA1303_SET_OCP_H_MASK (3<<6)
+#define SMA1303_HIGH_OCP_4_5_LVL (0<<6)
+#define SMA1303_HIGH_OCP_3_2_LVL (1<<6)
+#define SMA1303_HIGH_OCP_2_1_LVL (2<<6)
+#define SMA1303_HIGH_OCP_0_9_LVL (3<<6)
+
+#define SMA1303_OCL_TEST_MASK (1<<5)
+#define SMA1303_OCL_NORMAL_MODE (0<<5)
+#define SMA1303_OCL_TEST_MODE (1<<5)
+
+#define SMA1303_LOOP_CHECK_MASK (1<<4)
+#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4)
+#define SMA1303_BST_LOOP_CHECK_MODE (1<<4)
+
+#define SMA1303_EN_SH_PRT_MASK (1<<3)
+#define SMA1303_EN_SH_PRT_DISABLE (0<<3)
+#define SMA1303_EN_SH_PRT_ENABLE (1<<3)
+
+/* SPK_TEST : 0x0D */
+#define SMA1303_VREF_MON_MASK (1<<3)
+#define SMA1303_VREF_NORMAL_MODE (0<<3)
+#define SMA1303_VREF_V_MON_MODE (1<<3)
+
+#define SMA1303_SPK_OCP_DLYN_MASK (1<<2)
+#define SMA1303_SPK_OCP_LONG_DELAY (0<<2)
+#define SMA1303_SPK_OCP_NORMAL (1<<2)
+
+#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0)
+#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0)
+#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0)
+
+/* MUTE_VOL_CTRL : 0x0E */
+#define SMA1303_VOL_SLOPE_MASK (3<<6)
+#define SMA1303_VOL_SLOPE_OFF (0<<6)
+#define SMA1303_VOL_SLOPE_SLOW (1<<6)
+#define SMA1303_VOL_SLOPE_MID (2<<6)
+#define SMA1303_VOL_SLOPE_FAST (3<<6)
+
+#define SMA1303_MUTE_SLOPE_MASK (3<<4)
+#define SMA1303_MUTE_SLOPE_OFF (0<<4)
+#define SMA1303_MUTE_SLOPE_SLOW (1<<4)
+#define SMA1303_MUTE_SLOPE_MID (2<<4)
+#define SMA1303_MUTE_SLOPE_FAST (3<<4)
+
+#define SMA1303_SPK_MUTE_MASK (1<<0)
+#define SMA1303_SPK_UNMUTE (0<<0)
+#define SMA1303_SPK_MUTE (1<<0)
+
+/* SYSTEM_CTRL1 :0x10 */
+#define SMA1303_SPK_MODE_MASK (7<<2)
+#define SMA1303_SPK_OFF (0<<2)
+#define SMA1303_SPK_MONO (1<<2)
+#define SMA1303_SPK_STEREO (4<<2)
+
+/* SYSTEM_CTRL2 : 0x11 */
+#define SMA1303_SPK_BS_MASK (1<<6)
+#define SMA1303_SPK_BS_BYP (0<<6)
+#define SMA1303_SPK_BS_EN (1<<6)
+#define SMA1303_SPK_LIM_MASK (1<<5)
+#define SMA1303_SPK_LIM_BYP (0<<5)
+#define SMA1303_SPK_LIM_EN (1<<5)
+
+#define SMA1303_LR_DATA_SW_MASK (1<<4)
+#define SMA1303_LR_DATA_SW_NORMAL (0<<4)
+#define SMA1303_LR_DATA_SW_SWAP (1<<4)
+
+#define SMA1303_MONOMIX_MASK (1<<0)
+#define SMA1303_MONOMIX_OFF (0<<0)
+#define SMA1303_MONOMIX_ON (1<<0)
+
+/* SYSTEM_CTRL3 : 0x12 */
+#define SMA1303_INPUT_MASK (3<<6)
+#define SMA1303_INPUT_0_DB (0<<6)
+#define SMA1303_INPUT_M6_DB (1<<6)
+#define SMA1303_INPUT_M12_DB (2<<6)
+#define SMA1303_INPUT_INFI_DB (3<<6)
+#define SMA1303_INPUT_R_MASK (3<<4)
+#define SMA1303_INPUT_R_0_DB (0<<4)
+#define SMA1303_INPUT_R_M6_DB (1<<4)
+#define SMA1303_INPUT_R_M12_DB (2<<4)
+#define SMA1303_INPUT_R_INFI_DB (3<<4)
+
+/* Modulator : 0x14 */
+#define SMA1303_SPK_HYSFB_MASK (3<<6)
+#define SMA1303_HYSFB_625K (0<<6)
+#define SMA1303_HYSFB_414K (1<<6)
+#define SMA1303_HYSFB_297K (2<<6)
+#define SMA1303_HYSFB_226K (3<<6)
+#define SMA1303_SPK_BDELAY_MASK (63<<0)
+
+/* SDM CONTROL : 0x33 */
+#define SMA1303_SDM_Q_SEL_MASK (1<<2)
+#define SMA1303_QUART_SEL_1_DIV_4 (0<<2)
+#define SMA1303_QUART_SEL_1_DIV_8 (1<<2)
+
+/* OTP_DATA1 : 0x34 */
+#define SMA1303_OTP_LVL_MASK (1<<5)
+#define SMA1303_OTP_LVL_NORMAL (0<<5)
+#define SMA1303_OTP_LVL_LOW (1<<5)
+
+/* PROTECTION : 0x36 */
+#define SMA1303_EDGE_DIS_MASK (1<<7)
+#define SMA1303_EDGE_DIS_ENABLE (0<<7)
+#define SMA1303_EDGE_DIS_DISABLE (1<<7)
+
+#define SMA1303_SPK_OCP_DIS_MASK (1<<3)
+#define SMA1303_SPK_OCP_ENABLE (0<<3)
+#define SMA1303_SPK_OCP_DISABLE (1<<3)
+
+#define SMA1303_OCP_MODE_MASK (1<<2)
+#define SMA1303_AUTO_RECOVER (0<<2)
+#define SMA1303_SHUT_DOWN_PERMANENT (1<<2)
+
+#define SMA1303_OTP_MODE_MASK (3<<0)
+#define SMA1303_OTP_MODE_DISABLE (0<<0)
+#define SMA1303_IG_THR1_SHUT_THR2 (1<<0)
+#define SMA1303_REC_THR1_SHUT_THR2 (2<<0)
+#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0)
+
+/* TEST2 : 0x3C */
+#define SMA1303_SPK_HSDM_BP_MASK (1<<4)
+#define SMA1303_SPK_HSDM_ENABLE (0<<4)
+#define SMA1303_SPK_HSDM_BYPASS (1<<4)
+
+#define SMA1303_SDM_SYNC_DIS_MASK (1<<5)
+#define SMA1303_SDM_SYNC_NORMAL (0<<5)
+#define SMA1303_SDM_SYNC_DISABLE (1<<5)
+
+/* ATEST2 : 0x3F */
+#define SMA1303_SPK_OUT_FREQ_MASK (1<<2)
+#define SMA1303_SPK_OUT_FREQ_360K (0<<2)
+#define SMA1303_SPK_OUT_FREQ_410K (1<<2)
+
+#define SMA1303_LOW_POWER_MODE_MASK (1<<3)
+#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3)
+#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3)
+
+#define SMA1303_THERMAL_ADJUST_MASK (3<<5)
+#define SMA1303_THERMAL_150_110 (0<<5)
+#define SMA1303_THERMAL_160_120 (1<<5)
+#define SMA1303_THERMAL_140_100 (2<<5)
+
+#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0)
+
+/* PLL_CTRL : 0x8E */
+#define SMA1303_TRM_LVL_MASK (1<<4)
+#define SMA1303_TRM_LVL_NORMAL (0<<4)
+#define SMA1303_TRM_LVL_LOW (1<<4)
+
+#define SMA1303_LOW_OCL_MODE_MASK (1<<3)
+#define SMA1303_LOW_OCL_MODE (0<<3)
+#define SMA1303_NORMAL_OCL_MODE (1<<3)
+
+#define SMA1303_PLL_PD2_MASK (7<<0)
+#define SMA1303_PLL_PD2 (7<<0)
+#define SMA1303_PLL_OPERATION2 (0<<0)
+
+/* POSTSCALER : 0x90 */
+#define SMA1303_BYP_POST_MASK (1<<0)
+#define SMA1303_EN_POST_SCALER (0<<0)
+#define SMA1303_BYP_POST_SCALER (1<<0)
+
+/* FDPEC CONTROL : 0x92 */
+#define SMA1303_FLT_VDD_GAIN_MASK (15<<4)
+#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4)
+#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4)
+#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4)
+#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4)
+#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4)
+#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4)
+#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4)
+#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4)
+#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4)
+#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4)
+#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4)
+#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4)
+#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4)
+#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4)
+#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4)
+#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4)
+
+#define SMA1303_DIS_FCHG_MASK (1<<2)
+#define SMA1303_EN_FAST_CHARGE (0<<2)
+#define SMA1303_DIS_FAST_CHARGE (1<<2)
+
+/* BOOST_CONTROL4 : 0x97 */
+#define SMA1303_TRM_VBST_MASK (7<<2)
+#define SMA1303_TRM_VBST_5P5 (0<<2)
+#define SMA1303_TRM_VBST_5P6 (1<<2)
+#define SMA1303_TRM_VBST_5P7 (2<<2)
+#define SMA1303_TRM_VBST_5P8 (3<<2)
+#define SMA1303_TRM_VBST_5P9 (4<<2)
+#define SMA1303_TRM_VBST_6P0 (5<<2)
+#define SMA1303_TRM_VBST_6P1 (6<<2)
+#define SMA1303_TRM_VBST_6P2 (7<<2)
+
+/* TOP_MAN1 : 0xA2 */
+#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7)
+#define SMA1303_PLL_LOCK_ENABLE (0<<7)
+#define SMA1303_PLL_LOCK_DISABLE (1<<7)
+
+#define SMA1303_PLL_PD_MASK (1<<6)
+#define SMA1303_PLL_OPERATION (0<<6)
+#define SMA1303_PLL_PD (1<<6)
+
+#define SMA1303_PLL_DIV_MASK (3<<4)
+#define SMA1303_PLL_OUT (0<<4)
+#define SMA1303_PLL_OUT_2 (1<<4)
+#define SMA1303_PLL_OUT_4 (2<<4)
+#define SMA1303_PLL_OUT_8 (3<<4)
+
+#define SMA1303_PLL_REF_CLK_MASK (1<<3)
+#define SMA1303_PLL_REF_CLK1 (0<<3)
+#define SMA1303_PLL_SCK (1<<3)
+
+#define SMA1303_DAC_DN_CONV_MASK (1<<2)
+#define SMA1303_DAC_DN_CONV_DISABLE (0<<2)
+#define SMA1303_DAC_DN_CONV_ENABLE (1<<2)
+
+#define SMA1303_SDO_IO_MASK (1<<1)
+#define SMA1303_HIGH_Z_LRCK_H (0<<1)
+#define SMA1303_HIGH_Z_LRCK_L (1<<1)
+
+#define SMA1303_SDO_OUTPUT2_MASK (1<<0)
+#define SMA1303_SDO_NORMAL (0<<0)
+#define SMA1303_SDO_OUTPUT_ONLY (1<<0)
+
+/* TOP_MAN2 : 0xA3 */
+#define SMA1303_MON_OSC_PLL_MASK (1<<7)
+#define SMA1303_PLL_SDO (0<<7)
+#define SMA1303_OSC_SDO (1<<7)
+
+#define SMA1303_TEST_CLKO_EN_MASK (1<<6)
+#define SMA1303_NORMAL_SDO (0<<6)
+#define SMA1303_CLK_OUT_SDO (1<<6)
+
+#define SMA1303_SDO_OUTPUT_MASK (1<<3)
+#define SMA1303_NORMAL_OUT (0<<3)
+#define SMA1303_HIGH_Z_OUT (1<<3)
+
+#define SMA1303_CLOCK_MON_MASK (1<<1)
+#define SMA1303_CLOCK_MON (0<<1)
+#define SMA1303_CLOCK_NOT_MON (1<<1)
+
+#define SMA1303_OSC_PD_MASK (1<<0)
+#define SMA1303_NORMAL_OPERATION_OSC (0<<0)
+#define SMA1303_POWER_DOWN_OSC (1<<0)
+
+/* TOP_MAN3 0xA4 */
+#define SMA1303_O_FORMAT_MASK (7<<5)
+#define SMA1303_O_FMT_LJ (1<<5)
+#define SMA1303_O_FMT_I2S (2<<5)
+#define SMA1303_O_FMT_TDM (4<<5)
+
+#define SMA1303_SCK_RATE_MASK (1<<3)
+#define SMA1303_SCK_64FS (0<<3)
+#define SMA1303_SCK_32FS (2<<3)
+
+#define SMA1303_LRCK_POL_MASK (1<<0)
+#define SMA1303_L_VALID (0<<0)
+#define SMA1303_R_VALID (1<<0)
+
+/* TDM1 FORMAT : 0xA5 */
+#define SMA1303_TDM_CLK_POL_MASK (1<<7)
+#define SMA1303_TDM_CLK_POL_RISE (0<<7)
+#define SMA1303_TDM_CLK_POL_FALL (1<<7)
+
+#define SMA1303_TDM_TX_MODE_MASK (1<<6)
+#define SMA1303_TDM_TX_MONO (0<<6)
+#define SMA1303_TDM_TX_STEREO (1<<6)
+
+#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0)
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1303_TDM_DL_MASK (1<<7)
+#define SMA1303_TDM_DL_16 (0<<7)
+#define SMA1303_TDM_DL_32 (1<<7)
+
+#define SMA1303_TDM_N_SLOT_MASK (1<<6)
+#define SMA1303_TDM_N_SLOT_4 (0<<6)
+#define SMA1303_TDM_N_SLOT_8 (1<<6)
+
+#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0)
+
+/* STATUS1 : 0xFA */
+#define SMA1303_OT1_OK_STATUS (1<<7)
+#define SMA1303_OT2_OK_STATUS (1<<6)
+
+/* STATUS2 : 0xFB */
+#define SMA1303_OCP_SPK_STATUS (1<<5)
+#define SMA1303_OCP_BST_STATUS (1<<4)
+#define SMA1303_OTP_STAT_OK_0 (5<<1)
+#define SMA1303_OTP_STAT_OK_1 (2<<2)
+
+#define SMA1303_CLK_MON_STATUS (1<<0)
+
+/* DEVICE_INFO : 0xFF */
+#define SMA1303_DEVICE_ID (2<<3)
+#define SMA1303_UVLO_BST_STATUS (1<<2)
+#define SMA1303_REV_NUM_STATUS (3<<0)
+#define SMA1303_REV_NUM_TV0 (0<<0)
+#define SMA1303_REV_NUM_TV1 (1<<0)
+
+#endif
diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c
new file mode 100644
index 000000000000..b683e676640d
--- /dev/null
+++ b/sound/soc/codecs/sma1307.c
@@ -0,0 +1,2065 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// sma1307.c -- sma1307 ALSA SoC Audio driver
+//
+// Copyright 2024 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuwha.park@irondevice.com>
+// Auther: Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "sma1307.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
+ _post_n, _n, _vco, _p_cp)\
+{\
+ .input_clk_name = _input_clk_name,\
+ .output_clk_name = _output_clk_name,\
+ .input_clk = _input_clk,\
+ .post_n = _post_n,\
+ .n = _n,\
+ .vco = _vco,\
+ .p_cp = _p_cp,\
+}
+
+static const char *setting_file = "sma1307_setting.bin";
+#define SMA1307_SETTING_CHECKSUM 0x100000
+
+/* PLL clock setting Table */
+struct sma1307_pll_match {
+ char *input_clk_name;
+ char *output_clk_name;
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int vco;
+ unsigned int p_cp;
+};
+
+struct sma1307_data {
+ char *name;
+ void (*init)(struct regmap *regmap);
+};
+
+struct sma1307_priv {
+ bool check_fault_status;
+ bool force_mute_status;
+ bool sw_ot1_prot;
+ char *name;
+ enum sma1307_mode amp_mode;
+ int binary_mode;
+ int dapm_aif_in;
+ int dapm_aif_out0;
+ int dapm_aif_out1;
+ int dapm_sdo_en;
+ int dapm_sdo_setting;
+ int num_of_pll_matches;
+ int check_fault_period;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct mutex default_lock;
+ struct regmap *regmap;
+ struct sma1307_setting_file set;
+ const struct sma1307_pll_match *pll_matches;
+ const struct sma1307_data *data;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int otp_trm2;
+ unsigned int otp_trm3;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot0_rx;
+ unsigned int tdm_slot1_rx;
+ unsigned int tdm_slot0_tx;
+ unsigned int tdm_slot1_tx;
+ unsigned int tsdw_cnt;
+};
+
+static const struct sma1307_pll_match sma1307_pll_matches[] = {
+ /* in_clk_name, out_clk_name, input_clk post_n, n, vco, p_cp */
+ PLL_MATCH("1.411MHz", "24.554MHz",
+ 1411200, 0x06, 0xD1, 0x88, 0x00),
+ PLL_MATCH("1.536MHz", "24.576MHz",
+ 1536000, 0x06, 0xC0, 0x88, 0x00),
+ PLL_MATCH("2.822MHz", "24.554MHz",
+ 2822400, 0x06, 0xD1, 0x88, 0x04),
+ PLL_MATCH("3.072MHz", "24.576MHz",
+ 3072000, 0x06, 0x60, 0x88, 0x00),
+ PLL_MATCH("6.144MHz", "24.576MHz",
+ 6144000, 0x06, 0x60, 0x88, 0x04),
+ PLL_MATCH("12.288MHz", "24.576MHz",
+ 12288000, 0x06, 0x60, 0x88, 0x08),
+ PLL_MATCH("19.2MHz", "24.48MHz",
+ 19200000, 0x06, 0x7B, 0x88, 0x0C),
+ PLL_MATCH("24.576MHz", "24.576MHz",
+ 24576000, 0x06, 0x60, 0x88, 0x0C),
+};
+
+static struct snd_soc_component *sma1307_amp_component;
+
+static void sma1307_startup(struct snd_soc_component *);
+static void sma1307_shutdown(struct snd_soc_component *);
+static void sma1307_reset(struct snd_soc_component *);
+static void sma1307_set_binary(struct snd_soc_component *);
+static void sma1307_set_default(struct snd_soc_component *);
+
+/* Initial register value - 6.0W SPK (8ohm load) */
+static const struct reg_default sma1307_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x52 },
+ { 0x03, 0x4C },
+ { 0x04, 0x47 },
+ { 0x05, 0x42 },
+ { 0x06, 0x40 },
+ { 0x07, 0x40 },
+ { 0x08, 0x3C },
+ { 0x09, 0x2F },
+ { 0x0A, 0x32 },
+ { 0x0B, 0x50 },
+ { 0x0C, 0x8C },
+ { 0x0D, 0x00 },
+ { 0x0E, 0x3F },
+ { 0x0F, 0x00 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x09 },
+ { 0x14, 0x12 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x85 },
+ { 0x1E, 0xA1 },
+ { 0x1F, 0x67 },
+ { 0x22, 0x00 },
+ { 0x23, 0x1F },
+ { 0x24, 0x7A },
+ { 0x25, 0x00 },
+ { 0x26, 0xFF },
+ { 0x27, 0x39 },
+ { 0x28, 0x54 },
+ { 0x29, 0x92 },
+ { 0x2A, 0xB0 },
+ { 0x2B, 0xED },
+ { 0x2C, 0xED },
+ { 0x2D, 0xFF },
+ { 0x2E, 0xFF },
+ { 0x2F, 0xFF },
+ { 0x30, 0xFF },
+ { 0x31, 0xFF },
+ { 0x32, 0xFF },
+ { 0x34, 0x01 },
+ { 0x35, 0x17 },
+ { 0x36, 0x92 },
+ { 0x37, 0x00 },
+ { 0x38, 0x01 },
+ { 0x39, 0x10 },
+ { 0x3E, 0x01 },
+ { 0x3F, 0x08 },
+ { 0x8B, 0x05 },
+ { 0x8C, 0x50 },
+ { 0x8D, 0x80 },
+ { 0x8E, 0x10 },
+ { 0x8F, 0x02 },
+ { 0x90, 0x02 },
+ { 0x91, 0x83 },
+ { 0x92, 0xC0 },
+ { 0x93, 0x00 },
+ { 0x94, 0xA4 },
+ { 0x95, 0x74 },
+ { 0x96, 0x57 },
+ { 0xA2, 0xCC },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x08 },
+ { 0xA8, 0x04 },
+ { 0xA9, 0x27 },
+ { 0xAA, 0x10 },
+ { 0xAB, 0x10 },
+ { 0xAC, 0x10 },
+ { 0xAD, 0x0F },
+ { 0xAE, 0xCD },
+ { 0xAF, 0x70 },
+ { 0xB0, 0x03 },
+ { 0xB1, 0xEF },
+ { 0xB2, 0x03 },
+ { 0xB3, 0xEF },
+ { 0xB4, 0xF3 },
+ { 0xB5, 0x3D },
+};
+
+static bool sma1307_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ case SMA1307_F5_READY_FOR_V_SAR:
+ case SMA1307_F7_READY_FOR_T_SAR ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_F8_STATUS_T1 ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+/* DB scale conversion of speaker volume */
+static const DECLARE_TLV_DB_SCALE(sma1307_spk_tlv, -6000, 50, 0);
+
+static const char *const sma1307_aif_in_source_text[] = {
+ "Mono", "Left", "Right"
+};
+
+static const char *const sma1307_sdo_setting_text[] = {
+ "Data_One_48k", "Data_Two_48k", "Data_Two_24k",
+ "Clk_PLL", "Clk_OSC"
+};
+
+static const char *const sma1307_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP",
+ "Vrms2_Avg", "Battery", "Temperature", "After_Delay"
+};
+
+static const char *const sma1307_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static const char *const sma1307_binary_mode_text[] = {
+ "Mode0", "Mode1", "Mode2", "Mode3", "Mode4"
+};
+
+static const char *const sma1307_reset_text[] = {
+ "Reset"
+};
+
+static const struct soc_enum sma1307_aif_in_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_in_source_text),
+ sma1307_aif_in_source_text);
+static const struct soc_enum sma1307_sdo_setting_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_sdo_setting_text),
+ sma1307_sdo_setting_text);
+static const struct soc_enum sma1307_aif_out_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_out_source_text),
+ sma1307_aif_out_source_text);
+static const struct soc_enum sma1307_tdm_slot_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_tdm_slot_text),
+ sma1307_tdm_slot_text);
+static const struct soc_enum sma1307_binary_mode_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_binary_mode_text),
+ sma1307_binary_mode_text);
+static const struct soc_enum sma1307_reset_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_reset_text),
+ sma1307_reset_text);
+
+static int sma1307_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->force_mute_status;
+
+ return 0;
+}
+
+static int sma1307_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->force_mute_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->force_mute_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_tdm_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val1, val2;
+
+ regmap_read(sma1307->regmap, SMA1307_A5_TDM1, &val1);
+ regmap_read(sma1307->regmap, SMA1307_A6_TDM2, &val2);
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val1 & SMA1307_TDM_SLOT0_RX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val1 & SMA1307_TDM_SLOT1_RX_POS_MASK;
+ sma1307->tdm_slot1_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val2 & SMA1307_TDM_SLOT0_TX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_tx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val2 & SMA1307_TDM_SLOT1_TX_POS_MASK;
+ sma1307->tdm_slot1_tx = ucontrol->value.integer.value[0];
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_tdm_slot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK, val);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK, val);
+ }
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return change;
+}
+
+static int sma1307_sw_ot1_prot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->sw_ot1_prot;
+
+ return 0;
+}
+
+static int sma1307_sw_ot1_prot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->sw_ot1_prot == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->sw_ot1_prot = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->check_fault_status;
+
+ return 0;
+}
+
+static int sma1307_check_fault_status_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->check_fault_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_period_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sma1307->check_fault_period;
+
+ return 0;
+}
+
+static int sma1307_check_fault_period_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ bool change = false;
+ int val = ucontrol->value.integer.value[0];
+
+ if (val < mc->min || val > mc->max)
+ return -EINVAL;
+ if (sma1307->check_fault_period == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_period = val;
+ }
+
+ return change;
+}
+
+static int sma1307_reset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_RESET_MASK, SMA1307_RESET_ON);
+ sma1307_reset(component);
+
+ snd_ctl_notify(component->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &kcontrol->id);
+
+ return true;
+}
+
+static int sma1307_binary_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_kcontrol_chip(kcontrol);
+
+ sma1307->binary_mode = (int)ucontrol->value.enumerated.item[0];
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static void sma1307_startup(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_ON);
+
+ if (sma1307->amp_mode == SMA1307_MONO_MODE) {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_MONO);
+ } else {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_STEREO);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_shutdown(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ /* for SMA1307A */
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK, SMA1307_SPK_MUTE);
+ /* Need to wait time for mute slope */
+ msleep(55);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK, SMA1307_SPK_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_OFF);
+}
+
+static int sma1307_aif_in_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_aif_in;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_MONO_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_ON);
+ break;
+ case SMA1307_LEFT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_NORMAL);
+ break;
+ case SMA1307_RIGHT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_SWAP);
+ break;
+ default:
+
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ sma1307->amp_mode = mux;
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_sdo_setting_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_sdo_setting;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_OUT_DATA_ONE_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_ONE_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_24K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_TWO_SDO_PER_CH_24K
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_CLK_PLL:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_PLL);
+
+ break;
+ case SMA1307_OUT_CLK_OSC:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_OSC);
+
+ break;
+ default:
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_aif_out_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = 0, val = 0, mask = 0;
+
+ if (!strcmp(w->name, SMA1307_AIF_OUT0_NAME)) {
+ mux = sma1307->dapm_aif_out0;
+ val = mux;
+ mask = SMA1307_SDO_OUT0_SEL_MASK;
+ } else if (!strcmp(w->name, SMA1307_AIF_OUT1_NAME)) {
+ mux = sma1307->dapm_aif_out1;
+ val = mux << 3;
+ mask = SMA1307_SDO_OUT1_SEL_MASK;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid widget - %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap, SMA1307_09_OUTPUT_CTRL,
+ mask, val);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_sdo_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_OUTPUT_PORT_ENABLE);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_LOGIC_OUTPUT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_INPUT_PORT_ONLY);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_HIGH_Z_OUTPUT);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_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:
+ sma1307_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sma1307_shutdown(component);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.enumerated.item[0] = (unsigned int)sma1307->dapm_aif_in;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_in_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_aif_in != val) {
+ change = true;
+ sma1307->dapm_aif_in = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.enumerated.item[0] =
+ (unsigned int)sma1307->dapm_sdo_setting;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_sdo_setting_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_setting != val) {
+ change = true;
+ sma1307->dapm_sdo_setting = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_aif_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ unsigned int val = 0;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out0;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out1;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+ ucontrol->value.enumerated.item[0] = val;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_out_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ if (sma1307->dapm_aif_out0 != val) {
+ change = true;
+ sma1307->dapm_aif_out0 = val;
+ } else
+ change = false;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ if (sma1307->dapm_aif_out1 != val) {
+ change = true;
+ sma1307->dapm_aif_out1 = val;
+ } else
+ change = false;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.integer.value[0] = (long)sma1307->dapm_sdo_en;
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if ((val < 0) || (val > 1)) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_en != val) {
+ change = true;
+ sma1307->dapm_sdo_en = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return change;
+}
+
+static const struct snd_kcontrol_new sma1307_aif_in_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_IN_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_in_get,
+ .put = sma1307_dapm_aif_in_put,
+ .private_value = (unsigned long)&sma1307_aif_in_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_setting_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SDO Setting",
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_sdo_setting_get,
+ .put = sma1307_dapm_sdo_setting_put,
+ .private_value = (unsigned long)&sma1307_sdo_setting_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out0_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT0_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out1_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT1_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_control =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, 0, 1, 0,
+ sma1307_dapm_sdo_enable_get, sma1307_dapm_sdo_enable_put);
+
+static const struct snd_kcontrol_new sma1307_enable_control =
+ SOC_DAPM_SINGLE("Switch", SMA1307_00_SYSTEM_CTRL, 0, 1, 0);
+
+static const struct snd_kcontrol_new sma1307_binary_mode_control[] = {
+ SOC_ENUM_EXT("Binary Mode", sma1307_binary_mode_enum,
+ snd_soc_get_enum_double, sma1307_binary_mode_put),
+};
+
+static const struct snd_kcontrol_new sma1307_snd_controls[] = {
+ SOC_SINGLE_TLV(SMA1307_VOL_CTRL_NAME, SMA1307_0A_SPK_VOL,
+ 0, 167, 1, sma1307_spk_tlv),
+ SOC_ENUM_EXT(SMA1307_TDM_RX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_RX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_RESET_CTRL_NAME, sma1307_reset_enum,
+ snd_soc_get_enum_double, sma1307_reset_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_FORCE_MUTE_CTRL_NAME, 0,
+ sma1307_force_mute_get, sma1307_force_mute_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_OT1_SW_PROT_CTRL_NAME, 0,
+ sma1307_sw_ot1_prot_get, sma1307_sw_ot1_prot_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_CHECK_FAULT_STATUS_NAME, 0,
+ sma1307_check_fault_status_get,
+ sma1307_check_fault_status_put),
+ SOC_SINGLE_EXT(SMA1307_CHECK_FAULT_PERIOD_NAME, SND_SOC_NOPM, 0, 600, 0,
+ sma1307_check_fault_period_get,
+ sma1307_check_fault_period_put),
+};
+
+static const struct snd_soc_dapm_widget sma1307_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_IN_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_in_source_control,
+ sma1307_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("SDO Setting", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_setting_control,
+ sma1307_sdo_setting_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT0_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out0_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT1_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out1_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_control,
+ sma1307_sdo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1307_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_enable_control),
+
+ /* stream domain */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sma1307_audio_map[] = {
+ /* Playback */
+ { "AIF IN Source", "Mono", "AIF IN" },
+ { "AIF IN Source", "Left", "AIF IN" },
+ { "AIF IN Source", "Right", "AIF IN" },
+
+ { "SDO Enable", "Switch", "AIF IN" },
+
+ { "SDO Setting", "Data_One_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_24k", "SDO Enable" },
+ { "SDO Setting", "Clk_PLL", "SDO Enable" },
+ { "SDO Setting", "Clk_OSC", "SDO Enable" },
+
+ { "AIF OUT0 Source", "Disable", "SDO Setting" },
+ { "AIF OUT0 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT0 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT0 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT0 Source", "Battery", "SDO Setting" },
+ { "AIF OUT0 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Delay", "SDO Setting" },
+
+ { "AIF OUT1 Source", "Disable", "SDO Setting" },
+ { "AIF OUT1 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT1 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT1 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT1 Source", "Battery", "SDO Setting" },
+ { "AIF OUT1 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Delay", "SDO Setting" },
+
+ { "Entry", NULL, "AIF OUT0 Source" },
+ { "Entry", NULL, "AIF OUT1 Source" },
+ { "Entry", NULL, "AIF IN Source" },
+
+ { "AMP Power", NULL, "Entry" },
+
+ { "AMP Enable", "Switch", "AMP Power" },
+ { "SPK", NULL, "AMP Enable" },
+
+ /* Capture */
+ { "AIF OUT", NULL, "AMP Enable" },
+};
+
+static void sma1307_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ int i = 0;
+
+ dev_dbg(component->dev, "%s: BCLK = %dHz\n", __func__, bclk);
+
+ if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_MCLK) {
+ dev_warn(component->dev, "%s: MCLK is not supported\n",
+ __func__);
+ } else if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1307->num_of_pll_matches; i++) {
+ if (sma1307->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1307->num_of_pll_matches) {
+ dev_warn(component->dev,
+ "%s: No matching value between pll table and SCK\n",
+ __func__);
+ return;
+ }
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ }
+
+ regmap_write(sma1307->regmap, SMA1307_8B_PLL_POST_N,
+ sma1307->pll_matches[i].post_n);
+ regmap_write(sma1307->regmap, SMA1307_8C_PLL_N,
+ sma1307->pll_matches[i].n);
+ regmap_write(sma1307->regmap, SMA1307_8D_PLL_A_SETTING,
+ sma1307->pll_matches[i].vco);
+ regmap_write(sma1307->regmap, SMA1307_8E_PLL_P_CP,
+ sma1307->pll_matches[i].p_cp);
+}
+
+static int sma1307_dai_hw_params_amp(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+
+ if (sma1307->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1307->frame_size;
+ else
+ bclk = params_rate(params) * params_physical_width(params)
+ * params_channels(params);
+
+ dev_dbg(component->dev,
+ "%s: rate = %d : bit size = %d : channel = %d\n",
+ __func__, params_rate(params), params_width(params),
+ params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ if (sma1307->last_bclk != bclk) {
+ sma1307_setup_pll(component, bclk);
+ sma1307->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+
+ case 96000:
+ dev_warn(component->dev,
+ "%s: %d rate not support SDO\n", __func__,
+ params_rate(params));
+ break;
+
+ default:
+ dev_err(component->dev, "%s: not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ /* substream->stream is SNDRV_PCM_STREAM_CAPTURE */
+ } else {
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_32FS |
+ SMA1307_DATA_16BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1307->format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_I2S_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK, SMA1307_LJ);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_LJ_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_16BIT);
+ break;
+ case 24:
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_24BIT);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_TDM_FORMAT);
+ break;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_dai_set_sysclk_amp(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1307_EXTERNAL_CLOCK_19_2:
+ case SMA1307_EXTERNAL_CLOCK_24_576:
+ case SMA1307_PLL_CLKIN_MCLK:
+ case SMA1307_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid clk id: %d\n",
+ __func__, clk_id);
+ return -EINVAL;
+ }
+ sma1307->sys_clk_id = clk_id;
+
+ return 0;
+}
+
+static int sma1307_dai_set_fmt_amp(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev,
+ "%s: %s\n", __func__, "I2S/TDM Device mode");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_DEVICE_MODE);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s: %s\n", __func__, "I2S/TDM Controller mode");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_CONTROLLER_MODE);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Controller/Device : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ sma1307->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Audio Interface Format : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Invert BCLK + Normal Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_SCK_RISING_MASK,
+ SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK
+ | SMA1307_SCK_RISING_MASK,
+ SMA1307_HIGH_FIRST_CH
+ | SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK,
+ SMA1307_HIGH_FIRST_CH);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Normal BCLK + Normal Frame");
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Bit & Frameclock : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s: slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1307->frame_size = slot_width * slots;
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK, SMA1307_TDM_FORMAT);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_TX_MODE_MASK,
+ SMA1307_TDM_TX_MONO);
+
+ switch (slot_width) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_16);
+ break;
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_32);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slot_width\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 4:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_4);
+ break;
+ case 8:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_8);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slots\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ if (sma1307->tdm_slot0_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK,
+ sma1307->tdm_slot0_rx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-rx %d set\n",
+ __func__, sma1307->tdm_slot0_rx);
+
+ if (sma1307->tdm_slot1_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK,
+ sma1307->tdm_slot1_rx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-rx %d set\n",
+ __func__, sma1307->tdm_slot1_rx);
+
+ if (sma1307->tdm_slot0_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK,
+ sma1307->tdm_slot0_tx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-tx %d set\n",
+ __func__, sma1307->tdm_slot0_tx);
+
+ if (sma1307->tdm_slot1_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK,
+ sma1307->tdm_slot1_tx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-tx %d set\n",
+ __func__, sma1307->tdm_slot1_tx);
+
+ return 0;
+}
+
+static int sma1307_dai_mute_stream(struct snd_soc_dai *dai, int mute,
+ int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+ if (mute) {
+ dev_dbg(component->dev, "%s: %s\n", __func__, "MUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_MUTE);
+ } else {
+ if (!sma1307->force_mute_status) {
+ dev_dbg(component->dev, "%s: %s\n", __func__,
+ "UNMUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_UNMUTE);
+ } else {
+ dev_dbg(sma1307->dev, "%s: FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1307_dai_ops_amp = {
+ .hw_params = sma1307_dai_hw_params_amp,
+ .set_fmt = sma1307_dai_set_fmt_amp,
+ .set_sysclk = sma1307_dai_set_sysclk_amp,
+ .set_tdm_slot = sma1307_dai_set_tdm_slot,
+ .mute_stream = sma1307_dai_mute_stream,
+};
+
+#define SMA1307_RATES_PLAYBACK SNDRV_PCM_RATE_8000_96000
+#define SMA1307_RATES_CAPTURE SNDRV_PCM_RATE_8000_48000
+#define SMA1307_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1307_dai[] = {
+ {
+ .name = "sma1307-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_PLAYBACK,
+ .formats = SMA1307_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_CAPTURE,
+ .formats = SMA1307_FORMATS,
+ },
+ .ops = &sma1307_dai_ops_amp,
+ },
+};
+
+static void sma1307_check_fault_worker(struct work_struct *work)
+{
+ struct sma1307_priv *sma1307 =
+ container_of(work, struct sma1307_priv, check_fault_work.work);
+ unsigned int status1_val, status2_val;
+ char *envp[3] = { NULL, NULL, NULL };
+
+ if (sma1307->tsdw_cnt)
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->cur_vol);
+ else
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->init_vol);
+
+ regmap_read(sma1307->regmap, SMA1307_FA_STATUS1, &status1_val);
+ regmap_read(sma1307->regmap, SMA1307_FB_STATUS2, &status2_val);
+
+ if (~status1_val & SMA1307_OT1_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT1(Over Temperature Level 1)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1");
+ if (sma1307->sw_ot1_prot) {
+ /* Volume control (Current Volume -3dB) */
+ if ((sma1307->cur_vol + 6) <= 0xFA) {
+ sma1307->cur_vol += 6;
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL,
+ sma1307->cur_vol);
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+ }
+ sma1307->tsdw_cnt++;
+ } else if (sma1307->tsdw_cnt) {
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, sma1307->init_vol);
+ sma1307->tsdw_cnt = 0;
+ sma1307->cur_vol = sma1307->init_vol;
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR");
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+
+ if (~status1_val & SMA1307_OT2_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT2(Over Temperature Level 2)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2");
+ }
+ if (status1_val & SMA1307_UVLO_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: UVLO(Under Voltage Lock Out)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO");
+ }
+ if (status1_val & SMA1307_OVP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OVP_BST(Over Voltage Protection)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST");
+ }
+ if (status2_val & SMA1307_OCP_SPK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_SPK(Over Current Protect SPK)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK");
+ }
+ if (status2_val & SMA1307_OCP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_BST(Over Current Protect Boost)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST");
+ }
+ if (status2_val & SMA1307_CLK_MON_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: CLK_FAULT(No clock input)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT");
+ }
+
+ if (envp[0] != NULL) {
+ if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp))
+ dev_err(sma1307->dev,
+ "%s: Error sending uevent\n", __func__);
+ kfree(envp[0]);
+ kfree(envp[1]);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file)
+{
+ const struct firmware *fw;
+ int size, offset, num_mode;
+ int ret;
+
+ ret = request_firmware(&fw, file, sma1307->dev);
+
+ if (ret) {
+ dev_err(sma1307->dev, "%s: failed to read \"%s\": %pe\n",
+ __func__, setting_file, ERR_PTR(ret));
+ sma1307->set.status = false;
+ return;
+ } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) {
+ dev_err(sma1307->dev, "%s: Invalid file\n", __func__);
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+
+ int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL);
+ if (!data) {
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+ size = fw->size >> 2;
+ memcpy(data, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ /* HEADER */
+ sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE;
+ sma1307->set.checksum = data[sma1307->set.header_size - 2];
+ sma1307->set.num_mode = data[sma1307->set.header_size - 1];
+ num_mode = sma1307->set.num_mode;
+ sma1307->set.header = devm_kmalloc_array(sma1307->dev,
+ sma1307->set.header_size,
+ sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.header) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.header, data,
+ sma1307->set.header_size * sizeof(int));
+
+ if ((sma1307->set.checksum >> 8) != SMA1307_SETTING_CHECKSUM) {
+ dev_err(sma1307->dev, "%s: checksum failed \"%s\"\n",
+ __func__, setting_file);
+ sma1307->set.status = false;
+ return;
+ }
+
+ /* DEFAULT */
+ sma1307->set.def_size = SMA1307_SETTING_DEFAULT_SIZE;
+ sma1307->set.def
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.def_size * sizeof(int), GFP_KERNEL);
+ if (!sma1307->set.def) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.def,
+ &data[sma1307->set.header_size],
+ sma1307->set.def_size * sizeof(int));
+
+ /* MODE */
+ offset = sma1307->set.header_size + sma1307->set.def_size;
+ sma1307->set.mode_size = DIV_ROUND_CLOSEST(size - offset, num_mode + 1);
+ for (int i = 0; i < num_mode; i++) {
+ sma1307->set.mode_set[i]
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.mode_size * 2 * sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.mode_set[i]) {
+ for (int j = 0; j < i; j++)
+ kfree(sma1307->set.mode_set[j]);
+ sma1307->set.status = false;
+ return;
+ }
+
+ for (int j = 0; j < sma1307->set.mode_size; j++) {
+ sma1307->set.mode_set[i][2 * j]
+ = data[offset + ((num_mode + 1) * j)];
+ sma1307->set.mode_set[i][2 * j + 1]
+ = data[offset + ((num_mode + 1) * j + i + 1)];
+ }
+ }
+
+ sma1307->set.status = true;
+
+}
+
+static void sma1307_reset(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int status = 0;
+
+ regmap_read(sma1307->regmap, SMA1307_FF_DEVICE_INDEX, &status);
+
+ sma1307->rev_num = status & SMA1307_REV_NUM_STATUS;
+ dev_dbg(component->dev, "%s: SMA1307 Revision %d\n",
+ __func__, sma1307->rev_num);
+ regmap_read(sma1307->regmap, SMA1307_99_OTP_TRM2, &sma1307->otp_trm2);
+ regmap_read(sma1307->regmap, SMA1307_9A_OTP_TRM3, &sma1307->otp_trm3);
+
+ if ((sma1307->otp_trm2 & SMA1307_OTP_STAT_MASK) != SMA1307_OTP_STAT_1)
+ dev_warn(component->dev, "%s: SMA1307 OTP Status Fail\n",
+ __func__);
+
+ /* Register Initial Value Setting */
+ sma1307_setting_loaded(sma1307, setting_file);
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+ else
+ sma1307_set_default(component);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_93_INT_CTRL,
+ SMA1307_DIS_INT_MASK, SMA1307_HIGH_Z_INT);
+ regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->init_vol);
+}
+
+static void sma1307_set_binary(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0, mode = 0;
+
+ for (i = 0; i < (sma1307->set.def_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ regmap_write(sma1307->regmap, i, sma1307->set.def[i]);
+
+ }
+ }
+ for (i = 0; i < (sma1307->set.mode_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ mode = sma1307->binary_mode;
+ regmap_write(sma1307->regmap,
+ sma1307->set.mode_set[mode][2 * i],
+ sma1307->set.mode_set[mode][2 * i +
+ 1]);
+ }
+ }
+}
+
+static void sma1307_set_default(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0;
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1307_reg_def); i++)
+ regmap_write(sma1307->regmap,
+ sma1307_reg_def[i].reg,
+ sma1307_reg_def[i].def);
+
+ if (!strcmp(sma1307->name, DEVICE_NAME_SMA1307AQ))
+ sma1307->data->init(sma1307->regmap);
+}
+
+static int sma1307_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ sma1307_amp_component = component;
+
+ snd_soc_add_component_controls(component, sma1307_binary_mode_control,
+ ARRAY_SIZE(sma1307_binary_mode_control));
+ sma1307_reset(component);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver sma1307_component = {
+ .probe = sma1307_probe,
+ .controls = sma1307_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1307_snd_controls),
+ .dapm_widgets = sma1307_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1307_dapm_widgets),
+ .dapm_routes = sma1307_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1307_audio_map),
+};
+
+static const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1307_FF_DEVICE_INDEX,
+ .readable_reg = sma1307_readable_register,
+ .writeable_reg = sma1307_writeable_register,
+ .volatile_reg = sma1307_volatile_register,
+
+ .reg_defaults = sma1307_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1307_reg_def),
+};
+
+static void sma1307aq_init(struct regmap *regmap)
+{
+ /* Guidelines for driving 4ohm load */
+ /* Brown Out Protection */
+ regmap_write(regmap, SMA1307_02_BROWN_OUT_PROT1, 0x62);
+ regmap_write(regmap, SMA1307_03_BROWN_OUT_PROT2, 0x5D);
+ regmap_write(regmap, SMA1307_04_BROWN_OUT_PROT3, 0x57);
+ regmap_write(regmap, SMA1307_05_BROWN_OUT_PROT8, 0x54);
+ regmap_write(regmap, SMA1307_06_BROWN_OUT_PROT9, 0x51);
+ regmap_write(regmap,
+ SMA1307_07_BROWN_OUT_PROT10, 0x4D);
+ regmap_write(regmap,
+ SMA1307_08_BROWN_OUT_PROT11, 0x4B);
+ regmap_write(regmap, SMA1307_27_BROWN_OUT_PROT4, 0x3C);
+ regmap_write(regmap, SMA1307_28_BROWN_OUT_PROT5, 0x5B);
+ regmap_write(regmap,
+ SMA1307_29_BROWN_OUT_PROT12, 0x78);
+ regmap_write(regmap,
+ SMA1307_2A_BROWN_OUT_PROT13, 0x96);
+ regmap_write(regmap,
+ SMA1307_2B_BROWN_OUT_PROT14, 0xB4);
+ regmap_write(regmap,
+ SMA1307_2C_BROWN_OUT_PROT15, 0xD3);
+ /* FDPEC Gain */
+ regmap_write(regmap, SMA1307_35_FDPEC_CTRL0, 0x16);
+ /* FLT Vdd */
+ regmap_write(regmap, SMA1307_92_FDPEC_CTRL1, 0xA0);
+ /* Boost Max */
+ regmap_write(regmap, SMA1307_AB_BOOST_CTRL4, 0x0F);
+}
+
+static const struct sma1307_data sma1307aq_data = {
+ .name = DEVICE_NAME_SMA1307AQ,
+ .init = sma1307aq_init,
+};
+
+static int sma1307_i2c_probe(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307;
+ const struct sma1307_data *data;
+ int ret = 0;
+ unsigned int device_info;
+
+ sma1307 = devm_kzalloc(&client->dev,
+ sizeof(*sma1307), GFP_KERNEL);
+ if (!sma1307)
+ return -ENOMEM;
+
+ sma1307->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1307->regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(sma1307->regmap),
+ "%s: failed to allocate register map\n", __func__);
+ }
+
+ data = device_get_match_data(&client->dev);
+ if (!data)
+ return -ENODEV;
+
+ sma1307->data = data;
+
+ /* set initial value as normal AMP IC status */
+ sma1307->name = client->name;
+ sma1307->format = SND_SOC_DAIFMT_I2S;
+ sma1307->sys_clk_id = SMA1307_PLL_CLKIN_BCLK;
+ sma1307->num_of_pll_matches = ARRAY_SIZE(sma1307_pll_matches);
+
+ sma1307->check_fault_period = CHECK_PERIOD_TIME;
+ sma1307->check_fault_status = true;
+ sma1307->init_vol = 0x32;
+ sma1307->cur_vol = sma1307->init_vol;
+ sma1307->sw_ot1_prot = true;
+
+ mutex_init(&sma1307->default_lock);
+
+ INIT_DELAYED_WORK(&sma1307->check_fault_work,
+ sma1307_check_fault_worker);
+
+ sma1307->dev = &client->dev;
+ sma1307->kobj = &client->dev.kobj;
+
+ i2c_set_clientdata(client, sma1307);
+
+ sma1307->pll_matches = sma1307_pll_matches;
+
+ regmap_read(sma1307->regmap,
+ SMA1307_FF_DEVICE_INDEX, &device_info);
+
+ if ((device_info & 0xF8) != SMA1307_DEVICE_ID) {
+ dev_err(&client->dev,
+ "%s: device initialization error (0x%02X)",
+ __func__, device_info);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "%s: chip version 0x%02X\n",
+ __func__, device_info);
+
+ i2c_set_clientdata(client, sma1307);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1307_component, sma1307_dai,
+ 1);
+
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to register component\n",
+ __func__);
+
+ return ret;
+ }
+
+ return ret;
+}
+
+static void sma1307_i2c_remove(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307 =
+ (struct sma1307_priv *)i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+}
+
+static const struct i2c_device_id sma1307_i2c_id[] = {
+ { "sma1307a" },
+ { "sma1307aq" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, sma1307_i2c_id);
+
+static const struct of_device_id sma1307_of_match[] = {
+ {
+ .compatible = "irondevice,sma1307a",
+ },
+ {
+ .compatible = "irondevice,sma1307aq",
+ .data = &sma1307aq_data //AEC-Q100 Qualificated
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, sma1307_of_match);
+
+static struct i2c_driver sma1307_i2c_driver = {
+ .driver = {
+ .name = "sma1307",
+ .of_match_table = sma1307_of_match,
+ },
+ .probe = sma1307_i2c_probe,
+ .remove = sma1307_i2c_remove,
+ .id_table = sma1307_i2c_id,
+};
+
+module_i2c_driver(sma1307_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1307 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("KS Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sma1307.h b/sound/soc/codecs/sma1307.h
new file mode 100644
index 000000000000..44aab52a32f9
--- /dev/null
+++ b/sound/soc/codecs/sma1307.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * sma1307.h -- sma1307 ALSA SoC Audio driver
+ *
+ * Copyright 2024 Iron Device 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 _SMA1307_H
+#define _SMA1307_H
+
+#include <sound/soc.h>
+
+enum sma1307_fault {
+ SMA1307_FAULT_OT1,
+ SMA1307_FAULT_OT2,
+ SMA1307_FAULT_UVLO,
+ SMA1307_FAULT_OVP_BST,
+ SMA1307_FAULT_OCP_SPK,
+ SMA1307_FAULT_OCP_BST,
+ SMA1307_FAULT_CLK
+};
+
+enum sma1307_mode {
+ SMA1307_MONO_MODE,
+ SMA1307_LEFT_MODE,
+ SMA1307_RIGHT_MODE,
+};
+
+enum sma1307_sdo_mode {
+ SMA1307_OUT_DATA_ONE_48K,
+ SMA1307_OUT_DATA_TWO_48K,
+ SMA1307_OUT_DATA_TWO_24K,
+ SMA1307_OUT_CLK_PLL,
+ SMA1307_OUT_CLK_OSC
+};
+
+enum sma1307_sdo_source {
+ SMA1307_OUT_DISABLE,
+ SMA1307_OUT_FORMAT_C,
+ SMA1307_OUT_MIXER_OUT,
+ SMA1307_OUT_AFTER_DSP,
+ SMA1307_OUT_VRMS2_AVG,
+ SMA1307_OUT_BATTERY,
+ SMA1307_OUT_TEMP,
+ SMA1307_OUT_AFTER_DELAY
+};
+
+struct sma1307_setting_file {
+ bool status;
+ char *header;
+ int *def;
+ int *mode_set[5];
+ int checksum;
+ int num_mode;
+ size_t header_size;
+ size_t def_size;
+ size_t mode_size;
+};
+
+#define SMA1307_I2C_ADDR_00 0x1e
+#define SMA1307_I2C_ADDR_01 0x3e
+#define SMA1307_I2C_ADDR_10 0x5e
+#define SMA1307_I2C_ADDR_11 0x7e
+
+#define DEVICE_NAME_SMA1307A "sma1307a"
+#define DEVICE_NAME_SMA1307AQ "sma1307aq"
+
+#define SMA1307_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1307_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1307_PLL_CLKIN_MCLK 0x02
+#define SMA1307_PLL_CLKIN_BCLK 0x03
+
+#define SMA1307_OFFSET_DEFAULT_MODE 0x00
+#define SMA1307_OFFSET_BURNING_MODE 0x01
+
+#define SMA1307_SETTING_HEADER_SIZE 0x08
+#define SMA1307_SETTING_DEFAULT_SIZE 0xC0
+
+#define SMA1307_DEFAULT_SET 0x00
+#define SMA1307_BINARY_FILE_SET 0x01
+
+/* Controls Name */
+#define SMA1307_REG_CTRL_NAME "Register Byte Control"
+#define SMA1307_VOL_CTRL_NAME "Speaker Volume"
+#define SMA1307_FORCE_MUTE_CTRL_NAME "Force Mute Switch"
+#define SMA1307_TDM_RX0_POS_NAME "TDM RX Slot0 Position"
+#define SMA1307_TDM_RX1_POS_NAME "TDM RX Slot1 Position"
+#define SMA1307_TDM_TX0_POS_NAME "TDM TX Slot0 Position"
+#define SMA1307_TDM_TX1_POS_NAME "TDM TX Slot1 Position"
+#define SMA1307_OT1_SW_PROT_CTRL_NAME "OT1 SW Protection Switch"
+#define SMA1307_RESET_CTRL_NAME "Reset Switch"
+#define SMA1307_CHECK_FAULT_STATUS_NAME "Check Fault Status"
+#define SMA1307_CHECK_FAULT_PERIOD_NAME "Check Fault Period"
+
+/* DAPM Name */
+#define SMA1307_AIF_IN_NAME "AIF IN Source"
+#define SMA1307_AIF_OUT0_NAME "AIF OUT0 Source"
+#define SMA1307_AIF_OUT1_NAME "AIF OUT1 Source"
+
+/*
+ * SMA1307 Register Definition
+ */
+
+/* SMA1307 Register Addresses */
+#define SMA1307_00_SYSTEM_CTRL 0x00
+#define SMA1307_01_INPUT_CTRL1 0x01
+#define SMA1307_02_BROWN_OUT_PROT1 0x02
+#define SMA1307_03_BROWN_OUT_PROT2 0x03
+#define SMA1307_04_BROWN_OUT_PROT3 0x04
+#define SMA1307_05_BROWN_OUT_PROT8 0x05
+#define SMA1307_06_BROWN_OUT_PROT9 0x06
+#define SMA1307_07_BROWN_OUT_PROT10 0x07
+#define SMA1307_08_BROWN_OUT_PROT11 0x08
+#define SMA1307_09_OUTPUT_CTRL 0x09
+#define SMA1307_0A_SPK_VOL 0x0A
+#define SMA1307_0B_BST_TEST 0x0B
+#define SMA1307_0C_BOOST_CTRL8 0x0C
+#define SMA1307_0D_SPK_TEST 0x0D
+#define SMA1307_0E_MUTE_VOL_CTRL 0x0E
+#define SMA1307_0F_VBAT_TEMP_SENSING 0x0F
+
+#define SMA1307_10_SYSTEM_CTRL1 0x10
+#define SMA1307_11_SYSTEM_CTRL2 0x11
+#define SMA1307_12_SYSTEM_CTRL3 0x12
+#define SMA1307_13_DELAY 0x13
+#define SMA1307_14_MODULATOR 0x14
+#define SMA1307_15_BASS_SPK1 0x15
+#define SMA1307_16_BASS_SPK2 0x16
+#define SMA1307_17_BASS_SPK3 0x17
+#define SMA1307_18_BASS_SPK4 0x18
+#define SMA1307_19_BASS_SPK5 0x19
+#define SMA1307_1A_BASS_SPK6 0x1A
+#define SMA1307_1B_BASS_SPK7 0x1B
+#define SMA1307_1C_BROWN_OUT_PROT20 0x1C
+#define SMA1307_1D_BROWN_OUT_PROT0 0x1D
+#define SMA1307_1E_TONE_GENERATOR 0x1E
+#define SMA1307_1F_TONE_FINE_VOLUME 0x1F
+
+#define SMA1307_22_COMP_HYS_SEL 0x22
+#define SMA1307_23_COMPLIM1 0x23
+#define SMA1307_24_COMPLIM2 0x24
+#define SMA1307_25_COMPLIM3 0x25
+#define SMA1307_26_COMPLIM4 0x26
+#define SMA1307_27_BROWN_OUT_PROT4 0x27
+#define SMA1307_28_BROWN_OUT_PROT5 0x28
+#define SMA1307_29_BROWN_OUT_PROT12 0x29
+#define SMA1307_2A_BROWN_OUT_PROT13 0x2A
+#define SMA1307_2B_BROWN_OUT_PROT14 0x2B
+#define SMA1307_2C_BROWN_OUT_PROT15 0x2C
+#define SMA1307_2D_BROWN_OUT_PROT6 0x2D
+#define SMA1307_2E_BROWN_OUT_PROT7 0x2E
+#define SMA1307_2F_BROWN_OUT_PROT16 0x2F
+
+#define SMA1307_30_BROWN_OUT_PROT17 0x30
+#define SMA1307_31_BROWN_OUT_PROT18 0x31
+#define SMA1307_32_BROWN_OUT_PROT19 0x32
+#define SMA1307_34_OCP_SPK 0x34
+#define SMA1307_35_FDPEC_CTRL0 0x35
+#define SMA1307_36_PROTECTION 0x36
+#define SMA1307_37_SLOPECTRL 0x37
+#define SMA1307_38_POWER_METER 0x38
+#define SMA1307_39_PMT_NZ_VAL 0x39
+#define SMA1307_3B_TEST1 0x3B
+#define SMA1307_3C_TEST2 0x3C
+#define SMA1307_3D_TEST3 0x3D
+#define SMA1307_3E_IDLE_MODE_CTRL 0x3E
+#define SMA1307_3F_ATEST2 0x3F
+#define SMA1307_8B_PLL_POST_N 0x8B
+#define SMA1307_8C_PLL_N 0x8C
+#define SMA1307_8D_PLL_A_SETTING 0x8D
+#define SMA1307_8E_PLL_P_CP 0x8E
+#define SMA1307_8F_ANALOG_TEST 0x8F
+
+#define SMA1307_90_CRESTLIM1 0x90
+#define SMA1307_91_CRESTLIM2 0x91
+#define SMA1307_92_FDPEC_CTRL1 0x92
+#define SMA1307_93_INT_CTRL 0x93
+#define SMA1307_94_BOOST_CTRL9 0x94
+#define SMA1307_95_BOOST_CTRL10 0x95
+#define SMA1307_96_BOOST_CTRL11 0x96
+#define SMA1307_97_OTP_TRM0 0x97
+#define SMA1307_98_OTP_TRM1 0x98
+#define SMA1307_99_OTP_TRM2 0x99
+#define SMA1307_9A_OTP_TRM3 0x9A
+
+#define SMA1307_A0_PAD_CTRL0 0xA0
+#define SMA1307_A1_PAD_CTRL1 0xA1
+#define SMA1307_A2_TOP_MAN1 0xA2
+#define SMA1307_A3_TOP_MAN2 0xA3
+#define SMA1307_A4_TOP_MAN3 0xA4
+#define SMA1307_A5_TDM1 0xA5
+#define SMA1307_A6_TDM2 0xA6
+#define SMA1307_A7_CLK_MON 0xA7
+#define SMA1307_A8_BOOST_CTRL1 0xA8
+#define SMA1307_A9_BOOST_CTRL2 0xA9
+#define SMA1307_AA_BOOST_CTRL3 0xAA
+#define SMA1307_AB_BOOST_CTRL4 0xAB
+#define SMA1307_AC_BOOST_CTRL5 0xAC
+#define SMA1307_AD_BOOST_CTRL6 0xAD
+#define SMA1307_AE_BOOST_CTRL7 0xAE
+#define SMA1307_AF_LPF 0xAF
+
+#define SMA1307_B0_RMS_TC1 0xB0
+#define SMA1307_B1_RMS_TC2 0xB1
+#define SMA1307_B2_AVG_TC1 0xB2
+#define SMA1307_B3_AVG_TC2 0xB3
+#define SMA1307_B4_PRVALUE1 0xB4
+#define SMA1307_B5_PRVALUE2 0xB5
+#define SMA1307_B8_SPK_NG_CTRL1 0xB8
+#define SMA1307_B9_SPK_NG_CTRL2 0xB9
+#define SMA1307_BA_DGC1 0xBA
+#define SMA1307_BB_DGC2 0xBB
+#define SMA1307_BC_DGC3 0xBC
+#define SMA1307_BD_MCBS_CTRL1 0xBD
+#define SMA1307_BE_MCBS_CTRL2 0xBE
+
+/* Status Register Read Only */
+#define SMA1307_F5_READY_FOR_V_SAR 0xF5
+#define SMA1307_F7_READY_FOR_T_SAR 0xF7
+#define SMA1307_F8_STATUS_T1 0xF8
+#define SMA1307_F9_STATUS_T2 0xF9
+#define SMA1307_FA_STATUS1 0xFA
+#define SMA1307_FB_STATUS2 0xFB
+#define SMA1307_FC_STATUS3 0xFC
+#define SMA1307_FD_STATUS4 0xFD
+#define SMA1307_FE_STATUS5 0xFE
+#define SMA1307_FF_DEVICE_INDEX 0xFF
+
+/* SMA1307 Registers Bit Fields */
+/* Power On/Off */
+#define SMA1307_POWER_MASK BIT(0)
+#define SMA1307_POWER_OFF 0
+#define SMA1307_POWER_ON BIT(0)
+
+/* Reset */
+#define SMA1307_RESET_MASK BIT(1)
+#define SMA1307_RESET_ON BIT(1)
+
+/* Left Polarity */
+#define SMA1307_LEFTPOL_MASK BIT(3)
+#define SMA1307_LOW_FIRST_CH 0
+#define SMA1307_HIGH_FIRST_CH BIT(3)
+
+/* SCK Falling/Rising */
+#define SMA1307_SCK_RISING_MASK BIT(2)
+#define SMA1307_SCK_FALLING_EDGE 0
+#define SMA1307_SCK_RISING_EDGE BIT(2)
+
+/* SPK Mute */
+#define SMA1307_SPK_MUTE_MASK BIT(0)
+#define SMA1307_SPK_UNMUTE 0
+#define SMA1307_SPK_MUTE BIT(0)
+
+/* SPK Mode */
+#define SMA1307_SPK_MODE_MASK (BIT(2)|BIT(3)|BIT(4))
+#define SMA1307_SPK_OFF 0
+#define SMA1307_SPK_MONO BIT(2)
+#define SMA1307_SPK_STEREO BIT(4)
+
+/* Mono Mix */
+#define SMA1307_MONOMIX_MASK BIT(0)
+#define SMA1307_MONOMIX_OFF 0
+#define SMA1307_MONOMIX_ON BIT(0)
+
+/* LR Data Swap */
+#define SMA1307_LR_DATA_SW_MASK BIT(4)
+#define SMA1307_LR_DATA_SW_NORMAL 0
+#define SMA1307_LR_DATA_SW_SWAP BIT(4)
+
+/* PLL On/Off */
+#define SMA1307_PLL_MASK BIT(6)
+#define SMA1307_PLL_ON 0
+#define SMA1307_PLL_OFF BIT(6)
+
+/* Input Format */
+#define SMA1307_I2S_MODE_MASK (BIT(4)|BIT(5)|BIT(6))
+#define SMA1307_STANDARD_I2S 0
+#define SMA1307_LJ BIT(4)
+#define SMA1307_RJ_16BIT BIT(6)
+#define SMA1307_RJ_18BIT (BIT(4)|BIT(6))
+#define SMA1307_RJ_20BIT (BIT(5)|BIT(6))
+#define SMA1307_RJ_24BIT (BIT(4)|BIT(5)|BIT(6))
+
+/* Controller / Device Setting */
+#define SMA1307_CONTROLLER_DEVICE_MASK BIT(7)
+#define SMA1307_DEVICE_MODE 0
+#define SMA1307_CONTROLLER_MODE BIT(7)
+
+/* Port Config */
+#define SMA1307_PORT_CONFIG_MASK (BIT(6)|BIT(7))
+#define SMA1307_INPUT_PORT_ONLY 0
+#define SMA1307_OUTPUT_PORT_ENABLE BIT(7)
+
+/* SDO Output */
+#define SMA1307_SDO_OUTPUT_MASK BIT(3)
+#define SMA1307_LOGIC_OUTPUT 0
+#define SMA1307_HIGH_Z_OUTPUT BIT(3)
+
+#define SMA1307_DATA_CLK_SEL_MASK (BIT(6)|BIT(7))
+#define SMA1307_SDO_DATA 0
+#define SMA1307_SDO_CLK_PLL BIT(6)
+#define SMA1307_SDO_CLK_OSC (BIT(6)|BIT(7))
+
+/* SDO Output2 */
+#define SMA1307_SDO_OUTPUT2_MASK BIT(0)
+#define SMA1307_ONE_SDO_PER_CH 0
+#define SMA1307_TWO_SDO_PER_CH BIT(0)
+
+/* SDO Output3 */
+#define SMA1307_SDO_OUTPUT3_MASK BIT(2)
+#define SMA1307_SDO_OUTPUT3_DIS 0
+#define SMA1307_TWO_SDO_PER_CH_24K BIT(2)
+
+/* SDO OUT1 Select*/
+#define SMA1307_SDO_OUT1_SEL_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_SDO1_DISABLE 0
+#define SMA1307_SDO1_FORMAT_C BIT(3)
+#define SMA1307_SDO1_MONO_MIX BIT(4)
+#define SMA1307_SDO1_AFTER_DSP (BIT(3)|BIT(4))
+#define SMA1307_SDO1_VRMS2_AVG BIT(5)
+#define SMA1307_SDO1_VBAT_MON (BIT(3)|BIT(5))
+#define SMA1307_SDO1_TEMP_MON (BIT(4)|BIT(5))
+#define SMA1307_SDO1_AFTER_DELAY (BIT(3)|BIT(4)|BIT(5))
+
+/* SDO OUT0 Select*/
+#define SMA1307_SDO_OUT0_SEL_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_SDO0_DISABLE 0
+#define SMA1307_SDO0_FORMAT_C BIT(0)
+#define SMA1307_SDO0_MONO_MIX BIT(1)
+#define SMA1307_SDO0_AFTER_DSP (BIT(0)|BIT(1))
+#define SMA1307_SDO0_VRMS2_AVG BIT(2)
+#define SMA1307_SDO0_VBAT_MON (BIT(0)|BIT(2))
+#define SMA1307_SDO0_TEMP_MON (BIT(1)|BIT(2))
+#define SMA1307_SDO0_AFTER_DELAY (BIT(0)|BIT(1)|BIT(2))
+
+/* INTERRUPT Operation */
+#define SMA1307_SEL_INT_MASK BIT(2)
+#define SMA1307_INT_CLEAR_AUTO 0
+#define SMA1307_INT_CLEAR_MANUAL BIT(2)
+
+/* INTERRUPT CLEAR */
+#define SMA1307_CLR_INT_MASK BIT(1)
+#define SMA1307_INT_READY 0
+#define SMA1307_INT_CLEAR BIT(1)
+
+/* INTERRUPT Disable */
+#define SMA1307_DIS_INT_MASK BIT(0)
+#define SMA1307_NORMAL_INT 0
+#define SMA1307_HIGH_Z_INT BIT(0)
+
+/* Interface Control */
+#define SMA1307_INTERFACE_MASK (BIT(5)|BIT(6)|BIT(7))
+#define SMA1307_LJ_FORMAT BIT(5)
+#define SMA1307_I2S_FORMAT (BIT(5)|BIT(6))
+#define SMA1307_TDM_FORMAT BIT(7)
+
+#define SMA1307_SCK_RATE_MASK (BIT(3)|BIT(4))
+#define SMA1307_SCK_64FS 0
+#define SMA1307_SCK_32FS BIT(4)
+
+#define SMA1307_DATA_WIDTH_MASK (BIT(1)|BIT(2))
+#define SMA1307_DATA_24BIT 0
+#define SMA1307_DATA_16BIT (BIT(1)|BIT(2))
+
+#define SMA1307_TDM_TX_MODE_MASK BIT(6)
+#define SMA1307_TDM_TX_MONO 0
+#define SMA1307_TDM_TX_STEREO BIT(6)
+
+#define SMA1307_TDM_SLOT0_RX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_0 0
+#define SMA1307_TDM_SLOT0_RX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_RX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_RX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_RX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_RX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_RX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_0 0
+#define SMA1307_TDM_SLOT1_RX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_RX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_RX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_RX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_RX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1307_TDM_DL_MASK BIT(7)
+#define SMA1307_TDM_DL_16 0
+#define SMA1307_TDM_DL_32 BIT(7)
+
+#define SMA1307_TDM_N_SLOT_MASK BIT(6)
+#define SMA1307_TDM_N_SLOT_4 0
+#define SMA1307_TDM_N_SLOT_8 BIT(6)
+
+#define SMA1307_TDM_SLOT0_TX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_0 0
+#define SMA1307_TDM_SLOT0_TX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_TX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_TX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_TX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_TX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_TX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_0 0
+#define SMA1307_TDM_SLOT1_TX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_TX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_TX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_TX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_TX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* OTP STATUS */
+#define SMA1307_OTP_STAT_MASK BIT(6)
+#define SMA1307_OTP_STAT_0 0
+#define SMA1307_OTP_STAT_1 BIT(6)
+
+/* STATUS */
+#define SMA1307_OT1_OK_STATUS BIT(7)
+#define SMA1307_OT2_OK_STATUS BIT(6)
+#define SMA1307_UVLO_STATUS BIT(5)
+#define SMA1307_OVP_BST_STATUS BIT(4)
+#define SMA1307_POWER_FLAG BIT(3)
+
+#define SMA1307_SCAN_CHK BIT(7)
+#define SMA1307_OCP_SPK_STATUS BIT(5)
+#define SMA1307_OCP_BST_STATUS BIT(4)
+#define SMA1307_BOP_STATE (BIT(1)|BIT(2)|BIT(3))
+#define SMA1307_CLK_MON_STATUS BIT(0)
+
+#define SMA1307_DEVICE_ID (BIT(3)|BIT(4))
+#define SMA1307_REV_NUM_STATUS (BIT(0)|BIT(1))
+#define SMA1307_REV_NUM_REV0 0
+#define SMA1307_REV_NUM_REV1 BIT(0)
+
+#endif
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index c8fd6367f6c0..c9766979b1d7 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIR (Digital Interface Reciever) driver
*
@@ -9,10 +10,6 @@
*
* Author: Vipin Kumar, <vipin.kumar@st.com>
* Copyright: (C) 2012 ST Microelectronics
- *
- * 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>
@@ -31,20 +28,22 @@ static const struct snd_soc_dapm_route dir_routes[] = {
{ "Capture", NULL, "spdif-in" },
};
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static struct snd_soc_codec_driver soc_codec_spdif_dir = {
- .component_driver = {
- .dapm_widgets = dir_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
- .dapm_routes = dir_routes,
- .num_dapm_routes = ARRAY_SIZE(dir_routes),
- },
+static const struct snd_soc_component_driver soc_codec_spdif_dir = {
+ .dapm_widgets = dir_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+ .dapm_routes = dir_routes,
+ .num_dapm_routes = ARRAY_SIZE(dir_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static struct snd_soc_dai_driver dir_stub_dai = {
@@ -60,16 +59,11 @@ static struct snd_soc_dai_driver dir_stub_dai = {
static int spdif_dir_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dir,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dir,
&dir_stub_dai, 1);
}
-static int spdif_dir_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dir_dt_ids[] = {
{ .compatible = "linux,spdif-dir", },
@@ -80,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
- .remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.of_match_table = of_match_ptr(spdif_dir_dt_ids),
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index 037aa1d45559..2409fd834f84 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIT driver
*
@@ -8,10 +9,6 @@
* Author: Steve Chen, <schen@mvista.com>
* Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
* Copyright: (C) 2009 Texas Instruments, India
- *
- * 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>
@@ -24,7 +21,8 @@
#define DRV_NAME "spdif-dit"
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
@@ -38,13 +36,14 @@ static const struct snd_soc_dapm_route dit_routes[] = {
{ "spdif-out", NULL, "Playback" },
};
-static struct snd_soc_codec_driver soc_codec_spdif_dit = {
- .component_driver = {
- .dapm_widgets = dit_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
- .dapm_routes = dit_routes,
- .num_dapm_routes = ARRAY_SIZE(dit_routes),
- },
+static const struct snd_soc_component_driver soc_codec_spdif_dit = {
+ .dapm_widgets = dit_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+ .dapm_routes = dit_routes,
+ .num_dapm_routes = ARRAY_SIZE(dit_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static struct snd_soc_dai_driver dit_stub_dai = {
@@ -60,16 +59,11 @@ static struct snd_soc_dai_driver dit_stub_dai = {
static int spdif_dit_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dit,
&dit_stub_dai, 1);
}
-static int spdif_dit_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dit_dt_ids[] = {
{ .compatible = "linux,spdif-dit", },
@@ -80,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
- .remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(spdif_dit_dt_ids),
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
new file mode 100644
index 000000000000..55f00ce7c718
--- /dev/null
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for SRC4XXX codecs
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "src4xxx.h"
+
+static int src4xxx_i2c_probe(struct i2c_client *i2c)
+{
+ return src4xxx_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &src4xxx_regmap_config), NULL);
+}
+
+static const struct i2c_device_id src4xxx_i2c_ids[] = {
+ { "src4392" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
+
+static const struct of_device_id src4xxx_of_match[] __maybe_unused = {
+ { .compatible = "ti,src4392", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, src4xxx_of_match);
+
+
+static struct i2c_driver src4xxx_i2c_driver = {
+ .driver = {
+ .name = "src4xxx",
+ .of_match_table = of_match_ptr(src4xxx_of_match),
+ },
+ .probe = src4xxx_i2c_probe,
+ .id_table = src4xxx_i2c_ids,
+};
+module_i2c_driver(src4xxx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC SRC4392 CODEC I2C driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c
new file mode 100644
index 000000000000..5a3489475225
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TI SRC4xxx Audio Codec driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/module.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "src4xxx.h"
+
+struct src4xxx {
+ struct regmap *regmap;
+ bool master[2];
+ int mclk_hz;
+ struct device *dev;
+};
+
+enum {SRC4XXX_PORTA, SRC4XXX_PORTB};
+
+/* SRC attenuation */
+static const DECLARE_TLV_DB_SCALE(src_tlv, -12750, 50, 0);
+
+static const struct snd_kcontrol_new src4xxx_controls[] = {
+ SOC_DOUBLE_R_TLV("SRC Volume",
+ SRC4XXX_SCR_CTL_30, SRC4XXX_SCR_CTL_31, 0, 255, 1, src_tlv),
+};
+
+/* I2S port control */
+static const char * const port_out_src_text[] = {
+ "loopback", "other_port", "DIR", "SRC"
+};
+static SOC_ENUM_SINGLE_DECL(porta_out_src_enum, SRC4XXX_PORTA_CTL_03, 4,
+ port_out_src_text);
+static SOC_ENUM_SINGLE_DECL(portb_out_src_enum, SRC4XXX_PORTB_CTL_05, 4,
+ port_out_src_text);
+static const struct snd_kcontrol_new porta_out_control =
+ SOC_DAPM_ENUM("Port A source select", porta_out_src_enum);
+static const struct snd_kcontrol_new portb_out_control =
+ SOC_DAPM_ENUM("Port B source select", portb_out_src_enum);
+
+/* Digital audio transmitter control */
+static const char * const dit_mux_text[] = {"Port A", "Port B", "DIR", "SRC"};
+static SOC_ENUM_SINGLE_DECL(dit_mux_enum, SRC4XXX_TX_CTL_07, 3, dit_mux_text);
+static const struct snd_kcontrol_new dit_mux_control =
+ SOC_DAPM_ENUM("DIT source", dit_mux_enum);
+
+/* SRC control */
+static const char * const src_in_text[] = {"Port A", "Port B", "DIR"};
+static SOC_ENUM_SINGLE_DECL(src_in_enum, SRC4XXX_SCR_CTL_2D, 0, src_in_text);
+static const struct snd_kcontrol_new src_in_control =
+ SOC_DAPM_ENUM("SRC source select", src_in_enum);
+
+/* DIR control */
+static const char * const dir_in_text[] = {"Ch 1", "Ch 2", "Ch 3", "Ch 4"};
+static SOC_ENUM_SINGLE_DECL(dir_in_enum, SRC4XXX_RCV_CTL_0D, 0, dir_in_text);
+static const struct snd_kcontrol_new dir_in_control =
+ SOC_DAPM_ENUM("Digital Input", dir_in_enum);
+
+static const struct snd_soc_dapm_widget src4xxx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("loopback_A"),
+ SND_SOC_DAPM_INPUT("other_port_A"),
+ SND_SOC_DAPM_INPUT("DIR_A"),
+ SND_SOC_DAPM_INPUT("SRC_A"),
+ SND_SOC_DAPM_MUX("Port A source",
+ SND_SOC_NOPM, 0, 0, &porta_out_control),
+
+ SND_SOC_DAPM_INPUT("loopback_B"),
+ SND_SOC_DAPM_INPUT("other_port_B"),
+ SND_SOC_DAPM_INPUT("DIR_B"),
+ SND_SOC_DAPM_INPUT("SRC_B"),
+ SND_SOC_DAPM_MUX("Port B source",
+ SND_SOC_NOPM, 0, 0, &portb_out_control),
+
+ SND_SOC_DAPM_INPUT("Port_A"),
+ SND_SOC_DAPM_INPUT("Port_B"),
+ SND_SOC_DAPM_INPUT("DIR_"),
+
+ /* Digital audio receivers and transmitters */
+ SND_SOC_DAPM_OUTPUT("DIR_OUT"),
+ SND_SOC_DAPM_OUTPUT("SRC_OUT"),
+ SND_SOC_DAPM_MUX("DIT Out Src", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIT_SHIFT, 1, &dit_mux_control),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF_A_RX", "Playback A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_A_TX", "Capture A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF_B_RX", "Playback B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_B_TX", "Capture B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("SRC source", SND_SOC_NOPM, 0, 0, &src_in_control),
+
+ SND_SOC_DAPM_INPUT("MCLK"),
+ SND_SOC_DAPM_INPUT("RXMCLKI"),
+ SND_SOC_DAPM_INPUT("RXMCLKO"),
+
+ SND_SOC_DAPM_INPUT("RX1"),
+ SND_SOC_DAPM_INPUT("RX2"),
+ SND_SOC_DAPM_INPUT("RX3"),
+ SND_SOC_DAPM_INPUT("RX4"),
+ SND_SOC_DAPM_MUX("Digital Input", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIR_SHIFT, 1, &dir_in_control),
+};
+
+static const struct snd_soc_dapm_route src4xxx_audio_routes[] = {
+ /* I2S Input to Output Routing */
+ {"Port A source", "loopback", "loopback_A"},
+ {"Port A source", "other_port", "other_port_A"},
+ {"Port A source", "DIR", "DIR_A"},
+ {"Port A source", "SRC", "SRC_A"},
+ {"Port B source", "loopback", "loopback_B"},
+ {"Port B source", "other_port", "other_port_B"},
+ {"Port B source", "DIR", "DIR_B"},
+ {"Port B source", "SRC", "SRC_B"},
+ /* DIT muxing */
+ {"DIT Out Src", "Port A", "Capture A"},
+ {"DIT Out Src", "Port B", "Capture B"},
+ {"DIT Out Src", "DIR", "DIR_OUT"},
+ {"DIT Out Src", "SRC", "SRC_OUT"},
+
+ /* SRC input selection */
+ {"SRC source", "Port A", "Port_A"},
+ {"SRC source", "Port B", "Port_B"},
+ {"SRC source", "DIR", "DIR_"},
+ /* SRC mclk selection */
+ {"SRC mclk source", "Master (MCLK)", "MCLK"},
+ {"SRC mclk source", "Master (RXCLKI)", "RXMCLKI"},
+ {"SRC mclk source", "Recovered receiver clk", "RXMCLKO"},
+ /* DIR input selection */
+ {"Digital Input", "Ch 1", "RX1"},
+ {"Digital Input", "Ch 2", "RX2"},
+ {"Digital Input", "Ch 3", "RX3"},
+ {"Digital Input", "Ch 4", "RX4"},
+};
+
+
+static const struct snd_soc_component_driver src4xxx_driver = {
+ .controls = src4xxx_controls,
+ .num_controls = ARRAY_SIZE(src4xxx_controls),
+
+ .dapm_widgets = src4xxx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(src4xxx_dapm_widgets),
+ .dapm_routes = src4xxx_audio_routes,
+ .num_dapm_routes = ARRAY_SIZE(src4xxx_audio_routes),
+};
+
+static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl = SRC4XXX_BUS_MASTER;
+ src4xxx->master[dai->id] = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ ctrl = 0;
+ src4xxx->master[dai->id] = false;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl |= SRC4XXX_BUS_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl |= SRC4XXX_BUS_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl |= SRC4XXX_BUS_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ regmap_update_bits(src4xxx->regmap, SRC4XXX_BUS_FMT(dai->id),
+ SRC4XXX_BUS_FMT_MS_MASK, ctrl);
+
+ return 0;
+}
+
+static int src4xxx_set_mclk_hz(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+
+ dev_info(component->dev, "changing mclk rate from %d to %d Hz\n",
+ src4xxx->mclk_hz, freq);
+ src4xxx->mclk_hz = freq;
+
+ return 0;
+}
+
+static int src4xxx_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 src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int mclk_div;
+ int val, pj, jd, d;
+ int reg;
+ int ret;
+
+ switch (dai->id) {
+ case SRC4XXX_PORTB:
+ reg = SRC4XXX_PORTB_CTL_06;
+ break;
+ default:
+ reg = SRC4XXX_PORTA_CTL_04;
+ break;
+ }
+
+ if (src4xxx->master[dai->id]) {
+ mclk_div = src4xxx->mclk_hz/params_rate(params);
+ if (src4xxx->mclk_hz != mclk_div*params_rate(params)) {
+ dev_err(component->dev,
+ "mclk %d / rate %d has a remainder.\n",
+ src4xxx->mclk_hz, params_rate(params));
+ return -EINVAL;
+ }
+
+ val = ((int)mclk_div - 128) / 128;
+ if ((val < 0) | (val > 3)) {
+ dev_err(component->dev,
+ "div register setting %d is out of range\n",
+ val);
+ dev_err(component->dev,
+ "unsupported sample rate %d Hz for the master clock of %d Hz\n",
+ params_rate(params), src4xxx->mclk_hz);
+ return -EINVAL;
+ }
+
+ /* set the TX DIV */
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ /* set the PLL for the digital receiver */
+ switch (src4xxx->mclk_hz) {
+ case 24576000:
+ pj = 0x22;
+ jd = 0x00;
+ d = 0x00;
+ break;
+ case 22579200:
+ pj = 0x22;
+ jd = 0x1b;
+ d = 0xa3;
+ break;
+ default:
+ /* don't error out here,
+ * other parts of the chip are still functional
+ * Dummy initialize variables to avoid
+ * -Wsometimes-uninitialized from clang.
+ */
+ dev_info(component->dev,
+ "Couldn't set the RCV PLL as this master clock rate is unknown. Chosen regmap values may not match real world values.\n");
+ pj = 0x0;
+ jd = 0xff;
+ d = 0xff;
+ break;
+ }
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_0F, pj);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_0F);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_10, jd);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_10);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_11, d);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_11);
+
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ return regmap_update_bits(src4xxx->regmap, reg,
+ SRC4XXX_MCLK_DIV_MASK, val);
+ } else {
+ dev_info(dai->dev, "not setting up MCLK as not master\n");
+ }
+
+ return 0;
+};
+
+static const struct snd_soc_dai_ops src4xxx_dai_ops = {
+ .hw_params = src4xxx_hw_params,
+ .set_sysclk = src4xxx_set_mclk_hz,
+ .set_fmt = src4xxx_set_dai_fmt,
+};
+
+#define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#define SRC4XXX_RATES (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 struct snd_soc_dai_driver src4xxx_dai_driver[] = {
+ {
+ .id = SRC4XXX_PORTA,
+ .name = "src4xxx-portA",
+ .playback = {
+ .stream_name = "Playback A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+ {
+ .id = SRC4XXX_PORTB,
+ .name = "src4xxx-portB",
+ .playback = {
+ .stream_name = "Playback B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+};
+
+static const struct reg_default src4xxx_reg_defaults[] = {
+ { SRC4XXX_PWR_RST_01, 0x00 }, /* all powered down intially */
+ { SRC4XXX_PORTA_CTL_03, 0x00 },
+ { SRC4XXX_PORTA_CTL_04, 0x00 },
+ { SRC4XXX_PORTB_CTL_05, 0x00 },
+ { SRC4XXX_PORTB_CTL_06, 0x00 },
+ { SRC4XXX_TX_CTL_07, 0x00 },
+ { SRC4XXX_TX_CTL_08, 0x00 },
+ { SRC4XXX_TX_CTL_09, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MSK_0B, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MODE_0C, 0x00 },
+ { SRC4XXX_RCV_CTL_0D, 0x00 },
+ { SRC4XXX_RCV_CTL_0E, 0x00 },
+ { SRC4XXX_RCV_PLL_0F, 0x00 }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_10, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_11, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RVC_IRQ_MSK_16, 0x00 },
+ { SRC4XXX_RVC_IRQ_MSK_17, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_18, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_19, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_1A, 0x00 },
+ { SRC4XXX_GPIO_1_1B, 0x00 },
+ { SRC4XXX_GPIO_2_1C, 0x00 },
+ { SRC4XXX_GPIO_3_1D, 0x00 },
+ { SRC4XXX_GPIO_4_1E, 0x00 },
+ { SRC4XXX_SCR_CTL_2D, 0x00 },
+ { SRC4XXX_SCR_CTL_2E, 0x00 },
+ { SRC4XXX_SCR_CTL_2F, 0x00 },
+ { SRC4XXX_SCR_CTL_30, 0x00 },
+ { SRC4XXX_SCR_CTL_31, 0x00 },
+};
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev))
+{
+ struct src4xxx *src4xxx;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ src4xxx = devm_kzalloc(dev, sizeof(*src4xxx), GFP_KERNEL);
+ if (!src4xxx)
+ return -ENOMEM;
+
+ src4xxx->regmap = regmap;
+ src4xxx->dev = dev;
+ src4xxx->mclk_hz = 0; /* mclk has not been configured yet */
+ dev_set_drvdata(dev, src4xxx);
+
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_RESET);
+ if (ret < 0)
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ usleep_range(1, 500); /* sleep for more then 500 ns */
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_POWER_DOWN);
+ if (ret < 0)
+ dev_err(dev, "Failed to decommission reset: %d\n", ret);
+ usleep_range(500, 1000); /* sleep for 500 us or more */
+
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_PWR_RST_01,
+ SRC4XXX_POWER_ENABLE, SRC4XXX_POWER_ENABLE);
+ if (ret < 0)
+ dev_err(dev, "Failed to port A and B : %d\n", ret);
+
+ /* set receiver to use master clock (rcv mclk is most likely jittery) */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0D,
+ SRC4XXX_RXCLK_MCLK, SRC4XXX_RXCLK_MCLK);
+ if (ret < 0)
+ dev_err(dev,
+ "Failed to enable mclk as the PLL1 DIR reference : %d\n", ret);
+
+ /* default to leaving the PLL2 running on loss of lock, divide by 8 */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0E,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL);
+ if (ret < 0)
+ dev_err(dev, "Failed to enable mclk rec and div : %d\n", ret);
+
+ ret = devm_snd_soc_register_component(dev, &src4xxx_driver,
+ src4xxx_dai_driver, ARRAY_SIZE(src4xxx_dai_driver));
+ if (ret == 0)
+ dev_info(dev, "src4392 probe ok %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(src4xxx_probe);
+
+static bool src4xxx_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SRC4XXX_RES_00:
+ case SRC4XXX_GLOBAL_ITR_STS_02:
+ case SRC4XXX_SRC_DIT_STS_0A:
+ case SRC4XXX_NON_AUDIO_D_12:
+ case SRC4XXX_RVC_STS_13:
+ case SRC4XXX_RVC_STS_14:
+ case SRC4XXX_RVC_STS_15:
+ case SRC4XXX_SUB_CODE_1F:
+ case SRC4XXX_SUB_CODE_20:
+ case SRC4XXX_SUB_CODE_21:
+ case SRC4XXX_SUB_CODE_22:
+ case SRC4XXX_SUB_CODE_23:
+ case SRC4XXX_SUB_CODE_24:
+ case SRC4XXX_SUB_CODE_25:
+ case SRC4XXX_SUB_CODE_26:
+ case SRC4XXX_SUB_CODE_27:
+ case SRC4XXX_SUB_CODE_28:
+ case SRC4XXX_PC_PREAMBLE_HI_29:
+ case SRC4XXX_PC_PREAMBLE_LO_2A:
+ case SRC4XXX_PD_PREAMBLE_HI_2B:
+ case SRC4XXX_PC_PREAMBLE_LO_2C:
+ case SRC4XXX_IO_RATIO_32:
+ case SRC4XXX_IO_RATIO_33:
+ return true;
+ }
+
+ if (reg > SRC4XXX_IO_RATIO_33 && reg < SRC4XXX_PAGE_SEL_7F)
+ return true;
+
+ return false;
+}
+
+const struct regmap_config src4xxx_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = SRC4XXX_IO_RATIO_33,
+
+ .reg_defaults = src4xxx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(src4xxx_reg_defaults),
+ .volatile_reg = src4xxx_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(src4xxx_regmap_config);
+
+MODULE_DESCRIPTION("ASoC SRC4XXX CODEC driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.h b/sound/soc/codecs/src4xxx.h
new file mode 100644
index 000000000000..5bf778fb9945
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.h
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// src4xxx.h -- SRC4XXX ALSA SoC audio driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt R Flax <flatmax@flatmax.com>
+
+#ifndef __SRC4XXX_H__
+#define __SRC4XXX_H__
+
+#define SRC4XXX_RES_00 0x00
+#define SRC4XXX_PWR_RST_01 0x01
+#define SRC4XXX_RESET 0x80
+#define SRC4XXX_POWER_DOWN 0x00
+#define SRC4XXX_POWER_ENABLE 0x20
+#define SRC4XXX_ENABLE_SRC 0x1
+#define SRC4XXX_ENABLE_SRC_SHIFT 0
+#define SRC4XXX_ENABLE_DIR 0x2
+#define SRC4XXX_ENABLE_DIR_SHIFT 1
+#define SRC4XXX_ENABLE_DIT 0x4
+#define SRC4XXX_ENABLE_DIT_SHIFT 2
+#define SRC4XXX_ENABLE_PORT_B 0x8
+#define SRC4XXX_ENABLE_PORT_B_SHIFT 3
+#define SRC4XXX_ENABLE_PORT_A 0x10
+#define SRC4XXX_ENABLE_PORT_A_SHIFT 4
+
+#define SRC4XXX_PORTA_CTL_03 0x03
+#define SRC4XXX_BUS_MASTER 0x8
+#define SRC4XXX_BUS_LEFT_J 0x0
+#define SRC4XXX_BUS_I2S 0x1
+#define SRC4XXX_BUS_RIGHT_J_16 0x4
+#define SRC4XXX_BUS_RIGHT_J_18 0x5
+#define SRC4XXX_BUS_RIGHT_J_20 0x6
+#define SRC4XXX_BUS_RIGHT_J_24 0x7
+#define SRC4XXX_BUS_FMT_MS_MASK 0xf
+
+#define SRC4XXX_PORTA_CTL_04 0x04
+#define SRC4XXX_MCLK_DIV_MASK 0x3
+
+#define SRC4XXX_BUS_FMT(id) (SRC4XXX_PORTA_CTL_03+2*id)
+#define SRC4XXX_BUS_CLK(id) (SRC4XXX_PORTA_CTL_04+2*id)
+
+#define SRC4XXX_PORTB_CTL_05 0x05
+#define SRC4XXX_PORTB_CTL_06 0x06
+
+#define SRC4XXX_TX_CTL_07 0x07
+#define SRC4XXX_TX_MCLK_DIV_MASK 0x60
+#define SRC4XXX_TX_MCLK_DIV_SHIFT 5
+
+#define SRC4XXX_TX_CTL_08 0x08
+#define SRC4XXX_TX_CTL_09 0x09
+#define SRC4XXX_SRC_DIT_IRQ_MSK_0B 0x0B
+#define SRC4XXX_SRC_BTI_EN 0x01
+#define SRC4XXX_SRC_TSLIP_EN 0x02
+#define SRC4XXX_SRC_DIT_IRQ_MODE_0C 0x0C
+#define SRC4XXX_RCV_CTL_0D 0x0D
+#define SRC4XXX_RXCLK_RXCKI 0x0
+#define SRC4XXX_RXCLK_MCLK 0x8
+#define SRC4XXX_RCV_CTL_0E 0x0E
+#define SRC4XXX_REC_MCLK_EN 0x1
+#define SRC4XXX_PLL2_DIV_0 (0x0<<1)
+#define SRC4XXX_PLL2_DIV_2 (0x1<<1)
+#define SRC4XXX_PLL2_DIV_4 (0x2<<1)
+#define SRC4XXX_PLL2_DIV_8 (0x3<<1)
+#define SRC4XXX_PLL2_LOL 0x8
+#define SRC4XXX_RCV_PLL_0F 0x0F
+#define SRC4XXX_RCV_PLL_10 0x10
+#define SRC4XXX_RCV_PLL_11 0x11
+#define SRC4XXX_RVC_IRQ_MSK_16 0x16
+#define SRC4XXX_RVC_IRQ_MSK_17 0x17
+#define SRC4XXX_RVC_IRQ_MODE_18 0x18
+#define SRC4XXX_RVC_IRQ_MODE_19 0x19
+#define SRC4XXX_RVC_IRQ_MODE_1A 0x1A
+#define SRC4XXX_GPIO_1_1B 0x1B
+#define SRC4XXX_GPIO_2_1C 0x1C
+#define SRC4XXX_GPIO_3_1D 0x1D
+#define SRC4XXX_GPIO_4_1E 0x1E
+#define SRC4XXX_SCR_CTL_2D 0x2D
+#define SRC4XXX_SCR_CTL_2E 0x2E
+#define SRC4XXX_SCR_CTL_2F 0x2F
+#define SRC4XXX_SCR_CTL_30 0x30
+#define SRC4XXX_SCR_CTL_31 0x31
+#define SRC4XXX_PAGE_SEL_7F 0x7F
+
+// read only registers
+#define SRC4XXX_GLOBAL_ITR_STS_02 0x02
+#define SRC4XXX_SRC_DIT_STS_0A 0x0A
+#define SRC4XXX_NON_AUDIO_D_12 0x12
+#define SRC4XXX_RVC_STS_13 0x13
+#define SRC4XXX_RVC_STS_14 0x14
+#define SRC4XXX_RVC_STS_15 0x15
+#define SRC4XXX_SUB_CODE_1F 0x1F
+#define SRC4XXX_SUB_CODE_20 0x20
+#define SRC4XXX_SUB_CODE_21 0x21
+#define SRC4XXX_SUB_CODE_22 0x22
+#define SRC4XXX_SUB_CODE_23 0x23
+#define SRC4XXX_SUB_CODE_24 0x24
+#define SRC4XXX_SUB_CODE_25 0x25
+#define SRC4XXX_SUB_CODE_26 0x26
+#define SRC4XXX_SUB_CODE_27 0x27
+#define SRC4XXX_SUB_CODE_28 0x28
+#define SRC4XXX_PC_PREAMBLE_HI_29 0x29
+#define SRC4XXX_PC_PREAMBLE_LO_2A 0x2A
+#define SRC4XXX_PD_PREAMBLE_HI_2B 0x2B
+#define SRC4XXX_PC_PREAMBLE_LO_2C 0x2C
+#define SRC4XXX_IO_RATIO_32 0x32
+#define SRC4XXX_IO_RATIO_33 0x33
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev));
+extern const struct regmap_config src4xxx_regmap_config;
+
+#endif /* __SRC4XXX_H__ */
diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c
new file mode 100644
index 000000000000..1d022643c307
--- /dev/null
+++ b/sound/soc/codecs/ssm2305.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices SSM2305 Amplifier Driver
+//
+// Copyright (C) 2018 Pengutronix, Marco Felsch <kernel@pengutronix.de>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "ssm2305"
+
+struct ssm2305 {
+ /* shutdown gpio */
+ struct gpio_desc *gpiod_shutdown;
+};
+
+static int ssm2305_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kctrl, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct ssm2305 *data = snd_soc_component_get_drvdata(c);
+
+ gpiod_set_value_cansleep(data->gpiod_shutdown,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ssm2305_dapm_widgets[] = {
+ /* Stereo input/output */
+ SND_SOC_DAPM_INPUT("L_IN"),
+ SND_SOC_DAPM_INPUT("R_IN"),
+ SND_SOC_DAPM_OUTPUT("L_OUT"),
+ SND_SOC_DAPM_OUTPUT("R_OUT"),
+
+ SND_SOC_DAPM_SUPPLY("Power", SND_SOC_NOPM, 0, 0, ssm2305_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route ssm2305_dapm_routes[] = {
+ { "L_OUT", NULL, "L_IN" },
+ { "R_OUT", NULL, "R_IN" },
+ { "L_IN", NULL, "Power" },
+ { "R_IN", NULL, "Power" },
+};
+
+static const struct snd_soc_component_driver ssm2305_component_driver = {
+ .dapm_widgets = ssm2305_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2305_dapm_widgets),
+ .dapm_routes = ssm2305_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm2305_dapm_routes),
+};
+
+static int ssm2305_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ssm2305 *priv;
+
+ /* Allocate the private data */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Get shutdown gpio */
+ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio\n");
+
+ return devm_snd_soc_register_component(dev, &ssm2305_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ssm2305_of_match[] = {
+ { .compatible = "adi,ssm2305", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm2305_of_match);
+#endif
+
+static struct platform_driver ssm2305_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(ssm2305_of_match),
+ },
+ .probe = ssm2305_probe,
+};
+
+module_platform_driver(ssm2305_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2305 amplifier driver");
+MODULE_AUTHOR("Marco Felsch <m.felsch@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 15486fd16269..06016e88dd27 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -1,20 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_data/ssm2518.h>
+#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -115,7 +113,7 @@ struct ssm2518 {
unsigned int sysclk;
const struct snd_pcm_hw_constraint_list *constraints;
- int enable_gpio;
+ struct gpio_desc *enable_gpio;
};
static const struct reg_default ssm2518_reg_defaults[] = {
@@ -336,8 +334,8 @@ static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
static int ssm2518_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 ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
unsigned int ctrl1, ctrl1_mask;
int mcs;
@@ -389,9 +387,9 @@ static int ssm2518_hw_params(struct snd_pcm_substream *substream,
SSM2518_POWER1_MCS_MASK, mcs << 1);
}
-static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -405,13 +403,13 @@ static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1 = 0, ctrl2 = 0;
bool invert_fclk;
int ret;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -484,8 +482,8 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
regcache_mark_dirty(ssm2518->regmap);
}
- if (gpio_is_valid(ssm2518->enable_gpio))
- gpio_set_value(ssm2518->enable_gpio, enable);
+ if (ssm2518->enable_gpio)
+ gpiod_set_value_cansleep(ssm2518->enable_gpio, enable);
regcache_cache_only(ssm2518->regmap, !enable);
@@ -498,10 +496,10 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
return ret;
}
-static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2518_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@@ -510,7 +508,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = ssm2518_set_power(ssm2518, true);
break;
case SND_SOC_BIAS_OFF:
@@ -524,7 +522,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1, ctrl2;
int left_slot, right_slot;
int ret;
@@ -609,7 +607,7 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ssm2518_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
if (ssm2518->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -624,9 +622,10 @@ static int ssm2518_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
- .digital_mute = ssm2518_mute,
+ .mute_stream = ssm2518_mute,
.set_fmt = ssm2518_set_dai_fmt,
.set_tdm_slot = ssm2518_set_tdm_slot,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm2518_dai = {
@@ -641,10 +640,10 @@ static struct snd_soc_dai_driver ssm2518_dai = {
.ops = &ssm2518_dai_ops,
};
-static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int ssm2518_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (clk_id != SSM2518_SYSCLK)
@@ -710,19 +709,17 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
SSM2518_POWER1_NO_BCLK, val);
}
-static const struct snd_soc_codec_driver ssm2518_codec_driver = {
- .set_bias_level = ssm2518_set_bias_level,
- .set_sysclk = ssm2518_set_sysclk,
- .idle_bias_off = true,
-
- .component_driver = {
- .controls = ssm2518_snd_controls,
- .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
- .dapm_widgets = ssm2518_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
- .dapm_routes = ssm2518_routes,
- .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
- },
+static const struct snd_soc_component_driver ssm2518_component_driver = {
+ .set_bias_level = ssm2518_set_bias_level,
+ .set_sysclk = ssm2518_set_sysclk,
+ .controls = ssm2518_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
+ .dapm_widgets = ssm2518_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
+ .dapm_routes = ssm2518_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ssm2518_regmap_config = {
@@ -736,10 +733,8 @@ static const struct regmap_config ssm2518_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
};
-static int ssm2518_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm2518_i2c_probe(struct i2c_client *i2c)
{
- struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
struct ssm2518 *ssm2518;
int ret;
@@ -747,22 +742,14 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ssm2518 == NULL)
return -ENOMEM;
- if (pdata) {
- ssm2518->enable_gpio = pdata->enable_gpio;
- } else if (i2c->dev.of_node) {
- ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
- if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
- return ssm2518->enable_gpio;
- } else {
- ssm2518->enable_gpio = -1;
- }
+ /* Start with enabling the chip */
+ ssm2518->enable_gpio = devm_gpiod_get_optional(&i2c->dev, NULL,
+ GPIOD_OUT_HIGH);
+ ret = PTR_ERR_OR_ZERO(ssm2518->enable_gpio);
+ if (ret)
+ return ret;
- if (gpio_is_valid(ssm2518->enable_gpio)) {
- ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
- GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
- if (ret)
- return ret;
- }
+ gpiod_set_consumer_name(ssm2518->enable_gpio, "SSM2518 nSD");
i2c_set_clientdata(i2c, ssm2518);
@@ -792,16 +779,11 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &ssm2518_component_driver,
&ssm2518_dai, 1);
}
-static int ssm2518_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id ssm2518_dt_ids[] = {
{ .compatible = "adi,ssm2518", },
@@ -811,7 +793,7 @@ MODULE_DEVICE_TABLE(of, ssm2518_dt_ids);
#endif
static const struct i2c_device_id ssm2518_i2c_ids[] = {
- { "ssm2518", 0 },
+ { "ssm2518" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
@@ -822,7 +804,6 @@ static struct i2c_driver ssm2518_driver = {
.of_match_table = of_match_ptr(ssm2518_dt_ids),
},
.probe = ssm2518_i2c_probe,
- .remove = ssm2518_i2c_remove,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h
index 62511d80518e..273fd0977470 100644
--- a/sound/soc/codecs/ssm2518.h
+++ b/sound/soc/codecs/ssm2518.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SND_SOC_CODECS_SSM2518_H__
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
index 173ba85ff59e..49c74cba17c7 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602/SSM2603/SSM2604 I2C audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,19 +19,12 @@
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ssm2602_i2c_probe(struct i2c_client *client)
{
- return ssm2602_probe(&client->dev, id->driver_data,
+ return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client),
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
}
-static int ssm2602_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ssm2602_i2c_id[] = {
{ "ssm2602", SSM2602 },
{ "ssm2603", SSM2602 },
@@ -55,7 +47,6 @@ static struct i2c_driver ssm2602_i2c_driver = {
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_i2c_probe,
- .remove = ssm2602_i2c_remove,
.id_table = ssm2602_i2c_id,
};
module_i2c_driver(ssm2602_i2c_driver);
diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c
index 842f373045c6..bb49fb6b62ad 100644
--- a/sound/soc/codecs/ssm2602-spi.c
+++ b/sound/soc/codecs/ssm2602-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602 SPI audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,12 +19,6 @@ static int ssm2602_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &ssm2602_regmap_config));
}
-static int ssm2602_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ssm2602_of_match[] = {
{ .compatible = "adi,ssm2602", },
{ }
@@ -38,7 +31,6 @@ static struct spi_driver ssm2602_spi_driver = {
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_spi_probe,
- .remove = ssm2602_spi_remove,
};
module_spi_driver(ssm2602_spi_driver);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 9b341c23f62b..c29324403e9d 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -1,31 +1,17 @@
-/*
- * File: sound/soc/codecs/ssm2602.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: Driver for ssm2602 sound chip
- *
- * Modified:
- * Copyright 2008 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// File: sound/soc/codecs/ssm2602.c
+// Author: Cliff Cai <Cliff.Cai@analog.com>
+//
+// Created: Tue June 06 2008
+// Description: Driver for ssm2602 sound chip
+//
+// Modified:
+// Copyright 2008 Analog Devices Inc.
+//
+// Bugs: Enter bugs at http://blackfin.uclinux.org/
+
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -54,10 +40,29 @@ struct ssm2602_priv {
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
-static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = {
+ { .reg = 0x00, .def = 0x0097 },
+ { .reg = 0x01, .def = 0x0097 },
+ { .reg = 0x02, .def = 0x0079 },
+ { .reg = 0x03, .def = 0x0079 },
+ { .reg = 0x04, .def = 0x000a },
+ { .reg = 0x05, .def = 0x0008 },
+ { .reg = 0x06, .def = 0x009f },
+ { .reg = 0x07, .def = 0x000a },
+ { .reg = 0x08, .def = 0x0000 },
+ { .reg = 0x09, .def = 0x0000 }
+};
+
+/*
+ * ssm2602 register patch
+ * Workaround for playback distortions after power up: activates digital
+ * core, and then powers on output, DAC, and whole chip at the same time
+ */
+
+static const struct reg_sequence ssm2602_patch[] = {
+ { SSM2602_ACTIVE, 0x01 },
+ { SSM2602_PWR, 0x07 },
+ { SSM2602_RESET, 0x00 },
};
@@ -104,7 +109,6 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */
@@ -114,10 +118,31 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
+static const struct snd_kcontrol_new mic_ctl =
+ SOC_DAPM_SINGLE("Switch", SSM2602_APANA, 1, 1, 1);
+
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /*
+ * According to the ssm2603 data sheet (control register sequencing),
+ * the digital core should be activated only after all necessary bits
+ * in the power register are enabled, and a delay determined by the
+ * decoupling capacitor on the VMID pin has passed. If the digital core
+ * is activated too early, or even before the ADC is powered up, audible
+ * artifacts appear at the beginning and end of the recorded signal.
+ *
+ * In practice, audible artifacts disappear well over 500 ms.
+ */
+ msleep(500);
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
@@ -139,6 +164,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+SND_SOC_DAPM_SWITCH_E("Mic Switch", SSM2602_APANA, 1, 1, &mic_ctl,
+ ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU),
+
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
@@ -171,9 +199,11 @@ static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"},
- {"Input Mux", "Mic", "Mic Bias"},
+ {"Input Mux", "Mic", "Mic Switch"},
{"ADC", NULL, "Input Mux"},
+ {"Mic Switch", NULL, "Mic Bias"},
+
{"Mic Bias", NULL, "MICIN"},
};
@@ -262,9 +292,12 @@ static inline int ssm2602_get_coeff(int mclk, int rate)
int i;
for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
- if (ssm2602_coeff_table[i].rate == rate &&
- ssm2602_coeff_table[i].mclk == mclk)
- return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].rate == rate) {
+ if (ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].mclk == mclk / 2)
+ return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2;
+ }
}
return -EINVAL;
}
@@ -273,8 +306,8 @@ static int ssm2602_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 ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
unsigned int iface;
@@ -308,8 +341,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
static int ssm2602_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
if (ssm2602->sysclk_constraints) {
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -320,9 +353,9 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
@@ -337,8 +370,8 @@ static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
if (dir == SND_SOC_CLOCK_IN) {
if (clk_id != SSM2602_SYSCLK)
@@ -347,18 +380,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (freq) {
case 12288000:
case 18432000:
+ case 24576000:
+ case 36864000:
ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
break;
case 11289600:
case 16934400:
+ case 22579200:
+ case 33868800:
ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
break;
case 12000000:
+ case 24000000:
ssm2602->sysclk_constraints = NULL;
break;
default:
return -EINVAL;
}
+
ssm2602->sysclk = freq;
} else {
unsigned int mask;
@@ -389,15 +428,15 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(codec_dai->component);
unsigned int iface = 0;
/* set master/slave audio interface */
- 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:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -445,10 +484,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2602_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -487,9 +526,10 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
- .digital_mute = ssm2602_mute,
+ .mute_stream = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm2602_dai = {
@@ -507,23 +547,23 @@ static struct snd_soc_dai_driver ssm2602_dai = {
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = &ssm2602_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
-static int ssm2602_resume(struct snd_soc_codec *codec)
+static int ssm2602_resume(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
regcache_sync(ssm2602->regmap);
return 0;
}
-static int ssm2602_codec_probe(struct snd_soc_codec *codec)
+static int ssm2602_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int ret;
regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
@@ -531,7 +571,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
- ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
+ ret = snd_soc_add_component_controls(component, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
if (ret)
return ret;
@@ -545,9 +585,9 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2602_routes));
}
-static int ssm2604_codec_probe(struct snd_soc_codec *codec)
+static int ssm2604_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
@@ -559,17 +599,20 @@ static int ssm2604_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2604_routes));
}
-static int ssm260x_codec_probe(struct snd_soc_codec *codec)
+static int ssm260x_component_probe(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int ret;
ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
+ regmap_register_patch(ssm2602->regmap, ssm2602_patch,
+ ARRAY_SIZE(ssm2602_patch));
+
/* set the update bits */
regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
@@ -581,30 +624,30 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec)
switch (ssm2602->type) {
case SSM2602:
- ret = ssm2602_codec_probe(codec);
+ ret = ssm2602_component_probe(component);
break;
case SSM2604:
- ret = ssm2604_codec_probe(codec);
+ ret = ssm2604_component_probe(component);
break;
}
return ret;
}
-static const struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm260x_codec_probe,
- .resume = ssm2602_resume,
- .set_bias_level = ssm2602_set_bias_level,
- .suspend_bias_off = true,
-
- .component_driver = {
- .controls = ssm260x_snd_controls,
- .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
- .dapm_widgets = ssm260x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
- .dapm_routes = ssm260x_routes,
- .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
- },
+static const struct snd_soc_component_driver soc_component_dev_ssm2602 = {
+ .probe = ssm260x_component_probe,
+ .resume = ssm2602_resume,
+ .set_bias_level = ssm2602_set_bias_level,
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
@@ -620,8 +663,8 @@ const struct regmap_config ssm2602_regmap_config = {
.volatile_reg = ssm2602_register_volatile,
.cache_type = REGCACHE_RBTREE,
- .reg_defaults_raw = ssm2602_reg,
- .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+ .reg_defaults = ssm2602_reg,
+ .num_reg_defaults = ARRAY_SIZE(ssm2602_reg),
};
EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
@@ -641,7 +684,7 @@ int ssm2602_probe(struct device *dev, enum ssm2602_type type,
ssm2602->type = type;
ssm2602->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ssm2602,
&ssm2602_dai, 1);
}
EXPORT_SYMBOL_GPL(ssm2602_probe);
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index 747538847689..05073380a4af 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codecs/ssm2602.h
* Author: Cliff Cai <Cliff.Cai@analog.com>
@@ -8,21 +9,6 @@
* Copyright 2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SSM2602_H
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
new file mode 100644
index 000000000000..8c6665677a17
--- /dev/null
+++ b/sound/soc/codecs/ssm3515.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+//
+// Analog Devices' SSM3515 audio amp driver
+//
+// Copyright (C) The Asahi Linux Contributors
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+
+#define SSM3515_PWR 0x00
+#define SSM3515_PWR_APWDN_EN BIT(7)
+#define SSM3515_PWR_BSNS_PWDN BIT(6)
+#define SSM3515_PWR_S_RST BIT(1)
+#define SSM3515_PWR_SPWDN BIT(0)
+
+#define SSM3515_GEC 0x01
+#define SSM3515_GEC_EDGE BIT(4)
+#define SSM3515_GEC_EDGE_SHIFT 4
+#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0)
+
+#define SSM3515_DAC 0x02
+#define SSM3515_DAC_HV BIT(7)
+#define SSM3515_DAC_MUTE BIT(6)
+#define SSM3515_DAC_HPF BIT(5)
+#define SSM3515_DAC_LPM BIT(4)
+#define SSM3515_DAC_FS GENMASK(2, 0)
+
+#define SSM3515_DAC_VOL 0x03
+
+#define SSM3515_SAI1 0x04
+#define SSM3515_SAI1_DAC_POL BIT(7)
+#define SSM3515_SAI1_BCLK_POL BIT(6)
+#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3)
+#define SSM3515_SAI1_FSYNC_MODE BIT(2)
+#define SSM3515_SAI1_SDATA_FMT BIT(1)
+#define SSM3515_SAI1_SAI_MODE BIT(0)
+
+#define SSM3515_SAI2 0x05
+#define SSM3515_SAI2_DATA_WIDTH BIT(7)
+#define SSM3515_SAI2_AUTO_SLOT BIT(4)
+#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0)
+
+#define SSM3515_VBAT_OUT 0x06
+
+#define SSM3515_STATUS 0x0a
+#define SSM3515_STATUS_UVLO_REG BIT(6)
+#define SSM3515_STATUS_LIM_EG BIT(5)
+#define SSM3515_STATUS_CLIP BIT(4)
+#define SSM3515_STATUS_AMP_OC BIT(3)
+#define SSM3515_STATUS_OTF BIT(2)
+#define SSM3515_STATUS_OTW BIT(1)
+#define SSM3515_STATUS_BAT_WARN BIT(0)
+
+static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SSM3515_STATUS:
+ case SSM3515_VBAT_OUT:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default ssm3515_reg_defaults[] = {
+ { SSM3515_PWR, 0x81 },
+ { SSM3515_GEC, 0x01 },
+ { SSM3515_DAC, 0x32 },
+ { SSM3515_DAC_VOL, 0x40 },
+ { SSM3515_SAI1, 0x11 },
+ { SSM3515_SAI2, 0x00 },
+};
+
+static const struct regmap_config ssm3515_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = ssm3515_volatile_reg,
+ .max_register = 0xb,
+ .reg_defaults = ssm3515_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+struct ssm3515_data {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+// The specced range is -71.25...24.00 dB with step size of 0.375 dB,
+// and a mute item below that. This is represented by -71.62...24.00 dB
+// with the mute item mapped onto the low end.
+static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400);
+
+static const char * const ssm3515_ana_gain_text[] = {
+ "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span",
+};
+
+static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC,
+ __bf_shf(SSM3515_GEC_ANA_GAIN),
+ ssm3515_ana_gain_text);
+
+static const struct snd_kcontrol_new ssm3515_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL,
+ 0, 255, 1, ssm3515_dac_volume),
+ SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC,
+ __bf_shf(SSM3515_GEC_EDGE), 1, 0),
+ SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC,
+ __bf_shf(SSM3515_DAC_HV), 1, 1),
+ SOC_SINGLE("HPF Switch", SSM3515_DAC,
+ __bf_shf(SSM3515_DAC_HPF), 1, 0),
+ SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1,
+ __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0),
+ SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum),
+};
+
+static void ssm3515_read_faults(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_read(component, SSM3515_STATUS);
+ if (ret <= 0) {
+ /*
+ * If the read was erroneous, ASoC core has printed a message,
+ * and that's all that's appropriate in handling the error here.
+ */
+ return;
+ }
+
+ dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n",
+ FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "",
+ FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "",
+ FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "",
+ FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "",
+ FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "",
+ FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "",
+ FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : "");
+}
+
+static int ssm3515_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ /* Start out muted */
+ ret = snd_soc_component_update_bits(component, SSM3515_DAC,
+ SSM3515_DAC_MUTE, SSM3515_DAC_MUTE);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the 'master power-down' */
+ ret = snd_soc_component_update_bits(component, SSM3515_PWR,
+ SSM3515_PWR_SPWDN, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(dai->component,
+ SSM3515_DAC,
+ SSM3515_DAC_MUTE,
+ FIELD_PREP(SSM3515_DAC_MUTE, mute));
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int ssm3515_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, rateval;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16:
+ case SNDRV_PCM_FORMAT_S24:
+ ret = snd_soc_component_update_bits(component,
+ SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH,
+ FIELD_PREP(SSM3515_SAI2_DATA_WIDTH,
+ params_width(params) == 16));
+ if (ret < 0)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000 ... 12000:
+ rateval = 0;
+ break;
+ case 16000 ... 24000:
+ rateval = 1;
+ break;
+ case 32000 ... 48000:
+ rateval = 2;
+ break;
+ case 64000 ... 96000:
+ rateval = 3;
+ break;
+ case 128000 ... 192000:
+ rateval = 4;
+ break;
+ case 48001 ... 63999: /* this is ...72000 but overlaps */
+ rateval = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ SSM3515_DAC, SSM3515_DAC_FS,
+ FIELD_PREP(SSM3515_DAC_FS, rateval));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */
+ int ret;
+ u8 sai1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ sai1 |= SSM3515_SAI1_BCLK_POL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fpol_inv = 1;
+ sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fpol_inv = 0;
+ sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ fpol_inv ^= 1;
+ break;
+ }
+
+ /* Set the serial input to 'TDM mode' */
+ sai1 |= SSM3515_SAI1_SAI_MODE;
+
+ if (fpol_inv) {
+ /*
+ * We configure the codec in a 'TDM mode', in which the
+ * FSYNC_MODE bit of SAI1 is supposed to select between
+ * what the datasheet calls 'Pulsed FSYNC mode' and '50%
+ * FSYNC mode'.
+ *
+ * Experiments suggest that this bit in fact simply selects
+ * the FSYNC polarity, so go with that.
+ */
+ sai1 |= SSM3515_SAI1_FSYNC_MODE;
+ }
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI1,
+ SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT |
+ SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int ssm3515_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;
+ int slot, tdm_bclks_val, ret;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ slot = __ffs(tx_mask);
+
+ if (tx_mask & ~BIT(slot))
+ return -EINVAL;
+
+ switch (slot_width) {
+ case 16:
+ tdm_bclks_val = 0;
+ break;
+ case 24:
+ tdm_bclks_val = 1;
+ break;
+ case 32:
+ tdm_bclks_val = 2;
+ break;
+ case 48:
+ tdm_bclks_val = 3;
+ break;
+ case 64:
+ tdm_bclks_val = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI1,
+ SSM3515_SAI1_TDM_BCLKS,
+ FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val));
+ if (ret